From 56093d4e2e5cc0cc860e79bbbd9258e5d2e2ada2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 16 May 2010 14:17:36 -0700 Subject: [PATCH 001/345] kernel: Bring up to date with Marvell's patches Marvell keeps their own "stock" kernel that's diverged from linux-next in several ways. This patch brings linux-next into line with Marvell's changes. --- .config | 1579 + Documentation/android.txt | 121 + Makefile | 2 +- arch/arm/Kconfig | 99 +- arch/arm/Makefile | 5 + arch/arm/boot/compressed/head.S | 17 + arch/arm/configs/msm_defconfig | 2 + arch/arm/configs/pxa168_android_mlc_defconfig | 2116 ++ arch/arm/configs/pxa168_android_mmc_defconfig | 2049 ++ .../configs/pxa168_avengers_lite_defconfig | 2115 ++ arch/arm/configs/pxa168_defconfig | 703 +- arch/arm/include/asm/dma-mapping.h | 6 +- arch/arm/include/asm/dma.h | 6 + arch/arm/include/asm/elf.h | 4 + arch/arm/include/asm/ftrace.h | 10 + arch/arm/include/asm/memory.h | 2 +- arch/arm/kernel/entry-common.S | 19 +- arch/arm/kernel/ftrace.c | 50 +- arch/arm/kernel/signal.c | 9 + arch/arm/mach-mmp/Kconfig | 76 +- arch/arm/mach-mmp/Makefile | 22 +- arch/arm/mach-mmp/aspenite.c | 1619 +- arch/arm/mach-mmp/avengers_lite.c | 1160 +- arch/arm/mach-mmp/clock.c | 242 +- arch/arm/mach-mmp/clock.h | 65 +- arch/arm/mach-mmp/common.c | 29 + arch/arm/mach-mmp/common.h | 6 + arch/arm/mach-mmp/devices.c | 553 +- arch/arm/mach-mmp/dkb_generic.c | 475 + arch/arm/mach-mmp/dvfm.c | 749 + arch/arm/mach-mmp/dvfm_stats.c | 191 + arch/arm/mach-mmp/edge.c | 827 + arch/arm/mach-mmp/include/mach/camera.h | 25 + arch/arm/mach-mmp/include/mach/clock.h | 30 + arch/arm/mach-mmp/include/mach/cm3602.h | 12 + arch/arm/mach-mmp/include/mach/cputype.h | 90 +- arch/arm/mach-mmp/include/mach/devices.h | 89 +- arch/arm/mach-mmp/include/mach/dvfm.h | 174 + arch/arm/mach-mmp/include/mach/gpio_ec.h | 9 + arch/arm/mach-mmp/include/mach/gpio_ir.h | 69 + arch/arm/mach-mmp/include/mach/hardware.h | 5 + arch/arm/mach-mmp/include/mach/ir_key_def.h | 66 + arch/arm/mach-mmp/include/mach/irqs.h | 41 +- arch/arm/mach-mmp/include/mach/max8660.h | 160 + arch/arm/mach-mmp/include/mach/memory.h | 5 + arch/arm/mach-mmp/include/mach/mfp-pxa168.h | 170 +- arch/arm/mach-mmp/include/mach/mfp-pxa910.h | 99 +- .../arm/mach-mmp/include/mach/mfp-teton_bga.h | 31 + arch/arm/mach-mmp/include/mach/mfp.h | 12 + arch/arm/mach-mmp/include/mach/micco.h | 462 + arch/arm/mach-mmp/include/mach/mmc.h | 69 + arch/arm/mach-mmp/include/mach/mspm_prof.h | 34 + .../mach-mmp/include/mach/nand_supported.h | 398 + arch/arm/mach-mmp/include/mach/ov529.h | 21 + arch/arm/mach-mmp/include/mach/portofino.h | 25 + arch/arm/mach-mmp/include/mach/power_button.h | 10 + arch/arm/mach-mmp/include/mach/power_mcu.h | 193 + arch/arm/mach-mmp/include/mach/pxa168.h | 212 +- arch/arm/mach-mmp/include/mach/pxa168_dvfm.h | 211 + arch/arm/mach-mmp/include/mach/pxa168_eth.h | 25 + arch/arm/mach-mmp/include/mach/pxa168_pcie.h | 20 + arch/arm/mach-mmp/include/mach/pxa168_pm.h | 382 + arch/arm/mach-mmp/include/mach/pxa168fb.h | 412 + arch/arm/mach-mmp/include/mach/pxa3xx_bbm.h | 130 + arch/arm/mach-mmp/include/mach/pxa3xx_nand.h | 82 + arch/arm/mach-mmp/include/mach/pxa910-squ.h | 114 + arch/arm/mach-mmp/include/mach/pxa910.h | 68 +- arch/arm/mach-mmp/include/mach/pxa910_dvfm.h | 282 + arch/arm/mach-mmp/include/mach/pxa910_ire.h | 108 + arch/arm/mach-mmp/include/mach/pxa910_pm.h | 16 + arch/arm/mach-mmp/include/mach/pxa930_acipc.h | 136 + arch/arm/mach-mmp/include/mach/regs-apbc.h | 73 +- arch/arm/mach-mmp/include/mach/regs-apmu.h | 57 +- arch/arm/mach-mmp/include/mach/regs-ciu.h | 51 + arch/arm/mach-mmp/include/mach/regs-icu.h | 21 +- arch/arm/mach-mmp/include/mach/regs-mpmu.h | 46 + arch/arm/mach-mmp/include/mach/regs-pcie.h | 195 + arch/arm/mach-mmp/include/mach/regs-rtc.h | 24 + arch/arm/mach-mmp/include/mach/regs-smc.h | 37 + arch/arm/mach-mmp/include/mach/regs-ssp.h | 1 + arch/arm/mach-mmp/include/mach/regs-timers.h | 1 + arch/arm/mach-mmp/include/mach/resource.h | 15 + arch/arm/mach-mmp/include/mach/rtc.h | 11 + arch/arm/mach-mmp/include/mach/sanremo.h | 512 + arch/arm/mach-mmp/include/mach/ssp.h | 1 + arch/arm/mach-mmp/include/mach/system.h | 42 +- .../mach-mmp/include/mach/timer_services.h | 114 + arch/arm/mach-mmp/include/mach/timex.h | 4 + arch/arm/mach-mmp/include/mach/uncompress.h | 23 +- arch/arm/mach-mmp/io.c | 45 + arch/arm/mach-mmp/ipcam.c | 516 + arch/arm/mach-mmp/irq.c | 55 + arch/arm/mach-mmp/mmc.c | 89 + arch/arm/mach-mmp/pxa168.c | 323 +- arch/arm/mach-mmp/pxa168_dfc_ll.S | 378 + arch/arm/mach-mmp/pxa168_dvfm.c | 1441 + arch/arm/mach-mmp/pxa168_dvfm_stats.c | 660 + arch/arm/mach-mmp/pxa168_mspm_idle.c | 361 + arch/arm/mach-mmp/pxa168_mspm_prof.c | 437 + arch/arm/mach-mmp/pxa168_pcie.c | 671 + arch/arm/mach-mmp/pxa168_pm.c | 1291 + arch/arm/mach-mmp/pxa168_pm_ll.S | 892 + arch/arm/mach-mmp/pxa910-squ.c | 144 + arch/arm/mach-mmp/pxa910.c | 229 +- arch/arm/mach-mmp/pxa910_dvfm.c | 953 + arch/arm/mach-mmp/pxa910_mspm_idle.c | 339 + arch/arm/mach-mmp/pxa910_mspm_prof.c | 415 + arch/arm/mach-mmp/pxa910_pm.c | 426 + arch/arm/mach-mmp/pxa910_pm_ll.S | 13 + arch/arm/mach-mmp/resource/Makefile | 5 + arch/arm/mach-mmp/resource/misc.c | 46 + arch/arm/mach-mmp/tavorevb.c | 701 + arch/arm/mach-mmp/teton_bga.c | 704 + arch/arm/mach-mmp/time.c | 211 +- arch/arm/mach-mmp/timer_services.c | 503 + arch/arm/mach-mmp/ttc_dkb.c | 1487 + arch/arm/mach-pxa/corgi.c | 2 +- arch/arm/mach-pxa/include/mach/pm.h | 3 + arch/arm/mach-pxa/include/mach/pxa-regs.h | 191 + arch/arm/mach-pxa/include/mach/pxa3xx-regs.h | 9 + arch/arm/mach-pxa/lubbock.c | 2 +- arch/arm/mach-pxa/mainstone.c | 2 +- arch/arm/mach-pxa/palmtx.c | 2 +- arch/arm/mach-pxa/palmz72.c | 2 +- arch/arm/mach-pxa/pm.c | 3 + arch/arm/mach-pxa/poodle.c | 2 +- arch/arm/mach-pxa/spitz.c | 2 +- arch/arm/mach-pxa/tavorevb.c | 2 +- arch/arm/mm/Kconfig | 20 + arch/arm/mm/cache-v6.S | 17 + arch/arm/mm/proc-mohawk.S | 319 +- arch/arm/plat-pxa/Kconfig | 2 + arch/arm/plat-pxa/Makefile | 12 +- arch/arm/plat-pxa/dma.c | 283 +- arch/arm/plat-pxa/generic.c | 150 + arch/arm/plat-pxa/imm/Kconfig | 44 + arch/arm/plat-pxa/imm/Makefile | 19 + arch/arm/plat-pxa/imm/imm.h | 160 + arch/arm/plat-pxa/imm/ioremap.c | 104 + arch/arm/plat-pxa/imm/policy.c | 620 + arch/arm/plat-pxa/imm/sram.c | 935 + arch/arm/plat-pxa/imm/sram.h | 70 + arch/arm/plat-pxa/include/plat/dma.h | 2 + arch/arm/plat-pxa/include/plat/generic.h | 10 + arch/arm/plat-pxa/include/plat/i2c.h | 2 + arch/arm/plat-pxa/include/plat/imm.h | 225 + arch/arm/plat-pxa/include/plat/mfp.h | 76 +- arch/arm/plat-pxa/include/plat/part_table.h | 738 + arch/arm/plat-pxa/include/plat/pfn_cfg.h | 71 + .../include/plat}/pxa27x_keypad.h | 0 .../include/plat}/pxa2xx_spi.h | 0 arch/arm/plat-pxa/include/plat/pxa3xx_otg.h | 172 + arch/arm/plat-pxa/include/plat/pxa3xx_pmic.h | 161 + arch/arm/plat-pxa/include/plat/pxa_u2o.h | 197 + .../arm/plat-pxa/include/plat/pxausb_common.h | 20 + arch/arm/plat-pxa/include/plat/pxausb_comp.h | 114 + arch/arm/plat-pxa/include/plat/regs-ssp.h | 129 + arch/arm/plat-pxa/include/plat/ssp.h | 109 + arch/arm/plat-pxa/mfp.c | 32 +- arch/arm/plat-pxa/pmem.c | 65 + arch/arm/plat-pxa/pwm.c | 165 +- arch/arm/plat-pxa/pxa3xx_pmic.c | 298 + arch/arm/{mach-pxa => plat-pxa}/ssp.c | 120 +- arch/arm/tools/mach-types | 13 +- block/blk-core.c | 5 +- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/ata/libata-core.c | 2 + drivers/ata/libata.h | 25 + drivers/ata/pata_pcmcia.c | 34 + drivers/block/Kconfig | 6 + drivers/char/Kconfig | 37 + drivers/char/Makefile | 7 + drivers/char/dcc_tty.c | 326 + drivers/char/icr/Kconfig | 10 + drivers/char/icr/Makefile | 5 + drivers/char/icr/pxa168_ICRioctlTable.h | 368 + drivers/char/icr/pxa168_icr.c | 909 + drivers/char/icr/pxa168_icr.h | 424 + drivers/char/imm.c | 202 + drivers/char/mem.c | 15 + drivers/char/pxa-cnm.c | 460 + drivers/char/pxa910_ire.c | 1096 + drivers/char/pxa930_acipc.c | 653 + drivers/cpufreq/Kconfig | 13 + drivers/cpufreq/cpufreq_ondemand.c | 4 +- drivers/gpio/Kconfig | 41 + drivers/gpio/Makefile | 3 +- drivers/gpio/gpiolib.c | 4 +- drivers/gpio/max8660.c | 472 + drivers/gpio/pca953x.c | 460 +- drivers/gpio/pca9575.c | 522 + drivers/hwmon/Kconfig | 30 + drivers/hwmon/Makefile | 4 + drivers/hwmon/bma020/Makefile | 12 + drivers/hwmon/bma020/bma020.c | 1959 ++ drivers/hwmon/bma020/bma020_fs.c | 1471 + drivers/hwmon/bma020/bma020_fs.h | 710 + drivers/hwmon/cm3602.c | 117 + drivers/i2c/busses/i2c-pxa.c | 143 +- drivers/i2c/chips/Kconfig | 156 + drivers/i2c/chips/Makefile | 32 + drivers/i2c/chips/gpio_ec.c | 47 + drivers/i2c/chips/micco.c | 1863 ++ drivers/i2c/chips/pca963x.c | 431 + drivers/i2c/chips/portofino.c | 236 + drivers/i2c/chips/sanremo.c | 1414 + drivers/input/Kconfig | 16 + drivers/input/Makefile | 2 + drivers/input/evbug.c | 8 +- drivers/input/evdev.c | 15 +- drivers/input/gpio_ir.c | 497 + drivers/input/keyboard/Kconfig | 2 +- drivers/input/keyboard/pxa27x_keypad.c | 358 +- drivers/input/keyreset.c | 229 + drivers/input/misc/Kconfig | 26 + drivers/input/misc/Makefile | 4 + drivers/input/misc/gpio_axis.c | 180 + drivers/input/misc/gpio_event.c | 224 + drivers/input/misc/gpio_input.c | 343 + drivers/input/misc/gpio_matrix.c | 406 + drivers/input/misc/gpio_output.c | 84 + drivers/input/misc/keychord.c | 386 + drivers/input/misc/power_button.c | 260 + drivers/input/misc/sensors.c | 624 + drivers/input/touchscreen/Kconfig | 47 +- drivers/input/touchscreen/Makefile | 5 + drivers/input/touchscreen/micco_touch.c | 599 + drivers/input/touchscreen/sanremo_touch.c | 251 + drivers/input/touchscreen/synaptics_i2c_rmi.c | 632 + drivers/input/touchscreen/tpo_touch.c | 335 + drivers/input/touchscreen/ts_linear.c | 153 + drivers/input/touchscreen/tsc2007.c | 602 +- drivers/leds/Kconfig | 6 + drivers/leds/Makefile | 1 + drivers/leds/ledtrig-sleep.c | 80 + drivers/media/radio/Kconfig | 31 + drivers/media/radio/Makefile | 3 +- drivers/media/radio/radio-si4703.c | 778 + drivers/media/video/Kconfig | 55 + drivers/media/video/Makefile | 12 +- drivers/media/video/cli5001.c | 1036 + drivers/media/video/cli5001.h | 3078 ++ drivers/media/video/cmmb/Kconfig | 24 + drivers/media/video/cmmb/Makefile | 5 + drivers/media/video/cmmb/ca.c | 409 + drivers/media/video/cmmb/ca.h | 110 + drivers/media/video/cmmb/if101.c | 692 + drivers/media/video/ov3640.c | 1894 ++ drivers/media/video/ov529_drv.c | 1076 + drivers/media/video/ov529_drv.h | 88 + drivers/media/video/ov529_hw_ops.h | 73 + drivers/media/video/ov529_smc.c | 316 + drivers/media/video/ov7660.c | 1491 + drivers/media/video/ov7670_pxa.c | 1539 + drivers/media/video/ov7740.c | 2813 ++ drivers/media/video/pxa168_camera.c | 2590 ++ drivers/media/video/pxa168_camera.h | 145 + drivers/media/video/pxa_camera.c | 2 + drivers/media/video/pxa_camera.h | 95 + drivers/media/video/siv120a.c | 1208 + drivers/misc/Kconfig | 47 + drivers/misc/Makefile | 13 + drivers/misc/apanic.c | 606 + drivers/misc/kernel_debugger.c | 79 + drivers/misc/pmem.c | 1345 + drivers/misc/sd8x_rfkill.c | 322 + drivers/misc/uid_stat.c | 153 + drivers/misc/wl127x-rfkill.c | 121 + drivers/mmc/card/Kconfig | 7 + drivers/mmc/card/block.c | 28 +- drivers/mmc/core/Kconfig | 17 + drivers/mmc/core/core.c | 5 + drivers/mmc/core/mmc.c | 63 +- drivers/mmc/core/mmc_ops.c | 119 + drivers/mmc/core/mmc_ops.h | 1 + drivers/mmc/core/sd.c | 2 +- drivers/mmc/core/sd_ops.c | 7 +- drivers/mmc/core/sdio_bus.c | 2 +- drivers/mmc/core/sdio_io.c | 81 +- drivers/mmc/host/Kconfig | 22 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/pxa_sdh.c | 1170 + drivers/mmc/host/pxa_sdh.h | 195 + drivers/mmc/host/sdhci-pxa168.c | 763 + drivers/mmc/host/sdhci.c | 199 +- drivers/mtd/Kconfig | 7 + drivers/mtd/Makefile | 1 + drivers/mtd/devices/m25p80.c | 36 + drivers/mtd/nand/Kconfig | 33 + drivers/mtd/nand/nand_base.c | 32 +- drivers/mtd/nand/pxa3xx_nand.c | 2554 +- drivers/mtd/onenand/generic.c | 53 +- drivers/mtd/onenand/onenand_base.c | 96 +- drivers/mtd/onenand/onenand_bbt.c | 5 +- drivers/mtd/pxa3xx_bbm.c | 1371 + drivers/mtd/tests/mtd_speedtest.c | 7 + drivers/net/Kconfig | 23 + drivers/net/Makefile | 3 + drivers/net/pppolac.c | 380 + drivers/net/pppopns.c | 357 + drivers/net/pxa168_eth.c | 2186 ++ drivers/net/sky2.c | 68 +- drivers/net/sky2.h | 33 + drivers/net/wireless/Kconfig | 189 + drivers/pcmcia/Kconfig | 16 + drivers/pcmcia/Makefile | 4 + drivers/pcmcia/cistpl.c | 78 +- drivers/pcmcia/pxa168_cf.c | 1079 + drivers/pcmcia/pxa168_cf.h | 227 + drivers/pcmcia/pxa2xx_base.c | 54 + drivers/power/Kconfig | 22 + drivers/power/Makefile | 5 + drivers/power/aspenite_battery.c | 162 + drivers/power/avengers_lite_power.c | 506 + drivers/power/power_mcu.c | 278 + drivers/power/power_supply_core.c | 29 +- drivers/power/power_supply_sysfs.c | 1 + drivers/power/sanremo_battery.c | 196 + drivers/rtc/Kconfig | 38 + drivers/rtc/Makefile | 5 + drivers/rtc/alarm-dev.c | 286 + drivers/rtc/alarm.c | 601 + drivers/rtc/class.c | 13 +- drivers/rtc/rtc-ec.c | 270 + drivers/rtc/rtc-isl1208.c | 269 +- drivers/rtc/rtc-mmp.c | 435 + drivers/serial/pxa.c | 694 +- drivers/serial/serial_core.c | 3 + drivers/spi/Kconfig | 2 +- drivers/spi/pxa2xx_spi.c | 13 +- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/android/Kconfig | 95 + drivers/staging/android/Makefile | 6 + drivers/staging/android/TODO | 10 + drivers/staging/android/binder.c | 3775 +++ drivers/staging/android/binder.h | 330 + drivers/staging/android/logger.c | 616 + drivers/staging/android/logger.h | 49 + drivers/staging/android/lowmemorykiller.c | 214 + drivers/staging/android/ram_console.c | 418 + drivers/staging/android/timed_gpio.c | 175 + drivers/staging/android/timed_gpio.h | 33 + drivers/staging/android/timed_output.c | 123 + drivers/staging/android/timed_output.h | 37 + drivers/switch/Kconfig | 15 + drivers/switch/Makefile | 4 + drivers/switch/switch_class.c | 174 + drivers/switch/switch_gpio.c | 171 + drivers/usb/Kconfig | 1 + drivers/usb/Makefile | 1 + drivers/usb/core/Kconfig | 2 +- drivers/usb/core/hcd.h | 4 + drivers/usb/core/hub.c | 4 + drivers/usb/gadget/Kconfig | 66 +- drivers/usb/gadget/Makefile | 7 + drivers/usb/gadget/android.c | 345 + drivers/usb/gadget/f_adb.c | 668 + drivers/usb/gadget/f_adb.h | 25 + drivers/usb/gadget/f_mass_storage.h | 52 + drivers/usb/gadget/gadget_chips.h | 8 +- drivers/usb/gadget/mv/Makefile | 5 + drivers/usb/gadget/mv/mvUsbCh9.h | 125 + drivers/usb/gadget/mv/mvUsbCore.h | 757 + drivers/usb/gadget/mv/mvUsbDebug.h | 112 + drivers/usb/gadget/mv/mvUsbDefs.h | 140 + drivers/usb/gadget/mv/mvUsbDesc.h | 163 + drivers/usb/gadget/mv/mvUsbDevApi.h | 164 + drivers/usb/gadget/mv/mvUsbDevCh9.c | 302 + drivers/usb/gadget/mv/mvUsbDevMain.c | 772 + drivers/usb/gadget/mv/mvUsbDevPrv.h | 270 + drivers/usb/gadget/mv/mvUsbDevRecv.c | 100 + drivers/usb/gadget/mv/mvUsbDevSend.c | 377 + drivers/usb/gadget/mv/mvUsbDevUtl.c | 651 + drivers/usb/gadget/mv/mvUsbHsDevCncl.c | 233 + drivers/usb/gadget/mv/mvUsbHsDevMain.c | 2039 ++ drivers/usb/gadget/mv/mvUsbHsDevUtl.c | 272 + drivers/usb/gadget/mv/mvUsbTypes.h | 240 + drivers/usb/gadget/mvLog.h | 125 + drivers/usb/gadget/mvOs.h | 149 + drivers/usb/gadget/mvUsb.h | 159 + drivers/usb/gadget/mvUsbRegs.h | 498 + drivers/usb/gadget/mv_gadget.c | 2930 ++ drivers/usb/gadget/pxa27x_udc.c | 8 + drivers/usb/gadget/pxa27x_udc.h | 20 +- drivers/usb/gadget/pxa_comp.c | 1338 + drivers/usb/gadget/u_serial.c | 23 +- drivers/usb/gadget/usbcons.c | 203 + drivers/usb/host/Kconfig | 16 +- drivers/usb/host/ehci-hcd.c | 20 + drivers/usb/host/ehci-hub.c | 11 +- drivers/usb/host/ehci-pxau2h.c | 348 + drivers/usb/host/ehci-pxau2o.c | 341 + drivers/usb/host/ehci.h | 4 + drivers/usb/otg/Makefile | 10 +- drivers/usb/otg/pxa300_otg.c | 320 + drivers/usb/otg/pxa300_otg.h | 29 + drivers/usb/otg/pxa310_otg.c | 1054 + drivers/usb/otg/pxa310_otg.h | 106 + drivers/usb/otg/pxa3xx_otg.c | 1309 + drivers/usb/otg/pxa3xx_otg_pmic.c | 87 + drivers/usb/otg/pxa930_otg.c | 331 + drivers/usb/otg/pxa930_otg.h | 78 + drivers/usb/otg/pxa_u2o.c | 477 + drivers/video/Makefile | 3 +- drivers/video/backlight/Kconfig | 8 + drivers/video/backlight/Makefile | 1 + drivers/video/backlight/portofino_bl.c | 179 + drivers/video/pxa168fb.c | 987 +- drivers/video/pxa168fb.h | 463 +- drivers/video/pxa168fb_ovly.c | 2186 ++ drivers/video/pxa910fb.c | 1732 + drivers/video/pxa910fb_ovly.c | 2304 ++ drivers/video/pxafb.c | 5 +- fs/Kconfig | 4 + fs/Makefile | 3 + fs/fat/dir.c | 9 + fs/fat/fat.h | 1 + fs/fat/inode.c | 9 + fs/jffs2/wbuf.c | 6 +- fs/proc/base.c | 35 +- fs/proc/generic.c | 3 + fs/ubifs/dir.c | 19 + fs/ubifs/file.c | 1 + fs/yaffs2/Kconfig | 164 + fs/yaffs2/Makefile | 10 + fs/yaffs2/devextras.h | 196 + fs/yaffs2/moduleconfig.h | 65 + fs/yaffs2/yaffs_checkptrw.c | 402 + fs/yaffs2/yaffs_checkptrw.h | 35 + fs/yaffs2/yaffs_ecc.c | 326 + fs/yaffs2/yaffs_ecc.h | 44 + fs/yaffs2/yaffs_fs.c | 2699 ++ fs/yaffs2/yaffs_getblockinfo.h | 34 + fs/yaffs2/yaffs_guts.c | 7739 +++++ fs/yaffs2/yaffs_guts.h | 912 + fs/yaffs2/yaffs_mtdif.c | 241 + fs/yaffs2/yaffs_mtdif.h | 32 + fs/yaffs2/yaffs_mtdif1.c | 361 + fs/yaffs2/yaffs_mtdif1.h | 28 + fs/yaffs2/yaffs_mtdif2.c | 251 + fs/yaffs2/yaffs_mtdif2.h | 29 + fs/yaffs2/yaffs_nand.c | 140 + fs/yaffs2/yaffs_nand.h | 44 + fs/yaffs2/yaffs_nandemul2k.h | 39 + fs/yaffs2/yaffs_packedtags1.c | 50 + fs/yaffs2/yaffs_packedtags1.h | 37 + fs/yaffs2/yaffs_packedtags2.c | 209 + fs/yaffs2/yaffs_packedtags2.h | 43 + fs/yaffs2/yaffs_qsort.c | 163 + fs/yaffs2/yaffs_qsort.h | 23 + fs/yaffs2/yaffs_tagscompat.c | 538 + fs/yaffs2/yaffs_tagscompat.h | 39 + fs/yaffs2/yaffs_tagsvalidity.c | 28 + fs/yaffs2/yaffs_tagsvalidity.h | 24 + fs/yaffs2/yaffsinterface.h | 21 + fs/yaffs2/yportenv.h | 203 + include/asm-arm/asm-offsets.h | 67 + include/asm-arm/mach-types.h | 27467 ++++++++++++++++ include/linux/android_aid.h | 26 + include/linux/android_alarm.h | 106 + include/linux/android_pmem.h | 93 + include/linux/ashmem.h | 48 + include/linux/bma020.h | 1191 + include/linux/card.h | 26 + include/linux/earlysuspend.h | 56 + include/linux/gpio_event.h | 154 + include/linux/i2c-id.h | 1 + include/linux/i2c/pca953x.h | 3 + include/linux/i2c/pca9575.h | 18 + include/linux/i2c/si4703.h | 15 + include/linux/if_pppolac.h | 35 + include/linux/if_pppopns.h | 34 + include/linux/if_pppox.h | 24 +- include/linux/kernel_debugger.h | 41 + include/linux/keychord.h | 52 + include/linux/keyreset.h | 27 + include/linux/miscdevice.h | 21 +- include/linux/mm.h | 1 + include/linux/mmc/core.h | 2 - include/linux/mmc/host.h | 17 + include/linux/mmc/mmc.h | 4 + .../mmc/host => include/linux/mmc}/sdhci.h | 63 +- include/linux/mmc/sdio_func.h | 10 + include/linux/msdos_fs.h | 12 + include/linux/mtd/bbm.h | 3 +- include/linux/mtd/mtd.h | 3 + include/linux/mtd/nand.h | 4 + include/linux/mtd/onenand.h | 2 + include/linux/power_supply.h | 4 + include/linux/preempt.h | 7 + include/linux/pxa168_ioctlICR.h | 410 + include/linux/sched.h | 2 + include/linux/sd8x_rfkill.h | 46 + include/linux/sensor-input.h | 41 + include/linux/serial_core.h | 1 + include/linux/sockios.h | 1 + include/linux/spi/cmmb.h | 9 + include/linux/switch.h | 67 + include/linux/synaptics_i2c_rmi.h | 53 + include/linux/uid_stat.h | 24 + include/linux/usb/android.h | 40 + include/linux/usb/composite.h | 7 + include/linux/usb/gadget.h | 8 + include/linux/usb/otg.h | 42 + include/linux/wakelock.h | 91 + include/linux/wifi_tiwlan.h | 32 + include/linux/wl127x-rfkill.h | 35 + include/media/v4l2-chip-ident.h | 8 + include/net/tcp.h | 2 + include/sound/wss.h | 1 + init/Kconfig | 14 + kernel/cgroup_freezer.c | 8 + kernel/cpuset.c | 7 + kernel/futex.c | 32 + kernel/panic.c | 5 +- kernel/power/Kconfig | 67 + kernel/power/Makefile | 5 + kernel/power/consoleearlysuspend.c | 78 + kernel/power/earlysuspend.c | 178 + kernel/power/fbearlysuspend.c | 153 + kernel/power/main.c | 10 + kernel/power/power.h | 24 + kernel/power/userwakelock.c | 219 + kernel/power/wakelock.c | 607 + kernel/printk.c | 54 + kernel/sysctl.c | 8 + mm/Makefile | 1 + mm/ashmem.c | 696 + mm/page_alloc.c | 19 +- mm/shmem.c | 13 +- net/Kconfig | 6 + net/core/dev.c | 13 +- net/ipv4/Makefile | 1 + net/ipv4/af_inet.c | 20 + net/ipv4/devinet.c | 12 +- net/ipv4/sysfs_net_ipv4.c | 88 + net/ipv6/af_inet6.c | 17 + net/rfkill/Kconfig | 5 + net/socket.c | 10 +- scripts/basic/.docproc.cmd | 63 + scripts/basic/.fixdep.cmd | 75 + scripts/basic/.hash.cmd | 39 + scripts/kconfig/.conf.cmd | 1 + scripts/kconfig/.conf.o.cmd | 57 + scripts/kconfig/.kxgettext.o.cmd | 47 + scripts/kconfig/.zconf.tab.o.cmd | 86 + scripts/kconfig/lex.zconf.c | 2429 ++ scripts/kconfig/zconf.hash.c | 239 + scripts/kconfig/zconf.tab.c | 2455 ++ scripts/recordmcount.pl | 1 + security/commoncap.c | 10 + sound/aoa/codecs/snd-aoa-codec-onyx.c | 1118 + sound/aoa/codecs/snd-aoa-codec-onyx.h | 75 + .../aoa/codecs/snd-aoa-codec-tas-basstreble.h | 134 + .../aoa/codecs/snd-aoa-codec-tas-gain-table.h | 209 + sound/aoa/codecs/snd-aoa-codec-tas.c | 1012 + sound/aoa/codecs/snd-aoa-codec-tas.h | 55 + sound/aoa/codecs/snd-aoa-codec-toonie.c | 150 + sound/aoa/core/snd-aoa-alsa.c | 99 + sound/aoa/core/snd-aoa-alsa.h | 16 + sound/aoa/core/snd-aoa-core.c | 162 + sound/aoa/core/snd-aoa-gpio-feature.c | 408 + sound/aoa/core/snd-aoa-gpio-pmf.c | 252 + sound/aoa/fabrics/snd-aoa-fabric-layout.c | 1120 + sound/aoa/soundbus/i2sbus/i2sbus-control.c | 193 + sound/aoa/soundbus/i2sbus/i2sbus-core.c | 450 + sound/aoa/soundbus/i2sbus/i2sbus-interface.h | 187 + sound/aoa/soundbus/i2sbus/i2sbus-pcm.c | 1062 + sound/pci/hda/hda_patch.h | 22 + sound/soc/at32/Kconfig | 34 + sound/soc/at32/Makefile | 11 + sound/soc/at32/at32-pcm.c | 492 + sound/soc/at32/at32-pcm.h | 79 + sound/soc/at32/at32-ssc.c | 849 + sound/soc/at32/at32-ssc.h | 59 + sound/soc/at32/playpaq_wm8510.c | 513 + sound/soc/at91/Kconfig | 10 + sound/soc/at91/Makefile | 6 + sound/soc/at91/at91-pcm.c | 434 + sound/soc/at91/at91-pcm.h | 72 + sound/soc/at91/at91-ssc.c | 791 + sound/soc/at91/at91-ssc.h | 27 + sound/soc/codecs/Kconfig | 3 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/autopower.c | 91 + sound/soc/codecs/autopower.h | 9 + sound/soc/codecs/cs4344.c | 267 + sound/soc/codecs/cs4344.h | 23 + sound/soc/codecs/micco.c | 1094 + sound/soc/codecs/sanremo-audio.c | 717 + sound/soc/codecs/sanremo-audio.h | 96 + sound/soc/codecs/wm8960.c | 4 +- sound/soc/pxa/Kconfig | 41 + sound/soc/pxa/Makefile | 11 + sound/soc/pxa/aspenite.c | 397 + sound/soc/pxa/avlite.c | 404 + sound/soc/pxa/edge.c | 358 + sound/soc/pxa/pxa3xx-pcm.c | 404 + sound/soc/pxa/pxa3xx-pcm.h | 44 + sound/soc/pxa/pxa3xx-ssp.c | 569 + sound/soc/pxa/pxa3xx-ssp.h | 56 + sound/soc/pxa/pxa910-squ.c | 509 + sound/soc/pxa/pxa910-squ.h | 39 + sound/soc/pxa/tavorevb.c | 181 + sound/soc/pxa/teton_bga.c | 255 + sound/soc/pxa/ttc-dkb.c | 188 + sound/soc/pxa/zylonite2.c | 341 + sound/usb/usx2y/usbusx2yaudio.c | 35 +- 610 files changed, 197212 insertions(+), 3171 deletions(-) create mode 100644 .config create mode 100644 Documentation/android.txt create mode 100644 arch/arm/configs/pxa168_android_mlc_defconfig create mode 100644 arch/arm/configs/pxa168_android_mmc_defconfig create mode 100644 arch/arm/configs/pxa168_avengers_lite_defconfig create mode 100644 arch/arm/mach-mmp/dkb_generic.c create mode 100644 arch/arm/mach-mmp/dvfm.c create mode 100644 arch/arm/mach-mmp/dvfm_stats.c create mode 100644 arch/arm/mach-mmp/edge.c create mode 100644 arch/arm/mach-mmp/include/mach/camera.h create mode 100644 arch/arm/mach-mmp/include/mach/clock.h create mode 100644 arch/arm/mach-mmp/include/mach/cm3602.h create mode 100644 arch/arm/mach-mmp/include/mach/dvfm.h create mode 100644 arch/arm/mach-mmp/include/mach/gpio_ec.h create mode 100644 arch/arm/mach-mmp/include/mach/gpio_ir.h create mode 100644 arch/arm/mach-mmp/include/mach/ir_key_def.h create mode 100644 arch/arm/mach-mmp/include/mach/max8660.h create mode 100644 arch/arm/mach-mmp/include/mach/mfp-teton_bga.h create mode 100644 arch/arm/mach-mmp/include/mach/micco.h create mode 100644 arch/arm/mach-mmp/include/mach/mmc.h create mode 100644 arch/arm/mach-mmp/include/mach/mspm_prof.h create mode 100644 arch/arm/mach-mmp/include/mach/nand_supported.h create mode 100644 arch/arm/mach-mmp/include/mach/ov529.h create mode 100644 arch/arm/mach-mmp/include/mach/portofino.h create mode 100644 arch/arm/mach-mmp/include/mach/power_button.h create mode 100644 arch/arm/mach-mmp/include/mach/power_mcu.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa168_dvfm.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa168_eth.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa168_pcie.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa168_pm.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa168fb.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa3xx_bbm.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa3xx_nand.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa910-squ.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa910_dvfm.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa910_ire.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa910_pm.h create mode 100644 arch/arm/mach-mmp/include/mach/pxa930_acipc.h create mode 100644 arch/arm/mach-mmp/include/mach/regs-ciu.h create mode 100644 arch/arm/mach-mmp/include/mach/regs-mpmu.h create mode 100644 arch/arm/mach-mmp/include/mach/regs-pcie.h create mode 100644 arch/arm/mach-mmp/include/mach/regs-rtc.h create mode 100644 arch/arm/mach-mmp/include/mach/regs-smc.h create mode 100644 arch/arm/mach-mmp/include/mach/regs-ssp.h create mode 100644 arch/arm/mach-mmp/include/mach/resource.h create mode 100644 arch/arm/mach-mmp/include/mach/rtc.h create mode 100644 arch/arm/mach-mmp/include/mach/sanremo.h create mode 100644 arch/arm/mach-mmp/include/mach/ssp.h create mode 100644 arch/arm/mach-mmp/include/mach/timer_services.h create mode 100644 arch/arm/mach-mmp/io.c create mode 100644 arch/arm/mach-mmp/ipcam.c create mode 100644 arch/arm/mach-mmp/irq.c create mode 100644 arch/arm/mach-mmp/mmc.c create mode 100644 arch/arm/mach-mmp/pxa168_dfc_ll.S create mode 100644 arch/arm/mach-mmp/pxa168_dvfm.c create mode 100644 arch/arm/mach-mmp/pxa168_dvfm_stats.c create mode 100644 arch/arm/mach-mmp/pxa168_mspm_idle.c create mode 100644 arch/arm/mach-mmp/pxa168_mspm_prof.c create mode 100644 arch/arm/mach-mmp/pxa168_pcie.c create mode 100644 arch/arm/mach-mmp/pxa168_pm.c create mode 100644 arch/arm/mach-mmp/pxa168_pm_ll.S create mode 100644 arch/arm/mach-mmp/pxa910-squ.c create mode 100644 arch/arm/mach-mmp/pxa910_dvfm.c create mode 100644 arch/arm/mach-mmp/pxa910_mspm_idle.c create mode 100644 arch/arm/mach-mmp/pxa910_mspm_prof.c create mode 100644 arch/arm/mach-mmp/pxa910_pm.c create mode 100644 arch/arm/mach-mmp/pxa910_pm_ll.S create mode 100644 arch/arm/mach-mmp/resource/Makefile create mode 100644 arch/arm/mach-mmp/resource/misc.c create mode 100644 arch/arm/mach-mmp/teton_bga.c create mode 100644 arch/arm/mach-mmp/timer_services.c create mode 100644 arch/arm/mach-pxa/include/mach/pxa-regs.h create mode 100644 arch/arm/plat-pxa/generic.c create mode 100644 arch/arm/plat-pxa/imm/Kconfig create mode 100644 arch/arm/plat-pxa/imm/Makefile create mode 100644 arch/arm/plat-pxa/imm/imm.h create mode 100644 arch/arm/plat-pxa/imm/ioremap.c create mode 100644 arch/arm/plat-pxa/imm/policy.c create mode 100644 arch/arm/plat-pxa/imm/sram.c create mode 100644 arch/arm/plat-pxa/imm/sram.h create mode 100644 arch/arm/plat-pxa/include/plat/generic.h create mode 100644 arch/arm/plat-pxa/include/plat/imm.h create mode 100644 arch/arm/plat-pxa/include/plat/part_table.h create mode 100644 arch/arm/plat-pxa/include/plat/pfn_cfg.h rename arch/arm/{mach-pxa/include/mach => plat-pxa/include/plat}/pxa27x_keypad.h (100%) rename arch/arm/{mach-pxa/include/mach => plat-pxa/include/plat}/pxa2xx_spi.h (100%) create mode 100644 arch/arm/plat-pxa/include/plat/pxa3xx_otg.h create mode 100644 arch/arm/plat-pxa/include/plat/pxa3xx_pmic.h create mode 100644 arch/arm/plat-pxa/include/plat/pxa_u2o.h create mode 100644 arch/arm/plat-pxa/include/plat/pxausb_common.h create mode 100644 arch/arm/plat-pxa/include/plat/pxausb_comp.h create mode 100644 arch/arm/plat-pxa/include/plat/regs-ssp.h create mode 100644 arch/arm/plat-pxa/include/plat/ssp.h create mode 100644 arch/arm/plat-pxa/pmem.c create mode 100644 arch/arm/plat-pxa/pxa3xx_pmic.c rename arch/arm/{mach-pxa => plat-pxa}/ssp.c (82%) create mode 100644 drivers/char/dcc_tty.c create mode 100644 drivers/char/icr/Kconfig create mode 100644 drivers/char/icr/Makefile create mode 100644 drivers/char/icr/pxa168_ICRioctlTable.h create mode 100644 drivers/char/icr/pxa168_icr.c create mode 100644 drivers/char/icr/pxa168_icr.h create mode 100644 drivers/char/imm.c create mode 100644 drivers/char/pxa-cnm.c create mode 100644 drivers/char/pxa910_ire.c create mode 100644 drivers/char/pxa930_acipc.c create mode 100644 drivers/gpio/max8660.c create mode 100644 drivers/gpio/pca9575.c create mode 100644 drivers/hwmon/bma020/Makefile create mode 100644 drivers/hwmon/bma020/bma020.c create mode 100644 drivers/hwmon/bma020/bma020_fs.c create mode 100644 drivers/hwmon/bma020/bma020_fs.h create mode 100644 drivers/hwmon/cm3602.c create mode 100644 drivers/i2c/chips/Kconfig create mode 100644 drivers/i2c/chips/Makefile create mode 100644 drivers/i2c/chips/gpio_ec.c create mode 100644 drivers/i2c/chips/micco.c create mode 100644 drivers/i2c/chips/pca963x.c create mode 100644 drivers/i2c/chips/portofino.c create mode 100644 drivers/i2c/chips/sanremo.c create mode 100644 drivers/input/gpio_ir.c create mode 100644 drivers/input/keyreset.c create mode 100644 drivers/input/misc/gpio_axis.c create mode 100644 drivers/input/misc/gpio_event.c create mode 100644 drivers/input/misc/gpio_input.c create mode 100644 drivers/input/misc/gpio_matrix.c create mode 100644 drivers/input/misc/gpio_output.c create mode 100644 drivers/input/misc/keychord.c create mode 100644 drivers/input/misc/power_button.c create mode 100644 drivers/input/misc/sensors.c create mode 100644 drivers/input/touchscreen/micco_touch.c create mode 100644 drivers/input/touchscreen/sanremo_touch.c create mode 100644 drivers/input/touchscreen/synaptics_i2c_rmi.c create mode 100644 drivers/input/touchscreen/tpo_touch.c create mode 100644 drivers/input/touchscreen/ts_linear.c create mode 100644 drivers/leds/ledtrig-sleep.c create mode 100644 drivers/media/radio/radio-si4703.c create mode 100644 drivers/media/video/cli5001.c create mode 100644 drivers/media/video/cli5001.h create mode 100644 drivers/media/video/cmmb/Kconfig create mode 100644 drivers/media/video/cmmb/Makefile create mode 100644 drivers/media/video/cmmb/ca.c create mode 100644 drivers/media/video/cmmb/ca.h create mode 100644 drivers/media/video/cmmb/if101.c create mode 100644 drivers/media/video/ov3640.c create mode 100644 drivers/media/video/ov529_drv.c create mode 100644 drivers/media/video/ov529_drv.h create mode 100644 drivers/media/video/ov529_hw_ops.h create mode 100644 drivers/media/video/ov529_smc.c create mode 100644 drivers/media/video/ov7660.c create mode 100644 drivers/media/video/ov7670_pxa.c create mode 100644 drivers/media/video/ov7740.c create mode 100644 drivers/media/video/pxa168_camera.c create mode 100644 drivers/media/video/pxa168_camera.h create mode 100644 drivers/media/video/pxa_camera.h create mode 100644 drivers/media/video/siv120a.c create mode 100644 drivers/misc/apanic.c create mode 100644 drivers/misc/kernel_debugger.c create mode 100644 drivers/misc/pmem.c create mode 100644 drivers/misc/sd8x_rfkill.c create mode 100644 drivers/misc/uid_stat.c create mode 100644 drivers/misc/wl127x-rfkill.c create mode 100644 drivers/mmc/host/pxa_sdh.c create mode 100644 drivers/mmc/host/pxa_sdh.h create mode 100644 drivers/mmc/host/sdhci-pxa168.c create mode 100644 drivers/mtd/pxa3xx_bbm.c create mode 100644 drivers/net/pppolac.c create mode 100644 drivers/net/pppopns.c create mode 100644 drivers/net/pxa168_eth.c create mode 100644 drivers/pcmcia/pxa168_cf.c create mode 100644 drivers/pcmcia/pxa168_cf.h create mode 100644 drivers/power/aspenite_battery.c create mode 100644 drivers/power/avengers_lite_power.c create mode 100644 drivers/power/power_mcu.c create mode 100644 drivers/power/sanremo_battery.c create mode 100644 drivers/rtc/alarm-dev.c create mode 100644 drivers/rtc/alarm.c create mode 100644 drivers/rtc/rtc-ec.c create mode 100644 drivers/rtc/rtc-mmp.c create mode 100644 drivers/staging/android/Kconfig create mode 100644 drivers/staging/android/Makefile create mode 100644 drivers/staging/android/TODO create mode 100644 drivers/staging/android/binder.c create mode 100644 drivers/staging/android/binder.h create mode 100644 drivers/staging/android/logger.c create mode 100644 drivers/staging/android/logger.h create mode 100644 drivers/staging/android/lowmemorykiller.c create mode 100644 drivers/staging/android/ram_console.c create mode 100644 drivers/staging/android/timed_gpio.c create mode 100644 drivers/staging/android/timed_gpio.h create mode 100644 drivers/staging/android/timed_output.c create mode 100644 drivers/staging/android/timed_output.h create mode 100644 drivers/switch/Kconfig create mode 100644 drivers/switch/Makefile create mode 100644 drivers/switch/switch_class.c create mode 100644 drivers/switch/switch_gpio.c create mode 100644 drivers/usb/gadget/android.c create mode 100644 drivers/usb/gadget/f_adb.c create mode 100644 drivers/usb/gadget/f_adb.h create mode 100644 drivers/usb/gadget/f_mass_storage.h create mode 100644 drivers/usb/gadget/mv/Makefile create mode 100644 drivers/usb/gadget/mv/mvUsbCh9.h create mode 100644 drivers/usb/gadget/mv/mvUsbCore.h create mode 100644 drivers/usb/gadget/mv/mvUsbDebug.h create mode 100644 drivers/usb/gadget/mv/mvUsbDefs.h create mode 100644 drivers/usb/gadget/mv/mvUsbDesc.h create mode 100644 drivers/usb/gadget/mv/mvUsbDevApi.h create mode 100644 drivers/usb/gadget/mv/mvUsbDevCh9.c create mode 100644 drivers/usb/gadget/mv/mvUsbDevMain.c create mode 100644 drivers/usb/gadget/mv/mvUsbDevPrv.h create mode 100644 drivers/usb/gadget/mv/mvUsbDevRecv.c create mode 100644 drivers/usb/gadget/mv/mvUsbDevSend.c create mode 100644 drivers/usb/gadget/mv/mvUsbDevUtl.c create mode 100644 drivers/usb/gadget/mv/mvUsbHsDevCncl.c create mode 100644 drivers/usb/gadget/mv/mvUsbHsDevMain.c create mode 100644 drivers/usb/gadget/mv/mvUsbHsDevUtl.c create mode 100644 drivers/usb/gadget/mv/mvUsbTypes.h create mode 100644 drivers/usb/gadget/mvLog.h create mode 100644 drivers/usb/gadget/mvOs.h create mode 100644 drivers/usb/gadget/mvUsb.h create mode 100644 drivers/usb/gadget/mvUsbRegs.h create mode 100644 drivers/usb/gadget/mv_gadget.c create mode 100644 drivers/usb/gadget/pxa_comp.c create mode 100644 drivers/usb/gadget/usbcons.c create mode 100644 drivers/usb/host/ehci-pxau2h.c create mode 100644 drivers/usb/host/ehci-pxau2o.c create mode 100644 drivers/usb/otg/pxa300_otg.c create mode 100644 drivers/usb/otg/pxa300_otg.h create mode 100644 drivers/usb/otg/pxa310_otg.c create mode 100644 drivers/usb/otg/pxa310_otg.h create mode 100644 drivers/usb/otg/pxa3xx_otg.c create mode 100644 drivers/usb/otg/pxa3xx_otg_pmic.c create mode 100644 drivers/usb/otg/pxa930_otg.c create mode 100644 drivers/usb/otg/pxa930_otg.h create mode 100644 drivers/usb/otg/pxa_u2o.c create mode 100644 drivers/video/backlight/portofino_bl.c create mode 100644 drivers/video/pxa168fb_ovly.c create mode 100644 drivers/video/pxa910fb.c create mode 100644 drivers/video/pxa910fb_ovly.c create mode 100644 fs/yaffs2/Kconfig create mode 100644 fs/yaffs2/Makefile create mode 100644 fs/yaffs2/devextras.h create mode 100644 fs/yaffs2/moduleconfig.h create mode 100644 fs/yaffs2/yaffs_checkptrw.c create mode 100644 fs/yaffs2/yaffs_checkptrw.h create mode 100644 fs/yaffs2/yaffs_ecc.c create mode 100644 fs/yaffs2/yaffs_ecc.h create mode 100644 fs/yaffs2/yaffs_fs.c create mode 100644 fs/yaffs2/yaffs_getblockinfo.h create mode 100644 fs/yaffs2/yaffs_guts.c create mode 100644 fs/yaffs2/yaffs_guts.h create mode 100644 fs/yaffs2/yaffs_mtdif.c create mode 100644 fs/yaffs2/yaffs_mtdif.h create mode 100644 fs/yaffs2/yaffs_mtdif1.c create mode 100644 fs/yaffs2/yaffs_mtdif1.h create mode 100644 fs/yaffs2/yaffs_mtdif2.c create mode 100644 fs/yaffs2/yaffs_mtdif2.h create mode 100644 fs/yaffs2/yaffs_nand.c create mode 100644 fs/yaffs2/yaffs_nand.h create mode 100644 fs/yaffs2/yaffs_nandemul2k.h create mode 100644 fs/yaffs2/yaffs_packedtags1.c create mode 100644 fs/yaffs2/yaffs_packedtags1.h create mode 100644 fs/yaffs2/yaffs_packedtags2.c create mode 100644 fs/yaffs2/yaffs_packedtags2.h create mode 100644 fs/yaffs2/yaffs_qsort.c create mode 100644 fs/yaffs2/yaffs_qsort.h create mode 100644 fs/yaffs2/yaffs_tagscompat.c create mode 100644 fs/yaffs2/yaffs_tagscompat.h create mode 100644 fs/yaffs2/yaffs_tagsvalidity.c create mode 100644 fs/yaffs2/yaffs_tagsvalidity.h create mode 100644 fs/yaffs2/yaffsinterface.h create mode 100644 fs/yaffs2/yportenv.h create mode 100644 include/asm-arm/asm-offsets.h create mode 100644 include/asm-arm/mach-types.h create mode 100644 include/linux/android_aid.h create mode 100644 include/linux/android_alarm.h create mode 100644 include/linux/android_pmem.h create mode 100644 include/linux/ashmem.h create mode 100644 include/linux/bma020.h create mode 100644 include/linux/card.h create mode 100644 include/linux/earlysuspend.h create mode 100644 include/linux/gpio_event.h create mode 100644 include/linux/i2c/pca9575.h create mode 100644 include/linux/i2c/si4703.h create mode 100644 include/linux/if_pppolac.h create mode 100644 include/linux/if_pppopns.h create mode 100644 include/linux/kernel_debugger.h create mode 100644 include/linux/keychord.h create mode 100644 include/linux/keyreset.h rename {drivers/mmc/host => include/linux/mmc}/sdhci.h (85%) create mode 100644 include/linux/pxa168_ioctlICR.h create mode 100644 include/linux/sd8x_rfkill.h create mode 100644 include/linux/sensor-input.h create mode 100644 include/linux/spi/cmmb.h create mode 100644 include/linux/switch.h create mode 100644 include/linux/synaptics_i2c_rmi.h create mode 100644 include/linux/uid_stat.h create mode 100644 include/linux/usb/android.h create mode 100644 include/linux/wakelock.h create mode 100644 include/linux/wifi_tiwlan.h create mode 100644 include/linux/wl127x-rfkill.h create mode 100644 kernel/power/consoleearlysuspend.c create mode 100644 kernel/power/earlysuspend.c create mode 100644 kernel/power/fbearlysuspend.c create mode 100644 kernel/power/userwakelock.c create mode 100644 kernel/power/wakelock.c create mode 100644 mm/ashmem.c create mode 100644 net/ipv4/sysfs_net_ipv4.c create mode 100644 scripts/basic/.docproc.cmd create mode 100644 scripts/basic/.fixdep.cmd create mode 100644 scripts/basic/.hash.cmd create mode 100644 scripts/kconfig/.conf.cmd create mode 100644 scripts/kconfig/.conf.o.cmd create mode 100644 scripts/kconfig/.kxgettext.o.cmd create mode 100644 scripts/kconfig/.zconf.tab.o.cmd create mode 100644 scripts/kconfig/lex.zconf.c create mode 100644 scripts/kconfig/zconf.hash.c create mode 100644 scripts/kconfig/zconf.tab.c create mode 100644 sound/aoa/codecs/snd-aoa-codec-onyx.c create mode 100644 sound/aoa/codecs/snd-aoa-codec-onyx.h create mode 100644 sound/aoa/codecs/snd-aoa-codec-tas-basstreble.h create mode 100644 sound/aoa/codecs/snd-aoa-codec-tas-gain-table.h create mode 100644 sound/aoa/codecs/snd-aoa-codec-tas.c create mode 100644 sound/aoa/codecs/snd-aoa-codec-tas.h create mode 100644 sound/aoa/codecs/snd-aoa-codec-toonie.c create mode 100644 sound/aoa/core/snd-aoa-alsa.c create mode 100644 sound/aoa/core/snd-aoa-alsa.h create mode 100644 sound/aoa/core/snd-aoa-core.c create mode 100644 sound/aoa/core/snd-aoa-gpio-feature.c create mode 100644 sound/aoa/core/snd-aoa-gpio-pmf.c create mode 100644 sound/aoa/fabrics/snd-aoa-fabric-layout.c create mode 100644 sound/aoa/soundbus/i2sbus/i2sbus-control.c create mode 100644 sound/aoa/soundbus/i2sbus/i2sbus-core.c create mode 100644 sound/aoa/soundbus/i2sbus/i2sbus-interface.h create mode 100644 sound/aoa/soundbus/i2sbus/i2sbus-pcm.c create mode 100644 sound/pci/hda/hda_patch.h create mode 100644 sound/soc/at32/Kconfig create mode 100644 sound/soc/at32/Makefile create mode 100644 sound/soc/at32/at32-pcm.c create mode 100644 sound/soc/at32/at32-pcm.h create mode 100644 sound/soc/at32/at32-ssc.c create mode 100644 sound/soc/at32/at32-ssc.h create mode 100644 sound/soc/at32/playpaq_wm8510.c create mode 100644 sound/soc/at91/Kconfig create mode 100644 sound/soc/at91/Makefile create mode 100644 sound/soc/at91/at91-pcm.c create mode 100644 sound/soc/at91/at91-pcm.h create mode 100644 sound/soc/at91/at91-ssc.c create mode 100644 sound/soc/at91/at91-ssc.h create mode 100644 sound/soc/codecs/autopower.c create mode 100644 sound/soc/codecs/autopower.h create mode 100644 sound/soc/codecs/cs4344.c create mode 100644 sound/soc/codecs/cs4344.h create mode 100644 sound/soc/codecs/micco.c create mode 100644 sound/soc/codecs/sanremo-audio.c create mode 100644 sound/soc/codecs/sanremo-audio.h create mode 100644 sound/soc/pxa/aspenite.c create mode 100644 sound/soc/pxa/avlite.c create mode 100644 sound/soc/pxa/edge.c create mode 100644 sound/soc/pxa/pxa3xx-pcm.c create mode 100644 sound/soc/pxa/pxa3xx-pcm.h create mode 100644 sound/soc/pxa/pxa3xx-ssp.c create mode 100644 sound/soc/pxa/pxa3xx-ssp.h create mode 100644 sound/soc/pxa/pxa910-squ.c create mode 100644 sound/soc/pxa/pxa910-squ.h create mode 100644 sound/soc/pxa/tavorevb.c create mode 100644 sound/soc/pxa/teton_bga.c create mode 100644 sound/soc/pxa/ttc-dkb.c create mode 100644 sound/soc/pxa/zylonite2.c diff --git a/.config b/.config new file mode 100644 index 00000000000000..7c0ff1457694b6 --- /dev/null +++ b/.config @@ -0,0 +1,1579 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.34 +# Fri Sep 10 13:29:37 2010 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_RELAY=y +CONFIG_NAMESPACES=y +# CONFIG_UTS_NS is not set +# CONFIG_IPC_NS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_NET_NS is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +# CONFIG_ASHMEM is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_PREEMPT_NOTIFIERS=y +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +CONFIG_ARCH_MMP=y +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_MACH_TAVOREVB is not set +CONFIG_PXA_SSP_LEGACY=y + +# +# Marvell(R) Wireless Memory Management Technology +# +# CONFIG_IMM is not set + +# +# Marvell PXA168 Processor Variants +# +# CONFIG_CPU_PXA168_B0 is not set + +# +# Marvell PXA168/910/MMP2 Implmentations +# +CONFIG_MACH_ASPENITE=y +# CONFIG_MACH_ZYLONITE2 is not set +# CONFIG_MACH_IPCAM is not set +# CONFIG_MACH_DKB_GENERIC is not set +# CONFIG_MACH_TTC_DKB is not set +# CONFIG_MACH_AVENGERS_LITE is not set +# CONFIG_MACH_EDGE is not set +CONFIG_MACH_TETON_BGA=y +# CONFIG_MACH_FLINT is not set +# CONFIG_MACH_MARVELL_JASPER is not set +CONFIG_GLOBAL_PREEMPT_NOTIFIERS=y +CONFIG_CPU_PXA168=y +# CONFIG_PXA_32KTIMER is not set +CONFIG_TIMER_SERVICES_MMP=y +CONFIG_PLAT_PXA=y + +# +# Processor Type +# +CONFIG_CPU_MOHAWK=y +# CONFIG_CPU_MOHAWK_OLD_ID is not set +CONFIG_CPU_L2_CACHE=y +# CONFIG_ENABLE_COREIDLE is not set +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_IWMMXT=y +CONFIG_COMMON_CLKDEV=y +CONFIG_FORCE_MAX_ZONEORDER=15 + +# +# Bus support +# +# CONFIG_PCI is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="ubi.mtd=4 root=ubi0_0 nfsroot=192.168.1.100:/rootfs rootfstype=ubifs ip=192.168.1.101:192.168.1.100::255.255.255.0::usb0:on console=ttyS0,115200 uart_dma loglevel=8" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +# CONFIG_PM is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +# CONFIG_CFG80211 is not set +# CONFIG_LIB80211 is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set +CONFIG_PXA3XX_BBM=y + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_SAMSUNG_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_8G_MLC_NAND is not set +# CONFIG_MICRON_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_512M_SLC_NAND is not set +CONFIG_DEFAULT_NAND=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_UB=y +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +CONFIG_PXA168_MSP=y +# CONFIG_MISC_DEVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +CONFIG_PXA168_ETH=y +# CONFIG_DM9000 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +# CONFIG_WLAN_8688_SDIO is not set +# CONFIG_USB_ZD1201 is not set +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +CONFIG_USB_CATC=y +CONFIG_USB_KAWETH=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_CIR=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +CONFIG_KEYBOARD_PXA27x=y +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +CONFIG_TSC2007=y +# CONFIG_TOUCHSCREEN_TPO is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set + +# +# Color Management Unit +# +CONFIG_PXA_ICR=y +# CONFIG_PXA_CNM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_SPI is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_GENERIC_IRQ=y +CONFIG_GPIO_PCA953X_IRQ=y +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_ADP5588 is not set +CONFIG_MAX8660=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_PCA9575 is not set + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_THERMAL is not set +# CONFIG_WATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA168=y +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y + +# +# SoC Audio for the Intel PXA3xx +# +CONFIG_SND_PXA3XX_SOC=y +CONFIG_SND_PXA3XX_SOC_SSP=y +CONFIG_SND_PXA3XX_SOC_ASPENITE=y +# CONFIG_SND_PXA168_SOC_AVLite is not set +CONFIG_SND_PXA3XX_SOC_TETON_BGA=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_CS4344=y +# CONFIG_SOUND_PRIME is not set +# CONFIG_HID_SUPPORT is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_PXA_U2H=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_PARANOID_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA_SDH=y +# CONFIG_MMC3 is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +# CONFIG_MMC_SDHCI_PLTFM is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_SWITCH is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +# CONFIG_STAGING is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_YAFFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +# CONFIG_LOGFS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +CONFIG_DEBUG_MEMORY_INIT=y +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_EARLY_PRINTK is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y diff --git a/Documentation/android.txt b/Documentation/android.txt new file mode 100644 index 00000000000000..72a62afdf20257 --- /dev/null +++ b/Documentation/android.txt @@ -0,0 +1,121 @@ + ============= + A N D R O I D + ============= + +Copyright (C) 2009 Google, Inc. +Written by Mike Chan + +CONTENTS: +--------- + +1. Android + 1.1 Required enabled config options + 1.2 Required disabled config options + 1.3 Recommended enabled config options +2. Contact + + +1. Android +========== + +Android (www.android.com) is an open source operating system for mobile devices. +This document describes configurations needed to run the Android framework on +top of the Linux kernel. + +To see a working defconfig look at msm_defconfig or goldfish_defconfig +which can be found at http://android.git.kernel.org in kernel/common.git +and kernel/msm.git + + +1.1 Required enabled config options +----------------------------------- +After building a standard defconfig, ensure that these options are enabled in +your .config or defconfig if they are not already. Based off the msm_defconfig. +You should keep the rest of the default options enabled in the defconfig +unless you know what you are doing. + +ANDROID_PARANOID_NETWORK +ASHMEM +CONFIG_FB_MODE_HELPERS +CONFIG_FONT_8x16 +CONFIG_FONT_8x8 +CONFIG_YAFFS_SHORT_NAMES_IN_RAM +DAB +EARLYSUSPEND +FB +FB_CFB_COPYAREA +FB_CFB_FILLRECT +FB_CFB_IMAGEBLIT +FB_DEFERRED_IO +FB_TILEBLITTING +HIGH_RES_TIMERS +INOTIFY +INOTIFY_USER +INPUT_EVDEV +INPUT_GPIO +INPUT_MISC +LEDS_CLASS +LEDS_GPIO +LOCK_KERNEL +LkOGGER +LOW_MEMORY_KILLER +MISC_DEVICES +NEW_LEDS +NO_HZ +POWER_SUPPLY +PREEMPT +RAMFS +RTC_CLASS +RTC_LIB +SWITCH +SWITCH_GPIO +TMPFS +UID_STAT +UID16 +USB_FUNCTION +USB_FUNCTION_ADB +USER_WAKELOCK +VIDEO_OUTPUT_CONTROL +WAKELOCK +YAFFS_AUTO_YAFFS2 +YAFFS_FS +YAFFS_YAFFS1 +YAFFS_YAFFS2 + + +1.2 Required disabled config options +------------------------------------ +CONFIG_YAFFS_DISABLE_LAZY_LOAD +DNOTIFY + + +1.3 Recommended enabled config options +------------------------------ +ANDROID_PMEM +ANDROID_RAM_CONSOLE +ANDROID_RAM_CONSOLE_ERROR_CORRECTION +SCHEDSTATS +DEBUG_PREEMPT +DEBUG_MUTEXES +DEBUG_SPINLOCK_SLEEP +DEBUG_INFO +FRAME_POINTER +CPU_FREQ +CPU_FREQ_TABLE +CPU_FREQ_DEFAULT_GOV_ONDEMAND +CPU_FREQ_GOV_ONDEMAND +CRC_CCITT +EMBEDDED +INPUT_TOUCHSCREEN +I2C +I2C_BOARDINFO +LOG_BUF_SHIFT=17 +SERIAL_CORE +SERIAL_CORE_CONSOLE + + +2. Contact +========== +website: http://android.git.kernel.org + +mailing-lists: android-kernel@googlegroups.com diff --git a/Makefile b/Makefile index 701bc65b3952fd..ebc8225f7a9673 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 34 -EXTRAVERSION = -rc7 +EXTRAVERSION = NAME = Sheep on Meth # *DOCUMENTATION* diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 92622eb5cc0da6..178b5562c4166b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -18,6 +18,8 @@ config ARM select HAVE_KPROBES if (!XIP_KERNEL) select HAVE_KRETPROBES if (HAVE_KPROBES) select HAVE_FUNCTION_TRACER if (!XIP_KERNEL) + select HAVE_DYNAMIC_FTRACE if (!XIP_KERNEL) + select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL) select HAVE_GENERIC_DMA_COHERENT select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZO @@ -523,6 +525,7 @@ config ARCH_MMP select GENERIC_CLOCKEVENTS select TICK_ONESHOT select PLAT_PXA + select PXA_SSP_LEGACY help Support for Marvell's PXA168/PXA910(MMP) and MMP2 processor line. @@ -1031,8 +1034,9 @@ source "arch/arm/common/Kconfig" config FORCE_MAX_ZONEORDER int - depends on SA1111 - default "9" + depends on SA1111 || ARCH_MMP + default "9" if SA1111 + default "15" if ARCH_MMP menu "Bus support" @@ -1058,7 +1062,7 @@ config ISA_DMA_API bool config PCI - bool "PCI support" if ARCH_INTEGRATOR_AP || ARCH_VERSATILE_PB || ARCH_IXP4XX || ARCH_KS8695 || MACH_ARMCORE + bool "PCI support" if ARCH_INTEGRATOR_AP || ARCH_VERSATILE_PB || ARCH_IXP4XX || ARCH_KS8695 || MACH_ARMCORE || ARCH_MMP help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside @@ -1182,6 +1186,7 @@ config HZ default 200 if ARCH_EBSA110 || ARCH_S3C2410 || ARCH_S5P6440 || ARCH_S5P6442 || ARCH_S5PV210 default OMAP_32K_TIMER_HZ if ARCH_OMAP && OMAP_32K_TIMER default AT91_TIMER_HZ if ARCH_AT91 + default 128 if ARCH_MMP && PXA_32KTIMER default 100 config THUMB2_KERNEL @@ -1645,6 +1650,94 @@ source "kernel/power/Kconfig" config ARCH_SUSPEND_POSSIBLE def_bool y +config PM_PXA168 + bool "PXA168 PM support (hibernate)" + depends on PM && CPU_PXA168 + default y + help + Say 'Y' here if you want hibernate support + +config PM_PXA910 + bool "PXA910 PM support" + depends on PM && CPU_PXA910 + default y + help + Say 'Y' here if you want low power mode support + +config DVFM + bool "DVFM support" + depends on PM + default y + help + Say 'Y' here if you want Dynamic Voltage and Frequency Management + +config DVFM_PXA168 + bool "PXA168 DVFM support" + depends on DVFM && CPU_PXA168 + default y + help + Say 'Y' here if you want DVFM on PXA168 processor + +config DVFM_PXA168_LCDDMA_WR + bool "PXA168 DFC & LCD DMA confliction work around" + depends on DVFM_PXA168 + default y + help + Say 'Y' here if you want add the DFC & LCD DAM confliction WR + + +config DVFM_PXA910 + bool "PXA910 DVFM support" + depends on DVFM && CPU_PXA910 + default n + help + Say 'Y' here if you want DVFM on PXA910 processor + +config MSPM + bool "Marvell Scalable Power Management support" + depends on DVFM_PXA168 || DVFM_PXA910 + default y + help + Say 'Y' here if you want Marvell Scalable Power Management + +config MSPM_PXA168 + bool "PXA168 MSPM support" + depends on MSPM && DVFM_PXA168 && PM_PXA168 + default y + help + Say 'Y' here if you want Marvell Scalable Power Management + +config MSPM_PXA168_STATS + bool "PXA168 MSPM Statistics support" + depends on MSPM && DVFM_PXA168 && PM_PXA168 + select RELAY + select DEBUG_FS + default y + help + This is used to collect statistics during the dynamic frequency + and voltage changes + +config MSPM_PXA910 + bool "PXA910 MSPM support" + depends on MSPM && DVFM_PXA910 && PM_PXA910 + default y + help + Say 'Y' here if you want Marvell Scalable Power Management + +config PXA910_DVFM_STATS + bool "PXA910 DVFM STATS" + depends on MSPM_PXA910 + default n + help + Say 'Y' here if you want DVFM STATS + +config PXA910_CLOCK_TRACE + bool "PXA910 ACTIVE CLOCK TRACE" + depends on MSPM_PXA910 + default n + help + Say 'Y' here if you want DVFM STATS + endmenu source "net/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index ed820e737a8a66..be141c02cee0db 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -20,6 +20,11 @@ GZFLAGS :=-9 #KBUILD_CFLAGS +=-pipe # Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb: KBUILD_CFLAGS +=$(call cc-option,-marm,) +MCOUNT = $(shell echo 'main(){}' | $(CC) -x c -S -o - - -pg | grep mcount | awk '{print $$2}') +ifeq ("$(MCOUNT)", "__gnu_mcount_nc") + KBUILD_CFLAGS +=-DKBUILD_NEW_GNU_MCOUNT + KBUILD_AFLAGS +=-DKBUILD_NEW_GNU_MCOUNT +endif # Do not use arch/arm/defconfig - it's always outdated. # Select a platform tht is kept up-to-date diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index c5191b1532e828..a4ff2b128d3dbd 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -697,6 +697,23 @@ proc_types: W(b) __armv4_mmu_cache_off W(b) __armv5tej_mmu_cache_flush +#ifdef CONFIG_CPU_MOHAWK_OLD_ID + .word 0x41159260 @ ARM926T (Marvell Mohawk) + .word 0xfffffff0 +#else + .word 0x56158000 + .word 0xfffff000 +#endif + b __armv4_mmu_cache_on + b __armv4_mmu_cache_off + b __armv5tej_mmu_cache_flush + + .word 0x56056930 + .word 0xff0ffff0 @ PXA935 + b __armv4_mmu_cache_on + b __armv4_mmu_cache_off + b __armv4_mmu_cache_flush + .word 0x56050000 @ Feroceon .word 0xff0f0000 W(b) __armv4_mmu_cache_on diff --git a/arch/arm/configs/msm_defconfig b/arch/arm/configs/msm_defconfig index fe25e3b9a45ab1..284f98887f1d3d 100644 --- a/arch/arm/configs/msm_defconfig +++ b/arch/arm/configs/msm_defconfig @@ -69,6 +69,7 @@ CONFIG_EPOLL=y CONFIG_SIGNALFD=y CONFIG_EVENTFD=y CONFIG_SHMEM=y +CONFIG_ASHMEM=y CONFIG_VM_EVENT_COUNTERS=y CONFIG_SLAB=y # CONFIG_SLUB is not set @@ -255,6 +256,7 @@ CONFIG_NET=y CONFIG_UNIX=y # CONFIG_NET_KEY is not set CONFIG_INET=y +CONFIG_ANDROID_PARANOID_NETWORK=y # CONFIG_IP_MULTICAST is not set # CONFIG_IP_ADVANCED_ROUTER is not set CONFIG_IP_FIB_HASH=y diff --git a/arch/arm/configs/pxa168_android_mlc_defconfig b/arch/arm/configs/pxa168_android_mlc_defconfig new file mode 100644 index 00000000000000..32071f09b36692 --- /dev/null +++ b/arch/arm/configs/pxa168_android_mlc_defconfig @@ -0,0 +1,2116 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.34 +# Tue Jul 27 10:54:47 2010 +# +CONFIG_ARM=y +CONFIG_HAVE_PWM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_RELAY=y +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="root" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ASHMEM=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_PROFILING=y +# CONFIG_OPROFILE is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_PREEMPT_NOTIFIERS=y +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +CONFIG_ARCH_MMP=y +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_MACH_TAVOREVB is not set +CONFIG_PXA_SSP=y +CONFIG_PXA_SSP_LEGACY=y + +# +# Marvell(R) Wireless Memory Management Technology +# +# CONFIG_IMM is not set + +# +# Marvell PXA168 Processor Variants +# +CONFIG_CPU_PXA168_B0=y + +# +# Marvell PXA168/910/MMP2 Implmentations +# +CONFIG_MACH_ASPENITE=y +CONFIG_MACH_ZYLONITE2=y +# CONFIG_MACH_IPCAM is not set +# CONFIG_MACH_DKB_GENERIC is not set +# CONFIG_MACH_TTC_DKB is not set +CONFIG_MACH_AVENGERS_LITE=y +# CONFIG_MACH_EDGE is not set +# CONFIG_MACH_TETON_BGA is not set +# CONFIG_MACH_FLINT is not set +# CONFIG_MACH_MARVELL_JASPER is not set +CONFIG_GLOBAL_PREEMPT_NOTIFIERS=y +CONFIG_CPU_PXA168=y +# CONFIG_PXA_32KTIMER is not set +# CONFIG_TIMER_SERVICES_MMP is not set +CONFIG_PLAT_PXA=y + +# +# Processor Type +# +CONFIG_CPU_MOHAWK=y +# CONFIG_CPU_MOHAWK_OLD_ID is not set +CONFIG_CPU_L2_CACHE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_IWMMXT=y +CONFIG_COMMON_CLKDEV=y +CONFIG_FORCE_MAX_ZONEORDER=15 + +# +# Bus support +# +# CONFIG_PCI is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="ubi.mtd=System ubi.mtd=Userdata ubi.mtd=MassStorage1 root=ubi0_0 rootfstype=ubifs ip=50.1.1.10:50.1.1.1::255.255.255.0::usb0:on console=ttyS0,115200 mem=256M uart_dma android" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SUSPEND is not set +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +# CONFIG_EARLYSUSPEND is not set +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_PM_PXA168=y +CONFIG_DVFM=y +CONFIG_DVFM_PXA168=y +CONFIG_DVFM_PXA168_LCDDMA_WR=y +CONFIG_MSPM=y +CONFIG_MSPM_PXA168=y +CONFIG_MSPM_PXA168_STATS=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +CONFIG_BT_HCIVHCI=y +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=m +# CONFIG_LIB80211_DEBUG is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set +CONFIG_PXA3XX_BBM=y + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_SAMSUNG_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_8G_MLC_NAND is not set +# CONFIG_MICRON_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_512M_SLC_NAND is not set +CONFIG_DEFAULT_NAND=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +CONFIG_MTD_UBI_DEBUG=y +CONFIG_MTD_UBI_DEBUG_MSG=y +# CONFIG_MTD_UBI_DEBUG_PARANOID is not set +# CONFIG_MTD_UBI_DEBUG_DISABLE_BGT is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES is not set + +# +# Additional UBI debugging messages +# +# CONFIG_MTD_UBI_DEBUG_MSG_BLD is not set +# CONFIG_MTD_UBI_DEBUG_MSG_EBA is not set +# CONFIG_MTD_UBI_DEBUG_MSG_WL is not set +# CONFIG_MTD_UBI_DEBUG_MSG_IO is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_PXA168_MSP is not set +CONFIG_MISC_DEVICES=y +# CONFIG_ANDROID_PMEM is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_KERNEL_DEBUGGER_CORE is not set +# CONFIG_ISL29003 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +CONFIG_SD8XXX_RFKILL=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_PXA168_ETH is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +CONFIG_WLAN_8688_SDIO=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPP_MPPE is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPPOLAC is not set +# CONFIG_PPPOPNS is not set +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_CIR is not set +CONFIG_INPUT_EVBUG=y +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +CONFIG_KEYBOARD_PXA27x=y +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +CONFIG_TOUCHSCREEN_TOUCHIT213=y +CONFIG_TSC2007=y +CONFIG_TOUCHSCREEN_TPO=y +# CONFIG_TOUCHSCREEN_W90X900 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_SENSOR is not set +CONFIG_AVENGERS_LITE_POWER_BUTTON=y +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set + +# +# Color Management Unit +# +# CONFIG_PXA_ICR is not set +# CONFIG_PXA_CNM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_PXA2XX=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_GENERIC_IRQ=y +# CONFIG_GPIO_PCA953X_IRQ is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_ADP5588 is not set +CONFIG_MAX8660=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_PCA9575 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +CONFIG_POWER_AVENGERS_LITE=y +CONFIG_BATTERY_ASPENITE=y +CONFIG_MCU_PM=y +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +CONFIG_BMA020=y +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_LIS3LV02D_I2C is not set +# CONFIG_SENSORS_CM3602 is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_AB4500_CORE is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA168=y +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +CONFIG_BACKLIGHT_PWM=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_FONT_6x11=y +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y + +# +# SoC Audio for the Intel PXA3xx +# +CONFIG_SND_PXA3XX_SOC=y +CONFIG_SND_PXA3XX_SOC_SSP=y +CONFIG_SND_PXA3XX_SOC_ASPENITE=y +CONFIG_SND_PXA168_SOC_AVLite=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_WM8960=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +# CONFIG_HID_3M_PCT is not set +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_KYE is not set +CONFIG_HID_GYRATION=y +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_HID_MAGICMOUSE is not set +CONFIG_HID_MICROSOFT=y +# CONFIG_HID_MOSART is not set +CONFIG_HID_MONTEREY=y +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +# CONFIG_HID_QUANTA is not set +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +# CONFIG_HID_STANTUM is not set +CONFIG_HID_SUNPLUS=y +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_ZEROPLUS is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_PXA_U2H=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_GADGET_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=y +# CONFIG_USB_SERIAL_CONSOLE is not set +# CONFIG_USB_EZUSB is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIEMENS_MPI is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +CONFIG_USB_SERIAL_OPTION=y +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_VIVOPAY_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA27X is not set +CONFIG_USB_GADGET_PXA_U2O=y +CONFIG_USB_PXA_U2O=y +CONFIG_USB_COMPOSITE=y +# CONFIG_USB_GADGET_S3C_HSOTG is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_ANDROID is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_NOKIA is not set +# CONFIG_USB_G_MULTI is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_PARANOID_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA_SDH=y +# CONFIG_MMC3 is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +# CONFIG_MMC_SDHCI_PLTFM is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +CONFIG_SWITCH=y +# CONFIG_SWITCH_GPIO is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_DS1307=y +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_MMP is not set +# CONFIG_RTC_DRV_EC is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT=y +CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR=0 +CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE=0 +CONFIG_ANDROID_TIMED_OUTPUT=y +# CONFIG_ANDROID_TIMED_GPIO is not set +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_USB_IP_COMMON is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_TRANZPORT is not set + +# +# Qualcomm MSM Camera And Video +# + +# +# Camera Sensor Selection +# +# CONFIG_POHMELFS is not set +# CONFIG_LINE6_USB is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set +# CONFIG_USB_SERIAL_QUATECH_USB2 is not set +# CONFIG_VT6656 is not set +# CONFIG_FB_UDL is not set + +# +# RAR Register Driver +# +# CONFIG_RAR_REGISTER is not set +# CONFIG_IIO is not set +# CONFIG_RAMZSWAP is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_STRIP is not set +# CONFIG_FB_SM7XX is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UBIFS_FS_DEBUG=y +CONFIG_UBIFS_FS_DEBUG_MSG_LVL=0 +# CONFIG_UBIFS_FS_DEBUG_CHKS is not set +# CONFIG_LOGFS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +CONFIG_NLS_CODEPAGE_936=y +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_EARLY_PRINTK is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y diff --git a/arch/arm/configs/pxa168_android_mmc_defconfig b/arch/arm/configs/pxa168_android_mmc_defconfig new file mode 100644 index 00000000000000..3d25c690d28d4d --- /dev/null +++ b/arch/arm/configs/pxa168_android_mmc_defconfig @@ -0,0 +1,2049 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.29 +# Mon Dec 21 18:26:22 2009 +# +CONFIG_ARM=y +CONFIG_HAVE_PWM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_CLASSIC_RCU=y +# CONFIG_TREE_RCU is not set +# CONFIG_PREEMPT_RCU is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_PREEMPT_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_GROUP_SCHED is not set +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ASHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_PROFILING=y +# CONFIG_OPROFILE is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_PREEMPT_NOTIFIERS=y +CONFIG_FREEZER=y + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_ORION5X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +CONFIG_ARCH_MMP=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_W90X900 is not set +CONFIG_MACH_TAVOREVB=y +CONFIG_PXA_SSP=y + +# +# Marvell(R) Wireless Memory Management Technology +# +CONFIG_IMM=y +# CONFIG_IMM_DEBUG is not set +CONFIG_IMM_API_SRAM=y + +# +# Marvell PXA168 Implmentations +# +CONFIG_MACH_ASPENITE=y +CONFIG_MACH_ZYLONITE2=y +CONFIG_MACH_TETON=y +# CONFIG_MACH_IPCAM is not set +# CONFIG_MACH_DKB_GENERIC is not set +CONFIG_MACH_TTC_DKB=y +CONFIG_MACH_AVENGERS_LITE=y +CONFIG_MACH_EDGE=y +CONFIG_GLOBAL_PREEMPT_NOTIFIERS=y +CONFIG_CPU_PXA168=y +CONFIG_CPU_PXA910=y +# CONFIG_PXA_32KTIMER is not set +CONFIG_PLAT_PXA=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_MOHAWK=y +# CONFIG_CPU_MOHAWK_OLD_ID is not set +CONFIG_CPU_L2_CACHE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_PABRT_NOIFAR=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +CONFIG_IWMMXT=y +CONFIG_COMMON_CLKDEV=y +CONFIG_FORCE_MAX_ZONEORDER=15 + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +CONFIG_ARCH_FLATMEM_HAS_HOLES=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +CONFIG_UNEVICTABLE_LRU=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="rootdelay=5 root=/dev/mmcblk0 ip=50.1.1.10:50.1.1.1::255.255.255.0::usb0:on console=ttyS1,115200 mem=256M uart_dma pxastorage=1G init=/init android" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND=y +CONFIG_SUSPEND_FREEZER=y +CONFIG_HAS_WAKELOCK=y +CONFIG_HAS_EARLYSUSPEND=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +CONFIG_EARLYSUSPEND=y +# CONFIG_NO_USER_SPACE_SCREEN_ACCESS_CONTROL is not set +CONFIG_CONSOLE_EARLYSUSPEND=y +# CONFIG_FB_EARLYSUSPEND is not set +# CONFIG_APM_EMULATION is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_PM_PXA168=y +# CONFIG_PM_PXA910 is not set +CONFIG_DVFM=y +CONFIG_DVFM_PXA168=y +# CONFIG_DVFM_PXA910 is not set +CONFIG_MSPM=y +CONFIG_MSPM_PXA168=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_COMPAT_NET_DEV_OPS=y +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +CONFIG_BT_HCIVHCI=y +# CONFIG_AF_RXRPC is not set +# CONFIG_PHONET is not set +CONFIG_WIRELESS=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_OLD_REGULATORY=y +CONFIG_WIRELESS_EXT=y +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=y +# CONFIG_LIB80211_DEBUG is not set +# CONFIG_MAC80211 is not set +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_TESTS is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set +CONFIG_PXA3XX_BBM=y + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_M25PXX_USE_FAST_READ is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_SAMSUNG_32G_MLC_NAND is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +CONFIG_MTD_ONENAND=y +# CONFIG_MTD_ONENAND_VERIFY_WRITE is not set +CONFIG_MTD_ONENAND_GENERIC=y +# CONFIG_MTD_ONENAND_OTP is not set +# CONFIG_MTD_ONENAND_2X_PROGRAM is not set +# CONFIG_MTD_ONENAND_SIM is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +CONFIG_MTD_UBI_DEBUG=y +CONFIG_MTD_UBI_DEBUG_MSG=y +# CONFIG_MTD_UBI_DEBUG_PARANOID is not set +# CONFIG_MTD_UBI_DEBUG_DISABLE_BGT is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES is not set + +# +# Additional UBI debugging messages +# +# CONFIG_MTD_UBI_DEBUG_MSG_BLD is not set +# CONFIG_MTD_UBI_DEBUG_MSG_EBA is not set +# CONFIG_MTD_UBI_DEBUG_MSG_WL is not set +# CONFIG_MTD_UBI_DEBUG_MSG_IO is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_PXA168_MSP is not set +CONFIG_MISC_DEVICES=y +CONFIG_ANDROID_PMEM=y +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_KERNEL_DEBUGGER_CORE is not set +# CONFIG_UID_STAT is not set +# CONFIG_WL127X_RFKILL is not set +CONFIG_SD8XXX_RFKILL=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_93CX6 is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_PXA168_ETH is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +CONFIG_WLAN_80211=y +# CONFIG_WLAN_8688_SDIO is not set +CONFIG_LIBERTAS=m +# CONFIG_LIBERTAS_USB is not set +CONFIG_LIBERTAS_SDIO=m +CONFIG_LIBERTAS_DEBUG=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_NET_RNDIS_WLAN is not set +# CONFIG_IWLWIFI_LEDS is not set +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPP_MPPE is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPPOLAC is not set +# CONFIG_PPPOPNS is not set +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +CONFIG_INPUT_POLLDEV=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_CIR is not set +CONFIG_INPUT_EVBUG=y +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_KEYBOARD_PXA27x=y +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +CONFIG_TOUCHSCREEN_TOUCHIT213=y +CONFIG_TSC2007=y +CONFIG_TOUCHSCREEN_SANREMO=y +CONFIG_TOUCHSCREEN_TPO=y +CONFIG_TOUCHSCREEN_MICCO=y +CONFIG_TOUCHSCREEN_MICCO_TIMER=y +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_SENSOR is not set +CONFIG_AVENGERS_LITE_POWER_BUTTON=y + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set +CONFIG_PXA930_ACIPC=y +# CONFIG_PXA910_IRE is not set + +# +# Color Management Unit +# +# CONFIG_PXA_ICR is not set +# CONFIG_PXA_CNM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +CONFIG_SANREMO=y +CONFIG_PORTOFINO=y +CONFIG_MICCO=y +CONFIG_MAX8660=y +CONFIG_MCU_PM=y +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_PXA2XX=y + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX732X is not set +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_GENERIC_IRQ=y +# CONFIG_GPIO_PCF857X is not set + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_PCA9575 is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_SANREMO is not set +# CONFIG_BATTERY_BQ27x00 is not set +CONFIG_POWER_AVENGERS_LITE=y +CONFIG_BATTERY_ASPENITE=y +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +CONFIG_BMA020=y +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_LIS3LV02D_I2C is not set +# CONFIG_SENSORS_CM3602 is not set +# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_HWMON is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_PCF50633 is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L2_COMMON=y +CONFIG_VIDEO_ALLOW_V4L1=y +CONFIG_VIDEO_V4L1_COMPAT=y +# CONFIG_DVB_CORE is not set +CONFIG_VIDEO_MEDIA=y + +# +# Multimedia drivers +# +# CONFIG_MEDIA_ATTACH is not set +CONFIG_MEDIA_TUNER=y +# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set +CONFIG_MEDIA_TUNER_SIMPLE=y +CONFIG_MEDIA_TUNER_TDA8290=y +CONFIG_MEDIA_TUNER_TDA9887=y +CONFIG_MEDIA_TUNER_TEA5761=y +CONFIG_MEDIA_TUNER_TEA5767=y +CONFIG_MEDIA_TUNER_MT20XX=y +CONFIG_MEDIA_TUNER_XC2028=y +CONFIG_MEDIA_TUNER_XC5000=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_V4L1=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set +# CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set + +# +# Encoders/decoders and other helper chips +# + +# +# Audio decoders +# +# CONFIG_VIDEO_TVAUDIO is not set +# CONFIG_VIDEO_TDA7432 is not set +# CONFIG_VIDEO_TDA9840 is not set +# CONFIG_VIDEO_TDA9875 is not set +# CONFIG_VIDEO_TEA6415C is not set +# CONFIG_VIDEO_TEA6420 is not set +# CONFIG_VIDEO_MSP3400 is not set +# CONFIG_VIDEO_CS5345 is not set +# CONFIG_VIDEO_CS53L32A is not set +# CONFIG_VIDEO_M52790 is not set +# CONFIG_VIDEO_TLV320AIC23B is not set +# CONFIG_VIDEO_WM8775 is not set +# CONFIG_VIDEO_WM8739 is not set +# CONFIG_VIDEO_VP27SMPX is not set + +# +# Video decoders +# +# CONFIG_VIDEO_BT819 is not set +# CONFIG_VIDEO_BT856 is not set +# CONFIG_VIDEO_BT866 is not set +# CONFIG_VIDEO_KS0127 is not set +CONFIG_VIDEO_OV3640=y +CONFIG_VIDEO_OV7670=m +CONFIG_VIDEO_OV7660=y +# CONFIG_VIDEO_SIV120A is not set +CONFIG_VIDEO_OV7740=y +# CONFIG_VIDEO_TCM825X is not set +# CONFIG_VIDEO_SAA7110 is not set +# CONFIG_VIDEO_SAA7111 is not set +# CONFIG_VIDEO_SAA7114 is not set +# CONFIG_VIDEO_SAA711X is not set +# CONFIG_VIDEO_SAA717X is not set +# CONFIG_VIDEO_SAA7191 is not set +# CONFIG_VIDEO_TVP514X is not set +# CONFIG_VIDEO_TVP5150 is not set +# CONFIG_VIDEO_VPX3220 is not set + +# +# Video and audio decoders +# +# CONFIG_VIDEO_CX25840 is not set + +# +# MPEG video encoders +# +# CONFIG_VIDEO_CX2341X is not set + +# +# Video encoders +# +# CONFIG_VIDEO_SAA7127 is not set +# CONFIG_VIDEO_SAA7185 is not set +# CONFIG_VIDEO_ADV7170 is not set +# CONFIG_VIDEO_ADV7175 is not set + +# +# Video improvement chips +# +# CONFIG_VIDEO_UPD64031A is not set +# CONFIG_VIDEO_UPD64083 is not set +# CONFIG_VIDEO_VIVI is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_CPIA2 is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_SOC_CAMERA is not set +CONFIG_V4L_USB_DRIVERS=y +CONFIG_USB_VIDEO_CLASS=y +CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y +# CONFIG_USB_GSPCA is not set +# CONFIG_VIDEO_PVRUSB2 is not set +# CONFIG_VIDEO_EM28XX is not set +# CONFIG_VIDEO_USBVISION is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_QUICKCAM_MESSENGER is not set +# CONFIG_USB_ET61X251 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_STV680 is not set +# CONFIG_USB_ZC0301 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_ZR364XX is not set +# CONFIG_USB_STKWEBCAM is not set +# CONFIG_USB_S2255 is not set +CONFIG_PXA168_CAMERA=y +# CONFIG_CLI5001_ISP is not set +CONFIG_CMMB=y +CONFIG_CMMB_IF101=y +CONFIG_CMMB_CA=y +CONFIG_RADIO_ADAPTERS=y +# CONFIG_USB_DSBR is not set +# CONFIG_USB_SI470X is not set +# CONFIG_USB_MR800 is not set +# CONFIG_RADIO_TEA5764 is not set +# CONFIG_RADIO_SI4703 is not set +# CONFIG_RADIO_FM_EURO is not set +# CONFIG_RADIO_FM_JAPAN_WIDEBAND is not set +# CONFIG_RADIO_FM_JAPAN is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA168=y +# CONFIG_FB_PXA910 is not set +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +CONFIG_BACKLIGHT_PWM=y +CONFIG_BACKLIGHT_PORTOFINO=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +# CONFIG_FONT_8x16 is not set +CONFIG_FONT_6x11=y +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y + +# +# SoC Audio for the Intel PXA3xx +# +CONFIG_SND_PXA3XX_SOC=y +CONFIG_SND_PXA3XX_SOC_SSP=y +CONFIG_SND_PXA3XX_SOC_ASPENITE=y +CONFIG_SND_PXA3XX_SOC_TETON=y +CONFIG_SND_PXA3XX_SOC_EDGE=y + +# +# SoC Audio for the Marvell PXA910 +# +CONFIG_SND_PXA910_SOC=y +CONFIG_SND_PXA910_SOC_TTC_DKB=y +CONFIG_SND_PXA168_SOC_AVLite=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_WM8960=y +CONFIG_SND_SOC_WM8978=y +CONFIG_SND_SOC_SANREMO_AUDIO=y +CONFIG_SND_SOC_CS4344=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +CONFIG_HID_COMPAT=y +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_EZKEY=y +CONFIG_HID_GYRATION=y +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y +# CONFIG_HID_NTRIG is not set +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +CONFIG_HID_SUNPLUS=y +# CONFIG_GREENASIA_FF is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_THRUSTMASTER_FF is not set +# CONFIG_ZEROPLUS_FF is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_PXA_U2H=y +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_GADGET_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed; +# + +# +# see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=y +# CONFIG_USB_SERIAL_CONSOLE is not set +# CONFIG_USB_EZUSB is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP2101 is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIEMENS_MPI is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +CONFIG_USB_SERIAL_OPTION=y +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_VST is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_PXA27X is not set +CONFIG_USB_GADGET_PXA_U2O=y +CONFIG_USB_PXA_U2O=y +CONFIG_USB_COMPOSITE=y +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +CONFIG_USB_ANDROID=y +# CONFIG_USB_CDC_COMPOSITE is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_PARANOID_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA_SDH=y +# CONFIG_MMC3 is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +# CONFIG_ACCESSIBILITY is not set +# CONFIG_NEW_LEDS is not set +CONFIG_SWITCH=y +# CONFIG_SWITCH_GPIO is not set +# CONFIG_SWITCH_HEADSET is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_ALARM=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_DS1307=y +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_MMP is not set +CONFIG_RTC_DRV_EC=y +# CONFIG_DMADEVICES is not set +# CONFIG_REGULATOR is not set +# CONFIG_UIO is not set +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set +# CONFIG_MEILHAUS is not set +# CONFIG_USB_IP_COMMON is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_USB_ATMEL is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_INPUT_MIMIO is not set +# CONFIG_TRANZPORT is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT=y +CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR=0 +CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE=0 +CONFIG_ANDROID_TIMED_OUTPUT=y +# CONFIG_ANDROID_TIMED_GPIO is not set +# CONFIG_ANDROID_TIMED_VIBRATOR is not set +CONFIG_ANDROID_LOW_MEMORY_KILLER=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +CONFIG_FILE_LOCKING=y +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +CONFIG_GENERIC_ACL=y + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UBIFS_FS_DEBUG=y +CONFIG_UBIFS_FS_DEBUG_MSG_LVL=0 +# CONFIG_UBIFS_FS_DEBUG_CHKS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +# CONFIG_SUNRPC_REGISTER_V4 is not set +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +CONFIG_NLS_CODEPAGE_936=y +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y + +# +# Tracers +# +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_CONTEXT_SWITCH_TRACER is not set +# CONFIG_BOOT_TRACER is not set +# CONFIG_TRACE_BRANCH_PROFILING is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_DYNAMIC_PRINTK_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_FIPS is not set +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/arch/arm/configs/pxa168_avengers_lite_defconfig b/arch/arm/configs/pxa168_avengers_lite_defconfig new file mode 100644 index 00000000000000..c6cdb9ee750640 --- /dev/null +++ b/arch/arm/configs/pxa168_avengers_lite_defconfig @@ -0,0 +1,2115 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.34 +# Tue Jul 27 10:54:47 2010 +# +CONFIG_ARM=y +CONFIG_HAVE_PWM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_HAVE_LATENCYTOP_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +CONFIG_CONSTRUCTORS=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_KERNEL_GZIP=y +# CONFIG_KERNEL_BZIP2 is not set +# CONFIG_KERNEL_LZMA is not set +# CONFIG_KERNEL_LZO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set + +# +# RCU Subsystem +# +CONFIG_TREE_RCU=y +# CONFIG_TREE_PREEMPT_RCU is not set +# CONFIG_TINY_RCU is not set +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_FANOUT=32 +# CONFIG_RCU_FANOUT_EXACT is not set +# CONFIG_TREE_RCU_TRACE is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +CONFIG_RELAY=y +# CONFIG_NAMESPACES is not set +# CONFIG_BLK_DEV_INITRD is not set +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_AIO=y +CONFIG_ASHMEM=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y + +# +# Kernel Performance Events And Counters +# +CONFIG_PERF_EVENTS=y +# CONFIG_PERF_COUNTERS is not set +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_COMPAT_BRK=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_PROFILING=y +# CONFIG_OPROFILE is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_KPROBES is not set +CONFIG_HAVE_KPROBES=y +CONFIG_HAVE_KRETPROBES=y +CONFIG_HAVE_CLK=y + +# +# GCOV-based kernel profiling +# +# CONFIG_GCOV_KERNEL is not set +# CONFIG_SLOW_WORK is not set +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_BLOCK=y +CONFIG_LBDAF=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_BLK_DEV_INTEGRITY is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_PREEMPT_NOTIFIERS=y +# CONFIG_INLINE_SPIN_TRYLOCK is not set +# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK is not set +# CONFIG_INLINE_SPIN_LOCK_BH is not set +# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +CONFIG_INLINE_SPIN_UNLOCK=y +# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_READ_TRYLOCK is not set +# CONFIG_INLINE_READ_LOCK is not set +# CONFIG_INLINE_READ_LOCK_BH is not set +# CONFIG_INLINE_READ_LOCK_IRQ is not set +# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +CONFIG_INLINE_READ_UNLOCK=y +# CONFIG_INLINE_READ_UNLOCK_BH is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y +# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +# CONFIG_INLINE_WRITE_TRYLOCK is not set +# CONFIG_INLINE_WRITE_LOCK is not set +# CONFIG_INLINE_WRITE_LOCK_BH is not set +# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +CONFIG_INLINE_WRITE_UNLOCK=y +# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +# CONFIG_MUTEX_SPIN_ON_OWNER is not set +# CONFIG_FREEZER is not set + +# +# System Type +# +CONFIG_MMU=y +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_GEMINI is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_STMP3XXX is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_DOVE is not set +# CONFIG_ARCH_KIRKWOOD is not set +# CONFIG_ARCH_LOKI is not set +# CONFIG_ARCH_MV78XX0 is not set +# CONFIG_ARCH_ORION5X is not set +CONFIG_ARCH_MMP=y +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set +# CONFIG_ARCH_PNX4008 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5P6442 is not set +# CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_S5PV210 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_MACH_TAVOREVB is not set +CONFIG_PXA_SSP=y +CONFIG_PXA_SSP_LEGACY=y + +# +# Marvell(R) Wireless Memory Management Technology +# +# CONFIG_IMM is not set + +# +# Marvell PXA168 Processor Variants +# +CONFIG_CPU_PXA168_B0=y + +# +# Marvell PXA168/910/MMP2 Implmentations +# +CONFIG_MACH_ASPENITE=y +CONFIG_MACH_ZYLONITE2=y +# CONFIG_MACH_IPCAM is not set +# CONFIG_MACH_DKB_GENERIC is not set +# CONFIG_MACH_TTC_DKB is not set +CONFIG_MACH_AVENGERS_LITE=y +# CONFIG_MACH_EDGE is not set +# CONFIG_MACH_TETON_BGA is not set +# CONFIG_MACH_FLINT is not set +# CONFIG_MACH_MARVELL_JASPER is not set +CONFIG_GLOBAL_PREEMPT_NOTIFIERS=y +CONFIG_CPU_PXA168=y +# CONFIG_PXA_32KTIMER is not set +# CONFIG_TIMER_SERVICES_MMP is not set +CONFIG_PLAT_PXA=y + +# +# Processor Type +# +CONFIG_CPU_MOHAWK=y +# CONFIG_CPU_MOHAWK_OLD_ID is not set +CONFIG_CPU_L2_CACHE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_PABRT_LEGACY=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set +CONFIG_ARM_L1_CACHE_SHIFT=5 +CONFIG_IWMMXT=y +CONFIG_COMMON_CLKDEV=y +CONFIG_FORCE_MAX_ZONEORDER=15 + +# +# Bus support +# +# CONFIG_PCI is not set +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_VMSPLIT_3G=y +# CONFIG_VMSPLIT_2G is not set +# CONFIG_VMSPLIT_1G is not set +CONFIG_PAGE_OFFSET=0xC0000000 +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +# CONFIG_HIGHMEM is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=999999 +# CONFIG_PHYS_ADDR_T_64BIT is not set +CONFIG_ZONE_DMA_FLAG=0 +CONFIG_VIRT_TO_BUS=y +# CONFIG_KSM is not set +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +CONFIG_ALIGNMENT_TRAP=y +# CONFIG_UACCESS_WITH_MEMCPY is not set + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/mmcblk0p1 rootfstype=ext3 ip=50.1.1.10:50.1.1.1::255.255.255.0::usb0:on console=ttyS0,115200 mem=256M uart_dma pxastorage=1G rootdelay=3 android" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Power Management +# +# CONFIG_CPU_IDLE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y +CONFIG_HAVE_AOUT=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_DEBUG is not set +# CONFIG_SUSPEND is not set +CONFIG_HAS_WAKELOCK=y +CONFIG_WAKELOCK=y +CONFIG_WAKELOCK_STAT=y +CONFIG_USER_WAKELOCK=y +# CONFIG_EARLYSUSPEND is not set +# CONFIG_APM_EMULATION is not set +# CONFIG_PM_RUNTIME is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_PM_PXA168=y +CONFIG_DVFM=y +CONFIG_DVFM_PXA168=y +CONFIG_DVFM_PXA168_LCDDMA_WR=y +CONFIG_MSPM=y +CONFIG_MSPM_PXA168=y +CONFIG_MSPM_PXA168_STATS=y +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_RDS is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_NET_DSA is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_PHONET is not set +# CONFIG_IEEE802154 is not set +# CONFIG_NET_SCHED is not set +# CONFIG_DCB is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIBTUSB is not set +# CONFIG_BT_HCIBTSDIO is not set +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_BCSP=y +CONFIG_BT_HCIUART_LL=y +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +CONFIG_BT_HCIVHCI=y +# CONFIG_BT_MRVL is not set +# CONFIG_AF_RXRPC is not set +CONFIG_WIRELESS=y +CONFIG_WIRELESS_EXT=y +CONFIG_WEXT_CORE=y +CONFIG_WEXT_PROC=y +CONFIG_WEXT_SPY=y +CONFIG_WEXT_PRIV=y +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT_SYSFS=y +CONFIG_LIB80211=m +# CONFIG_LIB80211_DEBUG is not set + +# +# CFG80211 needs to be enabled for MAC80211 +# +# CONFIG_WIMAX is not set +CONFIG_RFKILL=y +CONFIG_RFKILL_PM=y +# CONFIG_RFKILL_INPUT is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +# CONFIG_DEVTMPFS is not set +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +CONFIG_FW_LOADER=y +CONFIG_FIRMWARE_IN_KERNEL=y +CONFIG_EXTRA_FIRMWARE="" +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set +CONFIG_PXA3XX_BBM=y + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SST25L is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_SAMSUNG_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_8G_MLC_NAND is not set +# CONFIG_MICRON_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_512M_SLC_NAND is not set +CONFIG_DEFAULT_NAND=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +CONFIG_MTD_UBI_DEBUG=y +CONFIG_MTD_UBI_DEBUG_MSG=y +# CONFIG_MTD_UBI_DEBUG_PARANOID is not set +# CONFIG_MTD_UBI_DEBUG_DISABLE_BGT is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES is not set +# CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES is not set + +# +# Additional UBI debugging messages +# +# CONFIG_MTD_UBI_DEBUG_MSG_BLD is not set +# CONFIG_MTD_UBI_DEBUG_MSG_EBA is not set +# CONFIG_MTD_UBI_DEBUG_MSG_WL is not set +# CONFIG_MTD_UBI_DEBUG_MSG_IO is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +# CONFIG_PXA168_MSP is not set +CONFIG_MISC_DEVICES=y +# CONFIG_ANDROID_PMEM is not set +# CONFIG_AD525X_DPOT is not set +# CONFIG_ICS932S401 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +# CONFIG_KERNEL_DEBUGGER_CORE is not set +# CONFIG_ISL29003 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_DS1682 is not set +# CONFIG_TI_DAC7512 is not set +# CONFIG_UID_STAT is not set +# CONFIG_WL127X_RFKILL is not set +# CONFIG_APANIC is not set +CONFIG_SD8XXX_RFKILL=y +# CONFIG_C2PORT is not set + +# +# EEPROM support +# +# CONFIG_EEPROM_AT24 is not set +# CONFIG_EEPROM_AT25 is not set +# CONFIG_EEPROM_LEGACY is not set +# CONFIG_EEPROM_MAX6875 is not set +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_IWMC3200TOP is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI_MOD=y +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_LIBFC is not set +# CONFIG_LIBFCOE is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +CONFIG_SMC91X=y +# CONFIG_PXA168_ETH is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_ETHOC is not set +# CONFIG_SMC911X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DNET is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +# CONFIG_B44 is not set +# CONFIG_KS8842 is not set +# CONFIG_KS8851 is not set +# CONFIG_KS8851_MLL is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +CONFIG_WLAN=y +CONFIG_WLAN_8688_SDIO=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_HOSTAP is not set + +# +# Enable WiMAX (Networking options) to see the WiMAX drivers +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_HSO is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_SYNC_TTY=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPP_MPPE is not set +# CONFIG_PPPOE is not set +# CONFIG_PPPOL2TP is not set +# CONFIG_PPPOLAC is not set +# CONFIG_PPPOPNS is not set +# CONFIG_SLIP is not set +CONFIG_SLHC=y +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +CONFIG_INPUT_POLLDEV=y +# CONFIG_INPUT_SPARSEKMAP is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_CIR is not set +CONFIG_INPUT_EVBUG=y +# CONFIG_INPUT_KEYRESET is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +CONFIG_KEYBOARD_PXA27x=y +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_AD7877 is not set +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879_SPI is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +CONFIG_TOUCHSCREEN_TOUCHIT213=y +CONFIG_TSC2007=y +CONFIG_TOUCHSCREEN_TPO=y +# CONFIG_TOUCHSCREEN_W90X900 is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_CM109 is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_INPUT_GPIO is not set +# CONFIG_INPUT_KEYCHORD is not set +# CONFIG_INPUT_SENSOR is not set +CONFIG_AVENGERS_LITE_POWER_BUTTON=y +# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVMEM=y +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_MAX3100 is not set +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +# CONFIG_DCC_TTY is not set + +# +# Color Management Unit +# +# CONFIG_PXA_ICR is not set +# CONFIG_PXA_CNM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +# CONFIG_I2C_CHARDEV is not set +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +# CONFIG_SPI_BITBANG is not set +# CONFIG_SPI_GPIO is not set +CONFIG_SPI_PXA2XX=y +# CONFIG_SPI_XILINX is not set +# CONFIG_SPI_DESIGNWARE is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set + +# +# PPS support +# +# CONFIG_PPS is not set +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_GPIOLIB=y +# CONFIG_DEBUG_GPIO is not set +# CONFIG_GPIO_SYSFS is not set + +# +# Memory mapped GPIO expanders: +# +# CONFIG_GPIO_IT8761E is not set + +# +# I2C GPIO expanders: +# +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_GENERIC_IRQ=y +# CONFIG_GPIO_PCA953X_IRQ is not set +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_ADP5588 is not set +CONFIG_MAX8660=y + +# +# PCI GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_GPIO_MAX7301 is not set +# CONFIG_GPIO_MCP23S08 is not set +# CONFIG_GPIO_PCA9575 is not set +# CONFIG_GPIO_MC33880 is not set + +# +# AC97 GPIO expanders: +# +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_DS2782 is not set +# CONFIG_BATTERY_BQ27x00 is not set +# CONFIG_BATTERY_MAX17040 is not set +CONFIG_POWER_AVENGERS_LITE=y +CONFIG_BATTERY_ASPENITE=y +CONFIG_MCU_PM=y +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +CONFIG_BMA020=y +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Native drivers +# +# CONFIG_SENSORS_AD7414 is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADCXX is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7411 is not set +# CONFIG_SENSORS_ADT7462 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7475 is not set +# CONFIG_SENSORS_ASC7621 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_G760A is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM73 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_LTC4215 is not set +# CONFIG_SENSORS_LTC4245 is not set +# CONFIG_SENSORS_LM95241 is not set +# CONFIG_SENSORS_MAX1111 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_SHT15 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_AMC6821 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_TMP401 is not set +# CONFIG_SENSORS_TMP421 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_SENSORS_LIS3_SPI is not set +# CONFIG_SENSORS_LIS3_I2C is not set +# CONFIG_SENSORS_LIS3LV02D_I2C is not set +# CONFIG_SENSORS_CM3602 is not set +# CONFIG_THERMAL is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_MAX63XX_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_SSB_POSSIBLE=y + +# +# Sonics Silicon Backplane +# +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set +# CONFIG_MFD_SM501 is not set +# CONFIG_MFD_ASIC3 is not set +# CONFIG_HTC_EGPIO is not set +# CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set +# CONFIG_MFD_TMIO is not set +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +# CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_MFD_MC13783 is not set +# CONFIG_AB3100_CORE is not set +# CONFIG_EZX_PCAP is not set +# CONFIG_AB4500_CORE is not set +# CONFIG_REGULATOR is not set +# CONFIG_MEDIA_SUPPORT is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_TILEBLITTING=y + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA168=y +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 is not set +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y +CONFIG_BACKLIGHT_PWM=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_FONT_6x11=y +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_SPI=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y + +# +# SoC Audio for the Intel PXA3xx +# +CONFIG_SND_PXA3XX_SOC=y +CONFIG_SND_PXA3XX_SOC_SSP=y +CONFIG_SND_PXA3XX_SOC_ASPENITE=y +CONFIG_SND_PXA168_SOC_AVLite=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_WM8960=y +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_HID_PID is not set +# CONFIG_USB_HIDDEV is not set + +# +# Special HID drivers +# +# CONFIG_HID_3M_PCT is not set +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +# CONFIG_HID_DRAGONRISE is not set +CONFIG_HID_EZKEY=y +# CONFIG_HID_KYE is not set +CONFIG_HID_GYRATION=y +# CONFIG_HID_TWINHAN is not set +# CONFIG_HID_KENSINGTON is not set +CONFIG_HID_LOGITECH=y +# CONFIG_LOGITECH_FF is not set +# CONFIG_LOGIRUMBLEPAD2_FF is not set +# CONFIG_LOGIG940_FF is not set +# CONFIG_HID_MAGICMOUSE is not set +CONFIG_HID_MICROSOFT=y +# CONFIG_HID_MOSART is not set +CONFIG_HID_MONTEREY=y +# CONFIG_HID_NTRIG is not set +# CONFIG_HID_ORTEK is not set +CONFIG_HID_PANTHERLORD=y +# CONFIG_PANTHERLORD_FF is not set +CONFIG_HID_PETALYNX=y +# CONFIG_HID_QUANTA is not set +CONFIG_HID_SAMSUNG=y +CONFIG_HID_SONY=y +# CONFIG_HID_STANTUM is not set +CONFIG_HID_SUNPLUS=y +# CONFIG_HID_GREENASIA is not set +# CONFIG_HID_SMARTJOYPLUS is not set +# CONFIG_HID_TOPSEED is not set +# CONFIG_HID_THRUSTMASTER is not set +# CONFIG_HID_WACOM is not set +# CONFIG_HID_ZEROPLUS is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +# CONFIG_USB_OTG_BLACKLIST_HUB is not set +CONFIG_USB_MON=y +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_EHCI_TT_NEWSCHED=y +CONFIG_USB_EHCI_PXA_U2H=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set +# CONFIG_USB_GADGET_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +CONFIG_USB_SERIAL=y +# CONFIG_USB_SERIAL_CONSOLE is not set +# CONFIG_USB_EZUSB is not set +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP210X is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_FUNSOFT is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_IUU is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_MOTOROLA is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_QCAUX is not set +# CONFIG_USB_SERIAL_QUALCOMM is not set +# CONFIG_USB_SERIAL_SPCP8X5 is not set +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_SIEMENS_MPI is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +# CONFIG_USB_SERIAL_SYMBOL is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +CONFIG_USB_SERIAL_OPTION=y +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_SERIAL_OPTICON is not set +# CONFIG_USB_SERIAL_VIVOPAY_SERIAL is not set +# CONFIG_USB_SERIAL_DEBUG is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG is not set +# CONFIG_USB_GADGET_DEBUG_FILES is not set +# CONFIG_USB_GADGET_DEBUG_FS is not set +CONFIG_USB_GADGET_VBUS_DRAW=2 +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_PXA25X is not set +# CONFIG_USB_GADGET_R8A66597 is not set +# CONFIG_USB_GADGET_PXA27X is not set +CONFIG_USB_GADGET_PXA_U2O=y +CONFIG_USB_PXA_U2O=y +CONFIG_USB_COMPOSITE=y +# CONFIG_USB_GADGET_S3C_HSOTG is not set +# CONFIG_USB_GADGET_IMX is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_FSL_QE is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LANGWELL is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +CONFIG_USB_GADGET_DUALSPEED=y +# CONFIG_USB_ZERO is not set +# CONFIG_USB_AUDIO is not set +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_MASS_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set +# CONFIG_USB_MIDI_GADGET is not set +# CONFIG_USB_G_PRINTER is not set +# CONFIG_USB_ANDROID is not set +# CONFIG_USB_CDC_COMPOSITE is not set +# CONFIG_USB_G_NOKIA is not set +# CONFIG_USB_G_MULTI is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_PARANOID_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA_SDH=y +# CONFIG_MMC3 is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +# CONFIG_MMC_SDHCI_PLTFM is not set +# CONFIG_MMC_SPI is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +CONFIG_SWITCH=y +# CONFIG_SWITCH_GPIO is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +CONFIG_RTC_INTF_ALARM=y +CONFIG_RTC_INTF_ALARM_DEV=y +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +CONFIG_RTC_DRV_DS1307=y +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_BQ32K is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set +# CONFIG_RTC_DRV_RX8581 is not set +# CONFIG_RTC_DRV_RX8025 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_M41T94 is not set +# CONFIG_RTC_DRV_DS1305 is not set +# CONFIG_RTC_DRV_DS1390 is not set +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set +# CONFIG_RTC_DRV_DS3234 is not set +# CONFIG_RTC_DRV_PCF2123 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1286 is not set +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T35 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_MSM6242 is not set +# CONFIG_RTC_DRV_BQ4802 is not set +# CONFIG_RTC_DRV_RP5C01 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +# CONFIG_RTC_DRV_MMP is not set +# CONFIG_RTC_DRV_EC is not set +# CONFIG_DMADEVICES is not set +# CONFIG_AUXDISPLAY is not set +# CONFIG_UIO is not set + +# +# TI VLYNQ +# +CONFIG_STAGING=y +# CONFIG_STAGING_EXCLUDE_BUILD is not set + +# +# Android +# +CONFIG_ANDROID=y +CONFIG_ANDROID_BINDER_IPC=y +CONFIG_ANDROID_LOGGER=y +CONFIG_ANDROID_RAM_CONSOLE=y +CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE=y +CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT=y +CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR=0 +CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE=0 +CONFIG_ANDROID_TIMED_OUTPUT=y +# CONFIG_ANDROID_TIMED_GPIO is not set +CONFIG_ANDROID_LOW_MEMORY_KILLER=y +# CONFIG_USB_IP_COMMON is not set +# CONFIG_PRISM2_USB is not set +# CONFIG_ECHO is not set +# CONFIG_COMEDI is not set +# CONFIG_ASUS_OLED is not set +# CONFIG_TRANZPORT is not set + +# +# Qualcomm MSM Camera And Video +# + +# +# Camera Sensor Selection +# +# CONFIG_POHMELFS is not set +# CONFIG_LINE6_USB is not set +# CONFIG_USB_SERIAL_QUATECH2 is not set +# CONFIG_USB_SERIAL_QUATECH_USB2 is not set +# CONFIG_VT6656 is not set +# CONFIG_FB_UDL is not set + +# +# RAR Register Driver +# +# CONFIG_RAR_REGISTER is not set +# CONFIG_IIO is not set +# CONFIG_RAMZSWAP is not set +# CONFIG_BATMAN_ADV is not set +# CONFIG_STRIP is not set +# CONFIG_FB_SM7XX is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_BTRFS_FS is not set +# CONFIG_NILFS2_FS is not set +CONFIG_FILE_LOCKING=y +CONFIG_FSNOTIFY=y +CONFIG_DNOTIFY=y +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +CONFIG_FUSE_FS=y +# CONFIG_CUSE is not set +CONFIG_GENERIC_ACL=y + +# +# Caches +# +# CONFIG_FSCACHE is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_PROC_PAGE_MONITOR=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set +CONFIG_MISC_FILESYSTEMS=y +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=y +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +# CONFIG_YAFFS_EMPTY_LOST_AND_FOUND is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UBIFS_FS_DEBUG=y +CONFIG_UBIFS_FS_DEBUG_MSG_LVL=0 +# CONFIG_UBIFS_FS_DEBUG_CHKS is not set +# CONFIG_LOGFS is not set +CONFIG_CRAMFS=y +# CONFIG_SQUASHFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_OMFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +# CONFIG_NFS_V4_1 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_ACL_SUPPORT=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +CONFIG_NLS_CODEPAGE_936=y +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y +# CONFIG_DLM is not set + +# +# Kernel hacking +# +CONFIG_PRINTK_TIME=y +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +# CONFIG_STRIP_ASM_SYMS is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0 +CONFIG_DETECT_HUNG_TASK=y +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0 +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_DEBUG_OBJECTS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_KMEMLEAK is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_MEMORY_INIT is not set +# CONFIG_DEBUG_LIST is not set +# CONFIG_DEBUG_SG is not set +# CONFIG_DEBUG_NOTIFIERS is not set +# CONFIG_DEBUG_CREDENTIALS is not set +# CONFIG_BOOT_PRINTK_DELAY is not set +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_RCU_CPU_STALL_DETECTOR is not set +# CONFIG_BACKTRACE_SELF_TEST is not set +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set +# CONFIG_FAULT_INJECTION is not set +# CONFIG_LATENCYTOP is not set +# CONFIG_SYSCTL_SYSCALL_CHECK is not set +# CONFIG_PAGE_POISONING is not set +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_TRACING_SUPPORT=y +CONFIG_FTRACE=y +# CONFIG_FUNCTION_TRACER is not set +# CONFIG_IRQSOFF_TRACER is not set +# CONFIG_SCHED_TRACER is not set +# CONFIG_ENABLE_DEFAULT_TRACERS is not set +# CONFIG_BOOT_TRACER is not set +CONFIG_BRANCH_PROFILE_NONE=y +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set +# CONFIG_PROFILE_ALL_BRANCHES is not set +# CONFIG_STACK_TRACER is not set +# CONFIG_KMEMTRACE is not set +# CONFIG_WORKQUEUE_TRACER is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set +# CONFIG_SAMPLES is not set +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_KGDB is not set +CONFIG_ARM_UNWIND=y +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_STACK_USAGE is not set +CONFIG_DEBUG_LL=y +# CONFIG_EARLY_PRINTK is not set +# CONFIG_DEBUG_ICEDCC is not set +# CONFIG_OC_ETM is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITYFS is not set +# CONFIG_DEFAULT_SECURITY_SELINUX is not set +# CONFIG_DEFAULT_SECURITY_SMACK is not set +# CONFIG_DEFAULT_SECURITY_TOMOYO is not set +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_DEFAULT_SECURITY="" +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +CONFIG_CRYPTO_ALGAPI=y +CONFIG_CRYPTO_ALGAPI2=y +CONFIG_CRYPTO_AEAD2=y +CONFIG_CRYPTO_BLKCIPHER=y +CONFIG_CRYPTO_BLKCIPHER2=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_RNG2=y +CONFIG_CRYPTO_PCOMP=y +CONFIG_CRYPTO_MANAGER=y +CONFIG_CRYPTO_MANAGER2=y +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +CONFIG_CRYPTO_WORKQUEUE=y +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +CONFIG_CRYPTO_CBC=y +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_VMAC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_GHASH is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_RMD128 is not set +# CONFIG_CRYPTO_RMD160 is not set +# CONFIG_CRYPTO_RMD256 is not set +# CONFIG_CRYPTO_RMD320 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +CONFIG_CRYPTO_DEFLATE=y +# CONFIG_CRYPTO_ZLIB is not set +CONFIG_CRYPTO_LZO=y + +# +# Random Number Generation +# +# CONFIG_CRYPTO_ANSI_CPRNG is not set +CONFIG_CRYPTO_HW=y +# CONFIG_BINARY_PRINTF is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_GENERIC_FIND_LAST_BIT=y +CONFIG_CRC_CCITT=y +CONFIG_CRC16=y +# CONFIG_CRC_T10DIF is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y +CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y diff --git a/arch/arm/configs/pxa168_defconfig b/arch/arm/configs/pxa168_defconfig index 113511f91eb700..1e040504e7a7d4 100644 --- a/arch/arm/configs/pxa168_defconfig +++ b/arch/arm/configs/pxa168_defconfig @@ -1,13 +1,14 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.33-rc3 -# Tue Jan 12 08:57:10 2010 +# Linux kernel version: 2.6.34 +# Fri Jun 11 17:07:15 2010 # CONFIG_ARM=y CONFIG_SYS_SUPPORTS_APM_EMULATION=y CONFIG_GENERIC_GPIO=y CONFIG_GENERIC_TIME=y CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_HAVE_PROC_CPU=y CONFIG_GENERIC_HARDIRQS=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_HAVE_LATENCYTOP_SUPPORT=y @@ -18,6 +19,7 @@ CONFIG_GENERIC_IRQ_PROBE=y CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_NEED_DMA_MAP_STATE=y CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y CONFIG_VECTORS_BASE=0xffff0000 CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" @@ -28,7 +30,6 @@ CONFIG_CONSTRUCTORS=y # CONFIG_EXPERIMENTAL=y CONFIG_BROKEN_ON_SMP=y -CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 CONFIG_LOCALVERSION="" CONFIG_LOCALVERSION_AUTO=y @@ -58,11 +59,10 @@ CONFIG_RCU_FANOUT=32 # CONFIG_TREE_RCU_TRACE is not set # CONFIG_IKCONFIG is not set CONFIG_LOG_BUF_SHIFT=14 -# CONFIG_GROUP_SCHED is not set # CONFIG_CGROUPS is not set CONFIG_SYSFS_DEPRECATED=y CONFIG_SYSFS_DEPRECATED_V2=y -# CONFIG_RELAY is not set +CONFIG_RELAY=y CONFIG_NAMESPACES=y # CONFIG_UTS_NS is not set # CONFIG_IPC_NS is not set @@ -73,11 +73,12 @@ CONFIG_NAMESPACES=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_SYSCTL=y CONFIG_ANON_INODES=y +CONFIG_PANIC_TIMEOUT=0 # CONFIG_EMBEDDED is not set CONFIG_UID16=y CONFIG_SYSCTL_SYSCALL=y CONFIG_KALLSYMS=y -# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_ALL=y # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_HOTPLUG=y CONFIG_PRINTK=y @@ -91,10 +92,15 @@ CONFIG_TIMERFD=y CONFIG_EVENTFD=y CONFIG_SHMEM=y CONFIG_AIO=y +# CONFIG_ASHMEM is not set +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_PERF_USE_VMALLOC=y # # Kernel Performance Events And Counters # +# CONFIG_PERF_EVENTS is not set +# CONFIG_PERF_COUNTERS is not set CONFIG_VM_EVENT_COUNTERS=y CONFIG_COMPAT_BRK=y CONFIG_SLAB=y @@ -110,6 +116,7 @@ CONFIG_HAVE_CLK=y # # GCOV-based kernel profiling # +# CONFIG_GCOV_KERNEL is not set # CONFIG_SLOW_WORK is not set CONFIG_HAVE_GENERIC_DMA_COHERENT=y CONFIG_SLABINFO=y @@ -136,33 +143,34 @@ CONFIG_IOSCHED_CFQ=y CONFIG_DEFAULT_CFQ=y # CONFIG_DEFAULT_NOOP is not set CONFIG_DEFAULT_IOSCHED="cfq" +CONFIG_PREEMPT_NOTIFIERS=y # CONFIG_INLINE_SPIN_TRYLOCK is not set # CONFIG_INLINE_SPIN_TRYLOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK is not set # CONFIG_INLINE_SPIN_LOCK_BH is not set # CONFIG_INLINE_SPIN_LOCK_IRQ is not set # CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set -# CONFIG_INLINE_SPIN_UNLOCK is not set +CONFIG_INLINE_SPIN_UNLOCK=y # CONFIG_INLINE_SPIN_UNLOCK_BH is not set -# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set +CONFIG_INLINE_SPIN_UNLOCK_IRQ=y # CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set # CONFIG_INLINE_READ_TRYLOCK is not set # CONFIG_INLINE_READ_LOCK is not set # CONFIG_INLINE_READ_LOCK_BH is not set # CONFIG_INLINE_READ_LOCK_IRQ is not set # CONFIG_INLINE_READ_LOCK_IRQSAVE is not set -# CONFIG_INLINE_READ_UNLOCK is not set +CONFIG_INLINE_READ_UNLOCK=y # CONFIG_INLINE_READ_UNLOCK_BH is not set -# CONFIG_INLINE_READ_UNLOCK_IRQ is not set +CONFIG_INLINE_READ_UNLOCK_IRQ=y # CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set # CONFIG_INLINE_WRITE_TRYLOCK is not set # CONFIG_INLINE_WRITE_LOCK is not set # CONFIG_INLINE_WRITE_LOCK_BH is not set # CONFIG_INLINE_WRITE_LOCK_IRQ is not set # CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set -# CONFIG_INLINE_WRITE_UNLOCK is not set +CONFIG_INLINE_WRITE_UNLOCK=y # CONFIG_INLINE_WRITE_UNLOCK_BH is not set -# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set +CONFIG_INLINE_WRITE_UNLOCK_IRQ=y # CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set # CONFIG_MUTEX_SPIN_ON_OWNER is not set # CONFIG_FREEZER is not set @@ -176,6 +184,7 @@ CONFIG_MMU=y # CONFIG_ARCH_REALVIEW is not set # CONFIG_ARCH_VERSATILE is not set # CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_BCMRING is not set # CONFIG_ARCH_CLPS711X is not set # CONFIG_ARCH_GEMINI is not set # CONFIG_ARCH_EBSA110 is not set @@ -185,7 +194,6 @@ CONFIG_MMU=y # CONFIG_ARCH_STMP3XXX is not set # CONFIG_ARCH_NETX is not set # CONFIG_ARCH_H720X is not set -# CONFIG_ARCH_NOMADIK is not set # CONFIG_ARCH_IOP13XX is not set # CONFIG_ARCH_IOP32X is not set # CONFIG_ARCH_IOP33X is not set @@ -202,37 +210,64 @@ CONFIG_ARCH_MMP=y # CONFIG_ARCH_KS8695 is not set # CONFIG_ARCH_NS9XXX is not set # CONFIG_ARCH_W90X900 is not set +# CONFIG_ARCH_NUC93X is not set # CONFIG_ARCH_PNX4008 is not set # CONFIG_ARCH_PXA is not set # CONFIG_ARCH_MSM is not set +# CONFIG_ARCH_SHMOBILE is not set # CONFIG_ARCH_RPC is not set # CONFIG_ARCH_SA1100 is not set # CONFIG_ARCH_S3C2410 is not set # CONFIG_ARCH_S3C64XX is not set +# CONFIG_ARCH_S5P6440 is not set +# CONFIG_ARCH_S5P6442 is not set # CONFIG_ARCH_S5PC1XX is not set +# CONFIG_ARCH_S5PV210 is not set # CONFIG_ARCH_SHARK is not set # CONFIG_ARCH_LH7A40X is not set # CONFIG_ARCH_U300 is not set +# CONFIG_ARCH_U8500 is not set +# CONFIG_ARCH_NOMADIK is not set # CONFIG_ARCH_DAVINCI is not set # CONFIG_ARCH_OMAP is not set -# CONFIG_ARCH_BCMRING is not set -# CONFIG_ARCH_U8500 is not set # CONFIG_MACH_TAVOREVB is not set +CONFIG_PXA_SSP_LEGACY=y + +# +# Marvell(R) Wireless Memory Management Technology +# +# CONFIG_IMM is not set + +# +# Marvell PXA168 Processor Variants +# +# CONFIG_CPU_PXA168_B0 is not set # -# Marvell PXA168/910 Implmentations +# Marvell PXA168/910/MMP2 Implmentations # CONFIG_MACH_ASPENITE=y -CONFIG_MACH_ZYLONITE2=y -CONFIG_MACH_AVENGERS_LITE=y +# CONFIG_MACH_ZYLONITE2 is not set +# CONFIG_MACH_IPCAM is not set +# CONFIG_MACH_DKB_GENERIC is not set # CONFIG_MACH_TTC_DKB is not set +# CONFIG_MACH_AVENGERS_LITE is not set +# CONFIG_MACH_EDGE is not set +CONFIG_MACH_TETON_BGA=y +# CONFIG_MACH_FLINT is not set +# CONFIG_MACH_MARVELL_JASPER is not set +CONFIG_GLOBAL_PREEMPT_NOTIFIERS=y CONFIG_CPU_PXA168=y +# CONFIG_PXA_32KTIMER is not set +CONFIG_TIMER_SERVICES_MMP=y CONFIG_PLAT_PXA=y # # Processor Type # CONFIG_CPU_MOHAWK=y +# CONFIG_CPU_MOHAWK_OLD_ID is not set +CONFIG_CPU_L2_CACHE=y CONFIG_CPU_32v5=y CONFIG_CPU_ABRT_EV5T=y CONFIG_CPU_PABRT_LEGACY=y @@ -252,10 +287,12 @@ CONFIG_ARM_THUMB=y CONFIG_ARM_L1_CACHE_SHIFT=5 CONFIG_IWMMXT=y CONFIG_COMMON_CLKDEV=y +CONFIG_FORCE_MAX_ZONEORDER=15 # # Bus support # +# CONFIG_PCI is not set # CONFIG_PCI_SYSCALL is not set # CONFIG_ARCH_SUPPORTS_MSI is not set # CONFIG_PCCARD is not set @@ -271,9 +308,9 @@ CONFIG_VMSPLIT_3G=y # CONFIG_VMSPLIT_2G is not set # CONFIG_VMSPLIT_1G is not set CONFIG_PAGE_OFFSET=0xC0000000 -# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_NONE=y # CONFIG_PREEMPT_VOLUNTARY is not set -CONFIG_PREEMPT=y +# CONFIG_PREEMPT is not set CONFIG_HZ=100 CONFIG_AEABI=y CONFIG_OABI_COMPAT=y @@ -301,7 +338,7 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.2.100:/nfsroot/ ip=192.168.2.101:192.168.2.100::255.255.255.0::eth0:on console=ttyS0,115200 mem=128M" +CONFIG_CMDLINE="ubi.mtd=4 root=ubi0_0 nfsroot=192.168.1.100:/rootfs rootfstype=ubifs ip=192.168.1.101:192.168.1.100::255.255.255.0::usb0:on console=ttyS0,115200 uart_dma loglevel=8" # CONFIG_XIP_KERNEL is not set # CONFIG_KEXEC is not set @@ -325,10 +362,10 @@ CONFIG_FPE_NWFPE=y # Userspace binary formats # CONFIG_BINFMT_ELF=y -# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y CONFIG_HAVE_AOUT=y -# CONFIG_BINFMT_AOUT is not set -# CONFIG_BINFMT_MISC is not set +CONFIG_BINFMT_AOUT=y +CONFIG_BINFMT_MISC=y # # Power management options @@ -341,7 +378,6 @@ CONFIG_NET=y # Networking options # CONFIG_PACKET=y -# CONFIG_PACKET_MMAP is not set CONFIG_UNIX=y CONFIG_XFRM=y # CONFIG_XFRM_USER is not set @@ -377,6 +413,7 @@ CONFIG_TCP_CONG_CUBIC=y CONFIG_DEFAULT_TCP_CONG="cubic" # CONFIG_TCP_MD5SIG is not set # CONFIG_IPV6 is not set +CONFIG_ANDROID_PARANOID_NETWORK=y # CONFIG_NETWORK_SECMARK is not set # CONFIG_NETFILTER is not set # CONFIG_IP_DCCP is not set @@ -438,9 +475,120 @@ CONFIG_EXTRA_FIRMWARE="" # CONFIG_DEBUG_DEVRES is not set # CONFIG_SYS_HYPERVISOR is not set # CONFIG_CONNECTOR is not set -# CONFIG_MTD is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_TESTS is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set +CONFIG_PXA3XX_BBM=y + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_GPIO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_PXA3xx=y +# CONFIG_MTD_NAND_PXA3xx_BUILTIN is not set +# CONFIG_SAMSUNG_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_8G_MLC_NAND is not set +# CONFIG_MICRON_32G_MLC_NAND is not set +# CONFIG_SAMSUNG_512M_SLC_NAND is not set +CONFIG_DEFAULT_NAND=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# LPDDR flash memory drivers +# +# CONFIG_MTD_LPDDR is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=y +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +# CONFIG_MTD_UBI_GLUEBI is not set + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set # CONFIG_PARPORT is not set -# CONFIG_BLK_DEV is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set + +# +# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +# +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_UB=y +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_MG_DISK is not set +CONFIG_PXA168_MSP=y # CONFIG_MISC_DEVICES is not set CONFIG_HAVE_IDE=y # CONFIG_IDE is not set @@ -448,10 +596,40 @@ CONFIG_HAVE_IDE=y # # SCSI device support # +CONFIG_SCSI_MOD=y # CONFIG_RAID_ATTRS is not set -# CONFIG_SCSI is not set -# CONFIG_SCSI_DMA is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set # CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_DH is not set +# CONFIG_SCSI_OSD_INITIATOR is not set # CONFIG_ATA is not set # CONFIG_MD is not set CONFIG_NETDEVICES=y @@ -465,7 +643,8 @@ CONFIG_NETDEVICES=y CONFIG_NET_ETHERNET=y CONFIG_MII=y # CONFIG_AX88796 is not set -CONFIG_SMC91X=y +# CONFIG_SMC91X is not set +CONFIG_PXA168_ETH=y # CONFIG_DM9000 is not set # CONFIG_ETHOC is not set # CONFIG_SMC911X is not set @@ -484,11 +663,43 @@ CONFIG_SMC91X=y # CONFIG_NETDEV_1000 is not set # CONFIG_NETDEV_10000 is not set CONFIG_WLAN=y +# CONFIG_USB_ZD1201 is not set # CONFIG_HOSTAP is not set # # Enable WiMAX (Networking options) to see the WiMAX drivers # + +# +# USB Network Adapters +# +CONFIG_USB_CATC=y +CONFIG_USB_KAWETH=y +CONFIG_USB_PEGASUS=y +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_CDC_EEM is not set +# CONFIG_USB_NET_DM9601 is not set +# CONFIG_USB_NET_SMSC75XX is not set +# CONFIG_USB_NET_SMSC95XX is not set +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_MCS7830 is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +CONFIG_USB_NET_CDC_SUBSET=y +# CONFIG_USB_ALI_M5632 is not set +# CONFIG_USB_AN2720 is not set +CONFIG_USB_BELKIN=y +CONFIG_USB_ARMLINUX=y +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_KC2190 is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_NET_INT51X1 is not set +# CONFIG_USB_IPHETH is not set +# CONFIG_USB_SIERRA_NET is not set # CONFIG_WAN is not set # CONFIG_PPP is not set # CONFIG_SLIP is not set @@ -514,23 +725,63 @@ CONFIG_INPUT_MOUSEDEV=y CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set -# CONFIG_INPUT_EVDEV is not set +CONFIG_INPUT_EVDEV=y +CONFIG_CIR=y # CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_KEYRESET is not set # # Input Device Drivers # -# CONFIG_INPUT_KEYBOARD is not set +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ADP5588 is not set +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_QT2160 is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_KEYBOARD_MATRIX is not set +# CONFIG_KEYBOARD_MAX7359 is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_OPENCORES is not set +CONFIG_KEYBOARD_PXA27x=y +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set -# CONFIG_INPUT_TOUCHSCREEN is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_AD7879_I2C is not set +# CONFIG_TOUCHSCREEN_AD7879 is not set +# CONFIG_TOUCHSCREEN_DYNAPRO is not set +# CONFIG_TOUCHSCREEN_EETI is not set +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set +# CONFIG_TOUCHSCREEN_MCS5000 is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_INEXIO is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set +CONFIG_TSC2007=y +# CONFIG_TOUCHSCREEN_TPO is not set +# CONFIG_TOUCHSCREEN_W90X900 is not set # CONFIG_INPUT_MISC is not set # # Hardware I/O ports # -# CONFIG_SERIO is not set +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_SERIO_ALTERA_PS2 is not set # CONFIG_GAMEPORT is not set # @@ -540,7 +791,8 @@ CONFIG_VT=y CONFIG_CONSOLE_TRANSLATIONS=y CONFIG_VT_CONSOLE=y CONFIG_HW_CONSOLE=y -# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_VT_HW_CONSOLE_BINDING=y +CONFIG_DEVMEM=y CONFIG_DEVKMEM=y # CONFIG_SERIAL_NONSTANDARD is not set @@ -556,6 +808,7 @@ CONFIG_SERIAL_PXA=y CONFIG_SERIAL_PXA_CONSOLE=y CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_TIMBERDALE is not set CONFIG_UNIX98_PTYS=y # CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set # CONFIG_LEGACY_PTYS is not set @@ -564,7 +817,49 @@ CONFIG_UNIX98_PTYS=y # CONFIG_R3964 is not set # CONFIG_RAW_DRIVER is not set # CONFIG_TCG_TPM is not set -# CONFIG_I2C is not set +# CONFIG_DCC_TTY is not set + +# +# Color Management Unit +# +CONFIG_PXA_ICR=y +# CONFIG_PXA_CNM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_COMPAT=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# + +# +# I2C system bus drivers (mostly embedded / system-on-chip) +# +# CONFIG_I2C_DESIGNWARE is not set +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_XILINX is not set + +# +# External I2C/SMBus adapter drivers +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Other I2C/SMBus bus drivers +# +# CONFIG_I2C_PCA_PLATFORM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set # CONFIG_SPI is not set # @@ -579,10 +874,19 @@ CONFIG_GPIOLIB=y # # Memory mapped GPIO expanders: # +# CONFIG_GPIO_IT8761E is not set # # I2C GPIO expanders: # +# CONFIG_GPIO_MAX7300 is not set +# CONFIG_GPIO_MAX732X is not set +CONFIG_GPIO_PCA953X=y +CONFIG_GPIO_PCA953X_GENERIC_IRQ=y +CONFIG_GPIO_PCA953X_IRQ=y +# CONFIG_GPIO_PCF857X is not set +# CONFIG_GPIO_ADP5588 is not set +CONFIG_MAX8660=y # # PCI GPIO expanders: @@ -591,6 +895,7 @@ CONFIG_GPIOLIB=y # # SPI GPIO expanders: # +# CONFIG_GPIO_PCA9575 is not set # # AC97 GPIO expanders: @@ -611,14 +916,27 @@ CONFIG_SSB_POSSIBLE=y # Multifunction device drivers # # CONFIG_MFD_CORE is not set +# CONFIG_MFD_88PM860X is not set # CONFIG_MFD_SM501 is not set # CONFIG_MFD_ASIC3 is not set # CONFIG_HTC_EGPIO is not set # CONFIG_HTC_PASIC3 is not set +# CONFIG_HTC_I2CPLD is not set +# CONFIG_TPS65010 is not set +# CONFIG_TWL4030_CORE is not set # CONFIG_MFD_TMIO is not set # CONFIG_MFD_T7L66XB is not set # CONFIG_MFD_TC6387XB is not set # CONFIG_MFD_TC6393XB is not set +# CONFIG_PMIC_DA903X is not set +# CONFIG_PMIC_ADP5520 is not set +# CONFIG_MFD_MAX8925 is not set +# CONFIG_MFD_WM8400 is not set +# CONFIG_MFD_WM831X is not set +# CONFIG_MFD_WM8350_I2C is not set +# CONFIG_MFD_WM8994 is not set +# CONFIG_MFD_PCF50633 is not set +# CONFIG_AB3100_CORE is not set # CONFIG_REGULATOR is not set # CONFIG_MEDIA_SUPPORT is not set @@ -627,8 +945,40 @@ CONFIG_SSB_POSSIBLE=y # # CONFIG_VGASTATE is not set # CONFIG_VIDEO_OUTPUT_CONTROL is not set -# CONFIG_FB is not set -# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +# CONFIG_FB_BOOT_VESA_SUPPORT is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_FOREIGN_ENDIAN is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA168=y +# CONFIG_FB_VIRTUAL is not set +# CONFIG_FB_METRONOME is not set +# CONFIG_FB_MB862XX is not set +# CONFIG_FB_BROADSHEET is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +# CONFIG_LCD_ILI9320 is not set +# CONFIG_LCD_PLATFORM is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_GENERIC=y # # Display device support @@ -640,12 +990,191 @@ CONFIG_SSB_POSSIBLE=y # # CONFIG_VGA_CONSOLE is not set CONFIG_DUMMY_CONSOLE=y -# CONFIG_SOUND is not set +# CONFIG_FRAMEBUFFER_CONSOLE is not set +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +# CONFIG_SOUND_OSS_CORE_PRECLAIM is not set +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_JACK=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_HRTIMER is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set +# CONFIG_SND_RAWMIDI_SEQ is not set +# CONFIG_SND_OPL3_LIB_SEQ is not set +# CONFIG_SND_OPL4_LIB_SEQ is not set +# CONFIG_SND_SBAWE_SEQ is not set +# CONFIG_SND_EMU10K1_SEQ is not set +CONFIG_SND_DRIVERS=y +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set +CONFIG_SND_ARM=y +CONFIG_SND_USB=y +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_UA101 is not set +# CONFIG_SND_USB_CAIAQ is not set +CONFIG_SND_SOC=y + +# +# SoC Audio for the Intel PXA3xx +# +CONFIG_SND_PXA3XX_SOC=y +CONFIG_SND_PXA3XX_SOC_SSP=y +CONFIG_SND_PXA3XX_SOC_ASPENITE=y +CONFIG_SND_PXA3XX_SOC_TETON_BGA=y +CONFIG_SND_SOC_I2C_AND_SPI=y +# CONFIG_SND_SOC_ALL_CODECS is not set +CONFIG_SND_SOC_WM8753=y +CONFIG_SND_SOC_CS4344=y +# CONFIG_SOUND_PRIME is not set # CONFIG_HID_SUPPORT is not set -# CONFIG_USB_SUPPORT is not set -# CONFIG_MMC is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +CONFIG_USB_ARCH_HAS_EHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_MON is not set +# CONFIG_USB_WUSB is not set +# CONFIG_USB_WUSB_CBAF is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +# CONFIG_USB_EHCI_TT_NEWSCHED is not set +CONFIG_USB_EHCI_PXA_U2H=y +# CONFIG_USB_OXU210HP_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +# CONFIG_USB_HWA_HCD is not set +# CONFIG_USB_MUSB_HDRC is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set +# CONFIG_USB_TMC is not set + +# +# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +# + +# +# also be needed; see USB_STORAGE Help for more info +# +CONFIG_USB_STORAGE=y +CONFIG_USB_STORAGE_DEBUG=y +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_ONETOUCH is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_SEVSEG is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_GADGET is not set + +# +# OTG and related infrastructure +# +# CONFIG_USB_GPIO_VBUS is not set +# CONFIG_USB_ULPI is not set +# CONFIG_NOP_USB_XCEIV is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set +# CONFIG_MMC_EMBEDDED_SDIO is not set +# CONFIG_MMC_PARANOID_SD_INIT is not set + +# +# MMC/SD/SDIO Card Drivers +# +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BOUNCE is not set +CONFIG_MMC_BLOCK_PARANOID_RESUME=y +# CONFIG_SDIO_UART is not set +# CONFIG_MMC_TEST is not set + +# +# MMC/SD/SDIO Host Controller Drivers +# +CONFIG_MMC_PXA_SDH=y +# CONFIG_MMC3 is not set +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +# CONFIG_MMC_SDHCI_PLTFM is not set # CONFIG_MEMSTICK is not set # CONFIG_NEW_LEDS is not set +# CONFIG_SWITCH is not set # CONFIG_ACCESSIBILITY is not set CONFIG_RTC_LIB=y # CONFIG_RTC_CLASS is not set @@ -697,8 +1226,11 @@ CONFIG_GENERIC_ACL=y # # DOS/FAT/NT Filesystems # +CONFIG_FAT_FS=y # CONFIG_MSDOS_FS is not set -# CONFIG_VFAT_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" # CONFIG_NTFS_FS is not set # @@ -720,6 +1252,15 @@ CONFIG_MISC_FILESYSTEMS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_YAFFS_FS is not set +# CONFIG_JFFS2_FS is not set +CONFIG_UBIFS_FS=y +# CONFIG_UBIFS_FS_XATTR is not set +# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_ZLIB=y +# CONFIG_UBIFS_FS_DEBUG is not set +# CONFIG_LOGFS is not set CONFIG_CRAMFS=y # CONFIG_SQUASHFS is not set # CONFIG_VXFS_FS is not set @@ -747,6 +1288,7 @@ CONFIG_SUNRPC_GSS=y CONFIG_RPCSEC_GSS_KRB5=y # CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set +# CONFIG_CEPH_FS is not set # CONFIG_CIFS is not set # CONFIG_NCP_FS is not set # CONFIG_CODA_FS is not set @@ -755,9 +1297,64 @@ CONFIG_RPCSEC_GSS_KRB5=y # # Partition Types # -# CONFIG_PARTITION_ADVANCED is not set +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y -# CONFIG_NLS is not set +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set # CONFIG_DLM is not set # @@ -770,7 +1367,7 @@ CONFIG_FRAME_WARN=1024 CONFIG_MAGIC_SYSRQ=y # CONFIG_STRIP_ASM_SYMS is not set # CONFIG_UNUSED_SYMBOLS is not set -# CONFIG_DEBUG_FS is not set +CONFIG_DEBUG_FS=y # CONFIG_HEADERS_CHECK is not set CONFIG_DEBUG_KERNEL=y # CONFIG_DEBUG_SHIRQ is not set @@ -786,7 +1383,6 @@ CONFIG_SCHED_DEBUG=y # CONFIG_DEBUG_OBJECTS is not set # CONFIG_DEBUG_SLAB is not set # CONFIG_DEBUG_KMEMLEAK is not set -# CONFIG_DEBUG_PREEMPT is not set # CONFIG_DEBUG_RT_MUTEXES is not set # CONFIG_RT_MUTEX_TESTER is not set # CONFIG_DEBUG_SPINLOCK is not set @@ -812,16 +1408,18 @@ CONFIG_DEBUG_MEMORY_INIT=y # CONFIG_BACKTRACE_SELF_TEST is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +# CONFIG_LKDTM is not set # CONFIG_FAULT_INJECTION is not set # CONFIG_LATENCYTOP is not set # CONFIG_SYSCTL_SYSCALL_CHECK is not set # CONFIG_PAGE_POISONING is not set CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y CONFIG_TRACING_SUPPORT=y CONFIG_FTRACE=y # CONFIG_FUNCTION_TRACER is not set # CONFIG_IRQSOFF_TRACER is not set -# CONFIG_PREEMPT_TRACER is not set # CONFIG_SCHED_TRACER is not set # CONFIG_ENABLE_DEFAULT_TRACERS is not set # CONFIG_BOOT_TRACER is not set @@ -832,6 +1430,7 @@ CONFIG_BRANCH_PROFILE_NONE=y # CONFIG_KMEMTRACE is not set # CONFIG_WORKQUEUE_TRACER is not set # CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_DYNAMIC_DEBUG is not set # CONFIG_SAMPLES is not set CONFIG_HAVE_ARCH_KGDB=y # CONFIG_KGDB is not set @@ -943,9 +1542,9 @@ CONFIG_CRYPTO_DES=y # # Compression # -# CONFIG_CRYPTO_DEFLATE is not set +CONFIG_CRYPTO_DEFLATE=y # CONFIG_CRYPTO_ZLIB is not set -# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_LZO=y # # Random Number Generation @@ -960,14 +1559,18 @@ CONFIG_CRYPTO_HW=y CONFIG_BITREVERSE=y CONFIG_GENERIC_FIND_LAST_BIT=y CONFIG_CRC_CCITT=y -# CONFIG_CRC16 is not set +CONFIG_CRC16=y # CONFIG_CRC_T10DIF is not set # CONFIG_CRC_ITU_T is not set CONFIG_CRC32=y # CONFIG_CRC7 is not set # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y CONFIG_HAS_IOMEM=y CONFIG_HAS_IOPORT=y CONFIG_HAS_DMA=y CONFIG_NLATTR=y +CONFIG_GENERIC_ATOMIC64=y diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 69ce0727edb534..0b343e3773412f 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -9,6 +9,10 @@ #include #include +#if !defined(ARCH_DMA_CACHE_ALIGNMENT) +#define ARCH_DMA_CACHE_ALIGNMENT 32 +#endif + /* * page_to_dma/dma_to_virt/virt_to_dma are architecture private functions * used internally by the DMA-mapping API to provide DMA addresses. They @@ -146,7 +150,7 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask) static inline int dma_get_cache_alignment(void) { - return 32; + return ARCH_DMA_CACHE_ALIGNMENT; } static inline int dma_is_consistent(struct device *dev, dma_addr_t handle) diff --git a/arch/arm/include/asm/dma.h b/arch/arm/include/asm/dma.h index ca51143f97f179..056c944d662057 100644 --- a/arch/arm/include/asm/dma.h +++ b/arch/arm/include/asm/dma.h @@ -1,6 +1,12 @@ #ifndef __ASM_ARM_DMA_H #define __ASM_ARM_DMA_H +typedef unsigned int dmach_t; + +#include +#include +#include +#include #include /* diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h index 51662feb9f1dd0..11408da7eeaea9 100644 --- a/arch/arm/include/asm/elf.h +++ b/arch/arm/include/asm/elf.h @@ -91,6 +91,10 @@ extern char elf_platform[]; struct elf32_hdr; +struct task_struct; + +extern int dump_task_regs (struct task_struct *, elf_gregset_t *); + /* * This is used to ensure we don't load something for the wrong architecture. */ diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h index 103f7ee9731357..896221f21da605 100644 --- a/arch/arm/include/asm/ftrace.h +++ b/arch/arm/include/asm/ftrace.h @@ -2,12 +2,22 @@ #define _ASM_ARM_FTRACE #ifdef CONFIG_FUNCTION_TRACER +#ifndef KBUILD_NEW_GNU_MCOUNT #define MCOUNT_ADDR ((long)(mcount)) +#else +#define MCOUNT_ADDR ((long)(__gnu_mcount_nc)) +#endif #define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */ #ifndef __ASSEMBLY__ +struct dyn_arch_ftrace {}; extern void mcount(void); extern void __gnu_mcount_nc(void); + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ + return addr; +} #endif #endif diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 4312ee5e3d0b6d..932d233f596aa6 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -81,7 +81,7 @@ * between 2MB and 14MB inclusive. */ #ifndef CONSISTENT_DMA_SIZE -#define CONSISTENT_DMA_SIZE SZ_2M +#define CONSISTENT_DMA_SIZE (14 * 1024 * 1024) #endif #define CONSISTENT_END (0xffe00000UL) diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 2c1db77d78487c..a1d20210c15b26 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -93,15 +93,21 @@ ENDPROC(ret_from_fork) #ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_DYNAMIC_FTRACE -ENTRY(mcount) +ENTRY(__gnu_mcount_nc) stmdb sp!, {r0-r3, lr} mov r0, lr sub r0, r0, #MCOUNT_INSN_SIZE + bl ftrace_stub + ldmia sp!, {r0-r3, ip, lr} + mov pc, ip - .globl mcount_call -mcount_call: +ENTRY(mcount) + stmdb sp!, {r0-r3, lr} + mov r0, lr + sub r0, r0, #MCOUNT_INSN_SIZE bl ftrace_stub ldr lr, [fp, #-4] @ restore lr + msr CPSR_f, #0x60000000 ldmia sp!, {r0-r3, pc} ENTRY(ftrace_caller) @@ -114,7 +120,13 @@ ENTRY(ftrace_caller) ftrace_call: bl ftrace_stub ldr lr, [fp, #-4] @ restore lr +#ifndef KBUILD_NEW_GNU_MCOUNT + msr CPSR_f, #0x60000000 ldmia sp!, {r0-r3, pc} +#else + ldmia sp!, {r0-r3, ip, lr} + mov pc, ip +#endif #else @@ -154,6 +166,7 @@ trace: mov lr, pc mov pc, r2 ldr lr, [fp, #-4] @ restore lr + msr CPSR_f, #0x60000000 ldmia sp!, {r0-r3, pc} #endif /* CONFIG_DYNAMIC_FTRACE */ diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c index 0298286ad4ad59..6af45a61e01a1e 100644 --- a/arch/arm/kernel/ftrace.c +++ b/arch/arm/kernel/ftrace.c @@ -12,6 +12,7 @@ */ #include +#include #include #include @@ -21,11 +22,29 @@ #define BL_OFFSET_MASK 0x00ffffff static unsigned long bl_insn; -static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */ +/* + * for some arm toolchain like android toolchain would put a conditional + * instruction after mcount calling, so we need refresh the CPSR + * to jump over this inst + */ +static const unsigned long NOP = 0xe328f206; /* msr CPSR_f, #0x60000000 */ +static const unsigned long LDMIA = 0xE8BD4000; /* ldmia sp!, {lr} */ unsigned char *ftrace_nop_replace(void) { - return (char *)&NOP; + char *ret_vaule; +#ifndef KBUILD_NEW_GNU_MCOUNT + ret_vaule = (char *)&NOP; +#else + /* + * For the arm toolchain change the mount to __gnu_mount_nc since 4.1.1 + * and it add "push {lr}" behavior before call the __gnu_mount_nc, + * so we need to restore the lr, when we try to replace the jmp code + * to a normal code that don't impact performance + */ + ret_vaule = (char *)&LDMIA; +#endif + return ret_vaule; } /* construct a branch (BL) instruction to addr */ @@ -79,7 +98,7 @@ int ftrace_modify_code(unsigned long pc, unsigned char *old_code, if (!err && (replaced == old)) flush_icache_range(pc, pc + MCOUNT_INSN_SIZE); - return err; + return 0; } int ftrace_update_ftrace_func(ftrace_func_t func) @@ -95,9 +114,32 @@ int ftrace_update_ftrace_func(ftrace_func_t func) return ret; } +int ftrace_make_nop(struct module *mod, + struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned char *new, *old; + unsigned long ip = rec->ip; + + old = ftrace_call_replace(ip, addr); + new = ftrace_nop_replace(); + + return ftrace_modify_code(rec->ip, old, new); +} + +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ + unsigned char *new, *old; + unsigned long ip = rec->ip; + + old = ftrace_nop_replace(); + new = ftrace_call_replace(ip, addr); + + return ftrace_modify_code(rec->ip, old, new); +} + /* run from ftrace_init with irqs disabled */ int __init ftrace_dyn_arch_init(void *data) { - ftrace_mcount_set(data); + *(unsigned long *)data = 0; return 0; } diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 907d5a620bca26..15c46d27ca279d 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -602,6 +602,14 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info, static inline void setup_syscall_restart(struct pt_regs *regs) { + if (regs->ARM_ORIG_r0 == -ERESTARTNOHAND || + regs->ARM_ORIG_r0 == -ERESTARTSYS || + regs->ARM_ORIG_r0 == -ERESTARTNOINTR || + regs->ARM_ORIG_r0 == -ERESTART_RESTARTBLOCK) { + /* the syscall cannot be safely restarted, return -EINTR instead */ + regs->ARM_r0 = -EINTR; + return; + } regs->ARM_r0 = regs->ARM_ORIG_r0; regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; } @@ -734,6 +742,7 @@ static void do_signal(struct pt_regs *regs, int syscall) */ if (syscall) { if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) { + regs->ARM_r0 = -EAGAIN; /* prevent multiple restarts */ if (thumb_mode(regs)) { regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE; regs->ARM_pc -= 2; diff --git a/arch/arm/mach-mmp/Kconfig b/arch/arm/mach-mmp/Kconfig index 6ab843eaa35b50..89ae2af20d927e 100644 --- a/arch/arm/mach-mmp/Kconfig +++ b/arch/arm/mach-mmp/Kconfig @@ -1,5 +1,14 @@ if ARCH_MMP +menu "Marvell PXA168 Processor Variants" + +config CPU_PXA168_B0 + bool "PXA168 B0 Stepping" + help + Say 'Y' here if you want to support Marvell PXA168-based B0 + Silicon +endmenu + menu "Marvell PXA168/910/MMP2 Implmentations" config MACH_ASPENITE @@ -16,27 +25,61 @@ config MACH_ZYLONITE2 Say 'Y' here if you want to support the Marvell PXA168-based Zylonite2 Development Board. -config MACH_AVENGERS_LITE - bool "Marvell's PXA168 Avengers Lite Development Board" +config MACH_IPCAM + bool "Marvell's PXA168 IPCAM Development Board" select CPU_PXA168 help Say 'Y' here if you want to support the Marvell PXA168-based - Avengers Lite Development Board. + IPCAM Development Board. -config MACH_TAVOREVB - bool "Marvell's PXA910 TavorEVB Development Board" +config MACH_DKB_GENERIC + bool "Marvell's PXA910 Generic DKB Development Board" select CPU_PXA910 + select CPU_PXA168 help Say 'Y' here if you want to support the Marvell PXA910-based - TavorEVB Development Board. + DKB Development Board. + config MACH_TTC_DKB - bool "Marvell's PXA910 TavorEVB Development Board" + bool "Marvell's PXA910 TTC_DKB Development Board" select CPU_PXA910 + select CPU_PXA168 help Say 'Y' here if you want to support the Marvell PXA910-based TTC_DKB Development Board. +config MACH_TAVOREVB + bool "PXA910 Evaluation Board (aka TavorEVB)" + select CPU_PXA910 + select CPU_PXA168 + help + Say 'Y' here if you want to support the Marvell PXA910-based + TavorEVB Development Board. + +config MACH_AVENGERS_LITE + bool "Marvell's PXA168 Avengers lite Development Board" + select CPU_PXA168 + select HAVE_PWM + help + Say 'Y' here if you want to support the Marvell PXA168-based + Avengers lite Development Board. + +config MACH_EDGE + bool "Marvell's PXA168 Edge 2.0 Development Board" + select CPU_PXA168 + select HAVE_PWM + help + Say 'Y' here if you want to support the Marvell PXA168-based + Edge Development Board. + +config MACH_TETON_BGA + bool "Marvell's PXA168 Teton BGA Development Board" + select CPU_PXA168 + help + Say 'Y' here if you want to support the Marvell PXA168-based + Teton BGA Development Board. + config MACH_FLINT bool "Marvell's Flint Development Platform" select CPU_MMP2 @@ -56,18 +99,24 @@ config MACH_MARVELL_JASPER MMP2-based board can't be co-existed with PXA168-based & PXA910-based development board. Since MMP2 is compatible to ARMv6 architecture. - endmenu +config GLOBAL_PREEMPT_NOTIFIERS + bool "Enable global preempt notifier" + depends on PREEMPT_NOTIFIERS + default y + config CPU_PXA168 bool select CPU_MOHAWK + select PREEMPT_NOTIFIERS help Select code specific to PXA168 config CPU_PXA910 bool select CPU_MOHAWK + select PREEMPT_NOTIFIERS help Select code specific to PXA910 @@ -77,4 +126,15 @@ config CPU_MMP2 select CPU_32v6K help Select code specific to MMP2. MMP2 is ARMv6 compatible. + +config PXA_32KTIMER + bool "PXA 32K OS Timer" + help + enable 32KHz OS timer for PXA168/PXA910 Processor + +config TIMER_SERVICES_MMP + bool "timer services on MMP" + help + Provide high resolution timer services + endif diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile index 8b66d06739c4d9..d9c6481f0feaf0 100644 --- a/arch/arm/mach-mmp/Makefile +++ b/arch/arm/mach-mmp/Makefile @@ -2,18 +2,34 @@ # Makefile for Marvell's PXA168 processors line # -obj-y += common.o clock.o devices.o time.o +obj-y += common.o clock.o devices.o time.o resource/ # SoC support obj-$(CONFIG_CPU_PXA168) += pxa168.o irq-pxa168.o -obj-$(CONFIG_CPU_PXA910) += pxa910.o irq-pxa168.o +obj-$(CONFIG_CPU_PXA910) += pxa910.o pxa910-squ.o irq-pxa168.o obj-$(CONFIG_CPU_MMP2) += mmp2.o irq-mmp2.o # board support obj-$(CONFIG_MACH_ASPENITE) += aspenite.o obj-$(CONFIG_MACH_ZYLONITE2) += aspenite.o +obj-$(CONFIG_MACH_TETON_BGA) += teton_bga.o +obj-$(CONFIG_MACH_IPCAM) += ipcam.o +obj-$(CONFIG_MACH_DKB_GENERIC) += dkb_generic.o obj-$(CONFIG_MACH_AVENGERS_LITE)+= avengers_lite.o -obj-$(CONFIG_MACH_TAVOREVB) += tavorevb.o +obj-$(CONFIG_MACH_EDGE) += edge.o obj-$(CONFIG_MACH_TTC_DKB) += ttc_dkb.o +obj-$(CONFIG_MACH_TAVOREVB) += tavorevb.o +obj-$(CONFIG_DVFM) += dvfm.o +obj-$(CONFIG_PXA910_DVFM_STATS) += dvfm_stats.o +obj-$(CONFIG_DVFM_PXA168) += pxa168_dvfm.o pxa168_dfc_ll.o +obj-$(CONFIG_MSPM_PXA168_STATS) += pxa168_dvfm_stats.o +obj-$(CONFIG_DVFM_PXA910) += pxa910_dvfm.o +obj-$(CONFIG_MSPM_PXA168) += pxa168_mspm_idle.o pxa168_mspm_prof.o +obj-$(CONFIG_MSPM_PXA910) += pxa910_mspm_idle.o pxa910_mspm_prof.o +obj-$(CONFIG_PM_PXA168) += pxa168_pm.o pxa168_pm_ll.o +obj-$(CONFIG_PM_PXA910) += pxa910_pm.o pxa910_pm_ll.o +obj-$(CONFIG_TIMER_SERVICES_MMP)+= timer_services.o +obj-$(CONFIG_MMC) += mmc.o +obj-$(CONFIG_PCI) += pxa168_pcie.o io.o obj-$(CONFIG_MACH_FLINT) += flint.o obj-$(CONFIG_MACH_MARVELL_JASPER) += jasper.o diff --git a/arch/arm/mach-mmp/aspenite.c b/arch/arm/mach-mmp/aspenite.c index a2d307ec0420fc..f1660c59d58e71 100644 --- a/arch/arm/mach-mmp/aspenite.c +++ b/arch/arm/mach-mmp/aspenite.c @@ -12,10 +12,14 @@ #include #include #include +#include #include -#include -#include +#include +#include #include +#include +#include +#include #include #include @@ -23,11 +27,42 @@ #include #include #include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(CONFIG_SPI_PXA2XX) +#include +#include +#endif #include "common.h" +#include +#include + +/*used by expander max7312, 16 pins gpio expander */ +#define GPIO_EXT0(x) (NR_BUILTIN_GPIO + (x)) +#define GPIO_EXT1(x) (NR_BUILTIN_GPIO + 16 + (x)) +#define GPIO_EXT2(x) (NR_BUILTIN_GPIO + 16 + 16 + (x)) + +#define CARD_EN GPIO_EXT1(0) +#define CAM_PWDN GPIO_EXT1(1) +#define TW9907_PWDN GPIO_EXT1(4) +#define TW9907_RST_N GPIO_EXT1(2) -static unsigned long common_pin_config[] __initdata = { - /* Data Flash Interface */ +static unsigned long aspenite_pin_config[] __initdata = { + /* Data Flash Interface Nana or eMMC/eSD*/ +#if defined(CONFIG_MMC3) + GPIO16_SMC_nCS0_DIS, +#else GPIO0_DFI_D15, GPIO1_DFI_D14, GPIO2_DFI_D13, @@ -45,23 +80,143 @@ static unsigned long common_pin_config[] __initdata = { GPIO14_DFI_D1, GPIO15_DFI_D0, +#if defined(CONFIG_PXA168_CF) + /* Compact Flash Controller */ + GPIO19_CF_nCE1, + GPIO20_CF_nCE2, + GPIO22_ND_CLE, + GPIO23_CF_nALE, + GPIO25_CF_nRESET, + GPIO26_ND_RnB1, + GPIO27_ND_RnB2, + GPIO28_CF_RDY, + GPIO29_CF_STSCH, + GPIO30_CF_nREG, + GPIO31_CF_nIOIS16, +#if defined(CONFIG_PXA168_CF_USE_GPIO_CARDDETECT) + GPIO32_GPIO, /* CF_nCD1 IRQ */ + GPIO33_GPIO, /* CF_nCD2 IRQ */ +#else + GPIO32_CF_nCD1, + GPIO33_CF_nCD2, +#endif + GPIO34_SMC_nCS1, + GPIO35_CF_INPACK, + GPIO36_CF_nWAIT, +#else /* Static Memory Controller */ GPIO18_SMC_nCS0, - GPIO34_SMC_nCS1, GPIO23_SMC_nLUA, GPIO25_SMC_nLLA, + GPIO27_GPIO, /* Ethernet IRQ */ GPIO28_SMC_RDY, GPIO29_SMC_SCLK, + GPIO34_SMC_nCS1, GPIO35_SMC_BE1, GPIO36_SMC_BE2, - GPIO27_GPIO, /* Ethernet IRQ */ +#endif +#endif + /* LCD */ + GPIO56_LCD_FCLK_RD, + GPIO57_LCD_LCLK_A0, + GPIO58_LCD_PCLK_WR, + GPIO59_LCD_DENA_BIAS, + GPIO60_LCD_DD0, + GPIO61_LCD_DD1, + GPIO62_LCD_DD2, + GPIO63_LCD_DD3, + GPIO64_LCD_DD4, + GPIO65_LCD_DD5, + GPIO66_LCD_DD6, + GPIO67_LCD_DD7, + GPIO68_LCD_DD8, + GPIO69_LCD_DD9, + GPIO70_LCD_DD10, + GPIO71_LCD_DD11, + GPIO72_LCD_DD12, + GPIO73_LCD_DD13, + GPIO74_LCD_DD14, + GPIO75_LCD_DD15, + + GPIO76_LCD_DD16, + GPIO77_LCD_DD17, + GPIO78_LCD_DD18, + GPIO79_LCD_DD19, + GPIO80_LCD_DD20, + GPIO81_LCD_DD21, + GPIO82_LCD_DD22, + GPIO83_LCD_DD23, + /* i2c bus */ + GPIO105_CI2C_SDA, + GPIO106_CI2C_SCL, + +#if !defined(CONFIG_MTD_M25P80) /* UART1 */ GPIO107_UART1_RXD, GPIO108_UART1_TXD, + + /* Keypad */ + GPIO110_KP_MKIN0, + GPIO109_KP_MKIN1, + GPIO121_KP_MKIN4, + GPIO111_KP_MKOUT7, + GPIO112_KP_MKOUT6, +#else + /* SSP1 */ + GPIO107_SPI_NOR_RXD, + GPIO108_SPI_NOR_TXD, + GPIO109_SPI_NOR_SYSCLK, + GPIO110_GPIO, + GPIO111_SPI_NOR_CLK, +#endif + + /* SSP0 */ + GPIO113_I2S_MCLK, + GPIO114_I2S_FRM, + GPIO115_I2S_BCLK, + GPIO116_I2S_RXD, + GPIO117_I2S_TXD, + + /* MFU */ + GPIO86_TX_CLK, + GPIO87_TX_EN, + GPIO88_TX_DQ3, + GPIO89_TX_DQ2, + GPIO90_TX_DQ1, + GPIO91_TX_DQ0, + GPIO92_MII_CRS, + GPIO93_MII_COL, + GPIO94_RX_CLK, + GPIO95_RX_ER, + GPIO96_RX_DQ3, + GPIO97_RX_DQ2, + GPIO98_RX_DQ1, + GPIO99_RX_DQ0, + GPIO100_MII_MDC, + GPIO101_MII_MDIO, + GPIO103_RX_DV, + + /* mspro detect */ + GPIO84_MSP_CD, + }; -static struct smc91x_platdata smc91x_info = { +struct platform_device aspenite_device_battery = { + .name = "aspenite-battery", + .id = -1, +}; + +static inline void aspenite_add_battery(void) +{ + int ret; + ret = platform_device_register(&aspenite_device_battery); + if (ret) + dev_err(&aspenite_device_battery.dev, + "unable to register device: %d\n", ret); +} + +static struct smc91x_platdata zylonite2_smc91x_info = { .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, }; @@ -82,67 +237,1431 @@ static struct platform_device smc91x_device = { .name = "smc91x", .id = 0, .dev = { - .platform_data = &smc91x_info, + .platform_data = &zylonite2_smc91x_info, }, .num_resources = ARRAY_SIZE(smc91x_resources), .resource = smc91x_resources, }; -static struct mtd_partition aspenite_nand_partitions[] = { +#define ENET_RESET_N GPIO_EXT1(9) +#define ENET_COMA_N GPIO_EXT1(10) + +static int pxa168_eth_init(void) +{ + if (gpio_request(ENET_RESET_N, "ENET_RESET_N")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", ENET_RESET_N); + return -EIO; + } + + if (gpio_request(ENET_COMA_N, "ENET_COMA_N")){ + gpio_free(ENET_RESET_N); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", ENET_COMA_N); + return -EIO; + } + + gpio_direction_output(ENET_RESET_N, 1); + gpio_direction_output(ENET_COMA_N, 1); + + gpio_free(ENET_RESET_N); + gpio_free(ENET_COMA_N); + + return 0; +} + +static struct pxa168_eth_platform_data pxa168_eth_data = { + .phy_addr = 0, /* phy addr depends on boards */ + .force_phy_addr = 1, + .init = pxa168_eth_init, +}; + +#if defined(CONFIG_PCI) + + + +/* #define ASPENITE_REV5 */ +#if !defined(ASPENITE_REV5) + +#define PCIE_MINICARD_CD_N GPIO_EXT2(0) +#define PCIE_1P5V_SHDN_N GPIO_EXT2(1) +#define PCIE_3P3V_SHDN_N GPIO_EXT2(2) +#define PCIE_MINICARD_PERST_N GPIO_EXT2(3) +#define PCIE_MINICARD_WAKE_N GPIO_EXT2(4) +#define PCIE_MINICARD_CLKREQ_N GPIO_EXT2(5) +#define PCIE_REFCLK_OE GPIO_EXT2(6) + +#define PCIE_CARD_DECTECT GPIO_EXT2(0) + + +static unsigned int pcie_card_inserted(void) +{ + int ret = 0; + int res; + + res = gpio_get_value_cansleep(PCIE_CARD_DECTECT); + if (!(res < 0)) { + + /* + * Check input reg (0x00) bit 0 + * 0 = card is inserted + * 1 = card is not inserted + */ + ret = !res; + } + + return ret; +} + +int pxa168_gpio_pcie_init(void) +{ + /* Card inserted? */ + if (!pcie_card_inserted()) { + printk(KERN_ERR "pcie: No card detected.\n"); + return -EIO; + } + + if (gpio_request(PCIE_1P5V_SHDN_N, "PCIE_1P5V_SHDN_N")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", PCIE_1P5V_SHDN_N); + return -EIO; + } + + if (gpio_request(PCIE_3P3V_SHDN_N, "PCIE_3P3V_SHDN_N")) { + gpio_free(PCIE_1P5V_SHDN_N); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", PCIE_3P3V_SHDN_N); + return -EIO; + } + + if (gpio_request(PCIE_REFCLK_OE, "PCIE_REFCLK_OE")) { + gpio_free(PCIE_1P5V_SHDN_N); + gpio_free(PCIE_3P3V_SHDN_N); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", PCIE_REFCLK_OE); + return -EIO; + } + + if (gpio_request(PCIE_MINICARD_PERST_N, "PCIE_MINICARD_PERST_N")) { + gpio_free(PCIE_1P5V_SHDN_N); + gpio_free(PCIE_3P3V_SHDN_N); + gpio_free(PCIE_REFCLK_OE); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", PCIE_MINICARD_PERST_N); + return -EIO; + } + + if (gpio_direction_output(PCIE_MINICARD_PERST_N, 0)) + return -EIO; + if (gpio_direction_output(PCIE_1P5V_SHDN_N, 1)) + return -EIO; + if (gpio_direction_output(PCIE_1P5V_SHDN_N, 1)) + return -EIO; + if (gpio_direction_output(PCIE_3P3V_SHDN_N, 1)) + return -EIO; + /* wait for power supply to stabilize */ + mdelay(2); + if (gpio_direction_output(PCIE_REFCLK_OE, 1)) + return -EIO; + /* 4ms: PCIClock output to stabilize + + * 96ms: Tpvperl pr PCISIG Base1.0a design checklist + */ + mdelay(100); + if (gpio_direction_output(PCIE_MINICARD_PERST_N, 1)) + return -EIO; + + gpio_free(PCIE_1P5V_SHDN_N); + gpio_free(PCIE_3P3V_SHDN_N); + gpio_free(PCIE_REFCLK_OE); + gpio_free(PCIE_MINICARD_PERST_N); + + return 0; +} + +#else /* Rev 5 */ + +#define PCIE_3P3V_SHDN_N GPIO_EXT2(2) +#define PCIE_PRSNT2_N GPIO_EXT2(3) +#define PCIE_WAKE_N GPIO_EXT2(4) +#define PCIE_PWRGD GPIO_EXT2(5) +#define PCIE_REFCLK_OE GPIO_EXT2(6) + +int pxa168_gpio_pcie_init(void) +{ + + if (gpio_request(PCIE_3P3V_SHDN_N, "PCIE_3P3V_SHDN_N")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", PCIE_3P3V_SHDN_N); + return -EIO; + } + + if (gpio_request(PCIE_PWRGD, "PCIE_PWRGD")) { + gpio_free(PCIE_3P3V_SHDN_N); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", PCIE_PWRGD); + return -EIO; + } + + if (gpio_request(PCIE_REFCLK_OE, "PCIE_REFCLK_OE")) { + gpio_free(PCIE_3P3V_SHDN_N); + gpio_free(PCIE_PWRGD); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", PCIE_REFCLK_OE); + return -EIO; + } + + gpio_direction_output(PCIE_3P3V_SHDN_N, 1); + mdelay(2); + gpio_direction_output(PCIE_PWRGD, 1); + mdelay(2); + gpio_direction_output(PCIE_REFCLK_OE, 1); + mdelay(100); + + gpio_free(PCIE_3P3V_SHDN_N); + gpio_free(PCIE_PWRGD); + gpio_free(PCIE_REFCLK_OE); + + return 0; +} +#endif + +static struct pxa168_pcie_platform_data pxa168_pcie_data = { + .init = pxa168_gpio_pcie_init, +}; +#endif + +#if defined(CONFIG_PXA168_CF) && defined(CONFIG_PXA168_CF_USE_GPIO_CARDDETECT) +static struct resource pxa168_cf_resources[] = { + [0] = { + .start = 0xD4285000, + .end = 0xD4285800, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_PXA168_CF, + .end = IRQ_PXA168_CF, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_GPIO(32), + .end = IRQ_GPIO(32), + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device pxa168_cf_device = { + .name = "pxa168-cf", + .id = -1, + .resource = pxa168_cf_resources, + .num_resources = ARRAY_SIZE(pxa168_cf_resources), +}; + +static void __init pxa168_cf_init(void) +{ + platform_device_register(&pxa168_cf_device); +} +#endif + +/* + * mfp is shared in card, cam and tw9907, only one is effective + */ +typedef enum{ + SW_CARD = 0x01, + SW_CAM_ON = 0x02, + SW_CAM_OFF = 0x03, + SW_TW9907 = 0x04, +} SW_TYPE_T; + +static int aspenite_pinmux_switch(SW_TYPE_T type) +{ + int ret = 0; + + if (gpio_request(CARD_EN, "CARD_EN")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", CARD_EN); + return -EIO; + } + + if (gpio_request(CAM_PWDN, "CAM_PWDN")) { + gpio_free(CARD_EN); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", CAM_PWDN); + return -EIO; + } + + if (gpio_request(TW9907_PWDN, "TW9907_PWDN")) { + gpio_free(CARD_EN); + gpio_free(CAM_PWDN); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", TW9907_PWDN); + return -EIO; + } + + switch (type) { + case SW_CARD: + gpio_direction_output(CARD_EN, 1); + gpio_direction_output(CAM_PWDN, 1); + gpio_direction_output(TW9907_PWDN, 1); + break; + case SW_CAM_ON: + gpio_direction_output(CARD_EN, 0); + gpio_direction_output(CAM_PWDN, 0); + gpio_direction_output(TW9907_PWDN, 1); + break; + case SW_CAM_OFF: + gpio_direction_output(CARD_EN, 0); + gpio_direction_output(CAM_PWDN, 1); + gpio_direction_output(TW9907_PWDN, 1); + break; + case SW_TW9907: + gpio_direction_output(CARD_EN, 0); + gpio_direction_output(CAM_PWDN, 1); + gpio_direction_output(TW9907_PWDN, 0); + break; + default: + ret = -EIO; + break; + } + + gpio_free(CARD_EN); + gpio_free(CAM_PWDN); + gpio_free(TW9907_PWDN); + + return ret; +} + +#if defined(CONFIG_PXA168_MSP) +/* msp platform data */ +static mfp_cfg_t mfp_cfg_msp[] = { + GPIO40_MSP_DAT1, + GPIO41_MSP_DAT0, + GPIO43_MSP_DAT2, + GPIO44_MSP_DAT3, + GPIO42_MSP_BS, + GPIO50_MSP_SCLK, +}; + +static int mspro_mfp_config(void) +{ + int ret = 0; + + ret = aspenite_pinmux_switch(SW_CARD); + if (0 == ret) + mfp_config(ARRAY_AND_SIZE(mfp_cfg_msp)); + + return ret; +} + +static struct card_platform_data msp_ops = { + /* GPIO84 used as mspro detect pin */ + .pin_detect = MFP_PIN_GPIO84, + .mfp_config = mspro_mfp_config, +}; +#endif + +#if defined(CONFIG_PXA168_CAMERA) +static mfp_cfg_t aspenite_cam_pins[] = { + GPIO37_CAM_DAT7, + GPIO38_CAM_DAT6, + GPIO39_CAM_DAT5, + GPIO40_CAM_DAT4, + GPIO41_CAM_DAT3, + GPIO42_CAM_DAT2, + GPIO43_CAM_DAT1, + GPIO44_CAM_DAT0, + GPIO46_CAM_VSYNC, + GPIO48_CAM_HSYNC, + GPIO54_CAM_MCLK, + GPIO55_CAM_PCLK, +}; + +/* sensor init */ +static int sensor_power_onoff(int on, int id) +{ + /* + * on, 1, power on + * on, 0, power off + */ + int ret = 0; + if(on){ + ret = aspenite_pinmux_switch(SW_CAM_ON); + if (0 == ret) + mfp_config(ARRAY_AND_SIZE(aspenite_cam_pins)); + }else{ + ret = aspenite_pinmux_switch(SW_CAM_OFF); + } + return ret; +} + +static struct sensor_platform_data ov7670_sensor_data = { + .id = SENSOR_LOW, + .power_on = sensor_power_onoff, +}; + +/* sensor init over */ +#endif + +static struct fb_videomode video_modes[] = { + /* sharp_ls037 QVGA mode info */ + [0] = { + .pixclock = 158000, + .refresh = 60, + .xres = 240, + .yres = 320, + .hsync_len = 4, + .left_margin = 39, + .right_margin = 39, + .vsync_len = 1, + .upper_margin = 2, + .lower_margin = 3, + .sync = 0, + }, + /* sharp_ls037 VGA mode info */ + [1] = { + .pixclock = 39700, + .refresh = 60, + .xres = 480, + .yres = 640, + .hsync_len = 8, + .left_margin = 81, + .right_margin = 81, + .vsync_len = 1, + .upper_margin = 2, + .lower_margin = 7, + .sync = 0, + }, +}; + +static struct pxa168fb_mach_info zylonite2_lcd_info __initdata = { + .id = "Base", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_16_GPIO, + .dumb_mode = DUMB_MODE_RGB565, + .active = 1, + .panel_rbswap = 1, +}; + +static struct i2c_pxa_platform_data pwri2c_info __initdata = { + .use_pio = 1, +}; + +static u16 tpo_spi_cmdon[] = { + 0x080F, + 0x0C5F, + 0x1017, + 0x1420, + 0x1808, + 0x1c20, + 0x2020, + 0x2420, + 0x2820, + 0x2c20, + 0x3020, + 0x3420, + 0x3810, + 0x3c10, + 0x4010, + 0x4415, + 0x48aa, + 0x4cff, + 0x5086, + 0x548d, + 0x58d4, + 0x5cfb, + 0x602e, + 0x645a, + 0x6889, + 0x6cfe, + 0x705a, + 0x749b, + 0x78c5, + 0x7cff, + 0x80f0, + 0x84f0, + 0x8808, +}; + +static u16 tpo_spi_cmdoff[] = { + 0x0c5e, //standby +}; + +static void tpo_lcd_power(struct pxa168fb_info *fbi, unsigned int spi_gpio_cs, unsigned int spi_gpio_reset, int on) +{ + int err = 0; + if (on) { + if (spi_gpio_reset != -1) { + err = gpio_request(spi_gpio_reset, "TPO_LCD_SPI_RESET"); + if (err) { + printk("failed to request GPIO for TPO LCD RESET\n"); + return; + } + gpio_direction_output(spi_gpio_reset, 0); + msleep(100); + gpio_set_value(spi_gpio_reset, 1); + msleep(100); + gpio_free(spi_gpio_reset); + } + + pxa168fb_spi_send(fbi, tpo_spi_cmdon, + ARRAY_SIZE(tpo_spi_cmdon), + spi_gpio_cs); + + /* Put backlight back to default brightness */ + pxa3xx_pmic_set_voltage(VCC_MISC2, 1800); + + } else { + + /* reset below not needed at this time because we + * actually remove power from the display + */ + + /* put backlight to off to hide startup garbage */ + + pxa3xx_pmic_set_voltage(VCC_MISC2, 3300); + } +} + +static struct fb_videomode video_modes_aspen[] = { + /* lpj032l001b HVGA mode info */ + [0] = { + .pixclock = 30120, + .refresh = 60, + .xres = 800, + .yres = 480, + .hsync_len = 1, + .left_margin = 215, + .right_margin = 40, + .vsync_len = 1, + .upper_margin = 34, + .lower_margin = 10, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, + }, + + [1] = { + .pixclock = 16129, + .refresh = 60, + .xres = 1024, + .yres = 768, + .hsync_len = 136, + .left_margin = 160, + .right_margin = 24, + .vsync_len = 6, + .upper_margin = 29, + .lower_margin = 3, + .sync = 0, + }, + + [2] = { + .pixclock = 25641, + .refresh = 60, + .xres = 800, + .yres = 600, + .hsync_len = 128, + .left_margin = 88, + .right_margin = 40, + .vsync_len = 4, + .upper_margin = 23, + .lower_margin = 1, + .sync = 0, + }, + + [3] = { + .pixclock = 111111, + .refresh = 60, + .xres = 480, + .yres = 272, + .hsync_len = 1, + .left_margin = 43, + .right_margin = 2, + .vsync_len = 1, + .upper_margin = 12, + .lower_margin = 2, + .sync = 0, + }, + + [4] = { + .pixclock = 16129, + .refresh = 60, + .xres = 1280, + .yres = 720, + .hsync_len = 40, + .left_margin = 220, + .right_margin = 110, + .vsync_len = 5, + .upper_margin = 20, + .lower_margin = 5, + .sync = 0, + }, + + [5] = { + .pixclock = 39722, + .refresh = 60, + .xres = 640, + .yres = 480, + .hsync_len = 96, + .left_margin = 40, + .right_margin = 8, + .vsync_len = 2, + .upper_margin = 25, + .lower_margin = 2, + .sync = 0, + }, + +}; + +/* SPI Control Register. */ +#define CFG_SCLKCNT(div) (div<<24) /* 0xFF~0x2 */ +#define CFG_RXBITS(rx) ((rx - 1)<<16) /* 0x1F~0x1 */ +#define CFG_TXBITS(tx) ((tx - 1)<<8) /* 0x1F~0x1, 0x1: 2bits ... 0x1F: 32bits */ +#define CFG_SPI_ENA(spi) (spi<<3) +#define CFG_SPI_SEL(spi) (spi<<2) /* 1: port1; 0: port0 */ +#define CFG_SPI_3W4WB(wire) (wire<<1) /* 1: 3-wire; 0: 4-wire */ + +struct pxa168fb_mach_info aspenite_lcd_info __initdata = { + .id = "Base-aspen", + .modes = video_modes_aspen, + .num_modes = ARRAY_SIZE(video_modes_aspen), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_24, + .dumb_mode = DUMB_MODE_RGB888, + .active = 1, + .spi_ctrl = CFG_SCLKCNT(2) | CFG_TXBITS(16) | CFG_SPI_SEL(1) | CFG_SPI_3W4WB(1) | CFG_SPI_ENA(1), + .spi_gpio_cs = GPIO_EXT1(14), + .spi_gpio_reset = -1, + .panel_rbswap = 1, + .invert_pixclock = 1, + .pxa168fb_lcd_power = tpo_lcd_power, + .max_fb_size = 1024 * 768 * 4 * 2, +}; + +struct pxa168fb_mach_info aspenite_lcd_ovly_info __initdata = { + .id = "Ovly-aspen", + .modes = video_modes_aspen, + .num_modes = ARRAY_SIZE(video_modes_aspen), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_24, + .dumb_mode = DUMB_MODE_RGB888, + .active = 1, + .panel_rbswap = 1, + .max_fb_size = 1024 * 768 * 4 * 2, +}; + +#if defined(CONFIG_GPIO_PCA953X) +static int stamp_8688_wlan_poweron(void); + +/* GPIO expander max7312 could reuse PCA953X */ + +static struct pca953x_platform_data max7312_data[] = { + /* three max7312 in system */ + + [0] = { + .gpio_base = GPIO_EXT0(0), + }, + + [1] = { + .gpio_base = GPIO_EXT1(0), +#if defined(CONFIG_WLAN_8688_SDIO) + .poweron = stamp_8688_wlan_poweron, +#endif + }, +#if defined(CONFIG_PCI) + /* GPIO expander #3 */ + [2] = { + .gpio_base = GPIO_EXT2(0), + .invert = 0, + }, +#endif +}; + +#endif + +static struct i2c_board_info aspenite_i2c_board_info[] = { + +#if defined(CONFIG_GPIO_PCA953X) + { + .type = "max7312", + .addr = 0x10, /* 0x20/0x21 */ + .irq = IRQ_GPIO(122), + .platform_data = &max7312_data[0], + }, + + { + .type = "max7312", + .addr = 0x20, /* 0x40/0x41 */ + .irq = IRQ_GPIO(120), + .platform_data = &max7312_data[1], + }, + +#endif +#if defined(CONFIG_PCI) + { + .type = "max7312", + .addr = 0x28, /* 0x50/0x51 */ + .platform_data = &max7312_data[2], + }, +#endif +#if defined(CONFIG_PXA168_CAMERA) + { + .type = "ov7670", + .addr = 0x21, + .platform_data = &ov7670_sensor_data, + }, +#endif +#if defined(CONFIG_RTC_DRV_ISL1208) + { + .type = "isl1208", + .addr = 0x6f, + }, +#endif +}; + +#if defined(CONFIG_MAX8660) +static void max8660_init(void) +{ + /* There was 0.2 voltage decline on V3 according to the spec setting, the real voltage is 1.1v; + * use hardware default value on aspenite to high voltage damage the board; + * pxa3xx_pmic_set_voltage(VCC_CORE, 1300); + */ + pxa3xx_pmic_set_voltage(HDMI_1P2V, 1200); + pxa3xx_pmic_set_voltage(VCC_MVT, 1800); + pxa3xx_pmic_set_voltage(VCC_MISC1, 3000); /* SD voltage */ + pxa3xx_pmic_set_voltage(VCC_MISC2, 1800); /* Backlight control */ +} + +/* max8660_power_module[] should be consistent with enum + * in include/asm-arm/arch-pxa/pxa3xx_pmic.h + */ +static struct power_supply_module max8660_power_modules[] = { + /* {command, power_module}, */ + {VCC_CORE, MAX8660_V3}, + {HDMI_1P2V, MAX8660_V4}, + {VCC_MVT, MAX8660_V5}, + {VCC_MISC1, MAX8660_V6}, + {VCC_MISC2, MAX8660_V7}, + {0, 0}, +}; + +static struct power_chip max8660_chips[] = { + {MAX8660_ID, "max8660", max8660_power_modules}, + {0, NULL, NULL}, +}; + +static struct max8660_platform_data max8660_data = { + .platform_init = max8660_init, + .power_chips = max8660_chips, +}; +#endif + +static struct i2c_board_info pwri2c_board_info[] = +{ +#if defined(CONFIG_MAX8660) + { + .type = "max8660", + .addr = 0x34, + .platform_data = &max8660_data, + }, +#endif + +#if defined(CONFIG_TSC2007) + { + .type = "tsc2007", + .addr = 0x48, /* 0x90/0x91 */ + .irq = IRQ_GPIO(GPIO_EXT0(7)), /* IO7 of TSC2007 */ + }, +#endif + +}; + +static unsigned int aspenite_matrix_key_map[] = { + KEY(0, 7, KEY_LEFT), + KEY(4, 7, KEY_RIGHT), + KEY(0, 6, KEY_HOME), + KEY(4, 6, KEY_END), + KEY(1, 7, KEY_ENTER), /* keypad action */ + KEY(1, 6, KEY_SEND), +}; + +static unsigned int aspenite_android_matrix_key_map[] = { + KEY(0, 6, KEY_UP), /* SW 4 */ + KEY(0, 7, KEY_DOWN), /* SW 5 */ + KEY(1, 6, KEY_LEFT), /* SW 6 */ + KEY(1, 7, KEY_RIGHT), /* SW 7 */ + KEY(4, 6, KEY_MENU), /* SW 8 */ + KEY(4, 7, KEY_BACK), /* SW 9 */ +}; + +static struct pxa27x_keypad_platform_data aspenite_keypad_info __initdata = { + .matrix_key_rows = 8, + .matrix_key_cols = 8, + .matrix_key_map = aspenite_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(aspenite_matrix_key_map), + .debounce_interval = 30, +}; + +static struct pxa27x_keypad_platform_data aspenite_android_keypad_info __initdata = { + .matrix_key_rows = 8, + .matrix_key_cols = 8, + .matrix_key_map = aspenite_android_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(aspenite_android_matrix_key_map), + .debounce_interval = 30, +}; + +#if (defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MODULE)) \ + && defined(CONFIG_MTD_M25P80) + +static struct pxa2xx_spi_master pxa_ssp_master_info = { + .num_chipselect = 1, + .enable_dma = 1, +}; + +static struct pxa2xx_spi_chip m25pxx_spi_info = { + .tx_threshold = 1, + .rx_threshold = 1, + .timeout = 1000, + .gpio_cs = 110 +}; + +static struct spi_board_info __initdata spi_board_info[] = { { - .name = "bootloader", - .offset = 0, - .size = SZ_1M, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "reserved", - .offset = MTDPART_OFS_APPEND, - .size = SZ_128K, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "reserved", - .offset = MTDPART_OFS_APPEND, - .size = SZ_8M, - .mask_flags = MTD_WRITEABLE, - }, { - .name = "kernel", - .offset = MTDPART_OFS_APPEND, - .size = (SZ_2M + SZ_1M), - .mask_flags = 0, - }, { - .name = "filesystem", - .offset = MTDPART_OFS_APPEND, - .size = SZ_48M, - .mask_flags = 0, - } -}; - -static struct pxa3xx_nand_platform_data aspenite_nand_info = { - .enable_arbiter = 1, - .parts = aspenite_nand_partitions, - .nr_parts = ARRAY_SIZE(aspenite_nand_partitions), -}; - -static void __init common_init(void) -{ - mfp_config(ARRAY_AND_SIZE(common_pin_config)); + .modalias = "m25p80", + .mode = SPI_MODE_0, + .max_speed_hz = 260000, + .bus_num = 2, + .chip_select = 0, + .platform_data = NULL, + .controller_data = &m25pxx_spi_info, + .irq = -1, + }, +}; + +static void __init aspenite_init_spi(void) +{ + pxa168_add_ssp(1); + pxa168_add_spi(2, &pxa_ssp_master_info); + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); +} +#else +static inline void aspenite_init_spi(void) {} +#endif + +#if defined(CONFIG_SAMSUNG_32G_MLC_NAND) + +DECLARE_ANDROID_32G_V75_PARTITIONS(android_32G_v75_partitions); +DECLARE_32G_V75_PARTITIONS(generic_32G_v75_partitions); + +#elif defined(CONFIG_MICRON_32G_MLC_NAND) + +DECLARE_ANDROID_32G_V75_PARTITIONS(android_32G_v75_partitions); +DECLARE_32G_V75_PARTITIONS(generic_32G_v75_partitions); + +#elif defined(CONFIG_SAMSUNG_8G_MLC_NAND) + +DECLARE_ANDROID_8G_V75_PARTITIONS(android_8G_v75_partitions); +DECLARE_8G_V75_PARTITIONS(generic_8G_v75_partitions); + +#elif defined(CONFIG_SAMSUNG_512M_SLC_NAND) + +DECLARE_64M_V75_PARTITIONS(generic_64M_v75_partitions); + + +#else + +DECLARE_ANDROID_512M_V75_PARTITIONS(android_512m_v75_partitions); +DECLARE_512M_V75_PARTITIONS(generic_512m_v75_partitions); + +#endif + +static struct pxa3xx_nand_platform_data aspenite_nand_info; + +#if defined(CONFIG_SAMSUNG_32G_MLC_NAND) + +static void __init aspenite_add_nand(void) +{ + if (is_android()) { + aspenite_nand_info.parts[0] = android_32G_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(android_32G_v75_partitions); + } else { + aspenite_nand_info.parts[0] = generic_32G_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(generic_32G_v75_partitions); + } + + aspenite_nand_info.use_dma = 0; + aspenite_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *) &aspenite_nand_info); +} + +#elif defined(CONFIG_MICRON_32G_MLC_NAND) + +static void __init aspenite_add_nand(void) +{ + if (is_android()) { + aspenite_nand_info.parts[0] = android_32G_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(android_32G_v75_partitions); + } else { + aspenite_nand_info.parts[0] = generic_32G_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(generic_32G_v75_partitions); + } + + aspenite_nand_info.use_dma = 0; + aspenite_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *) &aspenite_nand_info); +} + +#elif defined(CONFIG_SAMSUNG_8G_MLC_NAND) + +static void __init aspenite_add_nand(void) +{ + if (is_android()) { + aspenite_nand_info.parts[0] = android_8G_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(android_8G_v75_partitions); + } else { + aspenite_nand_info.parts[0] = generic_8G_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(generic_8G_v75_partitions); + } + + aspenite_nand_info.use_dma = 0; + aspenite_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *) &aspenite_nand_info); +} + +#elif defined(CONFIG_SAMSUNG_512M_SLC_NAND) + +static void __init aspenite_add_nand(void) +{ + aspenite_nand_info.parts[0] = generic_64M_v75_partitions; + aspenite_nand_info.nr_parts[0] = + ARRAY_SIZE(generic_64M_v75_partitions); + + + aspenite_nand_info.use_dma = 0; + aspenite_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *) &aspenite_nand_info); +} + + +#else + +static void __init aspenite_add_nand(void) +{ + if (is_android()) { + aspenite_nand_info.parts[0] = android_512m_v75_partitions; + aspenite_nand_info.nr_parts[0] = ARRAY_SIZE(android_512m_v75_partitions); + } else { + aspenite_nand_info.parts[0] = generic_512m_v75_partitions; + aspenite_nand_info.nr_parts[0] = ARRAY_SIZE(generic_512m_v75_partitions); + } + + aspenite_nand_info.use_dma = 1; + aspenite_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *) &aspenite_nand_info); +} + +#endif + +#if defined(CONFIG_MMC_PXA_SDH) +static struct pfn_cfg mmc1_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, GPIO37_MMC1_DAT7, GPIO37_GPIO), + PFN_CFG(PIN_MMC_DAT6, GPIO38_MMC1_DAT6, GPIO38_GPIO), + PFN_CFG(PIN_MMC_DAT5, GPIO54_MMC1_DAT5, GPIO54_GPIO), + PFN_CFG(PIN_MMC_DAT4, GPIO48_MMC1_DAT4, GPIO48_GPIO), + PFN_CFG(PIN_MMC_DAT3, GPIO51_MMC1_DAT3, GPIO51_GPIO), + PFN_CFG(PIN_MMC_DAT2, GPIO52_MMC1_DAT2, GPIO52_GPIO), + PFN_CFG(PIN_MMC_DAT1, GPIO40_MMC1_DAT1, GPIO40_GPIO), + PFN_CFG(PIN_MMC_DAT0, GPIO41_MMC1_DAT0, GPIO41_GPIO), + PFN_CFG(PIN_MMC_CMD, GPIO49_MMC1_CMD, GPIO49_GPIO), + PFN_CFG(PIN_MMC_CLK, GPIO43_MMC1_CLK, GPIO43_GPIO), + PFN_CFG(PIN_MMC_CD, GPIO53_MMC1_CD, GPIO53_GPIO), + PFN_CFG(PIN_MMC_WP, GPIO46_MMC1_WP, GPIO46_GPIO), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; + +#if defined(CONFIG_WLAN_8688_SDIO) + /* sdh MMC2, wlan*/ +static struct pfn_cfg mmc2_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT6, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT5, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT4, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT3, GPIO90_MMC2_DAT3, GPIO90_GPIO), + PFN_CFG(PIN_MMC_DAT2, GPIO91_MMC2_DAT2, GPIO91_GPIO), + PFN_CFG(PIN_MMC_DAT1, GPIO92_MMC2_DAT1, GPIO92_GPIO), + PFN_CFG(PIN_MMC_DAT0, GPIO93_MMC2_DAT0, GPIO93_GPIO), + PFN_CFG(PIN_MMC_CMD, GPIO94_MMC2_CMD, GPIO94_GPIO), + PFN_CFG(PIN_MMC_CLK, GPIO95_MMC2_CLK, GPIO95_GPIO), + PFN_CFG(PIN_MMC_CD, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_WP, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; +#endif + +#if defined(CONFIG_MMC3) +static struct pfn_cfg mmc3_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, GPIO0_MMC3_DAT7, GPIO0_GPIO), + PFN_CFG(PIN_MMC_DAT6, GPIO1_MMC3_DAT6, GPIO1_GPIO), + PFN_CFG(PIN_MMC_DAT5, GPIO2_MMC3_DAT5, GPIO2_GPIO), + PFN_CFG(PIN_MMC_DAT4, GPIO3_MMC3_DAT4, GPIO3_GPIO), + PFN_CFG(PIN_MMC_DAT3, GPIO4_MMC3_DAT3, GPIO4_GPIO), + PFN_CFG(PIN_MMC_DAT2, GPIO5_MMC3_DAT2, GPIO5_GPIO), + PFN_CFG(PIN_MMC_DAT1, GPIO6_MMC3_DAT1, GPIO6_GPIO), + PFN_CFG(PIN_MMC_DAT0, GPIO7_MMC3_DAT0, GPIO7_GPIO), + PFN_CFG(PIN_MMC_CLK, GPIO8_MMC3_CLK, GPIO8_GPIO), + PFN_CFG(PIN_MMC_CMD, GPIO9_MMC3_CMD, GPIO9_GPIO), + PFN_CFG(PIN_MMC_CD, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_WP, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; +#endif + + +static int sdh_mfp_config_mmc1(void) +{ + int ret = 0; + + ret = aspenite_pinmux_switch(SW_CARD); + if (!ret) + pfn_config(mmc1_pfn_cfg, PFN_FN); + return ret; +} + +static struct pxasdh_platform_data aspenite_sdh_platform_data_mmc1 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_29_30 | MMC_VDD_30_31, + .mfp_config = sdh_mfp_config_mmc1, + .bus_width = 4, + .pfn_table = mmc1_pfn_cfg, +}; + +#if defined(CONFIG_BT_HCIUART) && defined(CONFIG_WLAN_8688_SDIO) +static mfp_cfg_t aspenite_bt_uart_pins[] = { + GPIO98_UART_SOUT, + GPIO99_UART_SIN, + GPIO100_UART_RTS, + GPIO101_UART_CTS, +}; + +static void bt_uart_mfp_config(void) +{ + mfp_config(ARRAY_AND_SIZE(aspenite_bt_uart_pins)); + return; +} + +static void __init aspenite_bt_init(void) +{ + bt_uart_mfp_config(); +} + +#endif + +#if defined(CONFIG_WLAN_8688_SDIO) +static int stamp_8688_wlan_poweron(void) +{ + int gpio_power = 0; + int gpio_reset = 0; + int gpio_wake = 0; + int gpio_h_wake = 0; + + gpio_power = GPIO_EXT1(5); + gpio_reset = GPIO_EXT1(6); + gpio_wake = GPIO_EXT1(8); + gpio_h_wake = GPIO_EXT1(7); + + if (gpio_request(gpio_power, "8688 wlan power down")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_power); + return -1; + } + + if(gpio_request(gpio_reset, "8688 wlan reset")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_reset); + gpio_free(gpio_power); + return -1; + } + if(gpio_request(gpio_wake, "8688 wlan gpio_wake")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_wake); + gpio_free(gpio_power); + gpio_free(gpio_reset); + return -1; + } + if(gpio_request(gpio_h_wake, "8688 wlan card gpio_h_wake")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_h_wake); + gpio_free(gpio_power); + gpio_free(gpio_reset); + gpio_free(gpio_wake); + return -1; + } + + gpio_direction_output(gpio_power, 0); + gpio_direction_output(gpio_reset, 0); + gpio_direction_output(gpio_wake, 0); + gpio_direction_input(gpio_h_wake); + mdelay(500); + gpio_direction_output(gpio_reset, 1); + gpio_direction_output(gpio_power, 1); + gpio_direction_output(gpio_wake, 1); + + gpio_free(gpio_power); + gpio_free(gpio_reset); + gpio_free(gpio_wake); + gpio_free(gpio_h_wake); + return 0; +} + +static struct pxasdh_platform_data aspenite_sdh_platform_data_mmc2 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_29_30 | MMC_VDD_30_31, + .bus_width = 4, + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .pfn_table = mmc2_pfn_cfg, +}; + +#endif +#if defined(CONFIG_MMC3) +static struct pxasdh_platform_data aspenite_sdh_platform_data_mmc3 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_29_30 | MMC_VDD_30_31, + .bus_width = 8, + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .pfn_table = mmc3_pfn_cfg, +}; +#endif +#endif + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static int gpio_usb_otg_pen = GPIO_EXT0(0); +static int gpio_usb_otg_stat1 = GPIO_EXT0(8); +static int gpio_usb_otg_stat2 = GPIO_EXT0(9); + +static int aspenite_u2o_vbus_status(unsigned base) +{ + int otg_stat1, otg_stat2, otg_pen, status = VBUS_LOW; + unsigned long flags; + + local_irq_save(flags); + +#if 1 /* remove the workaroud here */ +#ifdef CONFIG_USB_OTG + /* FIXME on aspenite R0 boards otg stat1/stat2 could not + * reflect VBUS status yet, check with U2O itself instead + */ + if (u2o_get(base, U2xOTGSC) & U2xOTGSC_BSV) + status = VBUS_HIGH; + else + status = VBUS_LOW; + + return status; +#endif +#endif + + if (gpio_request(gpio_usb_otg_pen, "USB OTG Power Enable")) { + printk(KERN_ERR "%s Max7312 USB_OTG_PEN GPIO Request" + " Failed\n", __func__); + return -1; + } + + if (gpio_request(gpio_usb_otg_stat1, "USB OTG VBUS stat1")) { + printk(KERN_ERR "%s Max7312 USB_OTG_STAT1 GPIO Request" + " Failed\n", __func__); + return -1; + } + + if (gpio_request(gpio_usb_otg_stat2, "USB OTG Power Enable")) { + printk(KERN_ERR "%s Max7312 USB_OTG_STAT2 GPIO Request" + " Failed\n", __func__); + return -1; + } + + gpio_direction_input(gpio_usb_otg_pen); + gpio_direction_input(gpio_usb_otg_stat1); + gpio_direction_input(gpio_usb_otg_stat2); + + otg_pen = __gpio_get_value(gpio_usb_otg_pen); + otg_stat1 = __gpio_get_value(gpio_usb_otg_stat1); + otg_stat2 = __gpio_get_value(gpio_usb_otg_stat2); + + if (otg_pen) { + status = VBUS_CHARGE; + if (otg_stat1 && otg_stat2) { + status |= VBUS_HIGH; + } + } else { + /* workaroud for some aspenite rev1 board that stat1=1 + * though vbus high, conflict with max3355 spec + * if (!otg_stat1 && !otg_stat2) */ + if (!otg_stat2) { + status = VBUS_HIGH; + } + } + + printk(KERN_DEBUG "%s otg_pen %d stat1 %d stat2 %d status %d\n\n", + __func__, otg_pen, otg_stat1, otg_stat2, status); + gpio_free(gpio_usb_otg_pen); + gpio_free(gpio_usb_otg_stat1); + gpio_free(gpio_usb_otg_stat2); + + local_irq_restore(flags); + return status; +} + +static irqreturn_t aspenite_u2o_vbus_event(int irq, void *(func)(int)) +{ + if (func) + func(1); + + return IRQ_HANDLED; +} + +static int aspenite_u2o_vbus_detect(void *func, int enable) +{ + int ret; + + if (enable) { + /* FIXME needed for future SRP support + * request_irq(IRQ_GPIO(gpio_usb_otg_stat1), + aspenite_u2o_vbus_event, IRQF_DISABLED | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "otg_stat1", func); + */ + ret = request_irq(IRQ_GPIO(gpio_usb_otg_stat2), + (irq_handler_t)aspenite_u2o_vbus_event, IRQF_DISABLED | + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "otg_stat2", func); + if (ret) + printk(KERN_INFO "request irq otgstat2 %d failed: %d\n", + IRQ_GPIO(gpio_usb_otg_stat2), ret); + } else { + /* free_irq(IRQ_GPIO(gpio_usb_otg_stat1), NULL); */ + free_irq(IRQ_GPIO(gpio_usb_otg_stat2), func); + } + + return 0; +} + +static int aspenite_u2o_vbus_set(int vbus_type) +{ + unsigned long flags; + + local_irq_save(flags); + + if (gpio_request(gpio_usb_otg_pen, "USB OTG Power Enable")) { + printk(KERN_ERR "%s Max7312 USB_OTG_PEN GPIO Request" + " Failed\n", __func__); + return -1; + } + + switch (vbus_type) { + case VBUS_SRP: + gpio_direction_output(gpio_usb_otg_pen,1); + udelay(10); + gpio_direction_output(gpio_usb_otg_pen,0); + break; + case VBUS_HIGH: + gpio_direction_output(gpio_usb_otg_pen,1); + break; + case VBUS_LOW: + gpio_direction_output(gpio_usb_otg_pen,0); + break; + default: + break; + } + gpio_free(gpio_usb_otg_pen); + + local_irq_restore(flags); + + return 0; +} +static int aspenite_otg_init(void) +{ + int gpio_usb_otg_stat1 = 0, gpio_usb_otg_stat2 = 0; + + if (gpio_request(gpio_usb_otg_stat1, "USB OTG Host Status 1") && + gpio_request(gpio_usb_otg_stat2, "USB OTG Host Status 2")) { + printk(KERN_ERR "Max7312 USB OTG Status GPIO Request Failed\n"); + return -EAGAIN; + } + + gpio_direction_input(gpio_usb_otg_stat1); + gpio_direction_input(gpio_usb_otg_stat2); + gpio_free(gpio_usb_otg_stat1); + gpio_free(gpio_usb_otg_stat2); + return 0; +} + +static int aspenite_u2o_vbus_set_ic(int function) +{ + printk(KERN_DEBUG "%s %d not implemented yet\n", __func__, function); + return 0; +} + +static struct otg_pmic_ops aspenite_otg_ops = { + .otg_vbus_init = aspenite_otg_init, + .otg_set_vbus = aspenite_u2o_vbus_set, + .otg_set_vbus_ic = aspenite_u2o_vbus_set_ic, + .otg_get_vbus_state = aspenite_u2o_vbus_status, +}; + +struct otg_pmic_ops *init_aspenite_otg_ops(void) +{ + return &aspenite_otg_ops; +} + +static struct pxa_usb_plat_info aspenite_u2o_info = { + .phy_init = pxa168_usb_phy_init, + .phy_deinit = pxa168_usb_phy_deinit, + .vbus_set = aspenite_u2o_vbus_set, + .vbus_status = aspenite_u2o_vbus_status, + .vbus_detect = aspenite_u2o_vbus_detect, + .init_pmic_ops = (void *)init_aspenite_otg_ops, +#ifdef CONFIG_USB_OTG + .is_otg = 1, +#else + .clk_gating = 1, +#endif +}; +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H +/* USB 2.0 Host Controller */ +static int aspenite_u2h_vbus_set (int enable) +{ + int gpio_u2h_vbus_on = GPIO_EXT0(4); + int gpio_u2h_vbus_flt_n = GPIO_EXT0(14); + + if(gpio_request(gpio_u2h_vbus_on, "USB Host VBUS_ON")) { + printk(KERN_ERR "Max7312 VBUS_ON GPIO Request Failed\n"); + return -EIO; + } + if(gpio_request(gpio_u2h_vbus_flt_n, "USB Host VBUS_FLT_N")) { + printk(KERN_ERR "Max7312 VBUS_FLT_N GPIO Request Failed\n"); + return -EIO; + } + if (gpio_u2h_vbus_on && gpio_u2h_vbus_flt_n) + { + if (enable) + gpio_direction_output(gpio_u2h_vbus_on, 1); + else + gpio_direction_output(gpio_u2h_vbus_on, 0); + + gpio_direction_input(gpio_u2h_vbus_flt_n); + gpio_free(gpio_u2h_vbus_on); + gpio_free(gpio_u2h_vbus_flt_n); + } + + return 0; +} + +static struct pxa_usb_plat_info aspenite_u2h_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_set = aspenite_u2h_vbus_set, +}; +#endif + +static void __init aspenite_init(void) +{ + mfp_config(ARRAY_AND_SIZE(aspenite_pin_config)); + pxa168_set_vdd_iox(VDD_IO0, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO1, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO2, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO3, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO4, VDD_IO_3P3V); + pxa168_mfp_set_fastio_drive(MFP_DS02X); /* on-chip devices */ pxa168_add_uart(1); - pxa168_add_nand(&aspenite_nand_info); + pxa168_add_freq(); + +#if defined(CONFIG_BT_HCIUART) && defined(CONFIG_WLAN_8688_SDIO) + pxa168_add_uart(3); +#endif + aspenite_add_nand(); + pxa168_add_ssp(0); + pxa168_add_twsi(0, &pwri2c_info, ARRAY_AND_SIZE(aspenite_i2c_board_info)); + pxa168_add_twsi(1, &pwri2c_info, ARRAY_AND_SIZE(pwri2c_board_info)); + if (is_android()) + pxa168_add_keypad(&aspenite_android_keypad_info); + else + pxa168_add_keypad(&aspenite_keypad_info); + +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&aspenite_u2o_info); +#endif + +#ifdef CONFIG_USB_OTG + pxa168_add_u2ootg(&aspenite_u2o_info); + pxa168_add_u2oehci(&aspenite_u2o_info); +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H + pxa168_add_u2h(&aspenite_u2h_info); +#endif + pxa168_add_mfu(&pxa168_eth_data); +#ifdef CONFIG_PCI + pxa168_add_pcie(&pxa168_pcie_data); +#endif +#if defined(CONFIG_MMC_PXA_SDH) + pxa168_add_sdh(0, &aspenite_sdh_platform_data_mmc1); +#if defined(CONFIG_WLAN_8688_SDIO) + pxa168_add_sdh(1, &aspenite_sdh_platform_data_mmc2); +#if defined(CONFIG_BT_HCIUART) + aspenite_bt_init(); +#endif +#endif +#if defined(CONFIG_MMC3) + pxa168_add_sdh(2, &aspenite_sdh_platform_data_mmc3); +#endif +#endif +#if defined(CONFIG_CIR) + pxa168_cir_init(); /*init the gpio */ +#endif +#if defined(CONFIG_PXA168_MSP) + pxa168_add_msp(&msp_ops); +#endif +#if defined(CONFIG_PXA168_CF) +#if defined(CONFIG_PXA168_CF_USE_GPIO_CARDDETECT) + pxa168_cf_init(); +#else + pxa168_add_cf(); +#endif +#endif + if (machine_is_aspenite()) { + pxa168_add_fb(&aspenite_lcd_info); + pxa168_add_fb_ovly(&aspenite_lcd_ovly_info); + } /* off-chip devices */ - platform_device_register(&smc91x_device); + if (machine_is_zylonite2()) { + pxa168_add_fb(&zylonite2_lcd_info); + platform_device_register(&smc91x_device); + } + + aspenite_init_spi(); +#if defined(CONFIG_PXA168_CAMERA) + pxa168_add_cam(); +#endif +#if defined(CONFIG_PXA_ICR) + pxa168_add_icr(); +#endif + +#if defined(CONFIG_BATTERY_ASPENITE) + aspenite_add_battery(); +#endif + } -MACHINE_START(ASPENITE, "PXA168-based Aspenite Development Platform") +MACHINE_START(ASPENITE, "PXA168 based Aspenite Development Platform") .phys_io = APB_PHYS_BASE, .boot_params = 0x00000100, .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, .map_io = pxa_map_io, .init_irq = pxa168_init_irq, .timer = &pxa168_timer, - .init_machine = common_init, + .init_machine = aspenite_init, MACHINE_END MACHINE_START(ZYLONITE2, "PXA168-based Zylonite2 Development Platform") @@ -152,5 +1671,5 @@ MACHINE_START(ZYLONITE2, "PXA168-based Zylonite2 Development Platform") .map_io = pxa_map_io, .init_irq = pxa168_init_irq, .timer = &pxa168_timer, - .init_machine = common_init, + .init_machine = aspenite_init, MACHINE_END diff --git a/arch/arm/mach-mmp/avengers_lite.c b/arch/arm/mach-mmp/avengers_lite.c index 8c3fa5d14f4bf4..309b7fc68d6d0a 100644 --- a/arch/arm/mach-mmp/avengers_lite.c +++ b/arch/arm/mach-mmp/avengers_lite.c @@ -3,8 +3,6 @@ * * Support for the Marvell PXA168-based Avengers lite Development Platform. * - * Copyright (C) 2009-2010 Marvell International Ltd. - * * 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 * publishhed by the Free Software Foundation. @@ -13,31 +11,1181 @@ #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 +#include +#include +#include #include "common.h" #include +#ifdef CONFIG_SD8XXX_RFKILL +#include +#endif +#include + +static unsigned int AVLITE_BOARDID = 0; +unsigned int board_is_1p9(void) +{ +#define BOARD_1P9 0x6 + + return (BOARD_1P9 == AVLITE_BOARDID); +} +EXPORT_SYMBOL(board_is_1p9); + +unsigned int board_is_2p5(void) +{ +#define BOARD_2P5 0x4 + + return (BOARD_2P5 == AVLITE_BOARDID); +} +EXPORT_SYMBOL(board_is_2p5); + +static int sd_debug = 0; +static int __init SD_debug_setup(char *__unused) +{ + sd_debug = 1; + return 1; +} +__setup("SD_debug", SD_debug_setup); + +int SD_debug_enable(void) +{ + return sd_debug; +} +EXPORT_SYMBOL(SD_debug_enable); /* Avengers lite MFP configurations */ static unsigned long avengers_lite_pin_config_V16F[] __initdata = { - /* DEBUG_UART */ - GPIO88_UART2_TXD, - GPIO89_UART2_RXD, + /***** UART_CAS ******/ + MFP_CFG(GPIO96, AF0), + MFP_CFG(GPIO97, AF1), + MFP_CFG(GPIO98, AF2),/* MFP_CFG(GPIO98, AF3), //FN2,3 */ + MFP_CFG(GPIO99, AF0), + + /***** BOARD_ID *****/ + MFP_CFG(GPIO100, AF0), + MFP_CFG(GPIO101, AF0), + MFP_CFG(GPIO20, AF0), + + /***** IIC ******/ + MFP_CFG(GPIO102, AF1), + MFP_CFG(GPIO103, AF1), + + /***** Camera ******/ + MFP_CFG(GPIO37, AF4), + MFP_CFG(GPIO38, AF4), + MFP_CFG(GPIO39, AF4), + MFP_CFG(GPIO40, AF4), + MFP_CFG(GPIO41, AF4), + MFP_CFG(GPIO42, AF4), + MFP_CFG(GPIO44, AF4), + MFP_CFG(GPIO45, AF4), + MFP_CFG(GPIO46, AF4), + MFP_CFG(GPIO48, AF4), + MFP_CFG(GPIO50, AF4), + MFP_CFG(GPIO54, AF4), + MFP_CFG(GPIO55, AF4), + + /***** for avlite ******/ + MFP_CFG(GPIO19, AF5), + MFP_CFG(GPIO23, AF5), + MFP_CFG(GPIO25, AF5), + + MFP_CFG(GPIO43, AF0), + MFP_CFG(GPIO47, AF0), + MFP_CFG(GPIO49, AF0), + MFP_CFG(GPIO51, AF0), + MFP_CFG(GPIO52, AF0), + MFP_CFG(GPIO53, AF0), + + MFP_CFG(GPIO110, AF0), + + MFP_CFG(GPIO104, AF0), + MFP_CFG(GPIO105, AF0), + MFP_CFG(GPIO106, AF0), + MFP_CFG(GPIO122, AF0), + + /***** fast IO bank *****/ + /***** LCD *****/ + MFP_CFG(GPIO56, AF1),/* fast IO drive strength control */ + MFP_CFG(GPIO57, AF1),/* fast IO drive strength control */ + MFP_CFG(GPIO58, AF1),/* fast IO drive strength control */ + MFP_CFG(GPIO59, AF1),/* fast IO drive strength control //attention */ + + MFP_CFG(GPIO60, AF1),/* voltage control */ + /* VCC_IO1 - 3.3V,VCC_IO0 - 3.3V:DS01X */ + /* VCC_IO1 - 3.3V,VCC_IO0 - 1.8V:DS02X */ + /* VCC_IO1 - 1.8V,VCC_IO0 - 3.3V:DS03X */ + /*VCC_IO1 - 1.8V,VCC_IO0 - 1.8V:DS04X */ + MFP_CFG(GPIO61, AF1),/* voltage control */ + /* VCC_IO3 - 3.3V,VCC_IO2 - 3.3V:DS01X */ + /* VCC_IO3 - 3.3V,VCC_IO2 - 1.8V:DS02X */ + /* VCC_IO3 - 1.8V,VCC_IO2 - 3.3V:DS03X */ + /* VCC_IO3 - 1.8V,VCC_IO2 - 1.8V:DS04X */ + MFP_CFG(GPIO62, AF1),/* voltage control */ + /* VCC_IO4 - 3.3V:DS01X */ + /*VCC_IO4 - 1.8V:DS03X */ + + MFP_CFG(GPIO63, AF1), + MFP_CFG(GPIO64, AF1), + MFP_CFG(GPIO65, AF1), + MFP_CFG(GPIO66, AF1), + MFP_CFG(GPIO67, AF1), + MFP_CFG(GPIO68, AF1), + MFP_CFG(GPIO69, AF1), + MFP_CFG(GPIO70, AF1), + MFP_CFG(GPIO71, AF1), + MFP_CFG(GPIO72, AF1), + MFP_CFG(GPIO73, AF1), + MFP_CFG(GPIO74, AF1), + MFP_CFG(GPIO75, AF1), + MFP_CFG(GPIO76, AF1), + MFP_CFG(GPIO77, AF1), + MFP_CFG(GPIO78, AF1), + MFP_CFG(GPIO79, AF1), + MFP_CFG(GPIO80, AF1), + MFP_CFG(GPIO81, AF1), + MFP_CFG(GPIO82, AF1), + MFP_CFG(GPIO83, AF1), + + /***** backlight control *****/ + MFP_CFG(GPIO34, AF0), + MFP_CFG(GPIO35, AF0), + MFP_CFG(GPIO36, AF0), + MFP_CFG(GPIO84, AF4), + MFP_CFG(GPIO85, AF0), + + /***** FLASH *****/ + MFP_CFG(GPIO0, AF5), + MFP_CFG(GPIO1, AF5), + MFP_CFG(GPIO2, AF5), + MFP_CFG(GPIO3, AF5), + MFP_CFG(GPIO4, AF5), + MFP_CFG(GPIO5, AF5), + MFP_CFG(GPIO6, AF5), + MFP_CFG(GPIO7, AF5), + + MFP_CFG(GPIO10, AF0), + MFP_CFG(GPIO11, AF0), + MFP_CFG(GPIO12, AF0), + MFP_CFG(GPIO13, AF0), + MFP_CFG(GPIO14, AF0), + MFP_CFG(GPIO15, AF0), + MFP_CFG(GPIO16, AF1), + MFP_CFG(GPIO17, AF0), + MFP_CFG(GPIO18, AF1), + + MFP_CFG(GPIO21, AF0), + MFP_CFG(GPIO22, AF0), + MFP_CFG(GPIO24, AF0), + MFP_CFG(GPIO26, AF1), + MFP_CFG(GPIO27, AF1), + + /***** DEBUG_UART *****/ + MFP_CFG(GPIO88, AF2),/* MFP_CFG(GPIO107,AF1), */ + MFP_CFG(GPIO89, AF2),/* MFP_CFG(GPIO108,AF1), */ + +#if !defined(CONFIG_MTD_M25P80) + MFP_CFG(GPIO109, AF0), +#else + MFP_CFG(GPIO109, AF4), +#endif + MFP_CFG(GPIO111, AF4), + MFP_CFG(GPIO107, AF4), + MFP_CFG(GPIO108, AF4), + MFP_CFG(GPIO112, AF0), + + /***** IIS_AUDIO *****/ + MFP_CFG(GPIO113, AF6), + MFP_CFG(GPIO114, AF1), + MFP_CFG(GPIO115, AF1), + MFP_CFG(GPIO116, AF2), + MFP_CFG(GPIO117, AF2), + + /***** SSP_CMMB reserved *****/ + MFP_CFG(GPIO118, AF0), + MFP_CFG(GPIO119, AF0), + MFP_CFG(GPIO120, AF0), + MFP_CFG(GPIO121, AF0), + + /***** FREE PIN *****/ + MFP_CFG(GPIO8, AF0), + MFP_CFG(GPIO9, AF0), +}; + +static unsigned long avengers_lite_boardid_pin_config[] = { + /***** BOARD_ID *****/ + MFP_CFG(GPIO101, AF0), + MFP_CFG(GPIO100, AF0), + MFP_CFG(GPIO20, AF0), +}; + +/* + * GPIO Keys + */ +static struct gpio_keys_button btn_button_table[] = { + [0] = { + .code = KEY_F1, + .gpio = MFP_PIN_GPIO4, + .active_low = 1, /* 0 for down 0, up 1; 1 for down 1, up 0 */ + .desc = "H_BTN button", + .type = EV_KEY, + /* .wakeup = */ + .debounce_interval = 10, /* 10 msec jitter elimination */ + }, + [1] = { + .code = KEY_F2, + .gpio = MFP_PIN_GPIO3, + .active_low = 1, /* 0 for down 0, up 1; 1 for down 1, up 0 */ + .desc = "O_BTN button", + .type = EV_KEY, + /* .wakeup = */ + .debounce_interval = 10, /* 10 msec jitter elimination */ + }, + [2] = { + .code = KEY_F3, + .gpio = MFP_PIN_GPIO2, + .active_low = 1, /* 0 for down 0, up 1; 1 for down 1, up 0 */ + .desc = "B_BTN button", + .type = EV_KEY, + /* .wakeup = */ + .debounce_interval = 10, /* 10 msec jitter elimination */ + }, + [3] = { + .code = KEY_F4, + .gpio = MFP_PIN_GPIO5, + .active_low = 1, /* 0 for down 0, up 1; 1 for down 1, up 0 */ + .desc = "S_BTN button", + .type = EV_KEY, + /* .wakeup = */ + .debounce_interval = 10, /* 10 msec jitter elimination */ + }, + [4] = { + .code = KEY_F5, + .gpio = MFP_PIN_GPIO1, + .active_low = 1, /* 0 for down 0, up 1; 1 for down 1, up 0 */ + .desc = "VUP button", + .type = EV_KEY, + /* .wakeup = */ + .debounce_interval = 10, /* 10 msec jitter elimination */ + }, + [5] = { + .code = KEY_F6, + .gpio = MFP_PIN_GPIO0, + .active_low = 1, /* 0 for down 0, up 1; 1 for down 1, up 0 */ + .desc = "VDN button", + .type = EV_KEY, + /* .wakeup = */ + .debounce_interval = 10, /* 10 msec jitter elimination */ + }, +}; + +static struct gpio_keys_platform_data gpio_keys_data = { + .buttons = btn_button_table, + .nbuttons = ARRAY_SIZE(btn_button_table), +}; + +static struct platform_device gpio_keys = { + .name = "gpio-keys", + .dev = { + .platform_data = &gpio_keys_data, + }, + .id = -1, +}; + +static void __init avengers_lite_gpio_keys_init(void) +{ + platform_device_register(&gpio_keys); +} + +/* + * + */ + +#define LCD_BL_PWR_EN (board_is_2p5()? MFP_PIN_GPIO25 : MFP_PIN_GPIO85) + +static void avengers_lcd_power(struct pxa168fb_info *fbi, + unsigned int spi_gpio_cs, unsigned int spi_gpio_reset, int on) +{ + int lcd_pwr_en; + int lcd_bl_pwr_en; + int lcd_lr, lcd_ud; + + lcd_pwr_en = MFP_PIN_GPIO34; + if (gpio_request(lcd_pwr_en, "lcd_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", + lcd_pwr_en); + goto out; + } + + lcd_bl_pwr_en = LCD_BL_PWR_EN; + if (gpio_request(lcd_bl_pwr_en, "lcd_bl_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", + lcd_bl_pwr_en); + goto out1; + } + + lcd_lr = MFP_PIN_GPIO35; + if (gpio_request(lcd_lr, "lcd_lr")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_lr); + goto out2; + } + + lcd_ud = MFP_PIN_GPIO36; + if (gpio_request(lcd_ud, "lcd_ud")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_ud); + goto out3; + } + + if (on) { + /* re-config lcd_lr/ud pin to enable lcd */ + gpio_direction_output(lcd_pwr_en, 1); + gpio_direction_output(lcd_bl_pwr_en, 1); + gpio_direction_output(lcd_lr, 1); + gpio_direction_output(lcd_ud, 0); + } else { + /* config lcd_lr/ud pin to gpio for power optimization */ + gpio_direction_output(lcd_pwr_en, 0); + gpio_direction_output(lcd_bl_pwr_en, 0); + gpio_direction_input(lcd_lr); + gpio_direction_input(lcd_ud); + } + + gpio_free(lcd_ud); +out3: + gpio_free(lcd_lr); +out2: + gpio_free(lcd_bl_pwr_en); +out1: + gpio_free(lcd_pwr_en); +out: + return; +} + +static struct fb_videomode video_modes[] = { + /* innolux WVGA mode info */ + [0] = { + .pixclock = 25000, + .refresh = 60, + .xres = 800, + .yres = 480, + .hsync_len = 1, + .left_margin = 45, + .right_margin = 210, + .vsync_len = 1, + .upper_margin = 22, + .lower_margin = 132, + .sync = 0, + }, +}; +static struct pxa168fb_mach_info avengers_lite_lcd_info __initdata = { + .id = "Base", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_GPIO, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .panel_rbswap = 0, + .max_fb_size = (DEFAULT_FB_SIZE + 5 * 1024 * 1024), + .pxa168fb_lcd_power = avengers_lcd_power, +}; + +struct pxa168fb_mach_info avengers_lite_lcd_ovly_info __initdata = { + .id = "Ovly", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_GPIO, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .panel_rbswap = 0, + .max_fb_size = (DEFAULT_FB_SIZE), +}; + +static struct pxa168fb_mach_info v1p9_avengers_lite_lcd_info __initdata = { + .id = "Base", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_24, + .dumb_mode = DUMB_MODE_RGB888, + .active = 1, + .panel_rbswap = 0, + .max_fb_size = (DEFAULT_FB_SIZE + 5 * 1024 * 1024), + .pxa168fb_lcd_power = avengers_lcd_power, +}; + +#if defined(CONFIG_PXA168_CAMERA) && defined(CONFIG_VIDEO_OV7740) +/* sensor init */ +static int sensor_power_onoff(int on, int sensor) +{ + + int ov7740_pwr_down; + + ov7740_pwr_down = MFP_PIN_GPIO96; + if (gpio_request(ov7740_pwr_down , "ov7740_pwr_down")) { + printk(KERN_INFO "gpio %d request failed\n", ov7740_pwr_down); + return -EIO; + } + + if (on) { + gpio_direction_output(ov7740_pwr_down, 0); + printk(KERN_INFO"ov7740 power on\n"); + } else { + gpio_direction_output(ov7740_pwr_down, 1); + printk(KERN_INFO"ov7740 power off\n"); + } + gpio_free(ov7740_pwr_down); + + return 0; +} + + +static struct sensor_platform_data ov7740_sensor_data = { + .id = SENSOR_LOW, + .power_on = sensor_power_onoff, +}; + +#endif +static void __init avengers_lite_lcd_init(void) +{ + int lcd_lr, lcd_ud; + + lcd_lr = MFP_PIN_GPIO35; + lcd_ud = MFP_PIN_GPIO36; + + if (gpio_request(lcd_lr, "lcd_lr")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_lr); + return; + } + gpio_direction_output(lcd_lr, 1); + gpio_free(lcd_lr); + + if (gpio_request(lcd_ud, "lcd_ud")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_ud); + return; + } + gpio_direction_output(lcd_ud, 0); + gpio_free(lcd_ud); + + if(board_is_1p9()) + pxa168_add_fb(&v1p9_avengers_lite_lcd_info); + else + pxa168_add_fb(&avengers_lite_lcd_info); + + pxa168_add_fb_ovly(&avengers_lite_lcd_ovly_info); +} + +static struct platform_pwm_backlight_data avengers_lite_backlight_data = { + .pwm_id = 0, + .max_brightness = 255, + .dft_brightness = 200, + .pwm_period_ns = 78770,/* 3921569, */ + /* + .pwm_id = 0, + .max_brightness = 1023, + .dft_brightness = 1023, + .pwm_period_ns = 78770, + */ +}; + +static struct platform_device avengers_lite_backlight_device = { + .name = "pwm-backlight", + .dev = { + .parent = &pxa168_device_pwm0.dev, + .platform_data = &avengers_lite_backlight_data, + }, +}; + +static void __init avengers_lite_backlight_register(void) +{ + int lcd_pwr_en; + int lcd_bl_pwr_en; + int ret = platform_device_register(&avengers_lite_backlight_device); + if (ret) + printk(KERN_ERR "avengers lite: failed to register backlight device: %d\n", ret); + + lcd_pwr_en = MFP_PIN_GPIO34; + + if (gpio_request(lcd_pwr_en, "lcd_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_pwr_en); + return; + } + gpio_direction_output(lcd_pwr_en, 1); + gpio_free(lcd_pwr_en); + + lcd_bl_pwr_en = LCD_BL_PWR_EN; + + if (gpio_request(lcd_bl_pwr_en, "lcd_bl_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_bl_pwr_en); + return; + } + gpio_direction_output(lcd_bl_pwr_en, 1); + gpio_free(lcd_bl_pwr_en); + + return; +} + +struct platform_device avengers_lite_power_mcu = { + .name = "power_mcu", +}; + +struct platform_device avengers_lite_power_supply = { + .name = "battery", +}; + +static void ack_standby(void) +{ + gpio_direction_output(MFP_PIN_GPIO53, 1); + mdelay(100); + gpio_direction_output(MFP_PIN_GPIO53, 0); +} + +static void ack_powerdwn(void) +{ + gpio_direction_output(MFP_PIN_GPIO51, 1); + mdelay(100); + gpio_direction_output(MFP_PIN_GPIO51, 0); +} + +static int power_button_init(irq_handler_t pwrdwn_handler, irq_handler_t standby_handler) +{ + int ret; + int irq; + + irq = gpio_to_irq(MFP_PIN_GPIO49); + ret = request_irq(irq , pwrdwn_handler, \ + IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING , \ + "shutdown detect", NULL); + if (ret) { + printk(KERN_ERR "%s: can't request detect standby irq\n", + __FUNCTION__); + goto out; + } + + irq = gpio_to_irq(MFP_PIN_GPIO52); + ret = request_irq(irq, standby_handler , \ + IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING , \ + "standby detect", NULL); + if (ret) { + printk(KERN_ERR "%s: can't request detect shutdown irq\n", + __FUNCTION__); + goto out; + } + + gpio_direction_input(MFP_PIN_GPIO49); + gpio_direction_input(MFP_PIN_GPIO52); + +out: + return ret; +} + +static struct power_button_platform_data power_button_data = { + .init = power_button_init, + .send_standby_ack = ack_standby, + .send_powerdwn_ack = ack_powerdwn, +}; + +struct platform_device avengers_lite_power_button = { + .name = "power-button", + .dev = { + .platform_data = &power_button_data, + }, + .id = -1, +}; + +struct platform_device avengers_lite_ca = { + .name = "pxa168-ca", +}; + +static struct i2c_pxa_platform_data pwri2c_info __initdata = { + .use_pio = 1, +}; + +static struct i2c_board_info avengers_lite_i2c_board_info[] = { +#if defined(CONFIG_TSC2007) + { + .type = "tsc2007", + .addr = 0x48, /* 0x90/0x91 */ + .irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO19)), + }, +#endif +#if defined(CONFIG_PXA168_CAMERA) && defined(CONFIG_VIDEO_OV7740) + { + .type = "ov7740", + .addr = 0x21, + .platform_data = &ov7740_sensor_data, + /* .irq = */ + }, +#endif +}; + +static struct i2c_board_info pwri2c_board_info[] = +{ + +#if defined(CONFIG_BMA020) + { + .type = "bma020", + .addr = 0x38, + .irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO109)), + }, +#endif +#if defined(CONFIG_MCU_PM) + { + .type = "power_mcu", + .addr = 0x2C, + }, +#endif +}; + +#if defined(CONFIG_MMC_PXA_SDH) +static struct pfn_cfg mmc1_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT6, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT5, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT4, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT3, MFP_CFG(GPIO90, AF1), MFP_CFG(GPIO90, AF0)), + PFN_CFG(PIN_MMC_DAT2, MFP_CFG(GPIO91, AF1), MFP_CFG(GPIO91, AF0)), + PFN_CFG(PIN_MMC_DAT1, MFP_CFG(GPIO92, AF1), MFP_CFG(GPIO92, AF0)), + PFN_CFG(PIN_MMC_DAT0, MFP_CFG(GPIO93, AF1), MFP_CFG(GPIO93, AF0)), + PFN_CFG(PIN_MMC_CMD, MFP_CFG(GPIO94, AF1), MFP_CFG(GPIO94, AF0)), + PFN_CFG(PIN_MMC_CLK, MFP_CFG(GPIO95, AF1), MFP_CFG(GPIO95, AF0)), + PFN_CFG(PIN_MMC_CD, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_WP, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; + +static struct pxasdh_platform_data avengers_lite_sdh_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .max_speed = 24000000, + .init = pxa_mci_init, + .exit = pxa_mci_exit, + .get_ro = pxa_mci_ro, + .pfn_table = mmc1_pfn_cfg, +}; + +static struct pfn_cfg mmc4_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT6, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT5, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT4, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT3, MFP_CFG(GPIO33, AF1), MFP_CFG(GPIO33, AF5)), + PFN_CFG(PIN_MMC_DAT2, MFP_CFG(GPIO32, AF1), MFP_CFG(GPIO32, AF5)), + PFN_CFG(PIN_MMC_DAT1, MFP_CFG(GPIO31, AF1), MFP_CFG(GPIO31, AF5)), + PFN_CFG(PIN_MMC_DAT0, MFP_CFG(GPIO30, AF1), MFP_CFG(GPIO30, AF5)), + PFN_CFG(PIN_MMC_CMD, MFP_CFG(GPIO28, AF1), MFP_CFG(GPIO28, AF5)), + PFN_CFG(PIN_MMC_CLK, MFP_CFG(GPIO29, AF1), MFP_CFG(GPIO29, AF5)), + PFN_CFG(PIN_MMC_CD, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_WP, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; + +static struct pxasdh_platform_data avengers_lite_sdh3_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .pfn_table = mmc4_pfn_cfg, +}; + +static void __init avengers_lite_init_mmc(void) +{ + int uart_to_sd; + + /* switch SD card and SD debug board */ + uart_to_sd = MFP_PIN_GPIO23; + if (gpio_request(uart_to_sd, "uart_to_sd")) { + pr_warning("failed to request GPIO for uart_to_sd\n"); + return; + } + if (SD_debug_enable()) + gpio_direction_output(uart_to_sd, 1); + else + gpio_direction_output(uart_to_sd, 0); + + gpio_free(uart_to_sd); + + /* MMC card detect & write protect for controller 0 */ + pxa_mmc_slot[1].gpio_detect = 1; + pxa_mmc_slot[1].gpio_cd = mfp_to_gpio(MFP_PIN_GPIO86); + pxa_mmc_slot[1].gpio_wp = mfp_to_gpio(MFP_PIN_GPIO87); + + pxa168_add_sdh(1, &avengers_lite_sdh_platform_data); + +#ifdef CONFIG_SD8XXX_RFKILL + add_sd8x_rfkill_device(mfp_to_gpio(MFP_PIN_GPIO104), 0, + &avengers_lite_sdh3_platform_data.pmmc); +#endif + pxa168_add_sdh(3, &avengers_lite_sdh3_platform_data); +} +#endif + +struct platform_device pxa168_device_rtc = { + .name = "ec-rtc", + .id = -1, +}; + +static void __init avengers_lite_init_rtc(void) +{ + platform_device_register(&pxa168_device_rtc); +} + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static struct pxa_usb_plat_info avengers_lite_u2o_info = { + .phy_init = pxa168_usb_phy_init, +}; +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H +/* USB 2.0 Host Controller */ +static struct u2h_work_t { + struct device *dev; + struct work_struct work; +} u2h_work; +static int gpio_u2h_oc = mfp_to_gpio(MFP_PIN_GPIO37); + +int avengers_lite_is_u2h_oc(void) +{ + int ret; + ret = gpio_get_value(gpio_u2h_oc); + pr_debug("gpio_u2h_oc %x\n", ret); + return !ret; +} + +static void avengers_lite_u2h_oc_worker(struct work_struct *work) +{ + pr_debug("%s dev %p\n", __func__, u2h_work.dev); + if (avengers_lite_is_u2h_oc()) + kobject_uevent(&u2h_work.dev->kobj, KOBJ_OFFLINE); + else + kobject_uevent(&u2h_work.dev->kobj, KOBJ_ONLINE); +} + +static irqreturn_t avengers_lite_u2h_oc_handler(int irq, void *data) +{ + pr_debug("%s\n", __func__); + schedule_work(&u2h_work.work); + return IRQ_HANDLED; +} + +static int avengers_lite_u2h_plat_init(struct device *dev) +{ + int ret; + + gpio_direction_input(gpio_u2h_oc); + ret = request_irq(IRQ_GPIO(gpio_u2h_oc), avengers_lite_u2h_oc_handler, + IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "u2h over current", NULL); + if (ret) { + printk("request u2h over current interrupt failed %d\n", ret); + return -EIO; + } + + INIT_WORK(&u2h_work.work, avengers_lite_u2h_oc_worker); + u2h_work.dev = dev; + + return 0; +} + +static int avengers_lite_u2h_vbus_set(int enable) +{ + int usb_p_en = mfp_to_gpio(MFP_PIN_GPIO106); + + if (gpio_request(usb_p_en, "usb_p_en")) { + printk(KERN_INFO "gpio %d request failed\n", usb_p_en); + return -EIO; + } + gpio_direction_output(usb_p_en, 0); + gpio_free(usb_p_en); + + return 0; +} + +static struct pxa_usb_plat_info avengers_lite_u2h_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_set = avengers_lite_u2h_vbus_set, + .plat_init = avengers_lite_u2h_plat_init, }; +#endif + +#if defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MODULE) +#define GPIO_CMMB_CS MFP_PIN_GPIO112 +#define GPIO_CMMB_IRQ MFP_PIN_GPIO25 +#define GPIO_CMMB_POWER MFP_PIN_GPIO110 + +static struct pxa2xx_spi_master pxa_ssp_master_info = { + .num_chipselect = 1, +#if !defined(CONFIG_MTD_M25P80) + .enable_dma = 1, +#endif +}; + +/* + * spi_finish used by spi_read_bytes to control + * GPIO_CMMB_CS to be pull down always + * when read not finished. + */ +int spi_finish = 1; + +static int cmmb_power_on(void) +{ + if (gpio_request(GPIO_CMMB_POWER, "cmmb if101 power")) { + pr_warning("failed to request GPIO for CMMB POWER\n"); + return -EIO; + } + gpio_direction_output(GPIO_CMMB_POWER, 0); + mdelay(100); + gpio_direction_output(GPIO_CMMB_POWER, 1); + gpio_free(GPIO_CMMB_POWER); + mdelay(100); + + printk("CMMB module is power on\n"); + + return 0; +} + +static int cmmb_power_off(void) +{ + if (gpio_request(GPIO_CMMB_POWER, "cmmb if101 power")) { + pr_warning("failed to request GPIO for CMMB POWER\n"); + return -EIO; + } + gpio_direction_output(GPIO_CMMB_POWER, 0); + gpio_free(GPIO_CMMB_POWER); + mdelay(100); + + printk("CMMB module is power off\n"); + + return 0; +} + +static struct cmmb_platform_data cmmb_info = { + .power_on = cmmb_power_on, + .power_off = cmmb_power_off, +}; + +static void cmmb_if101_cs(u32 cmd) +{ + if (!spi_finish && cmd == PXA2XX_CS_DEASSERT) + return; + gpio_set_value(GPIO_CMMB_CS, !(cmd == PXA2XX_CS_ASSERT)); +} + +static struct pxa2xx_spi_chip cmmb_if101_chip = { + .rx_threshold = 1, + .tx_threshold = 1, + .cs_control = cmmb_if101_cs, +}; + +/* bus_num must match id in pxa2xx_set_spi_info() call */ +#if !defined(CONFIG_MTD_M25P80) +static struct spi_board_info spi_board_info[] __initdata = { + { + .modalias = "cmmb_if101", + .platform_data = &cmmb_info, + .controller_data = &cmmb_if101_chip, + .irq = gpio_to_irq(mfp_to_gpio(GPIO_CMMB_IRQ)), + .max_speed_hz = 1000000, + .bus_num = 2, + .chip_select = 0, + .mode = SPI_MODE_0, + }, +}; +#else +static struct pxa2xx_spi_chip m25pxx_spi_info = { + .tx_threshold = 1, + .rx_threshold = 1, + .timeout = 1000, + .gpio_cs = 110 +}; + +static struct spi_board_info __initdata spi_board_info[] = { + { + .modalias = "m25p80", + .mode = SPI_MODE_0, + .max_speed_hz = 260000, + .bus_num = 2, + .chip_select = 0, + .platform_data = NULL, + .controller_data = &m25pxx_spi_info, + .irq = -1, + }, +}; +#endif + +static void __init avengers_lite_init_spi(void) +{ +#ifndef CONFIG_MTD_M25P80 + int err; + + err = gpio_request(GPIO_CMMB_CS, "cmmb if101 cs"); + if (err) { + pr_warning("failed to request GPIO for CMMB CS\n"); + return; + } + gpio_direction_output(GPIO_CMMB_CS, 1); + + err = gpio_request(GPIO_CMMB_IRQ, "cmmb if101 irq"); + if (err) { + pr_warning("failed to request GPIO for CMMB IRQ\n"); + return; + } + gpio_direction_input(GPIO_CMMB_IRQ); +#endif + pxa168_add_ssp(1); + pxa168_add_spi(2, &pxa_ssp_master_info); + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); +} +#else +static inline void avengers_lite_init_spi(void) {} +#endif + +static struct platform_device *devices[] __initdata = { + &pxa168_device_pwm0, + &avengers_lite_power_mcu, + &avengers_lite_power_supply, + &avengers_lite_power_button, +}; + +DECLARE_AVENGERSLITE_MLC_4G_ANDROID_PARTITIONS(avengerslite_mlc_4g_android_partitions); +DECLARE_AVENGERSLITE_MLC_1G_ANDROID_PARTITIONS(avengerslite_mlc_1g_android_partitions); +DECLARE_AVENGERSLITE_MLC_4G_MAEMO_PARTITIONS(avengerslite_mlc_4g_maemo_partitions); +DECLARE_AVENGERSLITE_MLC_1G_MAEMO_PARTITIONS(avengerslite_mlc_1g_maemo_partitions); +DECLARE_AVENGERSLITE_SLC_PARTITIONS(avengerslite_slc_partitions); +static struct pxa3xx_nand_platform_data avengers_lite_nand_info; +static void __init avengers_lite_add_nand(void) +{ + unsigned long long mlc_size = 0; + + mlc_size = get_mlc_size(); + printk(KERN_INFO "@%s Get mlc size from CMDLINE:%d\n", __FUNCTION__, (int)mlc_size); + + if (is_android()) { + if (board_is_2p5()) { + avengers_lite_nand_info.parts[0] = avengerslite_mlc_1g_android_partitions; + avengers_lite_nand_info.nr_parts[0] = ARRAY_SIZE(avengerslite_mlc_1g_android_partitions); + goto done; + } else { + avengers_lite_nand_info.parts[0] = avengerslite_slc_partitions; + avengers_lite_nand_info.nr_parts[0] = ARRAY_SIZE(avengerslite_slc_partitions); + } + + switch (mlc_size) { + case 1: /* 1G */ + avengers_lite_nand_info.parts[1] = avengerslite_mlc_1g_android_partitions; + avengers_lite_nand_info.nr_parts[1] = ARRAY_SIZE(avengerslite_mlc_1g_android_partitions); + break; + case 4: /* 4G */ + avengers_lite_nand_info.parts[1] = avengerslite_mlc_4g_android_partitions; + avengers_lite_nand_info.nr_parts[1] = ARRAY_SIZE(avengerslite_mlc_4g_android_partitions); + break; + default: + /* FIXME: do something or not */ + avengers_lite_nand_info.parts[1] = avengerslite_mlc_4g_android_partitions; + avengers_lite_nand_info.nr_parts[1] = ARRAY_SIZE(avengerslite_mlc_4g_android_partitions); + break; + } + } else { + if (board_is_2p5()) { + avengers_lite_nand_info.parts[0] = avengerslite_mlc_1g_maemo_partitions; + avengers_lite_nand_info.nr_parts[0] = ARRAY_SIZE(avengerslite_mlc_1g_maemo_partitions); + goto done; + } else { + avengers_lite_nand_info.parts[0] = avengerslite_slc_partitions; + avengers_lite_nand_info.nr_parts[0] = ARRAY_SIZE(avengerslite_slc_partitions); + } + + switch (mlc_size) { + case 1: /* 1G */ + avengers_lite_nand_info.parts[1] = avengerslite_mlc_1g_maemo_partitions; + avengers_lite_nand_info.nr_parts[1] = ARRAY_SIZE(avengerslite_mlc_1g_maemo_partitions); + break; + case 4: /* 4G */ + avengers_lite_nand_info.parts[1] = avengerslite_mlc_4g_maemo_partitions; + avengers_lite_nand_info.nr_parts[1] = ARRAY_SIZE(avengerslite_mlc_4g_maemo_partitions); + break; + default: + /* FIXME: do something or not */ + avengers_lite_nand_info.parts[1] = avengerslite_mlc_4g_maemo_partitions; + avengers_lite_nand_info.nr_parts[1] = ARRAY_SIZE(avengerslite_mlc_4g_maemo_partitions); + break; + } + /* make mlc whole as a massstorage room */ + } + +done: + avengers_lite_nand_info.use_dma = 1; + avengers_lite_nand_info.RD_CNT_DEL = 0; + avengers_lite_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *)&avengers_lite_nand_info); +} + +unsigned int mfp_read_boardid(unsigned long *cfgs, int num) { + unsigned long id = 0; + int i; + + for (i = 0; i < num; i++, cfgs++) { + unsigned long c = *cfgs; + int pin; + int pin_level; + + pin = MFP_PIN(c);/* get pin no. */ + pin_level = 0; + + gpio_direction_input(pin); + + pin_level = gpio_get_value(pin) ? 1 : 0; + id <<= 1; + id |= pin_level; + } + return id; +} + +static void gpio_ec_init(void) +{ + gpio_direction_input(MFP_PIN_GPIO89); + gpio_direction_output(MFP_PIN_GPIO88, 0); +} + +static void avenger_lite_power_off(void) +{ + pr_notice("notify EC to shutdown\n"); + gpio_direction_output(MFP_PIN_GPIO51, 1); + gpio_direction_output(MFP_PIN_GPIO53, 1); + mdelay(100); + gpio_direction_output(MFP_PIN_GPIO51, 0); + gpio_direction_output(MFP_PIN_GPIO53, 0); + + /* Spin to death... */ + while (1); +} + +/* android usb adb device data */ +static struct android_usb_platform_data android_data = { + .nluns = 2, +}; + +static struct platform_device android_usb = { + .name = "android_usb", + .dev = { + .platform_data = &android_data, + }, +}; + +static void __init android_init(void) +{ + platform_device_register(&android_usb); +} + +static void avengers_sel_debug(void) +{ + int sel_debug; + sel_debug = mfp_to_gpio(MFP_PIN_GPIO2); + if (gpio_request(sel_debug, "uart debug select")) { + printk(KERN_ERR "gpio %d request failed\n", sel_debug); + return; + } + gpio_direction_input(sel_debug); + if (!gpio_get_value(sel_debug)) + SD_debug_setup("used"); + gpio_free(sel_debug); +} static void __init avengers_lite_init(void) { + pxa168_set_vdd_iox(VDD_IO0, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO1, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO2, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO3, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO4, VDD_IO_3P3V); + pxa168_mfp_set_fastio_drive(MFP_DS02X); + + mfp_config(ARRAY_AND_SIZE(avengers_lite_boardid_pin_config)); + + AVLITE_BOARDID = mfp_read_boardid( + ARRAY_AND_SIZE(avengers_lite_boardid_pin_config)); + printk(KERN_INFO "BOARD ID=%d\n",AVLITE_BOARDID); + mfp_config(ARRAY_AND_SIZE(avengers_lite_pin_config_V16F)); + /* check whether to enable SD-to-UART debug */ + avengers_sel_debug(); + /* on-chip devices */ - pxa168_add_uart(2); + pxa168_add_uart(4); + avengers_lite_add_nand(); + pxa168_add_ssp(0); + pxa168_add_twsi(0, &pwri2c_info, ARRAY_AND_SIZE(avengers_lite_i2c_board_info)); + pxa168_add_twsi(1, &pwri2c_info, ARRAY_AND_SIZE(pwri2c_board_info)); +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&avengers_lite_u2o_info); +#endif +#ifdef CONFIG_USB_EHCI_PXA_U2H + pxa168_add_u2h(&avengers_lite_u2h_info); +#endif +#if defined(CONFIG_MMC_PXA_SDH) + avengers_lite_init_mmc(); +#endif + + pxa168_add_freq(); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + + avengers_lite_init_rtc(); + /* off-chip devices */ + avengers_lite_lcd_init(); + avengers_lite_backlight_register(); + avengers_lite_init_spi(); + +#if defined(CONFIG_PXA168_CAMERA) && defined(CONFIG_VIDEO_OV7740) + pxa168_add_cam(); +#endif + +#if defined(CONFIG_PXA_ICR) + pxa168_add_icr(); +#endif + +#if defined(CONFIG_KEYBOARD_GPIO) + avengers_lite_gpio_keys_init(); +#endif + +#ifdef CONFIG_ANDROID_PMEM + android_add_pmem("pmem", 0x01000000UL, 1, 0); + android_add_pmem("pmem_adsp", 0x00400000UL, 0, 0); +#endif + + if (is_android()) + android_init(); + + gpio_ec_init(); + + pm_power_off = avenger_lite_power_off; } MACHINE_START(AVENGERS_LITE, "PXA168 Avengers lite Development Platform") diff --git a/arch/arm/mach-mmp/clock.c b/arch/arm/mach-mmp/clock.c index 886e05648f08a5..ce4664529ca608 100644 --- a/arch/arm/mach-mmp/clock.c +++ b/arch/arm/mach-mmp/clock.c @@ -12,10 +12,22 @@ #include #include #include +#include #include +#include +#include #include "clock.h" +#include +#ifdef CONFIG_PXA910_CLOCK_TRACE +struct info_head clock_trace_list = { + .list = LIST_HEAD_INIT(clock_trace_list.list), + .lock = RW_LOCK_UNLOCKED, + .device = {0, 0, 0, 0, 0, 0, 0, 0}, +}; +#endif + static void apbc_clk_enable(struct clk *clk) { uint32_t clk_rst; @@ -26,14 +38,54 @@ static void apbc_clk_enable(struct clk *clk) static void apbc_clk_disable(struct clk *clk) { - __raw_writel(0, clk->clk_rst); + __raw_writel(0x00, clk->clk_rst); +} + +static void apbc_pwm_clk_disable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); + tmp &= ~(0x02L); + __raw_writel(tmp, clk->clk_rst); + tmp &= ~(0x03L); + __raw_writel(tmp, clk->clk_rst); } +struct clkops apbc_pwm_clk_ops = { + .enable = apbc_clk_enable, + .disable = apbc_pwm_clk_disable, +}; + struct clkops apbc_clk_ops = { .enable = apbc_clk_enable, .disable = apbc_clk_disable, }; +static int apbc_uart_clk_setrate(struct clk *clk, unsigned long rate) +{ + if ((clk->fnclksel && (rate <= clk->rate)) && + (!clk->fnclksel && (rate > clk->rate))) + return 0; + + if (clk->enabled) + clk->ops->disable(clk); + + if (rate > clk->rate) + clk->fnclksel = 0; + else + clk->fnclksel = 1; + + if (clk->enabled) + clk->ops->enable(clk); + + return 0; +} + +struct clkops apbc_uart_clk_ops = { + .enable = apbc_clk_enable, + .disable = apbc_clk_disable, + .setrate = apbc_uart_clk_setrate, +}; + static void apmu_clk_enable(struct clk *clk) { __raw_writel(clk->enable_val, clk->clk_rst); @@ -44,9 +96,112 @@ static void apmu_clk_disable(struct clk *clk) __raw_writel(0, clk->clk_rst); } +static int apmu_clk_setrate(struct clk *clk, unsigned long rate) +{ + __raw_writel(rate, clk->clk_rst); + return 0; +} + struct clkops apmu_clk_ops = { .enable = apmu_clk_enable, .disable = apmu_clk_disable, + .setrate = apmu_clk_setrate, +}; + + +static void pseudo_clk_enable(struct clk *clk) +{ +} + +static void pseudo_clk_disable(struct clk *clk) +{ +} + +struct clkops pseudo_clk_ops = { + .enable = pseudo_clk_enable, + .disable = pseudo_clk_disable, +}; + +static void u2o_clk_enable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); + + tmp |= 0x9; + __raw_writel(tmp, clk->clk_rst); +} + +static void u2o_clk_disable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); + + tmp &= ~0x9; + __raw_writel(tmp, clk->clk_rst); +} + +struct clkops u2o_clk_ops = { + .enable = u2o_clk_enable, + .disable = u2o_clk_disable, +}; + +static void u2h_clk_enable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); + + tmp |= 0x12; + __raw_writel(tmp, clk->clk_rst); +} + +static void u2h_clk_disable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); + + tmp &= ~0x12; + __raw_writel(tmp, clk->clk_rst); +} + +struct clkops u2h_clk_ops = { + .enable = u2h_clk_enable, + .disable = u2h_clk_disable, +}; + +static void sdh_clk_enable(struct clk *clk) +{ + u32 tmp = 0x1b; + + if (clk->clk_rst == APMU_SDH1) { + __raw_writel(tmp, APMU_SDH0); + } + if (clk->clk_rst == APMU_SDH3) { + __raw_writel(tmp, APMU_SDH2); + } + __raw_writel(tmp, clk->clk_rst); +} + +static void sdh_clk_disable(struct clk *clk) +{ + __raw_writel(0, clk->clk_rst); +} + +struct clkops sdh_clk_ops = { + .enable = sdh_clk_enable, + .disable = sdh_clk_disable, +}; + +static void cf_clk_enable(struct clk *clk) +{ + __raw_writel(0xD8, clk->clk_rst); + udelay(10); + __raw_writel(0xDB, clk->clk_rst); +} + +static void cf_clk_disable(struct clk *clk) +{ + __raw_writel(0, clk->clk_rst); +} + +struct clkops cf_clk_ops = { + .enable = cf_clk_enable, + .disable = cf_clk_disable, }; static DEFINE_SPINLOCK(clocks_lock); @@ -55,10 +210,24 @@ int clk_enable(struct clk *clk) { unsigned long flags; + struct clock_trace_info *p = NULL; + spin_lock_irqsave(&clocks_lock, flags); if (clk->enabled++ == 0) clk->ops->enable(clk); spin_unlock_irqrestore(&clocks_lock, flags); + +#ifdef CONFIG_PXA910_CLOCK_TRACE + list_for_each_entry(p, &clock_trace_list.list, list) { + if (clk == p->clk) { + write_lock(&clock_trace_list.lock); + set_bit(p->index, clock_trace_list.device); + write_unlock(&clock_trace_list.lock); + break; + } + } +#endif + return 0; } EXPORT_SYMBOL(clk_enable); @@ -66,6 +235,7 @@ EXPORT_SYMBOL(clk_enable); void clk_disable(struct clk *clk) { unsigned long flags; + struct clock_trace_info *p = NULL; WARN_ON(clk->enabled == 0); @@ -73,6 +243,18 @@ void clk_disable(struct clk *clk) if (--clk->enabled == 0) clk->ops->disable(clk); spin_unlock_irqrestore(&clocks_lock, flags); + +#ifdef CONFIG_PXA910_CLOCK_TRACE + list_for_each_entry(p, &clock_trace_list.list, list) { + if (clk == p->clk) { + write_lock(&clock_trace_list.lock); + clear_bit(p->index, clock_trace_list.device); + write_unlock(&clock_trace_list.lock); + break; + } + } +#endif + } EXPORT_SYMBOL(clk_disable); @@ -88,3 +270,61 @@ unsigned long clk_get_rate(struct clk *clk) return rate; } EXPORT_SYMBOL(clk_get_rate); + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long flags; + int ret = -EINVAL; + + if (clk->ops->setrate) { + spin_lock_irqsave(&clocks_lock, flags); + ret = clk->ops->setrate(clk, rate); + spin_unlock_irqrestore(&clocks_lock, flags); + } + + return ret; +} +EXPORT_SYMBOL(clk_set_rate); + +void clks_register(struct clk_lookup *clks, size_t num) +{ + int i; +#ifdef CONFIG_PXA910_CLOCK_TRACE + struct clock_trace_info *new = NULL; + int min; +#endif + + for (i = 0; i < num; i++) { + clkdev_add(&clks[i]); + +#ifdef CONFIG_PXA910_CLOCK_TRACE + write_lock(&clock_trace_list.lock); + min = find_first_zero_bit(clock_trace_list.device, 256); + new = kzalloc(sizeof(struct clock_trace_info), GFP_ATOMIC); + if (new == NULL) + return; + new->index = min; + new->clk = clks[i].clk; + if (clks[i].dev_id) + new->dev_id = clks[i].dev_id; + else + new->con_id = clks[i].con_id; + list_add_tail(&(new->list), &(clock_trace_list.list)); + set_bit(min, clock_trace_list.device); + write_unlock(&clock_trace_list.lock); +#endif + } + +#ifdef CONFIG_PXA910_CLOCK_TRACE + struct clock_trace_info *entry = NULL; + printk("Trace device clock list:\n"); + read_lock(&clock_trace_list.lock); + list_for_each_entry(entry, &clock_trace_list.list, list) { + printk("%s, ", entry->dev_id ? entry->dev_id: entry->con_id); + } + read_unlock(&clock_trace_list.lock); + printk( "\n"); + memset(clock_trace_list.device, 0, sizeof(clock_trace_list.device)); +#endif +} + diff --git a/arch/arm/mach-mmp/clock.h b/arch/arm/mach-mmp/clock.h index 016ae94691c04c..978ebc07070aaf 100644 --- a/arch/arm/mach-mmp/clock.h +++ b/arch/arm/mach-mmp/clock.h @@ -6,26 +6,22 @@ * published by the Free Software Foundation. */ -#include +#ifndef __MMP_CLOCK_H +#define __MMP_CLOCK_H -struct clkops { - void (*enable)(struct clk *); - void (*disable)(struct clk *); - unsigned long (*getrate)(struct clk *); -}; - -struct clk { - const struct clkops *ops; - - void __iomem *clk_rst; /* clock reset control register */ - int fnclksel; /* functional clock select (APBC) */ - uint32_t enable_val; /* value for clock enable (APMU) */ - unsigned long rate; - int enabled; -}; +#include extern struct clkops apbc_clk_ops; +extern struct clkops apbc_pwm_clk_ops; +extern struct clkops apbc_uart_clk_ops; extern struct clkops apmu_clk_ops; +extern struct clkops pseudo_clk_ops; +extern struct clkops u2o_clk_ops; +extern struct clkops u2h_clk_ops; +extern struct clkops cf_clk_ops; +extern struct clkops gc500_clk_ops; +extern struct clkops gc300_clk_ops; +extern struct clkops sdh_clk_ops; #define APBC_CLK(_name, _reg, _fnclksel, _rate) \ struct clk clk_##_name = { \ @@ -33,31 +29,45 @@ struct clk clk_##_name = { \ .fnclksel = _fnclksel, \ .rate = _rate, \ .ops = &apbc_clk_ops, \ -} + } -#define APBC_CLK_OPS(_name, _reg, _fnclksel, _rate, _ops) \ +#define APBC_UART_CLK(_name, _reg, _rate) \ +struct clk clk_##_name = { \ + .clk_rst = (void __iomem *)APBC_##_reg, \ + .fnclksel = 1, \ + .rate = _rate, \ + .ops = &apbc_uart_clk_ops, \ + } + +#define APBC_PWM_CLK(_name, _reg, _fnclksel, _rate) \ struct clk clk_##_name = { \ .clk_rst = (void __iomem *)APBC_##_reg, \ .fnclksel = _fnclksel, \ .rate = _rate, \ - .ops = _ops, \ -} + .ops = &apbc_pwm_clk_ops , \ + } #define APMU_CLK(_name, _reg, _eval, _rate) \ struct clk clk_##_name = { \ .clk_rst = (void __iomem *)APMU_##_reg, \ - .enable_val = _eval, \ .rate = _rate, \ + .enable_val = _eval, \ .ops = &apmu_clk_ops, \ -} + } -#define APMU_CLK_OPS(_name, _reg, _eval, _rate, _ops) \ +#define PSEUDO_CLK(_name, _reg, _eval, _rate) \ struct clk clk_##_name = { \ - .clk_rst = (void __iomem *)APMU_##_reg, \ + .rate = _rate, \ .enable_val = _eval, \ + .ops = &pseudo_clk_ops, \ + } + +#define APMU_CLK_OPS(_name, _reg, _rate, _ops) \ +struct clk clk_##_name = { \ + .clk_rst = (void __iomem *)APMU_##_reg, \ .rate = _rate, \ .ops = _ops, \ -} + } #define INIT_CLKREG(_clk, _devname, _conname) \ { \ @@ -66,5 +76,6 @@ struct clk clk_##_name = { \ .con_id = _conname, \ } -extern struct clk clk_pxa168_gpio; -extern struct clk clk_pxa168_timers; +extern void clks_register(struct clk_lookup *, size_t); + +#endif /* __MMP_CLOCK_H */ diff --git a/arch/arm/mach-mmp/common.c b/arch/arm/mach-mmp/common.c index e1e66c18b44633..c06383d05a258b 100644 --- a/arch/arm/mach-mmp/common.c +++ b/arch/arm/mach-mmp/common.c @@ -10,11 +10,16 @@ #include #include +#include +#include +#include #include #include #include +#include +#include #include "common.h" static struct map_desc standard_io_desc[] __initdata = { @@ -28,6 +33,11 @@ static struct map_desc standard_io_desc[] __initdata = { .virtual = AXI_VIRT_BASE, .length = AXI_PHYS_SIZE, .type = MT_DEVICE, + }, { + .pfn = __phys_to_pfn(PXA168_PCIE_PHYS_BASE), + .virtual = PXA168_PCIE_VIRT_BASE, + .length = PXA168_PCIE_SIZE, + .type = MT_DEVICE, }, }; @@ -35,3 +45,22 @@ void __init pxa_map_io(void) { iotable_init(standard_io_desc, ARRAY_SIZE(standard_io_desc)); } + +void release_RIPC(void) +{ + RIPC0_STATUS = 1; +} +void get_RIPC(void) +{ + volatile unsigned long status ; + + status = RIPC0_STATUS; + while(status!=0) { + if (!in_atomic() && !irqs_disabled()) + schedule(); + else + cpu_relax(); + status = RIPC0_STATUS; + } +} + diff --git a/arch/arm/mach-mmp/common.h b/arch/arm/mach-mmp/common.h index b4a0ba05a0f4fe..3735273d9be267 100644 --- a/arch/arm/mach-mmp/common.h +++ b/arch/arm/mach-mmp/common.h @@ -15,3 +15,9 @@ extern void __init mmp2_init_irq(void); extern void __init icu_init_irq(void); extern void __init pxa_map_io(void); +#define REG32(x) (*(volatile unsigned long *)(x)) +#define RIPC0_STATUS REG32(0xfe03D000) +extern void release_RIPC(void); +extern void get_RIPC(void); +extern struct mbus_dram_target_info pxa168_mbus_dram_info; +extern void pxa168_setup_cpu_mbus(void); diff --git a/arch/arm/mach-mmp/devices.c b/arch/arm/mach-mmp/devices.c index 191d9dea87316f..cbd7f36d6e5302 100644 --- a/arch/arm/mach-mmp/devices.c +++ b/arch/arm/mach-mmp/devices.c @@ -9,9 +9,14 @@ #include #include #include +#include -#include #include +#include +#include +#include + +#include int __init pxa_register_device(struct pxa_device_desc *desc, void *data, size_t size) @@ -20,6 +25,9 @@ int __init pxa_register_device(struct pxa_device_desc *desc, struct resource res[2 + MAX_RESOURCE_DMA]; int i, ret = 0, nres = 0; + if (desc == NULL) + return -EINVAL; + pdev = platform_device_alloc(desc->drv_name, desc->id); if (pdev == NULL) return -ENOMEM; @@ -67,3 +75,546 @@ int __init pxa_register_device(struct pxa_device_desc *desc, return platform_device_add(pdev); } + +PXA168_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 19, 20); +PXA168_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 21, 22); +/* special case for avengers to id = 0 (ttyS0) */ +#warning mapping uart2 to uart1 for avengers +PXA168_DEVICE(uart1b, "pxa2xx-uart", 0, UART2, 0xd4018000, 0x30, 21, 22); +PXA168_DEVICE(uart3, "pxa2xx-uart", 2, UART3, 0xd4026000, 0x30, 23, 24); +PXA168_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28); +PXA168_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4025000, 0x28); +PXA168_DEVICE(ssp0, "pxa168-ssp", 0, SSP0, 0xd401b000, 0x90, 52, 53); +PXA168_DEVICE(ssp1, "pxa168-ssp", 1, SSP1, 0xd401c000, 0x90, 54, 55); +PXA168_DEVICE(ssp2, "pxa168-ssp", 2, SSP2, 0xd401f000, 0x90, 56, 57); +PXA168_DEVICE(ssp3, "pxa168-ssp", 3, SSP3, 0xd4020000, 0x90, 58, 59); +PXA168_DEVICE(ssp4, "pxa168-ssp", 4, SSP4, 0xd4021000, 0x90, 60, 61); +PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8); +PXA168_DEVICE(fb_ovly, "pxa168fb_ovly", -1, LCD, 0xd420b000, 0x1c8); +PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c); +PXA168_DEVICE(nand, "pxa3xx-nand", -1, NAND, 0xD4283000, 0x200, 97, 99); +PXA168_DEVICE(onenand, "onenand", -1, NONE, 0x80000000, 0x100000); +PXA168_DEVICE_M(sdh0, "pxa-sdh", 0, MMC, 0xd4280000, 0x100); +PXA168_DEVICE_M(sdh1, "pxa-sdh", 1, MMC, 0xd4281000, 0x100); +PXA168_DEVICE(sdh2, "pxa-sdh", 2, MMC2, 0xd427e000, 0x100); +PXA168_DEVICE(sdh3, "pxa-sdh", 3, MMC2, 0xd427f000, 0x100); +PXA168_DEVICE(cf, "pxa168-cf", -1, CF, 0xd4285000, 0x800); +PXA168_DEVICE(mfu, "pxa168-mfu", -1, MFU, 0xc0800000, 0x0FFF); +PXA168_DEVICE(pcie, "pxa168-pcie", -1, PCIE_CORE, 0xd1200000, 0x0FFF); +PXA168_DEVICE(camera, "pxa168-camera", -1, CI, 0xd420a000, 0xfff); +PXA168_DEVICE(ov529, "pxa168-ov529", -1, NONE, SMC_CS0_PHYS_BASE, 0x100); +PXA168_DEVICE(msp, "pxa168-msp", -1, MSP, 0xd4286000, 0x0FFF); +PXA168_DEVICE(icr, "pxa168-icr", -1, ICR, 0xC0802000, 0x1000); + +/*PXA910 Specific*/ +PXA910_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 21, 22); +PXA910_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 23, 24); +PXA910_DEVICE(uart3, "pxa2xx-uart", 2, UART3, 0xd4036000, 0x30, 4, 5); +PXA910_DEVICE(ire, "pxa910-ire", -1, IRE, 0xd420C000, 0x90); +PXA910_DEVICE(ssp0, "pxa168-ssp", 0, SSP0, 0xd401b000, 0x90, 52, 53); +PXA910_DEVICE(ssp1, "pxa168-ssp", 1, SSP1, 0xd42a0c00, 0x90, 1, 2); +PXA910_DEVICE(ssp2, "pxa168-ssp", 2, SSP2, 0xd401C000, 0x90, 60, 61); +PXA910_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28); +PXA910_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4037000, 0x28); +PXA910_DEVICE(fb, "pxa910-fb", -1, LCD, 0xd420b000, 0x1ec); +PXA910_DEVICE(fb_ovly, "pxa910fb_ovly", -1, LCD, 0xd420b000, 0x1ec); +#ifdef CONFIG_CPU_PXA910 +static struct resource pxa910_resource_acipc[] = { + [0] = { + .start = 0xD401D000, + .end = 0xD401D0ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_PXA910_IPC_AP_DATAACK, + .end = IRQ_PXA910_IPC_AP_DATAACK, + .flags = IORESOURCE_IRQ, + .name = "IPC_AP_DATAACK", + }, + [2] = { + .start = IRQ_PXA910_IPC_AP_SET_CMD, + .end = IRQ_PXA910_IPC_AP_SET_CMD, + .flags = IORESOURCE_IRQ, + .name = "IPC_AP_SET_CMD", + }, + [3] = { + .start = IRQ_PXA910_IPC_AP_SET_MSG, + .end = IRQ_PXA910_IPC_AP_SET_MSG, + .flags = IORESOURCE_IRQ, + .name = "IPC_AP_SET_MSG", + }, +}; + +struct platform_device pxa910_device_acipc = { + .name = "pxa930-acipc", + .id = -1, + .resource = pxa910_resource_acipc, + .num_resources = ARRAY_SIZE(pxa910_resource_acipc), +}; +#endif + +#if defined (CONFIG_USB) || defined (CONFIG_USB_GADGET) + +/***************************************************************************** + * The registers read/write routines + *****************************************************************************/ + +unsigned u2o_get(unsigned base, unsigned offset) +{ + return readl(base + offset); +} + +void u2o_set(unsigned base, unsigned offset, unsigned value) +{ + volatile unsigned int reg; + + reg = readl(base + offset); + reg |= value; + writel(reg, base + offset); + __raw_readl(base + offset); + +} + +void u2o_clear(unsigned base, unsigned offset, unsigned value) +{ + volatile unsigned int reg; + + reg = readl(base + offset); + reg &= ~value; + writel(reg, base + offset); + __raw_readl(base + offset); +} + +void u2o_write(unsigned base, unsigned offset, unsigned value) +{ + writel(value, base + offset); + __raw_readl(base + offset); + +} + +/******************************************************************** + * USB 2.0 OTG controller + */ +int pxa168_usb_phy_init(unsigned base) +{ + static int init_done; + int count; + + if (init_done) { + printk(KERN_DEBUG "re-init phy\n\n"); + /* return; */ + } + + /* enable the pull up */ + if (cpu_is_pxa910_z0()) { + if (cpu_is_pxa910()) { + u32 U2H_UTMI_CTRL = + (u32)ioremap_nocache(0xc0000004, 4); + writel(1<<20, U2H_UTMI_CTRL); + } + } + + /* Initialize the USB PHY power */ + if (cpu_is_pxa910()) { + u2o_set(base, UTMI_CTRL, (1< +#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 +#include "common.h" +#include +#include + +#include +#include +#ifdef CONFIG_SD8XXX_RFKILL +#include +#endif + +#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) +/*used by expander max7312, 16 pins gpio expander */ +#define GPIO_EXT0(x) (NR_BUILTIN_GPIO + (x)) +#define GPIO_EXT1(x) (NR_BUILTIN_GPIO + 16 + (x)) +/*uart, smc, i2c are checked by bin.yang*/ +static unsigned long dkb_generic_pin_config[] __initdata = { + /* UART2 */ + GPIO47_UART2_RXD, + GPIO48_UART2_TXD, + + /* SMC */ + SM_nCS0_nCS0, + SM_ADV_SM_ADV, + SM_SCLK_SM_SCLK, + SM_SCLK_SM_SCLK, + SM_BE0_SM_BE0, + SM_BE1_SM_BE1, + + /* I2C */ + GPIO53_CI2C_SCL, + GPIO54_CI2C_SDA, + + /* SSP1 (I2S) */ + GPIO24_SSP1_SDATA_IN, + GPIO21_SSP1_BITCLK, + /*GPIO20_SSP1_SYSCLK,*/ + GPIO22_SSP1_SYNC, + GPIO23_SSP1_DATA_OUT, + GPIO124_MN_CLK_OUT, + GPIO123_CLK_REQ, + + /* DFI */ + DF_IO0_ND_IO0, + DF_IO1_ND_IO1, + DF_IO2_ND_IO2, + DF_IO3_ND_IO3, + DF_IO4_ND_IO4, + DF_IO5_ND_IO5, + DF_IO6_ND_IO6, + DF_IO7_ND_IO7, + DF_IO8_ND_IO8, + DF_IO9_ND_IO9, + DF_IO10_ND_IO10, + DF_IO11_ND_IO11, + DF_IO12_ND_IO12, + DF_IO13_ND_IO13, + DF_IO14_ND_IO14, + DF_IO15_ND_IO15, + DF_nCS0_SM_nCS2_nCS0, + DF_ALE_SM_WEn_ND_ALE, + DF_CLE_SM_OEn_ND_CLE, + DF_WEn_DF_WEn, + DF_REn_DF_REn, + DF_RDY0_DF_RDY0, + +}; + +#ifdef CONFIG_SANREMO +static int dkb_generic_is_ac_online(void) +{ + u8 tmp; + + return 0; +} + +static int dkb_generic_is_usb_online(void) +{ + return sanremo_usb_connect(); +} + +static char *dkb_generic_supplicants[] = { + "battery", +}; + +static struct pda_power_pdata dkb_generic_power_supply_info = { + .is_ac_online = dkb_generic_is_ac_online, + .is_usb_online = dkb_generic_is_usb_online, + .supplied_to = dkb_generic_supplicants, + .num_supplicants = ARRAY_SIZE(dkb_generic_supplicants), +}; + +static struct resource dkb_generic_power_supply_resources[] = { + [0] = { + .name = "ac", + }, + [1] = { + .name = "usb", + }, +}; + +static struct platform_device dkb_generic_power_supply = { + .name = "pda-power", + .id = -1, + .dev = { + .platform_data = &dkb_generic_power_supply_info, + }, + .resource = dkb_generic_power_supply_resources, + .num_resources = ARRAY_SIZE(dkb_generic_power_supply_resources), +}; + +static struct platform_device sanremo_battery = { + .name = "sanremo_battery", + .id = -1, +}; + +static struct platform_device portofino_bl_device = { + .name = "portofino-bl", + .id = 0, +}; + +static void __init dkb_generic_init_power(void) +{ + platform_device_register(&dkb_generic_power_supply); + platform_device_register(&sanremo_battery); +} +#else +static void __init dkb_generic_init_power(void) {} +#endif + +#if defined(CONFIG_SANREMO) || defined(CONFIG_SANREMO_MODULE) +static int sanremo_init_irq(void) +{ + return 0; +} + +static int sanremo_ack_irq(void) +{ + return 0; +} + +static void sanremo_init(void) +{ + u8 val; + /* Mask interrupts that are not needed */ + sanremo_write(SANREMO_INTERRUPT_ENABLE1, 0x00); + sanremo_write(SANREMO_INTERRUPT_ENABLE2, 0x00); + sanremo_write(SANREMO_INTERRUPT_ENABLE3, 0x00); + + /* disable LDO5 turn on/off by LDO3_EN */ + sanremo_read(SANREMO_MISC2, &val); + sanremo_write(SANREMO_MISC2, val | 0x80); + + /* enable LDO5 for AVDD_USB */ + sanremo_read(SANREMO_SUPPLIES_EN11, &val); + sanremo_write(SANREMO_SUPPLIES_EN11, val | 0x80); + + /* Set AVDD_USB voltage as 3.3V */ + sanremo_write(SANREMO_LDO5, 0x0F); + + /* avoid SRAM power off during sleep*/ + sanremo_read(SANREMO_SUPPLIES_EN11, &val); + printk("SANREMO_SUPPLIES_EN11 %x\n", val); + sanremo_write(SANREMO_SUPPLIES_EN11, val & 0xBF); /* never enable LDO4: CP controls it */ + sanremo_write(SANREMO_SUPPLIES_EN12, 0xFF); + + /* Enable the ONKEY power down functionality, power hold & watchdog disable */ + sanremo_write(SANREMO_WAKEUP, 0xA6); + + /* IRQ is masked during the power-up sequence and will not be released + * until they have been read for the first time */ + sanremo_write(SANREMO_INTERRUPT_STATUS1, 0x1F); + sanremo_write(SANREMO_INTERRUPT_STATUS2, 0xFF); + sanremo_write(SANREMO_INTERRUPT_STATUS3, 0xFF); + + sanremo_write(SANREMO_GPADC_MISC1, 0x0b); /*enable GADC for CP and touch*/ + sanremo_write(SANREMO_TSI_PREBIAS_TIME, 0x06); + sanremo_write(SANREMO_PD_PREBIAS_TIME, 0x50); + sanremo_write(SANREMO_RTC1, 0x40); /*Set RTC to use the external 32K frequency */ + sanremo_write(SANREMO_RTC_MISC2, 0x2); /* enable to set RTC to use external 32K frequency */ + sanremo_write(SANREMO_AUDIO_Side_Tone_1, 0x24); + sanremo_read(SANREMO_AUDIO_DAC_LO1_CTRL, &val); + sanremo_write(SANREMO_AUDIO_DAC_LO1_CTRL, val|0x60); + + sanremo_write(SANREMO_LDO12, 0x66); /*enable ldo12 for touch*/ + sanremo_write(SANREMO_LDO3, 0x2d); /*enable ldo12 for touch*/ + sanremo_write(SANREMO_VBUCK2_SET, 0x24); /*enable i2c 1.8_2.8 voltage rail */ + sanremo_write(SANREMO_LDO6, 0x1b); //set LDO6 to 2.65V for RF +} + +/* sanremo_power_module[] should be consistent with enum + * in include/asm-arm/arch-pxa/pxa3xx_pmic.h */ +static struct power_supply_module sanremo_power_modules[] = { + /* {command, power_module}, */ + {VCC_CORE, SAN_BUCK1}, + {VCC_USB, SAN_LDO5}, + {VCC_CAMERA_ANA, SAN_LDO12}, + {VCC_SDIO, SAN_LDO14}, +}; + + +static struct power_chip sanremo_chips[] = { + {0x40, "sanremoA0", sanremo_power_modules}, + {0x41, "sanremoA1", sanremo_power_modules}, + {0x48, "sanremoB0", sanremo_power_modules}, + {0, NULL, NULL}, +}; + +static struct sanremo_platform_data sanremo_data = { + .init_irq = sanremo_init_irq, + .ack_irq = sanremo_ack_irq, + .platform_init = sanremo_init, + .power_chips = sanremo_chips, + .tsi = NULL, +}; +#endif /* CONFIG_PXA3xx_SANREMO || CONFIG_PXA3xx_SANREMO_MODULE*/ + +static struct i2c_pxa_platform_data i2c_info = { + .use_pio = 0, + .get_ripc = get_RIPC, + .release_ripc = release_RIPC, +}; + +static struct i2c_pxa_platform_data pwri2c_info = { + .use_pio = 0, +}; + +#if defined(CONFIG_GPIO_PCA953X) + +/* GPIO expander max7312 could reuse PCA953X */ +static struct pca953x_platform_data max7312_data[] = { + [0] = { + .gpio_base = GPIO_EXT0(0), + }, +}; +#endif + +static struct i2c_board_info dkb_generic_i2c_board_info[] = +{ +#if defined(CONFIG_GPIO_PCA953X) + { + .type = "max7312", + .addr = 0x20, + .irq = IRQ_GPIO(80), + .platform_data = &max7312_data, + }, +#endif +#if defined(CONFIG_PORTOFINO) || defined(CONFIG_PORTOFINO_MODULE) + { + .type = "portofino", + .addr = 0x11, + }, +#endif +#if defined(CONFIG_SANREMO) || defined(CONFIG_SANREMO_MODULE) + { + .type = "sanremo", + .addr = 0x30, + .platform_data = &sanremo_data , + .irq = IRQ_PXA168_PMIC_INT, + }, + { + .type = "sanremo", + .addr = 0x34, + .platform_data = &sanremo_data , + .irq = IRQ_PXA168_PMIC_INT, + }, +#endif +}; + +static struct i2c_board_info pxa920_pwri2c_board_info[] = +{ +}; + +DECLARE_LAB_PARTITIONS(lab_partitions); +DECLARE_ANDROID_256M_V75_PARTITIONS(android_256m_v75_partitions); +DECLARE_256M_V75_PARTITIONS(generic_256m_v75_partitions); +static struct flash_platform_data dkb_generic_onenand_info; +static struct pxa3xx_nand_platform_data dkb_generic_nand_info; +static void __init dkb_generic_add_flash(void) +{ + if (is_lab()){ + dkb_generic_onenand_info.parts = lab_partitions; + dkb_generic_onenand_info.nr_parts = ARRAY_SIZE(lab_partitions); + dkb_generic_nand_info.parts[0] = lab_partitions; + dkb_generic_nand_info.nr_parts[0] = ARRAY_SIZE(lab_partitions); + }else if (is_android()) { + dkb_generic_onenand_info.parts = android_256m_v75_partitions; + dkb_generic_onenand_info.nr_parts = ARRAY_SIZE(android_256m_v75_partitions); + dkb_generic_nand_info.parts[0] = android_256m_v75_partitions; + dkb_generic_nand_info.nr_parts[0] = ARRAY_SIZE(android_256m_v75_partitions); + } else { + dkb_generic_onenand_info.parts = generic_256m_v75_partitions; + dkb_generic_onenand_info.nr_parts = ARRAY_SIZE(generic_256m_v75_partitions); + dkb_generic_nand_info.parts[0] = generic_256m_v75_partitions; + dkb_generic_nand_info.nr_parts[0] = ARRAY_SIZE(generic_256m_v75_partitions); + } + pxa168_add_onenand(&dkb_generic_onenand_info); + dkb_generic_nand_info.use_dma = 0; + dkb_generic_nand_info.enable_arbiter = 1; + pxa168_add_nand(&dkb_generic_nand_info); +} + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static int dkb_generic_vbus_status(unsigned base) +{ + int status = VBUS_LOW; + + if (pxa3xx_pmic_is_vbus_assert()) { + status = VBUS_HIGH; + } + return status; +} + +static int dkb_generic_vbus_detect(void *func, int enable) +{ + if (enable) { + pmic_callback_register(PMIC_EVENT_USB, func); + pxa3xx_pmic_set_pump(1); + } else { + pxa3xx_pmic_set_pump(0); + pmic_callback_unregister(PMIC_EVENT_USB, func); + } + + return 0; +} + +static struct pxa_usb_plat_info dkb_generic_u2o_info = { + .phy_init = pxa168_usb_phy_init, + .phy_deinit = pxa168_usb_phy_deinit, + .vbus_status = dkb_generic_vbus_status, + .vbus_detect = dkb_generic_vbus_detect, + .rely_on_vbus = 1, + .is_otg = 1, +}; +#endif + +static struct platform_device sensor_input_device = { + .name = "sensor_input", + .id = -1, +}; + +static void (*headset_update_func)(int state); + +static void sanremo_headset_interrupt(unsigned long event) +{ + if (headset_update_func) + headset_update_func(sanremo_get_headset_state()); +} + +static int dkb_generic_headset_detect(void *func, int enable) +{ + headset_update_func = func; + headset_update_func(1); + return sanremo_enable_headset_detect(sanremo_headset_interrupt, enable); +} + +static struct headset_switch_platform_data headset_switch_device_data = { + .name = "h2w", + .gpio = NULL, + .name_on = NULL, + .name_off = NULL, + .state_on = NULL, + .state_off = NULL, + .enable_detect = dkb_generic_headset_detect, +}; + +static struct platform_device headset_switch_device = { + .name = "headset", + .id = 0, + .dev = { + .platform_data = &headset_switch_device_data, + }, +}; + +static void __init dkb_generic_init_headset(void) +{ + platform_device_register(&headset_switch_device); +} + +static void __init dkb_generic_init(void) +{ + /*dummy driver init*/ + platform_device_register(&sensor_input_device); + + mfp_config(ARRAY_AND_SIZE(dkb_generic_pin_config)); + + /* on-chip devices */ + pxa910_add_uart(1); + pxa910_add_uart(2); + pxa910_add_uart(3); + pxa910_add_twsi(0, &i2c_info, ARRAY_AND_SIZE(dkb_generic_i2c_board_info)); + pxa910_add_twsi(1, &pwri2c_info, ARRAY_AND_SIZE(dkb_generic_i2c_board_info)); +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&dkb_generic_u2o_info); +#endif +#ifdef CONFIG_USB_OTG + pxa168_add_u2ootg(&dkb_generic_u2o_info); + pxa168_add_u2oehci(&dkb_generic_u2o_info); +#endif + pxa910_add_acipc(); + pxa910_add_ire(); +#if defined(CONFIG_PXA168_CAMERA) + pxa168_add_cam(); +#endif + pxa910_add_ssp(1); + pxa910_add_imm(); + pxa168_add_freq(); + + pxa910_add_rtc(); + dkb_generic_add_flash(); + +#ifdef CONFIG_SANREMO + platform_device_register(&portofino_bl_device); +#endif + + /*power device*/ + dkb_generic_init_power(); + + /*headset device*/ + dkb_generic_init_headset(); + + res_add_sanremo_vibrator(); + + pm_power_off = sanremo_turn_off_power; +} + +MACHINE_START(DKB_GENERIC, "PXA910-based Generic DKB Development Platform") + .phys_io = APB_PHYS_BASE, + .boot_params = 0x00000100, + .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, + .map_io = pxa_map_io, + .init_irq = pxa168_init_irq, + .timer = &pxa168_timer, + .init_machine = dkb_generic_init, +MACHINE_END diff --git a/arch/arm/mach-mmp/dvfm.c b/arch/arm/mach-mmp/dvfm.c new file mode 100644 index 00000000000000..a0ca38cfaf9a35 --- /dev/null +++ b/arch/arm/mach-mmp/dvfm.c @@ -0,0 +1,749 @@ +/* + * DVFM Abstract Layer + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007-2009 Marvell International Ltd. + * All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define MAX_DEVNAME_LEN 32 + +extern struct info_head clock_trace_list; + +/* This structure is used to dump device name list */ +struct name_list { + int id; + char name[MAX_DEVNAME_LEN]; +}; + +static ATOMIC_NOTIFIER_HEAD(dvfm_freq_notifier_list); + +/* This list links log of dvfm operation */ +static struct info_head dvfm_trace_list = { + .list = LIST_HEAD_INIT(dvfm_trace_list.list), + .lock = RW_LOCK_UNLOCKED, + .device = {0, 0, 0, 0, 0, 0, 0, 0}, +}; + +/* This idx is used for user debug */ +static int dvfm_dev_idx; + +struct dvfm_driver *dvfm_driver = NULL; +struct info_head *dvfm_op_list = NULL; + +unsigned int cur_op; /* current operating point */ +unsigned int def_op; /* default operating point */ +unsigned int op_nums = 0; /* number of operating point */ + +static atomic_t lp_count = ATOMIC_INIT(0); /* number of blocking lowpower mode */ + +extern struct sysdev_class cpu_sysdev_class; + +int dvfm_find_op(int index, struct op_info **op) +{ + struct op_info *p = NULL; + unsigned long flags; + + read_lock_irqsave(&dvfm_op_list->lock, flags); + if (list_empty(&dvfm_op_list->list)) { + read_unlock_irqrestore(&dvfm_op_list->lock, flags); + return -ENOENT; + } + list_for_each_entry(p, &dvfm_op_list->list, list) { + if (p->index == index) { + *op = p; + read_unlock_irqrestore(&dvfm_op_list->lock, flags); + return 0; + } + } + read_unlock_irqrestore(&dvfm_op_list->lock, flags); + return -ENOENT; +} + +/* Display current operating point */ +static ssize_t op_show(struct sys_device *sys_dev, + struct sysdev_attribute *attr, char *buf) +{ + struct op_info *op = NULL; + int len = 0, req_op; + + req_op = cur_op; + + if (dvfm_driver->dump) { + if (!dvfm_find_op(req_op, &op)) { + len = dvfm_driver->dump(dvfm_driver->priv, op, buf); + } + } + + return len; +} + +/* Set current operating point */ +static ssize_t op_store(struct sys_device *sys_dev, + struct sysdev_attribute *attr, + const char *buf, size_t len) +{ + int new_op; + + sscanf(buf, "%u", &new_op); + dvfm_request_op(new_op); + return len; +} +static SYSDEV_ATTR(op, 0644, op_show, op_store); + +/* Dump all operating point */ +static ssize_t ops_show(struct sys_device *sys_dev, + struct sysdev_attribute *attr, char *buf) +{ + struct op_info *entry = NULL; + int len = 0; + char *p = NULL; + + if (!dvfm_driver->dump) + return 0; + read_lock(&dvfm_op_list->lock); + if (!list_empty(&dvfm_op_list->list)) { + list_for_each_entry(entry, &dvfm_op_list->list, list) { + p = buf + len; + len += dvfm_driver->dump(dvfm_driver->priv, entry, p); + len += sprintf(buf + len, "\n"); + } + } + read_unlock(&dvfm_op_list->lock); + + return len; +} +SYSDEV_ATTR(ops, 0444, ops_show, NULL); + +/* Dump all enabled operating point */ +static ssize_t enable_op_show(struct sys_device *sys_dev, + struct sysdev_attribute *attr, char *buf) +{ + struct op_info *entry = NULL; + int len = 0; + char *p = NULL; + + if (!dvfm_driver->dump) + return 0; + read_lock(&dvfm_op_list->lock); + if (!list_empty(&dvfm_op_list->list)) { + list_for_each_entry(entry, &dvfm_op_list->list, list) { + if (find_first_bit(entry->device, DVFM_MAX_CLIENT)\ + == DVFM_MAX_CLIENT) { + p = buf + len; + len += dvfm_driver->dump(dvfm_driver->priv, entry, p); + } + } + } + read_unlock(&dvfm_op_list->lock); + + return len; +} + +static ssize_t enable_op_store(struct sys_device *sys_dev, + struct sysdev_attribute *attr, + const char *buf, size_t len) +{ + int op, level; + + sscanf(buf, "%u,%u", &op, &level); + if (level) { + dvfm_enable_op(op, dvfm_dev_idx); + } else + dvfm_disable_op(op, dvfm_dev_idx); + return len; +} +SYSDEV_ATTR(enable_op, 0644, enable_op_show, enable_op_store); + +/* + * Dump blocked device on specified OP. + * And dump the device list that is tracked. + */ +static ssize_t trace_show(struct sys_device *sys_dev, + struct sysdev_attribute *attr, char *buf) +{ + struct op_info *op_entry = NULL; + struct dvfm_trace_info *entry = NULL; + int len = 0, i; + /* copy of constraint in operating point */ + unsigned long blocked[8]; + + for (i = 0; i < op_nums; i++) { + memset(blocked, 0, sizeof(blocked)); + + read_lock(&dvfm_op_list->lock); + /* op list shouldn't be empty because op_nums is valid */ + list_for_each_entry(op_entry, &dvfm_op_list->list, list) { + if (op_entry->index == i) + memcpy(blocked, (op_entry->device),\ + sizeof(blocked)); + } + read_unlock(&dvfm_op_list->lock); + + /* If no device is blocking this OP, continue to check next OP. */ + if (find_first_bit(blocked, DVFM_MAX_CLIENT) == DVFM_MAX_CLIENT) + continue; + + len += sprintf(buf + len, "Blocked devices on OP%d:", i); + + read_lock(&dvfm_trace_list.lock); + list_for_each_entry(entry, &dvfm_trace_list.list, list) { + if (test_bit(entry->index, blocked)) + len += sprintf(buf + len, "%s, ", entry->name); + } + read_unlock(&dvfm_trace_list.lock); + + len += sprintf(buf + len, "\n"); + } + if (len == 0) + len += sprintf(buf + len, "None device block OP\n"); + len += sprintf(buf + len, "Trace device list:\n"); + + read_lock(&dvfm_trace_list.lock); + list_for_each_entry(entry, &dvfm_trace_list.list, list) { + len += sprintf(buf + len, "%s, ", entry->name); + } + read_unlock(&dvfm_trace_list.lock); + + len += sprintf(buf + len, "\n"); + return len; +} +SYSDEV_ATTR(trace, 0444, trace_show, NULL); + +static ssize_t clock_trace_show(struct sys_device *sys_dev, + struct sysdev_attribute *attr, char *buf) +{ + struct clock_trace_info *p = NULL; + int len = 0; + +#ifdef CONFIG_PXA910_CLOCK_TRACE + len += sprintf(buf + len, "CLOCK ON:\n"); + list_for_each_entry(p, &clock_trace_list.list, list) { + if (test_bit(p->index, clock_trace_list.device)) { + if (p->dev_id) + len += sprintf(buf + len, "%s, ", p->dev_id); + else if (p->con_id) + len += sprintf(buf + len, "%s, ", p->con_id); + } + } + len += sprintf(buf + len, "\n"); +#endif + return len; +} +SYSDEV_ATTR(clocktrace, 0444, clock_trace_show, NULL); + +static struct attribute *dvfm_attr[] = { + &attr_op.attr, + &attr_ops.attr, + &attr_enable_op.attr, + &attr_trace.attr, + &attr_clocktrace.attr, +}; + +int dvfm_op_count(void) +{ + int ret = -EINVAL; + + if (dvfm_driver && dvfm_driver->count) + ret = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list); + return ret; +} +EXPORT_SYMBOL(dvfm_op_count); + +int dvfm_get_op(struct op_info **p) +{ + int req_op; + + req_op = cur_op; + + if (dvfm_find_op(req_op, p)) + return -EINVAL; + return req_op; +} +EXPORT_SYMBOL(dvfm_get_op); + +int dvfm_get_defop(void) +{ + return def_op; +} +EXPORT_SYMBOL(dvfm_get_defop); + +int dvfm_get_opinfo(int index, struct op_info **p) +{ + if (dvfm_find_op(index, p)) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL(dvfm_get_opinfo); + +int dvfm_set_op(struct dvfm_freqs *freqs, unsigned int new, + unsigned int relation) +{ + int ret = -EINVAL; + + /* check whether dvfm is enabled */ + if (!dvfm_driver || !dvfm_driver->count) + return -EINVAL; + if (dvfm_driver->set) + ret = dvfm_driver->set(dvfm_driver->priv, freqs, new, relation); + return ret; +} + +/* Request operating point. System may set higher frequency because of + * device constraint. + */ +int dvfm_request_op(int index) +{ + int ret = -EFAULT; + + /* check whether dvfm is enabled */ + if (!dvfm_driver || !dvfm_driver->count) + return -EINVAL; + if (dvfm_driver->request_set) + ret = dvfm_driver->request_set(dvfm_driver->priv, index); + return ret; +} +EXPORT_SYMBOL(dvfm_request_op); + +/* + * Device remove the constraint on OP. + */ +int dvfm_enable_op(int index, int dev_idx) +{ + struct op_info *p = NULL; + int num; + + /* check whether dvfm is enabled */ + if (!dvfm_driver || !dvfm_driver->count) + return -EINVAL; + /* only registered device can invoke DVFM operation */ + if ((dev_idx >= DVFM_MAX_CLIENT) || dev_idx < 0) + return -ENOENT; + num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list); + if (num <= index) + return -ENOENT; + if (!dvfm_find_op(index, &p)) { + write_lock(&dvfm_op_list->lock); + /* remove device ID */ + clear_bit(dev_idx, p->device); + write_unlock(&dvfm_op_list->lock); + dvfm_driver->enable_op(dvfm_driver->priv, index, RELATION_LOW); + } + return 0; +} + +/* + * Device set constraint on OP + */ +int dvfm_disable_op(int index, int dev_idx) +{ + struct op_info *p = NULL; + int num; + + /* check whether dvfm is enabled */ + if (!dvfm_driver || !dvfm_driver->count) + return -EINVAL; + /* only registered device can invoke DVFM operation */ + if ((dev_idx >= DVFM_MAX_CLIENT) || dev_idx < 0) + return -ENOENT; + num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list); + if (num <= index) + return -ENOENT; + if (!dvfm_find_op(index, &p)) { + write_lock(&dvfm_op_list->lock); + /* set device ID */ + set_bit(dev_idx, p->device); + write_unlock(&dvfm_op_list->lock); + dvfm_driver->disable_op(dvfm_driver->priv, index, RELATION_LOW); + } + return 0; +} +EXPORT_SYMBOL(dvfm_enable_op); +EXPORT_SYMBOL(dvfm_disable_op); + +int dvfm_enable_op_name(char *name, int dev_idx) +{ + struct op_info *p = NULL; + int index; + + if (!dvfm_driver || !dvfm_driver->name || !name) + return -EINVAL; + /* only registered device can invoke DVFM operation */ + if ((dev_idx >= DVFM_MAX_CLIENT) || dev_idx < 0) + return -ENOENT; + list_for_each_entry(p, &dvfm_op_list->list, list) { + if (!strcmp(dvfm_driver->name(dvfm_driver->priv, p), name)) { + index = p->index; + write_lock(&dvfm_op_list->lock); + clear_bit(dev_idx, p->device); + write_unlock(&dvfm_op_list->lock); + dvfm_driver->enable_op(dvfm_driver->priv, + index, RELATION_LOW); + break; + } + } + return 0; +} + +int dvfm_disable_op_name(char *name, int dev_idx) +{ + struct op_info *p = NULL; + int index; + + if (!dvfm_driver || !dvfm_driver->name || !name) + return -EINVAL; + /* only registered device can invoke DVFM operation */ + if ((dev_idx >= DVFM_MAX_CLIENT) || dev_idx < 0) + return -ENOENT; + list_for_each_entry(p, &dvfm_op_list->list, list) { + if (!strcmp(dvfm_driver->name(dvfm_driver->priv, p), name)) { + index = p->index; + write_lock(&dvfm_op_list->lock); + set_bit(dev_idx, p->device); + write_unlock(&dvfm_op_list->lock); + dvfm_driver->disable_op(dvfm_driver->priv, + index, RELATION_LOW); + break; + } + } + return 0; +} +EXPORT_SYMBOL(dvfm_enable_op_name); +EXPORT_SYMBOL(dvfm_disable_op_name); + +/* Only enable those safe operating point */ +int dvfm_enable(int dev_idx) +{ + if (!dvfm_driver || !dvfm_driver->count || !dvfm_driver->enable_dvfm) + return -ENOENT; + return dvfm_driver->enable_dvfm(dvfm_driver->priv, dev_idx); +} + +/* return whether the result is zero */ +int dvfm_disable(int dev_idx) +{ + if (!dvfm_driver || !dvfm_driver->count || !dvfm_driver->disable_dvfm) + return -ENOENT; + return dvfm_driver->disable_dvfm(dvfm_driver->priv, dev_idx); +} + +/* return whether the result is zero */ +int dvfm_enable_pm(void) +{ + return atomic_inc_and_test(&lp_count); +} + +/* return whether the result is zero */ +int dvfm_disable_pm(void) +{ + return atomic_dec_and_test(&lp_count); +} + +int dvfm_notifier_frequency(struct dvfm_freqs *freqs, unsigned int state) +{ + int ret; + + switch (state) { + case DVFM_FREQ_PRECHANGE: + ret = atomic_notifier_call_chain(&dvfm_freq_notifier_list, + DVFM_FREQ_PRECHANGE, freqs); + if (ret != NOTIFY_DONE) + pr_debug("Failure in device driver before " + "switching frequency\n"); + break; + case DVFM_FREQ_POSTCHANGE: + ret = atomic_notifier_call_chain(&dvfm_freq_notifier_list, + DVFM_FREQ_POSTCHANGE, freqs); + if (ret != NOTIFY_DONE) + pr_debug("Failure in device driver after " + "switching frequency\n"); + break; + default: + ret = -EINVAL; + } + return ret; +} + +int dvfm_register_notifier(struct notifier_block *nb, unsigned int list) +{ + int ret; + + switch (list) { + case DVFM_FREQUENCY_NOTIFIER: + ret = atomic_notifier_chain_register( + &dvfm_freq_notifier_list, nb); + break; + default: + ret = -EINVAL; + } + return ret; +} +EXPORT_SYMBOL(dvfm_register_notifier); + +int dvfm_unregister_notifier(struct notifier_block *nb, unsigned int list) +{ + int ret; + + switch (list) { + case DVFM_FREQUENCY_NOTIFIER: + ret = atomic_notifier_chain_unregister( + &dvfm_freq_notifier_list, nb); + break; + default: + ret = -EINVAL; + } + return ret; +} +EXPORT_SYMBOL(dvfm_unregister_notifier); + +/* + * add device into trace list + * return device index + */ +static int add_device(char *name) +{ + struct dvfm_trace_info *entry = NULL, *new = NULL; + int min; + + min = find_first_zero_bit(dvfm_trace_list.device, DVFM_MAX_CLIENT); + /* client list is full */ + if (min == DVFM_MAX_CLIENT) + return -EINVAL; + + /* If device trace table is NULL */ + new = kzalloc(sizeof(struct dvfm_trace_info), GFP_ATOMIC); + if (new == NULL) + goto out_mem; + /* add new item */ + strcpy(new->name, name); + new->index = min; + /* insert the new item in increasing order */ + list_for_each_entry(entry, &dvfm_trace_list.list, list) { + if (entry->index > min) { + list_add_tail(&(new->list), &(entry->list)); + goto inserted; + } + } + list_add_tail(&(new->list), &(dvfm_trace_list.list)); +inserted: + set_bit(min, dvfm_trace_list.device); + + return min; +out_mem: + return -ENOMEM; +} + +/* + * Query the device number that registered in DVFM + */ +int dvfm_query_device_num(void) +{ + int count = 0; + struct dvfm_trace_info *entry = NULL; + + read_lock(&dvfm_trace_list.lock); + list_for_each_entry(entry, &dvfm_trace_list.list, list) { + count++; + } + read_unlock(&dvfm_trace_list.lock); + return count; +} +EXPORT_SYMBOL(dvfm_query_device_num); + +/* + * Query all device name that registered in DVFM + */ +int dvfm_query_device_list(void *mem, int len) +{ + int count = 0, size; + struct dvfm_trace_info *entry = NULL; + struct name_list *p = (struct name_list *)mem; + + count = dvfm_query_device_num(); + size = sizeof(struct name_list); + if (len < count * size) + return -ENOMEM; + + read_lock(&dvfm_trace_list.lock); + list_for_each_entry(entry, &dvfm_trace_list.list, list) { + p->id = entry->index; + strcpy(p->name, entry->name); + p++; + } + read_unlock(&dvfm_trace_list.lock); + return 0; +} +EXPORT_SYMBOL(dvfm_query_device_list); + +/* + * Device driver register itself to DVFM before any operation. + * The number of registered device is limited in 32. + */ +int dvfm_register(char *name, int *id) +{ + struct dvfm_trace_info *p = NULL; + int len, idx; + + if (name == NULL) + return -EINVAL; + + /* device name is stricted in 32 bytes */ + len = strlen(name); + if (len > DVFM_MAX_NAME) + len = DVFM_MAX_NAME; + write_lock(&dvfm_trace_list.lock); + list_for_each_entry(p, &dvfm_trace_list.list, list) { + if (!strcmp(name, p->name)) { + /* + * Find device in device trace table + * Skip to allocate new ID + */ + *id = p->index; + goto out; + } + } + idx = add_device(name); + if (idx < 0) + goto out_num; + *id = idx; +out: + write_unlock(&dvfm_trace_list.lock); + return 0; +out_num: + write_unlock(&dvfm_trace_list.lock); + return -EINVAL; +} +EXPORT_SYMBOL(dvfm_register); + +/* + * Release the device and free the device index. + */ +int dvfm_unregister(char *name, int *id) +{ + struct dvfm_trace_info *p = NULL; + int len, num, i; + + if (!dvfm_driver || !dvfm_driver->count || (name == NULL)) + return -EINVAL; + + /* device name is stricted in 32 bytes */ + len = strlen(name); + if (len > DVFM_MAX_NAME) + len = DVFM_MAX_NAME; + + num = dvfm_driver->count(dvfm_driver->priv, dvfm_op_list); + + write_lock(&dvfm_trace_list.lock); + if (list_empty(&dvfm_trace_list.list)) + goto out; + list_for_each_entry(p, &dvfm_trace_list.list, list) { + if (!strncmp(name, p->name, len)) { + /* remove all dvfm constraint on this device. */ + for (i = 0; i < num; i++) + dvfm_enable_op(i, p->index); + /* clear the device index */ + clear_bit(p->index, dvfm_trace_list.device); + *id = -1; + list_del(&p->list); + kfree(p); + break; + } + } + write_unlock(&dvfm_trace_list.lock); + return 0; +out: + write_unlock(&dvfm_trace_list.lock); + return -ENOENT; +} +EXPORT_SYMBOL(dvfm_unregister); + +static int dvfm_add(struct sys_device *sys_dev) +{ + int i, n; + int ret; + + n = ARRAY_SIZE(dvfm_attr); + for (i = 0; i < n; i++) { + ret = sysfs_create_file(&(sys_dev->kobj), dvfm_attr[i]); + if (ret) + return -EIO; + } + return 0; +} + +static int dvfm_rm(struct sys_device *sys_dev) +{ + int i, n; + n = ARRAY_SIZE(dvfm_attr); + for (i = 0; i < n; i++) { + sysfs_remove_file(&(sys_dev->kobj), dvfm_attr[i]); + } + return 0; +} + +static int dvfm_suspend(struct sys_device *sysdev, pm_message_t pmsg) +{ + return 0; +} + +static int dvfm_resume(struct sys_device *sysdev) +{ + return 0; +} + +static struct sysdev_driver dvfm_sysdev_driver = { + .add = dvfm_add, + .remove = dvfm_rm, + .suspend = dvfm_suspend, + .resume = dvfm_resume, +}; + +int dvfm_register_driver(struct dvfm_driver *driver_data, struct info_head *op_list) +{ + int ret; + if (!driver_data || !driver_data->set) + return -EINVAL; + if (dvfm_driver) + return -EBUSY; + dvfm_driver = driver_data; + + if (!op_list) + return -EINVAL; + dvfm_op_list = op_list; + + /* enable_op need to invoke dvfm operation */ + dvfm_register("User", &dvfm_dev_idx); + ret = sysdev_driver_register(&cpu_sysdev_class, &dvfm_sysdev_driver); + return ret; +} + +int dvfm_unregister_driver(struct dvfm_driver *driver) +{ + sysdev_driver_unregister(&cpu_sysdev_class, &dvfm_sysdev_driver); + dvfm_unregister("User", &dvfm_dev_idx); + dvfm_driver = NULL; + return 0; +} + +MODULE_DESCRIPTION("Basic DVFM support"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-mmp/dvfm_stats.c b/arch/arm/mach-mmp/dvfm_stats.c new file mode 100644 index 00000000000000..7fc9d8fa6bf127 --- /dev/null +++ b/arch/arm/mach-mmp/dvfm_stats.c @@ -0,0 +1,191 @@ +/* + * DVFM Statistic Driver + * + * Copyright (C) 2007 Marvell Corporation + * Haojian Zhuang + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct op_stats_type { + unsigned int timestamp; + unsigned int op_idx; + unsigned int runtime; + unsigned int idletime; + unsigned int overflow; /* high halfword is run, low is idle */ +}; + +struct op_cycle_type { + unsigned int op_idx; + unsigned int runtime; + unsigned int idletime; + unsigned int count; +}; +#define OP_NUM 20 +/* Detail time cost on all OPs are stored in op_stats table */ +static struct op_stats_type *op_stats_p = NULL; +static struct op_cycle_type op_ticks_array[OP_NUM]; +static spinlock_t stats_lock = SPIN_LOCK_UNLOCKED; + +extern unsigned int cur_op; +extern int mspm_op_num; +/* Interface under SYSFS */ + +/* Display duty cycles on all operating points */ +static ssize_t duty_cycle_show(struct sys_device *sys_dev, + struct sysdev_attribute *attr, char *buf) +{ + int len, i; + unsigned int total_ticks; + + total_ticks = 0; + for (i = 0; i < mspm_op_num; i++) { + total_ticks += op_ticks_array[i].runtime + + op_ticks_array[i].idletime; + printk("%d, %d\n", op_ticks_array[i].runtime, op_ticks_array[i].idletime); + } + + if (total_ticks == 0) { + len = sprintf(buf, "No OP change, no duty cycle info\n"); + return len; + } + len = sprintf(buf, "Duty cycle of operating point list:\n"); + for (i = 0; i < mspm_op_num; i++) { + len += sprintf(buf + len, "op%d run:%u%% idle:%u%%\n", + i, op_ticks_array[i].runtime * 100/ total_ticks, + op_ticks_array[i].idletime * 100 / total_ticks); + } + return len; +} +SYSDEV_ATTR(duty_cycle, 0444, duty_cycle_show, NULL); + +/* Display costed time on all operating points */ +static ssize_t ticks_show(struct sys_device *sys_dev, struct sysdev_attribute *attr, char *buf) +{ + int len, i; + len = sprintf(buf, "Ticks of operating point list:\n"); + for (i = 0; i < mspm_op_num; i++) { + len += sprintf(buf + len, "op%d, run ticks:%u, idle ticks:%u " + "run second:%u, idle second:%u\n", + i, op_ticks_array[i].runtime, + op_ticks_array[i].idletime, + dvfm_driver->ticks_to_sec(op_ticks_array[i].runtime), + dvfm_driver->ticks_to_sec(op_ticks_array[i].idletime)); + } + return len; +} +SYSDEV_ATTR(ticks, 0444, ticks_show, NULL); + +static struct attribute *dvfm_stats_attr[] = { + &attr_duty_cycle.attr, + &attr_ticks.attr, +}; + +static void update_op_cycle(int op_idx, unsigned int runtime, + unsigned int idletime) +{ + op_ticks_array[op_idx].runtime += runtime; + op_ticks_array[op_idx].idletime += idletime; + //printk("%d, %d\n", op_ticks_array[op_idx].runtime, op_ticks_array[op_idx].idletime); +} + +/* + * Add this information into timeslot table. + * This table records the time cost on different cpu state and different + * operating points. + */ +int dvfm_add_timeslot(int op_idx, int cpu_state) +{ + static unsigned int prev_timestamp; + unsigned int timestamp, time, size; + unsigned int idle_overflow = 0, run_overflow = 0; + unsigned long flags; + + spin_lock_irqsave(&stats_lock, flags); + timestamp = dvfm_driver->read_time(); + time = (timestamp >= prev_timestamp) ? timestamp - prev_timestamp + : 0xFFFFFFFF - prev_timestamp + timestamp; + prev_timestamp = timestamp; + if (cpu_state == CPU_STATE_IDLE) { + update_op_cycle(op_idx, 0, time); + } else { + update_op_cycle(op_idx, time, 0); + } + spin_unlock_irqrestore(&stats_lock, flags); + return 0; +} + +static int stats_add(struct sys_device *sys_dev) +{ + int i, n, ret; + n = ARRAY_SIZE(dvfm_stats_attr); + for (i = 0; i < n; i++) { + ret = sysfs_create_file(&(sys_dev->kobj), dvfm_stats_attr[i]); + if (ret) + return ret; + } + return 0; +} + +static int stats_rm(struct sys_device *sys_dev) +{ + int i, n; + n = ARRAY_SIZE(dvfm_stats_attr); + for (i = 0; i < n; i++) { + sysfs_remove_file(&(sys_dev->kobj), dvfm_stats_attr[i]); + } + return 0; +} + +static int stats_suspend(struct sys_device *sysdev, pm_message_t pmsg) +{ + return 0; +} + +static int stats_resume(struct sys_device *sysdev) +{ + return 0; +} + +static struct sysdev_driver dvfm_stats_driver = { + .add = stats_add, + .remove = stats_rm, + .suspend = stats_suspend, + .resume = stats_resume, +}; + +int __init dvfm_stats_init(void) +{ + int ret; + + memset(&op_ticks_array, 0, sizeof(struct op_cycle_type) * OP_NUM); + ret = sysdev_driver_register(&cpu_sysdev_class, &dvfm_stats_driver); + if (ret) + printk(KERN_ERR "Can't register DVFM STATS in sysfs\n"); + return ret; +} + +void __exit dvfm_stats_exit(void) +{ + sysdev_driver_unregister(&cpu_sysdev_class, &dvfm_stats_driver); +} + +module_init(dvfm_stats_init); +module_exit(dvfm_stats_exit); + diff --git a/arch/arm/mach-mmp/edge.c b/arch/arm/mach-mmp/edge.c new file mode 100644 index 00000000000000..534aac240cb7b3 --- /dev/null +++ b/arch/arm/mach-mmp/edge.c @@ -0,0 +1,827 @@ +/* + * linux/arch/arm/mach-mmp/edge.c + * + * Support for the Marvell PXA168-based Edge 2.0 Development Platform. + * + * 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 + * publishhed 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" +#include +#ifdef CONFIG_SD8XXX_RFKILL +#include +#endif +#include + +/* Edge 2.0 MFP configurations */ +static unsigned long edge_pin_config[] __initdata = { + /* DEBUG UART2 */ + MFP_CFG(GPIO88, AF3), + MFP_CFG(GPIO89, AF3), + + /* MMC2_DET/WP */ + MFP_CFG(GPIO86, AF0), + MFP_CFG(GPIO87, AF0), + + /* MMC2 DAT3-0/CLK/CMD */ + MFP_CFG(GPIO90, AF1), + MFP_CFG(GPIO91, AF1), + MFP_CFG(GPIO92, AF1), + MFP_CFG(GPIO93, AF1), + MFP_CFG(GPIO94, AF1), + MFP_CFG(GPIO95, AF1), + + /* SD_PWR_EN */ + MFP_CFG(GPIO105, AF0), + + /* JBALL, JBALL_DOWN - EXT_WAKEUP */ + MFP_CFG(GPIO96, AF0), /* JBALL_PRESS */ + MFP_CFG(GPIO25, AF5), /* JBALL_LEFT */ + MFP_CFG(GPIO104, AF0), /* JBALL_RIGHT */ + MFP_CFG(GPIO112, AF0), /* JBALL_UP */ + + /* PEN DIGITIZER */ + MFP_CFG(GPIO97, AF0), /* PEN_DETECT ???*/ + MFP_CFG(GPIO100, AF0), /* PEN_SLEEP */ + MFP_CFG(GPIO109, AF0), /* PEN_RESETn */ + MFP_CFG_DRV(GPIO98, AF2, FAST), /* PEN_RXD, UART3_TXD */ + MFP_CFG_DRV(GPIO99, AF2, FAST), /* PEN_TXD, UART3_RXD */ + MFP_CFG_DRV(GPIO101, AF2, FAST), /* PEN_CTS, UART3_RTS */ + + /* I2C */ + MFP_CFG_DRV(GPIO102, AF1, SLOW), + MFP_CFG_DRV(GPIO103, AF1, SLOW), + + /* 3G Mini_PCIe HUAWEI EM660 Module */ + MFP_CFG(GPIO106, AF0), /* PERST# */ + MFP_CFG(GPIO43, AF0), /* W_DISABLE# */ + + /* SW_LCDn[0:3] */ + GPIO37_KP_DKIN0, + GPIO38_KP_DKIN1, + GPIO39_KP_DKIN2, + GPIO42_KP_DKIN3, + + /* SW_VOL */ + MFP_CFG(GPIO40, AF0), /* SW_VOL_UP# */ + MFP_CFG(GPIO41, AF0), /* SW_VOL_DOWN# */ + + /* SW_EPDn[0:3] */ + MFP_CFG(GPIO44, AF0), + MFP_CFG(GPIO45, AF0), + MFP_CFG(GPIO47, AF0), + MFP_CFG(GPIO46, AF0), + + /* HALL sensor */ + MFP_CFG(GPIO48, AF0), /* UNIT_OPEN */ + + /* EC power control */ + MFP_CFG(GPIO49, AF0), /* SD_REQ# */ + MFP_CFG(GPIO51, AF0), /* SD_GNT# */ + MFP_CFG(GPIO52, AF0), /* SLP_REQ# */ + MFP_CFG(GPIO53, AF0), /* STR_GNT# */ + MFP_CFG(GPIO54, AF0), /* VCORE_INT, gpio47 on ava ??? */ + + /* LCD */ + GPIO56_LCD_FCLK_RD, + GPIO57_LCD_LCLK_A0, + GPIO58_LCD_PCLK_WR, + GPIO59_LCD_DENA_BIAS, + GPIO60_LCD_DD0, + GPIO61_LCD_DD1, + GPIO62_LCD_DD2, + GPIO63_LCD_DD3, + GPIO64_LCD_DD4, + GPIO65_LCD_DD5, + GPIO66_LCD_DD6, + GPIO67_LCD_DD7, + GPIO68_LCD_DD8, + GPIO69_LCD_DD9, + GPIO70_LCD_DD10, + GPIO71_LCD_DD11, + GPIO72_LCD_DD12, + GPIO73_LCD_DD13, + GPIO74_LCD_DD14, + GPIO75_LCD_DD15, + GPIO76_LCD_DD16, + GPIO77_LCD_DD17, + + MFP_CFG(GPIO34, AF0), /* LCD_PWR_EN */ + MFP_CFG(GPIO35, AF0), /* LCD_PWDN# */ + + MFP_CFG(GPIO78, AF0), /* EPSON_RESET# */ + MFP_CFG(GPIO79, AF0), /* EPSON_IRQ */ + MFP_CFG(GPIO80, AF0), /* EPSON_DC, ??? GPIO22 */ + MFP_CFG(GPIO81, AF0), /* EPSON_RDY */ + + /* USB host port power control */ + MFP_CFG(GPIO122, AF0), /* USBH_RST# */ + MFP_CFG(GPIO20, AF0), /* USB_CAM_EN# */ + MFP_CFG(GPIO82, AF0), /* USB_tp_P_EN# USB customer 2 */ + MFP_CFG(GPIO83, AF0), /* USB_kb_P_EN# USB customer 1 */ + + /* backlight control */ + MFP_CFG(GPIO84, AF4), /* LCD_PWM */ + MFP_CFG(GPIO85, AF0), /* LCD_BL_PWR_EN */ + + /* FLASH */ + GPIO0_DFI_D15, + GPIO1_DFI_D14, + GPIO2_DFI_D13, + GPIO3_DFI_D12, + GPIO4_DFI_D11, + GPIO5_DFI_D10, + GPIO6_DFI_D9, + GPIO7_DFI_D8, + GPIO8_DFI_D7, + GPIO9_DFI_D6, + GPIO10_DFI_D5, + GPIO11_DFI_D4, + GPIO12_DFI_D3, + GPIO13_DFI_D2, + GPIO14_DFI_D1, + GPIO15_DFI_D0, + GPIO16_ND_nCS0, /* MLC CS0# */ + GPIO26_ND_RnB1, /* MLC_NF_R_B0# */ + GPIO21_ND_ALE, /* MLC NF_WE# */ + /* shared with EPSON panel */ + GPIO24_ND_nRE, /* AP_NF_RE#, EPSON_RDn */ + GPIO17_ND_nWE, /* AP_NF_WE#, EPSON_WDn */ + GPIO22_ND_CLE, /* AP_NF_CLE#, EPSON_DC ??? GPIO80 */ + + /* EPSON panel */ + GPIO18_SMC_nCS1, /* EPSON_CS# */ + + /* tsc2007 irq */ + MFP_CFG(GPIO19, AF5), + + /* RTC */ + MFP_CFG(GPIO23, AF5), /* RTC_INT# */ + + /* not used, GPIO27_ND_RnB2 */ + MFP_CFG(GPIO27, AF1), + + /* SD_WIFI */ + MFP_CFG(GPIO28, AF1), + MFP_CFG(GPIO29, AF1), + MFP_CFG(GPIO30, AF1), + MFP_CFG(GPIO31, AF1), + MFP_CFG(GPIO32, AF1), + MFP_CFG(GPIO33, AF1), + + /* WIFI */ + MFP_CFG(GPIO50, AF0), /* SW_WIFI_ON */ /* could be used as gpio key tmply */ + MFP_CFG(GPIO55, AF0), /* GP_PD_WLAN# */ + MFP_CFG(GPIO118, AF0), /* AP_WPD_1# */ + MFP_CFG(GPIO119, AF0), /* AP_WPD_2# */ + MFP_CFG(GPIO120, AF0), /* RST_WIFI# */ + MFP_CFG(GPIO121, AF0), /* WIFI_LED# */ + + /* G-sensor BMA020 */ + MFP_CFG(GPIO36, AF0), /* G_INT */ + + /* SSP2, SPI NOR */ + GPIO107_SPI_NOR_RXD, + GPIO108_SPI_NOR_TXD, + GPIO110_GPIO, + GPIO111_SPI_NOR_CLK, + + /* SSP0, AUDIO */ + MFP_CFG(GPIO113, AF6), + MFP_CFG(GPIO114, AF1), + MFP_CFG(GPIO115, AF1), + MFP_CFG(GPIO116, AF2), + MFP_CFG(GPIO117, AF2), +}; + +/* + * GPIO Keys + */ +static struct gpio_keys_button btn_button_table[] = { + { + .code = KEY_F4, + .gpio = MFP_PIN_GPIO40, /* SW1, SW_VOL_UP# */ + .active_low = 1, //0 for down 0, up 1; 1 for down 1, up 0 + .desc = "VUP button", + .type = EV_KEY, + //.wakeup = + .debounce_interval = 10, //10 msec jitter elimination + }, + { + .code = KEY_F5, + .gpio = MFP_PIN_GPIO41, /* SW2, SW_VOL_DOWN# */ + .active_low = 1, //0 for down 0, up 1; 1 for down 1, up 0 + .desc = "VDN button", + .type = EV_KEY, + //.wakeup = + .debounce_interval = 10, //10 msec jitter elimination + }, +}; + +static struct gpio_keys_platform_data gpio_keys_data = { + .buttons = btn_button_table, + .nbuttons = ARRAY_SIZE(btn_button_table), +}; + +static struct platform_device gpio_keys = { + .name = "gpio-keys", + .dev = { + .platform_data = &gpio_keys_data, + }, + .id = -1, +}; + +static void __init edge_gpio_keys_init(void) +{ + platform_device_register(&gpio_keys); +} + +static struct fb_videomode video_modes[] = { + /* innolux WVGA mode info */ + [0] = { + .pixclock = 22222, + .refresh = 60, + .xres = 1024, + .yres = 600, + .hsync_len = 176, + .left_margin = 0, + .right_margin = 0, + .vsync_len = 25, + .upper_margin = 0, + .lower_margin = 0, + .sync = FB_SYNC_VERT_HIGH_ACT|FB_SYNC_HOR_HIGH_ACT, + }, +}; + +static struct pxa168fb_mach_info edge_lcd_info __initdata = { + .id = "Base", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_GPIO, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .panel_rbswap = 0, + .invert_pixclock = 1, + .max_fb_size = (DEFAULT_FB_SIZE + 5 * 1024 * 1024), +}; + +struct pxa168fb_mach_info edge_lcd_ovly_info __initdata = { + .id = "Ovly", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_GPIO, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .panel_rbswap = 0, + .max_fb_size = 1024 * 768 *4, +}; + +static int __init edge_lcd_init(void) +{ + int lcd_pwdn, lcd_pwr_en; + + /* LCD_PWR_EN */ + lcd_pwr_en = MFP_PIN_GPIO34; + if(gpio_request(lcd_pwr_en, "lcd_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_pwr_en); + return -EIO; + } + gpio_direction_output(lcd_pwr_en, 1); + gpio_free(lcd_pwr_en); + + /* LCD_PWDN# */ + lcd_pwdn = MFP_PIN_GPIO35; + if(gpio_request(lcd_pwdn, "lcd_pwdn")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_pwdn); + return -EIO; + } + gpio_direction_output(lcd_pwdn, 1); + gpio_free(lcd_pwdn); + + /* register device */ + pxa168_add_fb(&edge_lcd_info); + pxa168_add_fb_ovly(&edge_lcd_ovly_info); + + return 0; +} + +static struct platform_pwm_backlight_data edge_backlight_data = { + .pwm_id = 0, + .max_brightness = 255, + .dft_brightness = 200, + .pwm_period_ns = 78770, +}; + +static struct platform_device edge_backlight_device = { + .name = "pwm-backlight", + .dev = { + .parent = &pxa168_device_pwm0.dev, + .platform_data = &edge_backlight_data, + }, +}; + +static int __init edge_backlight_register(void) +{ + int lcd_bl_pwr_en; + int ret = platform_device_register(&edge_backlight_device); + if (ret) + printk(KERN_ERR "edge: failed to register backlight device: %d\n", ret); + + /* LCD_BL_PWR_EN */ + lcd_bl_pwr_en = MFP_PIN_GPIO85; + if(gpio_request(lcd_bl_pwr_en, "lcd_bl_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", lcd_bl_pwr_en); + return -EIO; + } + gpio_direction_output(lcd_bl_pwr_en, 1); + gpio_free(lcd_bl_pwr_en); + + return 0; +} + +static struct i2c_pxa_platform_data pwri2c_info __initdata = { + .use_pio = 1, +}; + +/* touch screen, rtc, audio codec */ +static struct i2c_board_info edge_i2c_board_info[] = { +#if defined(CONFIG_TSC2007) + { + .type = "tsc2007", + .addr = 0x48, /* 0x90/0x91 */ + .irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO19)), + }, +#endif +#if defined(CONFIG_RTC_DRV_DS1307) + { + .type = "ds1337", + .addr = 0x68, /* 0xD0 */ + }, +#endif +}; + +static struct i2c_board_info pwri2c_board_info[] = +{ +#if defined(CONFIG_BMA020) + { + .type ="bma020", + .addr =0x38, + .irq =IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO36)), /* G_INT */ + }, +#endif +#if defined(CONFIG_MCU_PM) + { + .type ="power_mcu", + .addr =0x2C, /* why not 0x3c as it's 0x78 in schematic ??? */ + }, +#endif +}; + +static struct pxa27x_keypad_platform_data edge_android_keypad_info \ + __initdata = { + + .direct_key_map = { KEY_F9, KEY_F6, KEY_F7, KEY_F8 }, + .direct_key_num = 4, + .debounce_interval = 30, +}; + +#if defined(CONFIG_MMC_PXA_SDH) +static int edge_sdh_init(struct device *dev, + irq_handler_t detect_int, void *data) +{ + int sd_pwr_en; + int ap_wpd_1n, ap_wpd_2n, rst_wifi; + int err, cd_irq, gpio_cd; + + /* SD_PWR_EN */ + sd_pwr_en = MFP_PIN_GPIO105; + if(gpio_request(sd_pwr_en, "sd_pwr_en")) { + printk(KERN_INFO "gpio %d request failed\n", sd_pwr_en); + return -EIO; + } + gpio_direction_output(sd_pwr_en, 1);/* should fix to 0 for 1.5Q */ + gpio_free(sd_pwr_en); + + /* V3P3_WLAN - AP_WPD_1#; V1P8_WLAN - AP_WPD_2# */ + ap_wpd_1n = mfp_to_gpio(MFP_PIN_GPIO118); + ap_wpd_2n = mfp_to_gpio(MFP_PIN_GPIO119); + + if(gpio_request(ap_wpd_1n, "ap_wpd_1n")) { + printk(KERN_INFO "gpio %d request failed\n", ap_wpd_1n); + return -EIO; + } + if(gpio_request(ap_wpd_2n, "ap_wpd_2n")) { + printk(KERN_INFO "gpio %d request failed\n", ap_wpd_2n); + return -EIO; + } + + gpio_direction_output(ap_wpd_1n, 1); + gpio_direction_output(ap_wpd_2n, 1); + gpio_free(ap_wpd_1n); + gpio_free(ap_wpd_2n); + + /* RST_WIFI# to high */ + rst_wifi = mfp_to_gpio(MFP_PIN_GPIO120); + if(gpio_request(rst_wifi, "rst_wifi")) { + printk(KERN_INFO "gpio %d request failed\n", rst_wifi); + return -EIO; + } + gpio_direction_output(rst_wifi, 1); + gpio_free(rst_wifi); + + /* MMC2_DET */ + gpio_cd = mfp_to_gpio(MFP_PIN_GPIO86); + cd_irq = gpio_to_irq(gpio_cd); + + /* + * setup GPIO for MMC controller + */ + err = gpio_request(gpio_cd, "mmc card detect"); + if (err) + goto err_request_cd; + gpio_direction_input(gpio_cd); + + err = request_irq(cd_irq, detect_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "MMC card detect", data); + if (err) { + printk(KERN_ERR "%s: MMC/SD/SDIO: " + "can't request card detect IRQ\n", __func__); + goto err_request_irq; + } + + return 0; + +err_request_irq: + gpio_free(gpio_cd); +err_request_cd: + return err; +} + +static struct pxasdh_platform_data edge_sdh_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .init = edge_sdh_init, + .quirks = SDHCI_QUIRK_NO_BUSY_IRQ, +}; + +static struct pxasdh_platform_data edge_sdh3_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, +}; + +static void __init edge_init_mmc(void) +{ + /* mmc2, mmc/sd socket */ + pxa168_add_sdh(1, &edge_sdh_platform_data); + + /* mmc4, wifi */ +#ifdef CONFIG_SD8XXX_RFKILL + add_sd8x_rfkill_device(mfp_to_gpio(MFP_PIN_GPIO55), 0, + &edge_sdh3_platform_data.pmmc); +#endif + pxa168_add_sdh(3, &edge_sdh3_platform_data); +} +#endif + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static struct pxa_usb_plat_info edge_u2o_info = { + .phy_init = pxa168_usb_phy_init, +}; +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H +static int edge_u2h_plat_init (struct device *dev) +{ + int usbh_rst = mfp_to_gpio(MFP_PIN_GPIO122); + + /* USBH_RST# */ + if(gpio_request(usbh_rst, "usbh_rst")) { + printk(KERN_INFO "gpio %d request failed\n", usbh_rst); + return -EIO; + } + gpio_direction_output(usbh_rst, 1); + gpio_free(usbh_rst); + return 0; +} + +static int edge_u2h_vbus_set (int enable) +{ + int usb_cam_en = mfp_to_gpio(MFP_PIN_GPIO20); + int usb_tp_p_en = mfp_to_gpio(MFP_PIN_GPIO82); + int usb_kb_p_en = mfp_to_gpio(MFP_PIN_GPIO83); + + /* USB_CAM_EN# */ + if(gpio_request(usb_cam_en, "usb_cam_en")) { + printk(KERN_INFO "gpio %d request failed\n", usb_cam_en); + return -EIO; + } + gpio_direction_output(usb_cam_en, 0); + gpio_free(usb_cam_en); + + /* USB_tp_P_EN# */ + if(gpio_request(usb_tp_p_en, "usb_tp_p_en")) { + printk(KERN_INFO "gpio %d request failed\n", usb_tp_p_en); + return -EIO; + } + gpio_direction_output(usb_tp_p_en, 0); + gpio_free(usb_tp_p_en); + + /* USB_kb_P_EN# */ + if(gpio_request(usb_kb_p_en, "usb_kb_p_en")) { + printk(KERN_INFO "gpio %d request failed\n", usb_kb_p_en); + return -EIO; + } + gpio_direction_output(usb_kb_p_en, 0); + gpio_free(usb_kb_p_en); + + return 0; +} + +static struct pxa_usb_plat_info edge_u2h_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_set = edge_u2h_vbus_set, + .plat_init = edge_u2h_plat_init, +}; +#endif + +#if defined(CONFIG_MTD_M25P80) +static struct pxa2xx_spi_master pxa_ssp_master_info = { + .num_chipselect = 1, +}; + +static struct pxa2xx_spi_chip m25pxx_spi_info = { + .tx_threshold = 1, + .rx_threshold = 1, + .timeout = 1000, + .gpio_cs = 110 +}; + +static struct spi_board_info __initdata spi_board_info[] = { + { + .modalias = "m25p80", + .mode = SPI_MODE_0, + .max_speed_hz = 260000, + .bus_num = 2, + .chip_select = 0, + .platform_data = NULL, + .controller_data = &m25pxx_spi_info, + .irq = -1, + }, +}; + +static void __init edge_init_spi(void) +{ + pxa168_add_ssp(1); + pxa168_add_spi(2, &pxa_ssp_master_info); + spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); +} +#else +static inline void edge_init_spi(void) {} +#endif + +struct platform_device edge_power_mcu = { + .name = "power_mcu", +}; + +struct platform_device edge_power_supply = { + .name = "battery", +}; + +static void ack_standby(void) +{ + gpio_direction_output(MFP_PIN_GPIO53, 1); + mdelay(100); + gpio_direction_output(MFP_PIN_GPIO53, 0); +} + +static void ack_powerdwn(void) +{ + gpio_direction_output(MFP_PIN_GPIO51, 1); + mdelay(100); + gpio_direction_output(MFP_PIN_GPIO51, 0); +} + +static int power_button_init(irq_handler_t pwrdwn_handler, irq_handler_t standby_handler) +{ + int ret; + int irq; + + irq = gpio_to_irq(MFP_PIN_GPIO49); + ret = request_irq(irq , pwrdwn_handler, \ + IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING , \ + "shutdown detect", NULL); + if (ret) { + printk(KERN_ERR "%s: can't request detect standby irq\n", + __FUNCTION__); + goto out; + } + + irq = gpio_to_irq(MFP_PIN_GPIO52); + ret = request_irq(irq, standby_handler , \ + IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING , \ + "standby detect", NULL); + if (ret) { + printk(KERN_ERR "%s: can't request detect shutdown irq\n", + __FUNCTION__); + goto out; + } + + gpio_direction_input(MFP_PIN_GPIO49); + gpio_direction_input(MFP_PIN_GPIO52); + +out: + return ret; +} + +static struct power_button_platform_data power_button_data = { + .init = power_button_init, + .send_standby_ack = ack_standby, + .send_powerdwn_ack = ack_powerdwn, +}; + +struct platform_device edge_power_button = { + .name = "power-button", + .dev = { + .platform_data = &power_button_data, + }, + .id = -1, +}; + + + +static struct platform_device *devices[] __initdata = { + &pxa168_device_pwm0, + &edge_power_mcu, + &edge_power_supply, + &edge_power_button, +}; + +DECLARE_AVENGERSLITE_MLC_4G_ANDROID_PARTITIONS(edge_mlc_4g_android_partitions); +DECLARE_AVENGERSLITE_MLC_1G_ANDROID_PARTITIONS(edge_mlc_1g_android_partitions); +DECLARE_AVENGERSLITE_MLC_4G_MAEMO_PARTITIONS(edge_mlc_4g_maemo_partitions); +DECLARE_AVENGERSLITE_MLC_1G_MAEMO_PARTITIONS(edge_mlc_1g_maemo_partitions); +static struct pxa3xx_nand_platform_data edge_nand_info; +static void __init edge_add_nand(void) +{ + unsigned long long mlc_size = 0; + + mlc_size = get_mlc_size(); + printk (KERN_INFO "@%s Get mlc size from CMDLINE:%d\n", __FUNCTION__, (int)mlc_size); + + if (is_android()) { + switch (mlc_size) + { + case 1: /* 1G */ + edge_nand_info.parts[0] = edge_mlc_1g_android_partitions; + edge_nand_info.nr_parts[0] = ARRAY_SIZE(edge_mlc_1g_android_partitions); + break; + case 4: /* 4G */ + edge_nand_info.parts[0] = edge_mlc_4g_android_partitions; + edge_nand_info.nr_parts[0] = ARRAY_SIZE(edge_mlc_4g_android_partitions); + break; + default: + /* FIXME: do something or not */ + edge_nand_info.parts[0] = edge_mlc_4g_android_partitions; + edge_nand_info.nr_parts[0] = ARRAY_SIZE(edge_mlc_4g_android_partitions); + break; + } + } else { + switch (mlc_size) + { + case 1: /* 1G */ + edge_nand_info.parts[0] = edge_mlc_1g_maemo_partitions; + edge_nand_info.nr_parts[0] = ARRAY_SIZE(edge_mlc_1g_maemo_partitions); + break; + case 4: /* 4G */ + edge_nand_info.parts[0] = edge_mlc_4g_maemo_partitions; + edge_nand_info.nr_parts[0] = ARRAY_SIZE(edge_mlc_4g_maemo_partitions); + break; + default: + /* FIXME: do something or not */ + edge_nand_info.parts[0] = edge_mlc_4g_maemo_partitions; + edge_nand_info.nr_parts[0] = ARRAY_SIZE(edge_mlc_4g_maemo_partitions); + break; + } + /* make mlc whole as a massstorage room */ + } + + edge_nand_info.use_dma = 1; + edge_nand_info.RD_CNT_DEL = 0; + edge_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *) &edge_nand_info); +} + +static void gpio_ec_init(void) +{ + /* ??? no uart lines connected, how to control battery */ +} + +static void edge_power_off(void) +{ + pr_notice("notify EC to shutdown\n"); + gpio_direction_output(MFP_PIN_GPIO51, 1); + gpio_direction_output(MFP_PIN_GPIO53, 1); + mdelay(100); + gpio_direction_output(MFP_PIN_GPIO51, 0); + gpio_direction_output(MFP_PIN_GPIO53, 0); + + /* Spin to death... */ + while (1); +} + +static void __init edge_init(void) +{ + pxa168_set_vdd_iox(VDD_IO0, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO1, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO2, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO3, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO4, VDD_IO_3P3V); + pxa168_mfp_set_fastio_drive(MFP_DS02X); + + mfp_config(ARRAY_AND_SIZE(edge_pin_config)); + + /* on-chip devices */ + pxa168_add_uart(2); + edge_add_nand(); + pxa168_add_ssp(0); + pxa168_add_twsi(0, &pwri2c_info, ARRAY_AND_SIZE(edge_i2c_board_info)); + pxa168_add_twsi(1, &pwri2c_info, ARRAY_AND_SIZE(pwri2c_board_info)); + + pxa168_add_keypad(&edge_android_keypad_info); + +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&edge_u2o_info); +#endif +#ifdef CONFIG_USB_EHCI_PXA_U2H + pxa168_add_u2h(&edge_u2h_info); +#endif +#if defined(CONFIG_MMC_PXA_SDH) + edge_init_mmc(); +#endif + platform_add_devices(devices, ARRAY_SIZE(devices)); + + /* off-chip devices */ + edge_lcd_init(); + edge_backlight_register(); + + pxa168_add_freq(); + edge_init_spi(); + +#if defined(CONFIG_KEYBOARD_GPIO) + edge_gpio_keys_init(); +#endif + +#ifdef CONFIG_ANDROID_PMEM + android_add_pmem("pmem", 0x01000000UL, 1, 0); + android_add_pmem("pmem_adsp", 0x00400000UL, 0, 0); +#endif + + gpio_ec_init(); + + pm_power_off = edge_power_off; +} + +MACHINE_START(EDGE, "PXA168 Edge Development Platform") + .phys_io = APB_PHYS_BASE, + .boot_params = 0x00000100, + .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, + .map_io = pxa_map_io, + .init_irq = pxa168_init_irq, + .timer = &pxa168_timer, + .init_machine = edge_init, +MACHINE_END diff --git a/arch/arm/mach-mmp/include/mach/camera.h b/arch/arm/mach-mmp/include/mach/camera.h new file mode 100644 index 00000000000000..62dc8e7f75d075 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/camera.h @@ -0,0 +1,25 @@ +#ifndef __ASM_ARCH_CAMERA_H__ +#define __ASM_ARCH_CAMERA_H__ + +#define SENSOR_LOW 0 +#define SENSOR_HIGH 1 + +struct cam_platform_data { + unsigned int vsync_gpio; + int (*init)(void); + void (*deinit)(void); + void (*suspend)(void); + void (*resume)(void); + void (*sync_to_gpio)(void); + void (*sync_from_gpio)(void); +}; + +struct clk; +struct sensor_platform_data { + int id; + int (*power_on)(int,int); + int (*platform_set)(int, struct clk*); +}; + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/clock.h b/arch/arm/mach-mmp/include/mach/clock.h new file mode 100644 index 00000000000000..e918e6af39a983 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/clock.h @@ -0,0 +1,30 @@ +/* + * linux/arch/arm/mach-mmp/clock.h + * + * 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. + */ + +#ifndef __MACH_CLOCK_H +#define __MACH_CLOCK_H + +#include + +struct clkops { + void (*enable)(struct clk *); + void (*disable)(struct clk *); + unsigned long (*getrate)(struct clk *); + int (*setrate)(struct clk *, unsigned long); +}; + +struct clk { + const struct clkops *ops; + void __iomem *clk_rst; /* clock/reset register */ + int fnclksel; /* functional clock select (APBC) */ + uint32_t enable_val; /* register value to enable (APMU) */ + unsigned long rate; + int enabled; +}; + +#endif /* __MACH_CLOCK_H */ diff --git a/arch/arm/mach-mmp/include/mach/cm3602.h b/arch/arm/mach-mmp/include/mach/cm3602.h new file mode 100644 index 00000000000000..e0be18ed54f982 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/cm3602.h @@ -0,0 +1,12 @@ +#ifndef _CM3602_H +#define _CM3602_H + +struct cm3602_platform_data { + char *name; + void (*power)(int on); + int (*get_aout)(); + int (*get_pout)(); +}; + +#endif /*_SENSOR_INPUT_H*/ + diff --git a/arch/arm/mach-mmp/include/mach/cputype.h b/arch/arm/mach-mmp/include/mach/cputype.h index 83b18721d93305..2fbe53b2ae54ca 100644 --- a/arch/arm/mach-mmp/include/mach/cputype.h +++ b/arch/arm/mach-mmp/include/mach/cputype.h @@ -1,7 +1,11 @@ #ifndef __ASM_MACH_CPUTYPE_H #define __ASM_MACH_CPUTYPE_H +#include #include +#include + +#define CHIP_ID (AXI_VIRT_BASE + 0x82c00) /* * CPU Stepping OLD_ID CPU_ID CHIP_ID @@ -11,20 +15,44 @@ * MMP2 Z0 0x560f5811 */ +#ifndef CONFIG_CPU_MOHAWK_OLD_ID + #ifdef CONFIG_CPU_PXA168 -# define __cpu_is_pxa168(id) \ - ({ unsigned int _id = ((id) >> 8) & 0xff; _id == 0x84; }) +# define __cpu_is_pxa168(id, cid) \ + ({ unsigned int _id = ((id) >> 8) & 0xff; \ + unsigned int _cid = (cid) & 0xfff; \ + _id == 0x84 && _cid != 0x910; }) #else -# define __cpu_is_pxa168(id) (0) +# define __cpu_is_pxa168(id, cid) (0) #endif #ifdef CONFIG_CPU_PXA910 -# define __cpu_is_pxa910(id) \ - ({ unsigned int _id = ((id) >> 8) & 0xff; _id == 0x80; }) +# define __cpu_is_pxa910(id, cid) \ + ({ unsigned int _id = ((id) >> 8) & 0xff; \ + unsigned int _cid = (cid) & 0xfff; \ + (_id == 0x84 || _id == 0x80) && (_cid == 0x910 || _cid == 0x920); }) +#else +# define __cpu_is_pxa910(id, cid) (0) +#endif + #else -# define __cpu_is_pxa910(id) (0) + +#ifdef CONFIG_CPU_PXA168 +# define __cpu_is_pxa168(id, cid) \ + ({ unsigned int _id = (id) & 0xffff; _id == 0x9263; }) +#else +# define __cpu_is_pxa168(id, cid) (0) #endif +#ifdef CONFIG_CPU_PXA910 +# define __cpu_is_pxa910(id, cid) \ + ({ unsigned int _id = (id) & 0xffff; _id == 0x9262; }) +#else +# define __cpu_is_pxa910(id, cid) (0) +#endif + +#endif /* CONFIG_CPU_MOHAWK_OLD_ID */ + #ifdef CONFIG_CPU_MMP2 # define __cpu_is_mmp2(id) \ ({ unsigned int _id = ((id) >> 8) & 0xff; _id == 0x58; }) @@ -32,8 +60,54 @@ # define __cpu_is_mmp2(id) (0) #endif -#define cpu_is_pxa168() ({ __cpu_is_pxa168(read_cpuid_id()); }) -#define cpu_is_pxa910() ({ __cpu_is_pxa910(read_cpuid_id()); }) +#define cpu_is_pxa168() ({ __cpu_is_pxa168(read_cpuid_id(), __raw_readl(CHIP_ID)); }) +#define cpu_is_pxa910() ({ __cpu_is_pxa910(read_cpuid_id(), __raw_readl(CHIP_ID)); }) #define cpu_is_mmp2() ({ __cpu_is_mmp2(read_cpuid_id()); }) +static inline int cpu_is_pxa910_z0(void) +{ + unsigned int chip_id = __raw_readl(CHIP_ID); + if (cpu_is_pxa910() && ((chip_id & 0x00f00000) == 0x00a00000)) + return 1; + else + return 0; +} + +static inline int cpu_is_pxa910_Ax(void) +{ + unsigned int chip_id = __raw_readl(CHIP_ID); + if (cpu_is_pxa910() && ((chip_id & 0x00ff0000) >= 0x00f10000)) + return 1; + else + return 0; +} + +static inline int cpu_is_pxa168_S0(void) +{ + unsigned int chip_id = __raw_readl(CHIP_ID); + if (cpu_is_pxa168() && ((chip_id & 0x0000ffff) == 0x0000c910)) + return 1; + else + return 0; +} + +static inline int cpu_is_pxa168_A0(void) +{ + unsigned int chip_id = __raw_readl(CHIP_ID); + if (cpu_is_pxa168() && ((chip_id & 0x00f0ffff) == 0x00a0a168)) + return 1; + else + return 0; +} + +static inline int cpu_is_pxa920_z2(void) +{ + unsigned int chip_id = __raw_readl(CHIP_ID); + if (cpu_is_pxa910() && ((chip_id & 0x00fff000) == 0x0070c000)) + return 1; + else + return 0; +} + + #endif /* __ASM_MACH_CPUTYPE_H */ diff --git a/arch/arm/mach-mmp/include/mach/devices.h b/arch/arm/mach-mmp/include/mach/devices.h index 1fa0a492454adc..39eff5d1927bb1 100644 --- a/arch/arm/mach-mmp/include/mach/devices.h +++ b/arch/arm/mach-mmp/include/mach/devices.h @@ -1,4 +1,7 @@ +#ifndef __ASM_MACH_DEVICES_H +#define __ASM_MACH_DEVICES_H #include +#include #define MAX_RESOURCE_DMA 2 @@ -24,17 +27,41 @@ struct pxa_device_desc pxa168_device_##_name __initdata = { \ .dma = { _dma }, \ }; -#define PXA910_DEVICE(_name, _drv, _id, _irq, _start, _size, _dma...) \ -struct pxa_device_desc pxa910_device_##_name __initdata = { \ - .dev_name = "pxa910-" #_name, \ +#define PXA168_DEVICE_M(_name, _drv, _id, _irq, _start, _size, _dma...) \ +struct pxa_device_desc pxa168_device_##_name = { \ + .dev_name = "pxa168-" #_name, \ .drv_name = _drv, \ .id = _id, \ - .irq = IRQ_PXA910_##_irq, \ + .irq = IRQ_PXA168_##_irq, \ .start = _start, \ .size = _size, \ .dma = { _dma }, \ +}; \ +EXPORT_SYMBOL(pxa168_device_##_name); + +#define PXA910_DEVICE(_name, _drv, _id, _irq, _start, _size, _dma...) \ +struct pxa_device_desc pxa910_device_##_name __initdata = { \ + .dev_name = "pxa910-" #_name, \ + .drv_name = _drv, \ + .id = _id, \ + .irq = IRQ_PXA910_##_irq, \ + .start = _start, \ + .size = _size, \ + .dma = { _dma }, \ }; +#define PXA910_DEVICE_M(_name, _drv, _id, _irq, _start, _size, _dma...) \ +struct pxa_device_desc pxa910_device_##_name = { \ + .dev_name = "pxa910-" #_name, \ + .drv_name = _drv, \ + .id = _id, \ + .irq = IRQ_PXA910_##_irq, \ + .start = _start, \ + .size = _size, \ + .dma = { _dma }, \ +}; \ +EXPORT_SYMBOL(pxa910_device_##_name); + #define MMP2_DEVICE(_name, _drv, _id, _irq, _start, _size, _dma...) \ struct pxa_device_desc mmp2_device_##_name __initdata = { \ .dev_name = "mmp2-" #_name, \ @@ -46,4 +73,58 @@ struct pxa_device_desc mmp2_device_##_name __initdata = { \ .dma = { _dma }, \ } +extern struct platform_device pxa168_device_u2o; +extern struct platform_device pxa168_device_u2h; +extern struct platform_device pxa168_device_u2oehci; +extern struct platform_device pxa168_device_u2ootg; +extern struct pxa_device_desc pxa168_device_uart1; +extern struct pxa_device_desc pxa168_device_uart1b; +extern struct pxa_device_desc pxa168_device_uart2; +extern struct pxa_device_desc pxa168_device_uart3; +extern struct pxa_device_desc pxa168_device_fb; +extern struct pxa_device_desc pxa168_device_fb_ovly; +extern struct pxa_device_desc pxa168_device_twsi0; +extern struct pxa_device_desc pxa168_device_twsi1; +extern struct platform_device pxa168_device_pwm0; +extern struct platform_device pxa168_device_pwm1; +extern struct platform_device pxa168_device_pwm2; +extern struct platform_device pxa168_device_pwm3; +extern struct pxa_device_desc pxa168_device_ssp0; +extern struct pxa_device_desc pxa168_device_ssp1; +extern struct pxa_device_desc pxa168_device_ssp2; +extern struct pxa_device_desc pxa168_device_ssp3; +extern struct pxa_device_desc pxa168_device_ssp4; +extern struct pxa_device_desc pxa168_device_keypad; +extern struct pxa_device_desc pxa168_device_nand; +extern struct pxa_device_desc pxa168_device_onenand; +extern struct pxa_device_desc pxa168_device_mfu; +extern struct pxa_device_desc pxa168_device_pcie; +extern struct pxa_device_desc pxa168_device_sdh0; +extern struct pxa_device_desc pxa168_device_sdh1; +extern struct pxa_device_desc pxa168_device_sdh2; +extern struct pxa_device_desc pxa168_device_sdh3; +extern struct platform_device pxa168_device_freq; +extern struct platform_device pxa168_device_cir; +extern struct pxa_device_desc pxa168_device_camera; +extern struct pxa_device_desc pxa168_device_ov529; +extern struct pxa_device_desc pxa168_device_msp; +extern struct pxa_device_desc pxa168_device_cf; +extern struct pxa_device_desc pxa168_device_icr; + +/*PXA910 Specific*/ +extern struct platform_device pxa910_device_acipc; +extern struct pxa_device_desc pxa910_device_ire; +extern struct pxa_device_desc pxa910_device_ssp0; +extern struct pxa_device_desc pxa910_device_ssp1; +extern struct pxa_device_desc pxa910_device_ssp2; +extern struct platform_device pxa910_device_imm; +extern struct platform_device pxa910_device_rtc; +extern struct pxa_device_desc pxa910_device_fb; +extern struct pxa_device_desc pxa910_device_fb_ovly; +extern struct platform_device pxa168_device_cs4344; + extern int pxa_register_device(struct pxa_device_desc *, void *, size_t); +extern int pxa168_usb_phy_init(unsigned base); +extern int pxa168_usb_phy_deinit(unsigned base); + +#endif /* __ASM_MACH_DEVICES_H */ diff --git a/arch/arm/mach-mmp/include/mach/dvfm.h b/arch/arm/mach-mmp/include/mach/dvfm.h new file mode 100644 index 00000000000000..1c7ecea2261abd --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/dvfm.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2003-2004 Intel Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef DVFM_H +#define DVFM_H + + +#ifdef __KERNEL__ +enum { + FV_NOTIFIER_QUERY_SET = 1, + FV_NOTIFIER_PRE_SET = 2, + FV_NOTIFIER_POST_SET = 3, +}; + +#include + +#define MAXTOKENS 80 +#define CONSTRAINT_NAME_LEN 20 + +#define DVFM_MAX_NAME 32 +#define DVFM_MAX_CLIENT 256 + +#define DVFM_FREQUENCY_NOTIFIER 0 +#define DVFM_LOWPOWER_NOTIFIER 1 + +#define DVFM_FREQ_PRECHANGE 0 +#define DVFM_FREQ_POSTCHANGE 1 + +#define DVFM_LOWPOWER_PRECHANGE 0 +#define DVFM_LOWPOWER_POSTCHANGE 1 + +/* set the lowest operating point that is equal or higher than specified */ +#define RELATION_LOW 0 +/* set the highest operating point that is equal or lower than specified */ +#define RELATION_HIGH 1 +/* set the specified operating point */ +#define RELATION_STICK 2 + +/* Both of these states are used in statistical calculation */ +#define CPU_STATE_RUN 1 +#define CPU_STATE_IDLE 2 + +/* + * operating point definition + */ + +struct op_info { + void *op; + struct list_head list; + unsigned int index; + /* + * Store the client ID that is blocking OP. + * DVFM_MAX_CLIENT can't exceed the storage limitation. + */ + unsigned long device[8]; +}; + +struct dvfm_freqs { + unsigned int old; /* operating point index */ + unsigned int new; /* operating point index */ + struct op_info old_info; + struct op_info new_info; + unsigned int flags; +}; + +struct info_head { + struct list_head list; + rwlock_t lock; + /* + * Store the client ID that is blocking OP. + * DVFM_MAX_CLIENT can't exceed the storage limitation. + */ + unsigned long device[8]; +}; + +struct head_notifier { + spinlock_t lock; + struct notifier_block *head; +}; + +struct dvfm_lock { + spinlock_t lock; + unsigned long flags; + int dev_idx; + int count; +}; + +/* + * Store the dev_id and dev_name. + * Registered device number can't be larger than 32. + */ +struct dvfm_trace_info { + struct list_head list; + int index; /* index is [0,31] */ + unsigned int dev_id; /* dev_id == 1 << index */ + char name[DVFM_MAX_NAME]; +}; + +struct clock_trace_info { + struct list_head list; + int index; /* index is [0,31] */ + char *dev_id; + char *con_id; + struct clk *clk; +}; + +struct dvfm_driver { + int (*get_opinfo)(void *driver_data, void *info); + int (*count)(void *driver_data, struct info_head *op_table); + int (*set)(void *driver_data, struct dvfm_freqs *freq, unsigned int new, + unsigned int relation); + int (*dump)(void *driver_data, struct op_info *md, char *buf); + char * (*name)(void *driver_data, struct op_info *md); + int (*request_set)(void *driver_data, int index); + int (*enable_dvfm)(void *driver_data, int dev_id); + int (*disable_dvfm)(void *driver_data, int dev_id); + int (*enable_op)(void *driver_data, int index, int relation); + int (*disable_op)(void *driver_data, int index, int relation); + int (*volt_show)(void *driver_data, char *buf); + unsigned int (*ticks_to_usec)(unsigned int); + unsigned int (*ticks_to_sec)(unsigned int); + unsigned int (*read_time)(void); + void *priv; +}; + +extern struct dvfm_driver *dvfm_driver; +extern struct info_head *dvfm_op_list; +extern unsigned int op_nums; + +extern int dvfm_notifier_frequency(struct dvfm_freqs *freqs, unsigned int state); +extern int dvfm_notifier_lowpower(struct dvfm_freqs *freqs, unsigned int state); +extern int dvfm_register_notifier(struct notifier_block *nb, unsigned int list); +extern int dvfm_unregister_notifier(struct notifier_block *nb, unsigned int list); +extern int dvfm_register_driver(struct dvfm_driver *driver_data, struct info_head *op_list); +extern int dvfm_unregister_driver(struct dvfm_driver *driver); +extern int dvfm_register(char *name, int *); +extern int dvfm_unregister(char *name, int *); +extern int dvfm_query_device_num(void); +extern int dvfm_query_device_list(void *, int); + +extern int dvfm_enable_op(int, int); +extern int dvfm_disable_op(int, int); +extern int dvfm_enable(int); +extern int dvfm_enable_op_name(char *, int); +extern int dvfm_disable_op_name(char *, int); +extern int dvfm_disable(int); + +extern int dvfm_set_op(struct dvfm_freqs *, unsigned int, unsigned int); +extern int dvfm_get_op(struct op_info **); +extern int dvfm_get_defop(void); +extern int dvfm_get_opinfo(int, struct op_info **); +extern int dvfm_request_op(int); +extern int dvfm_op_count(void); +extern int dvfm_find_op(int , struct op_info **); +extern int dvfm_trace(char *); + +extern int dvfm_add_event(int, int, int, int); +extern int dvfm_add_timeslot(int, int); +extern int calc_switchtime_start(int, int, unsigned int); +extern int calc_switchtime_end(int, int, unsigned int); +extern unsigned int cur_op; /* current operating point */ +extern unsigned int def_op; /* default operating point */ +#endif + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/gpio_ec.h b/arch/arm/mach-mmp/include/mach/gpio_ec.h new file mode 100644 index 00000000000000..620a230d340d88 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/gpio_ec.h @@ -0,0 +1,9 @@ +#ifndef _GPIO_EC_H_ +#define _GPIO_EC_H_ + +int set_bus_busy(); +int set_bus_free(); +void gpio_ec_init(); + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/gpio_ir.h b/arch/arm/mach-mmp/include/mach/gpio_ir.h new file mode 100644 index 00000000000000..e2f27d66e9c95c --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/gpio_ir.h @@ -0,0 +1,69 @@ +/* + * linux/include/asm-arm/arch-ttc/gpio_ir.h + * + * Support for the ASPEN /Zyloniteii /Teton development Platforms + * + * Copyright (C) 2008 Marvell International Ltd. + * + * 2008-010-12: Ofer Zaarur + * + * 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. + * + */ + +#define uart_putc(x) /*printk("x") */ + +#include + +/* + * Ugly macro :-) + */ +#define DISABLE_IRINT() +#define ENABLE_IRINT() + +#define IR_IDLE_LENGTH 0x7fffffff +#define CUSTOM_CODE 0x916E +#define CODE_SIZE 16 + +/* + * General Purpose I/O + */ +#define GAPMASK0 (0x9C) /* BW GPIO unmask Register GPIO<31:0> */ +#define GCPMASK0 (0xA8) /* BW GPIO mask Register GPIO<31:0> */ + +/* More handy macros. The argument is a literal GPIO number. */ + +/* + * Infrared pin definition + */ +#define IR_PIN ir_pin /* GPIO pin number */ +#define IR_SHIFT (IR_PIN % (sizeof(int) * 8)) /* Shift6*/ +#define GPIO_BIT(x) (1 << ((x) & 0x1f)) + +typedef struct { + unsigned int ir_encode; + unsigned int ir_key; +} ir_key_table_t; + +struct cir_device { + struct platform_device *pdev; + struct input_dev *input_dev; + void __iomem *mmio_base; + unsigned long phys_base; + int irq; +}; + +struct cir_dev { + struct cir_device *cir; + int irq; +}; + +extern unsigned int ir_key; +extern unsigned int ir_key_recvflag; + +void readout_ircbuffer(unsigned long nodata); +int infrared_int_handler(unsigned int data); +int cir_init(struct cir_dev *dev); +void cir_exit(struct cir_dev *dev); diff --git a/arch/arm/mach-mmp/include/mach/hardware.h b/arch/arm/mach-mmp/include/mach/hardware.h index 99264a5ce5e40d..4e1fdba9b31e2b 100644 --- a/arch/arm/mach-mmp/include/mach/hardware.h +++ b/arch/arm/mach-mmp/include/mach/hardware.h @@ -1,4 +1,9 @@ #ifndef __ASM_MACH_HARDWARE_H #define __ASM_MACH_HARDWARE_H +#define cpu_is_pxa3xx() 0 +#define pcibios_assign_all_busses() 1 +#define PCIBIOS_MIN_IO 0x1000 +#define PCIBIOS_MIN_MEM 0x01000000 + #endif /* __ASM_MACH_HARDWARE_H */ diff --git a/arch/arm/mach-mmp/include/mach/ir_key_def.h b/arch/arm/mach-mmp/include/mach/ir_key_def.h new file mode 100644 index 00000000000000..3354468f967709 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/ir_key_def.h @@ -0,0 +1,66 @@ +/* + * linux/include/asm-arm/arch-ttc/ir_key_def.h + * + * Support for the ASPEN /Zyloniteii /Teton development Platforms + * + * Copyright (C) 2008 Marvell International Ltd. + * + * 2008-010-12: Ofer Zaarur + * + * 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. + * + */ +/* + * IR key definition + */ +typedef enum { + MV_IR_KEY_NULL = 0, + + /* digital keys */ + MV_IR_KEY_DIGIT_1, + MV_IR_KEY_DIGIT_16 = 2, + MV_IR_KEY_DIGIT_17, + MV_IR_KEY_DIGIT_18, + MV_IR_KEY_DIGIT_19, + MV_IR_KEY_DIGIT_20, + MV_IR_KEY_DIGIT_21, + MV_IR_KEY_DIGIT_22, + MV_IR_KEY_DIGIT_23, + MV_IR_KEY_DIGIT_24, + MV_IR_KEY_DIGIT_26, + + MV_IR_KEY_DIGIT_28 = 30, + MV_IR_KEY_DIGIT_29 = 48, + MV_IR_KEY_DIGIT_30 = 46, + MV_IR_KEY_DIGIT_31 = 32, + MV_IR_KEY_DIGIT_32 = 18, + MV_IR_KEY_DIGIT_33 = 33, + + MV_IR_KEY_DIGIT_2 = 34, + MV_IR_KEY_DIGIT_3, + MV_IR_KEY_DIGIT_4, + MV_IR_KEY_DIGIT_5, + MV_IR_KEY_DIGIT_6, + MV_IR_KEY_DIGIT_7, + + MV_IR_KEY_DIGIT_8 = 103, + MV_IR_KEY_DIGIT_9, + MV_IR_KEY_DIGIT_10 = 105, + MV_IR_KEY_DIGIT_11 = 28, + MV_IR_KEY_DIGIT_12 = 106, + MV_IR_KEY_DIGIT_13, + MV_IR_KEY_DIGIT_14 = 108, + MV_IR_KEY_DIGIT_15, + + + MV_IR_KEY_DIGIT_25, + MV_IR_KEY_DIGIT_27, + +} MV_IR_KEY_CODE_t; + +#define MV_IR_HOLDKEY_FLAG (1 << 31) +#define MV_IR_HOLDKEY_KEY(key) (key | MV_IR_HOLDKEY_FLAG) +#define MV_IR_HOLDKEY2KEY(key) (key & (~MV_IR_HOLDKEY_FLAG)) + diff --git a/arch/arm/mach-mmp/include/mach/irqs.h b/arch/arm/mach-mmp/include/mach/irqs.h index 02701196ea0336..23817ba8c34c29 100644 --- a/arch/arm/mach-mmp/include/mach/irqs.h +++ b/arch/arm/mach-mmp/include/mach/irqs.h @@ -19,7 +19,7 @@ #define IRQ_PXA168_TIMER1 13 #define IRQ_PXA168_TIMER2 14 #define IRQ_PXA168_TIMER3 15 -#define IRQ_PXA168_CMU 16 +#define IRQ_PXA168_ICR 16 #define IRQ_PXA168_SSP4 17 #define IRQ_PXA168_MSP_WAKEUP 19 #define IRQ_PXA168_CF_WAKEUP 20 @@ -32,11 +32,12 @@ #define IRQ_PXA168_UART1 27 #define IRQ_PXA168_UART2 28 #define IRQ_PXA168_UART3 29 +#define IRQ_PXA168_PCIE_DMA 32 +#define IRQ_PXA168_PCIE_CORE 33 #define IRQ_PXA168_WDT 35 -#define IRQ_PXA168_MAIN_PMU 36 #define IRQ_PXA168_FRQ_CHANGE 38 -#define IRQ_PXA168_SDH1 39 -#define IRQ_PXA168_SDH2 40 +#define IRQ_PXA168_MMC 39 +#define IRQ_PXA168_MMC2 40 #define IRQ_PXA168_LCD 41 #define IRQ_PXA168_CI 42 #define IRQ_PXA168_USB1 44 @@ -48,7 +49,7 @@ #define IRQ_PXA168_USB2 51 #define IRQ_PXA168_AC97 57 #define IRQ_PXA168_TWSI1 58 -#define IRQ_PXA168_AP_PMU 60 +#define IRQ_PXA168_PMU 60 #define IRQ_PXA168_SM_INT 63 /* @@ -56,13 +57,14 @@ */ #define IRQ_PXA910_NONE (-1) #define IRQ_PXA910_AIRQ 0 -#define IRQ_PXA910_SSP3 1 -#define IRQ_PXA910_SSP2 2 -#define IRQ_PXA910_SSP1 3 +#define IRQ_PXA910_SSP2 1 +#define IRQ_PXA910_SSP1 2 +#define IRQ_PXA910_SSP0 3 #define IRQ_PXA910_PMIC_INT 4 #define IRQ_PXA910_RTC_INT 5 #define IRQ_PXA910_RTC_ALARM 6 #define IRQ_PXA910_TWSI0 7 +#define IRQ_PXA910_UART1 27 #define IRQ_PXA910_GPU 8 #define IRQ_PXA910_KEYPAD 9 #define IRQ_PXA910_ROTARY 10 @@ -71,9 +73,6 @@ #define IRQ_PXA910_AP1_TIMER1 13 #define IRQ_PXA910_AP1_TIMER2 14 #define IRQ_PXA910_AP1_TIMER3 15 -#define IRQ_PXA910_IPC_AP0 16 -#define IRQ_PXA910_IPC_AP1 17 -#define IRQ_PXA910_IPC_AP2 18 #define IRQ_PXA910_IPC_AP3 19 #define IRQ_PXA910_IPC_AP4 20 #define IRQ_PXA910_IPC_CP0 21 @@ -82,8 +81,7 @@ #define IRQ_PXA910_IPC_CP3 24 #define IRQ_PXA910_IPC_CP4 25 #define IRQ_PXA910_L2_DDR 26 -#define IRQ_PXA910_UART2 27 -#define IRQ_PXA910_UART3 28 +#define IRQ_PXA910_UART2 28 #define IRQ_PXA910_AP2_TIMER1 29 #define IRQ_PXA910_AP2_TIMER2 30 #define IRQ_PXA910_CP2_TIMER1 31 @@ -109,10 +107,16 @@ #define IRQ_PXA910_USB2 51 #define IRQ_PXA910_TWSI1 54 #define IRQ_PXA910_CP_GPIO 55 -#define IRQ_PXA910_UART1 59 /* Slow UART */ +#define IRQ_PXA910_UART3 59 #define IRQ_PXA910_AP_PMU 60 #define IRQ_PXA910_SM_INT 63 /* from PinMux */ +#ifdef CONFIG_CPU_PXA910 +#define IRQ_PXA910_IPC_AP_DATAACK 16 +#define IRQ_PXA910_IPC_AP_SET_CMD 17 +#define IRQ_PXA910_IPC_AP_SET_MSG 18 +#endif + /* * Interrupt numbers for MMP2 */ @@ -218,14 +222,17 @@ #define IRQ_MMP2_MUX_END (IRQ_MMP2_SSP_BASE + 2) -#define IRQ_GPIO_START 128 -#define IRQ_GPIO_NUM 192 +#define IRQ_GPIO_START 64 +#define IRQ_GPIO_NUM 128 #define IRQ_GPIO(x) (IRQ_GPIO_START + (x)) +#define IRQ_TO_GPIO(x) ((x) - IRQ_GPIO_START) /* Board IRQ - 64 by default, increase if not enough */ #define IRQ_BOARD_START (IRQ_GPIO_START + IRQ_GPIO_NUM) #define IRQ_BOARD_END (IRQ_BOARD_START + 64) -#define NR_IRQS (IRQ_BOARD_END) +#define NR_BOARD_IRQ 64 + +#define NR_IRQS (IRQ_GPIO_START + IRQ_GPIO_NUM + NR_BOARD_IRQ) #endif /* __ASM_MACH_IRQS_H */ diff --git a/arch/arm/mach-mmp/include/mach/max8660.h b/arch/arm/mach-mmp/include/mach/max8660.h new file mode 100644 index 00000000000000..7d8fad0b403f50 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/max8660.h @@ -0,0 +1,160 @@ +/* + * include/asm-arm/arch-pxa/max8660.h + * + * Copyright (C) 2009, Marvell Corporation. + * + * 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. + */ + +#ifndef _MAX8660_H_ +#define _MAX8660_H_ +#include +#include + +#define MAX8660ETL 1 +#undef MAX8660AETL + +#undef V1_SET1_IN +#define V1_SET1_UNCONNECTED 1 +#undef V1_SET1_GROUND + +#undef V2_SET2_IN +#undef V2_SET2_UNCONNECTED +#define V2_SET2_GROUND 1 + + +#define MAX8660_ID 0x34 +#define MAX8660_REG_NUM 0xb + +static const int max8660_reg_map[MAX8660_REG_NUM]={ + 0x10, 0x12, 0x20, + 0x23, 0x24, 0x29, + 0x2A, 0x32, 0x33, + 0x39, 0x80 +}; + + +/* definition for MAX8660 Register remapping */ +#define MAX8660_OVER1 0 /* the real address should be 0x10 */ +#define MAX8660_OVER2 1 /* the real address should be 0x12 */ +#define MAX8660_VCC1 2 /* the real address should be 0x20 */ +#define MAX8660_ADTV1 3 /* the real address should be 0x23 */ +#define MAX8660_ADTV2 4 /* the real address should be 0x24 */ +#define MAX8660_SDTV1 5 /* the real address should be 0x29 */ +#define MAX8660_SDTV2 6 /* the real address should be 0x2A */ +#define MAX8660_MDTV1 7 /* the real address should be 0x32 */ +#define MAX8660_MDTV2 8 /* the real address should be 0x33 */ +#define MAX8660_L12VCR 9 /* the real address should be 0x39 */ +#define MAX8660_FPWM 0xa /* the real address should be 0x80 */ + + +#define OVER1_EN3 1<<0 +#define OVER1_EN4 1<<2 +#define OVER2_EN6 1<<1 +#define OVER2_EN7 1<<2 + +#define VCC1_AGO 1<<0 +#define VCC1_AVS 1<<1 +#define VCC1_SGO 1<<4 +#define VCC1_SVS 1<<5 +#define VCC1_MGO 1<<6 +#define VCC1_MVS 1<<7 + +#define FPWM_FPWM1 1<<0 +#define FPWM_FPWM2 1<<1 +#define FPWM_FPWM3 1<<2 +#define FPWM_FPWM4 1<<3 +#define FPWM_ARD3 1<<6 +#define FPWM_ADR4 1<<7 + +enum { + MAX8660_V1 = 1, + MAX8660_V2, + MAX8660_V3, + MAX8660_V4, + MAX8660_V5, + MAX8660_V6, + MAX8660_V7, + MAX8660_V8, +}; + +#if defined(MAX8660ETL) + +#if defined(V1_SET1_IN) +#define MAX8660_V1_DEFAULT 3300 +#elif defined(V1_SET1_UNCONNECTED) +#define MAX8660_V1_DEFAULT 3000 +#elif defined(V1_SET1_GROUND) +#define MAX8660_V1_DEFAULT 2850 +#endif + +#if defined(V2_SET2_IN) +#define MAX8660_V2_DEFAULT 3300 +#elif defined(V2_SET2_UNCONNECTED) +#define MAX8660_V2_DEFAULT 2500 +#elif defined(V2_SET2_GROUND) +#define MAX8660_V2_DEFAULT 1800 +#endif + +#elif defined(MAX8660AETL) + +#if defined(V1_SET1_IN) +#define MAX8660_V1_DEFAULT 2500 +#elif defined(V1_SET1_UNCONNECTED) +#define MAX8660_V1_DEFAULT 2000 +#elif defined(V1_SET1_GROUND) +#define MAX8660_V1_DEFAULT 1800 +#endif + +#if defined(V2_SET2_IN) +#define MAX8660_V2_DEFAULT 2500 +#elif defined(V2_SET2_UNCONNECTED) +#define MAX8660_V2_DEFAULT 2000 +#elif defined(V2_SET2_GROUND) +#define MAX8660_V2_DEFAULT 1800 +#endif + +#endif + + +#define MAX8660_V3_BASE 725 +#define MAX8660_V3_STEP 25 +#define MAX8660_V3_MAX 1800 +#define MAX8660_V3_DEFAULT 1400 + +#define MAX8660_V4_BASE 725 +#define MAX8660_V4_STEP 25 +#define MAX8660_V4_MAX 1800 +#define MAX8660_V4_DEFAULT 1400 + +#define MAX8660_V5_BASE 1700 +#define MAX8660_V5_STEP 25 +#define MAX8660_V5_MAX 2000 +#define MAX8660_V5_DEFAULT 1800 + +#define MAX8660_V6_BASE 1800 +#define MAX8660_V6_STEP 100 +#define MAX8660_V6_MAX 3300 +#define MAX8660_V6_DEFAULT 1800 + +#define MAX8660_V7_BASE 1800 +#define MAX8660_V7_STEP 100 +#define MAX8660_V7_MAX 3300 +#define MAX8660_V7_DEFAULT 1800 + +#define MAX8660_V8_DEFAULT 3300 + +struct max8660_platform_data { + int (*init_irq)(void); + int (*ack_irq)(void); + void (*platform_init)(void); + spinlock_t lock; + struct work_struct work; + struct power_chip *power_chips; +}; + + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/memory.h b/arch/arm/mach-mmp/include/mach/memory.h index bdb21d70714cdf..20a82ba0006ff3 100644 --- a/arch/arm/mach-mmp/include/mach/memory.h +++ b/arch/arm/mach-mmp/include/mach/memory.h @@ -11,4 +11,9 @@ #define PHYS_OFFSET UL(0x00000000) +#if defined(CONFIG_CPU_L2_CACHE) +#define NET_IP_ALIGN 64 +#define ARCH_DMA_CACHE_ALIGNMENT 64 +#endif + #endif /* __ASM_MACH_MEMORY_H */ diff --git a/arch/arm/mach-mmp/include/mach/mfp-pxa168.h b/arch/arm/mach-mmp/include/mach/mfp-pxa168.h index ded43c455ec398..86c7add671305e 100644 --- a/arch/arm/mach-mmp/include/mach/mfp-pxa168.h +++ b/arch/arm/mach-mmp/include/mach/mfp-pxa168.h @@ -3,6 +3,13 @@ #include +/* + * IO type Normal FAST + * VERY_SLOW 1X 1X + * SLOW 1X 2X + * MEDIUM 2X 3X + * FAST 3X 4X + */ #define MFP_DRIVE_VERY_SLOW (0x0 << 13) #define MFP_DRIVE_SLOW (0x1 << 13) #define MFP_DRIVE_MEDIUM (0x2 << 13) @@ -192,10 +199,10 @@ #define GPIO31_CF_nIOIS16 MFP_CFG(GPIO31, AF3) #define GPIO32_CF_nCD1 MFP_CFG(GPIO32, AF3) #define GPIO33_CF_nCD2 MFP_CFG(GPIO33, AF3) +#define GPIO35_CF_INPACK MFP_CFG(GPIO35, AF3) +#define GPIO36_CF_nWAIT MFP_CFG(GPIO36, AF3) -/* UART */ -#define GPIO88_UART2_TXD MFP_CFG(GPIO88, AF2) -#define GPIO89_UART2_RXD MFP_CFG(GPIO89, AF2) +/* UART1 */ #define GPIO107_UART1_TXD MFP_CFG_DRV(GPIO107, AF1, FAST) #define GPIO107_UART1_RXD MFP_CFG_DRV(GPIO107, AF2, FAST) #define GPIO108_UART1_RXD MFP_CFG_DRV(GPIO108, AF1, FAST) @@ -209,19 +216,46 @@ #define GPIO112_UART1_DTR MFP_CFG(GPIO111, AF1) #define GPIO112_UART1_DCD MFP_CFG(GPIO112, AF2) +/* UART3 */ +#define GPIO98_UART_SOUT MFP_CFG(GPIO98, AF3) +#define GPIO99_UART_SIN MFP_CFG(GPIO99, AF3) +#define GPIO100_UART_RTS MFP_CFG(GPIO100, AF3) +#define GPIO101_UART_CTS MFP_CFG(GPIO101, AF3) + /* MMC1 */ -#define GPIO37_MMC1_DAT7 MFP_CFG(GPIO37, AF1) -#define GPIO38_MMC1_DAT6 MFP_CFG(GPIO38, AF1) -#define GPIO54_MMC1_DAT5 MFP_CFG(GPIO54, AF1) -#define GPIO48_MMC1_DAT4 MFP_CFG(GPIO48, AF1) -#define GPIO51_MMC1_DAT3 MFP_CFG(GPIO51, AF1) -#define GPIO52_MMC1_DAT2 MFP_CFG(GPIO52, AF1) -#define GPIO40_MMC1_DAT1 MFP_CFG(GPIO40, AF1) -#define GPIO41_MMC1_DAT0 MFP_CFG(GPIO41, AF1) -#define GPIO49_MMC1_CMD MFP_CFG(GPIO49, AF1) -#define GPIO43_MMC1_CLK MFP_CFG(GPIO43, AF1) -#define GPIO53_MMC1_CD MFP_CFG(GPIO53, AF1) -#define GPIO46_MMC1_WP MFP_CFG(GPIO46, AF1) +#define GPIO37_MMC1_DAT7 MFP_CFG_DRV(GPIO37, AF1, FAST) +#define GPIO38_MMC1_DAT6 MFP_CFG_DRV(GPIO38, AF1, FAST) +#define GPIO54_MMC1_DAT5 MFP_CFG_DRV(GPIO54, AF1, FAST) +#define GPIO48_MMC1_DAT4 MFP_CFG_DRV(GPIO48, AF1, FAST) +#define GPIO51_MMC1_DAT3 MFP_CFG_DRV(GPIO51, AF1, FAST) +#define GPIO52_MMC1_DAT2 MFP_CFG_DRV(GPIO52, AF1, FAST) +#define GPIO40_MMC1_DAT1 MFP_CFG_DRV(GPIO40, AF1, FAST) +#define GPIO41_MMC1_DAT0 MFP_CFG_DRV(GPIO41, AF1, FAST) +#define GPIO49_MMC1_CMD MFP_CFG_DRV(GPIO49, AF1, FAST) +#define GPIO43_MMC1_CLK MFP_CFG_DRV(GPIO43, AF1, FAST) +#define GPIO53_MMC1_CD MFP_CFG_DRV(GPIO53, AF1, FAST) +#define GPIO46_MMC1_WP MFP_CFG_DRV(GPIO46, AF1, FAST) + +/*MMC2*/ +#define GPIO90_MMC2_DAT3 MFP_CFG_DRV(GPIO90, AF1, FAST) +#define GPIO91_MMC2_DAT2 MFP_CFG_DRV(GPIO91, AF1, FAST) +#define GPIO92_MMC2_DAT1 MFP_CFG_DRV(GPIO92, AF1, FAST) +#define GPIO93_MMC2_DAT0 MFP_CFG_DRV(GPIO93, AF1, FAST) +#define GPIO94_MMC2_CMD MFP_CFG_DRV(GPIO94, AF1, FAST) +#define GPIO95_MMC2_CLK MFP_CFG_DRV(GPIO95, AF1, FAST) + +/* MMC3*/ +#define GPIO0_MMC3_DAT7 MFP_CFG_DRV_PULL(GPIO0, AF6, FAST, HIGH) +#define GPIO1_MMC3_DAT6 MFP_CFG_DRV_PULL(GPIO1, AF6, FAST, HIGH) +#define GPIO2_MMC3_DAT5 MFP_CFG_DRV_PULL(GPIO2, AF6, FAST, HIGH) +#define GPIO3_MMC3_DAT4 MFP_CFG_DRV_PULL(GPIO3, AF6, FAST, HIGH) +#define GPIO4_MMC3_DAT3 MFP_CFG_DRV_PULL(GPIO4, AF6, FAST, HIGH) +#define GPIO5_MMC3_DAT2 MFP_CFG_DRV_PULL(GPIO5, AF6, FAST, HIGH) +#define GPIO6_MMC3_DAT1 MFP_CFG_DRV_PULL(GPIO6, AF6, FAST, HIGH) +#define GPIO7_MMC3_DAT0 MFP_CFG_DRV_PULL(GPIO7, AF6, FAST, HIGH) +#define GPIO8_MMC3_CLK MFP_CFG_DRV(GPIO8, AF6, FAST) +#define GPIO9_MMC3_CMD MFP_CFG_DRV_PULL(GPIO9, AF6, FAST, HIGH) +#define GPIO16_SMC_nCS0_DIS MFP_CFG_DRV_PULL(GPIO16, AF0, FAST, HIGH) /* LCD */ #define GPIO84_LCD_CS MFP_CFG(GPIO84, AF1) @@ -266,6 +300,112 @@ #define GPIO116_I2S_RXD MFP_CFG(GPIO116,AF2) #define GPIO117_I2S_TXD MFP_CFG(GPIO117,AF2) +/* SPI NOR */ +#define GPIO107_SPI_NOR_RXD MFP_CFG(GPIO107, AF4) +#define GPIO108_SPI_NOR_TXD MFP_CFG(GPIO108, AF4) +#define GPIO109_SPI_NOR_SYSCLK MFP_CFG(GPIO109, AF4) +#define GPIO111_SPI_NOR_CLK MFP_CFG(GPIO111, AF4) +#define GPIO112_SPI_NOR_FRM MFP_CFG(GPIO112, AF4) + +/* Keypad */ +#define GPIO37_KP_MKIN4 MFP_CFG(GPIO37, AF6) +#define GPIO38_KP_MKIN5 MFP_CFG(GPIO38, AF6) +#define GPIO39_KP_MKIN6 MFP_CFG(GPIO39, AF6) +#define GPIO40_KP_MKIN0 MFP_CFG(GPIO40, AF6) +#define GPIO41_KP_MKIN1 MFP_CFG(GPIO41, AF6) +#define GPIO42_KP_MKIN7 MFP_CFG(GPIO42, AF6) +#define GPIO43_KP_MKIN2 MFP_CFG(GPIO43, AF6) +#define GPIO44_KP_MKIN3 MFP_CFG(GPIO44, AF6) +#define GPIO107_KP_MKIN3 MFP_CFG(GPIO107, AF7) +#define GPIO108_KP_MKIN2 MFP_CFG(GPIO108, AF7) +#define GPIO109_KP_MKIN1 MFP_CFG(GPIO109, AF7) +#define GPIO110_KP_MKIN0 MFP_CFG(GPIO110, AF7) +#define GPIO118_KP_MKIN7 MFP_CFG(GPIO118, AF7) +#define GPIO119_KP_MKIN6 MFP_CFG(GPIO119, AF7) +#define GPIO120_KP_MKIN5 MFP_CFG(GPIO120, AF7) +#define GPIO121_KP_MKIN4 MFP_CFG(GPIO121, AF7) +#define GPIO104_KP_MKOUT5 MFP_CFG(GPIO104, AF6) +#define GPIO105_KP_MKOUT4 MFP_CFG(GPIO105, AF6) +#define GPIO106_KP_MKOUT3 MFP_CFG(GPIO106, AF6) +#define GPIO37_KP_MKOUT0 MFP_CFG(GPIO37, AF3) +#define GPIO38_KP_MKOUT1 MFP_CFG(GPIO38, AF3) +#define GPIO39_KP_MKOUT2 MFP_CFG(GPIO39, AF3) +#define GPIO40_KP_MKOUT3 MFP_CFG(GPIO40, AF3) +#define GPIO46_KP_MKOUT0 MFP_CFG(GPIO46, AF6) +#define GPIO49_KP_MKOUT1 MFP_CFG(GPIO49, AF6) +#define GPIO50_KP_MKOUT2 MFP_CFG(GPIO50, AF6) +#define GPIO51_KP_MKOUT3 MFP_CFG(GPIO51, AF6) +#define GPIO52_KP_MKOUT4 MFP_CFG(GPIO52, AF6) +#define GPIO53_KP_MKOUT6 MFP_CFG(GPIO53, AF6) +#define GPIO54_KP_MKOUT7 MFP_CFG(GPIO54, AF6) +#define GPIO55_KP_MKOUT5 MFP_CFG(GPIO55, AF6) +#define GPIO111_KP_MKOUT7 MFP_CFG(GPIO111, AF7) +#define GPIO112_KP_MKOUT6 MFP_CFG(GPIO112, AF7) +#define GPIO37_KP_DKIN0 MFP_CFG(GPIO37, AF7) +#define GPIO38_KP_DKIN1 MFP_CFG(GPIO38, AF7) +#define GPIO39_KP_DKIN2 MFP_CFG(GPIO39, AF7) +#define GPIO40_KP_DKIN0 MFP_CFG(GPIO40, AF7) +#define GPIO41_KP_DKIN1 MFP_CFG(GPIO41, AF7) +#define GPIO42_KP_DKIN3 MFP_CFG(GPIO42, AF7) +#define GPIO43_KP_DKIN2 MFP_CFG(GPIO43, AF7) +#define GPIO44_KP_DKIN3 MFP_CFG(GPIO44, AF7) +#define GPIO45_KP_DKIN4 MFP_CFG(GPIO45, AF7) +#define GPIO46_KP_DKIN5 MFP_CFG(GPIO46, AF7) +#define GPIO47_KP_DKIN6 MFP_CFG(GPIO47, AF7) +#define GPIO48_KP_DKIN7 MFP_CFG(GPIO48, AF7) +#define GPIO29_KP_DKIN0 MFP_CFG(GPIO29, AF7) +#define GPIO30_KP_DKIN1 MFP_CFG(GPIO30, AF7) +#define GPIO31_KP_DKIN2 MFP_CFG(GPIO31, AF7) +#define GPIO32_KP_DKIN3 MFP_CFG(GPIO32, AF7) +#define GPIO33_KP_DKIN4 MFP_CFG(GPIO33, AF7) +#define GPIO34_KP_DKIN5 MFP_CFG(GPIO34, AF7) +#define GPIO35_KP_DKIN6 MFP_CFG(GPIO35, AF7) +#define GPIO36_KP_DKIN7 MFP_CFG(GPIO36, AF7) + +/* MFU */ +#define GPIO86_TX_CLK MFP_CFG(GPIO86, AF5) +#define GPIO87_TX_EN MFP_CFG(GPIO87, AF5) +#define GPIO88_TX_DQ3 MFP_CFG(GPIO88, AF5) +#define GPIO89_TX_DQ2 MFP_CFG(GPIO89, AF5) +#define GPIO90_TX_DQ1 MFP_CFG(GPIO90, AF5) +#define GPIO91_TX_DQ0 MFP_CFG(GPIO91, AF5) +#define GPIO92_MII_CRS MFP_CFG(GPIO92, AF5) +#define GPIO93_MII_COL MFP_CFG(GPIO93, AF5) +#define GPIO94_RX_CLK MFP_CFG(GPIO94, AF5) +#define GPIO95_RX_ER MFP_CFG(GPIO95, AF5) +#define GPIO96_RX_DQ3 MFP_CFG(GPIO96, AF5) +#define GPIO97_RX_DQ2 MFP_CFG(GPIO97, AF5) +#define GPIO98_RX_DQ1 MFP_CFG(GPIO98, AF5) +#define GPIO99_RX_DQ0 MFP_CFG(GPIO99, AF5) +#define GPIO100_MII_MDC MFP_CFG(GPIO100, AF5) +#define GPIO101_MII_MDIO MFP_CFG(GPIO101, AF5) +#define GPIO103_RX_DV MFP_CFG(GPIO103, AF5) + +/* CAMERA */ +#define GPIO37_CAM_DAT7 MFP_CFG(GPIO37, AF4) +#define GPIO38_CAM_DAT6 MFP_CFG(GPIO38, AF4) +#define GPIO39_CAM_DAT5 MFP_CFG(GPIO39, AF4) +#define GPIO40_CAM_DAT4 MFP_CFG(GPIO40, AF4) +#define GPIO41_CAM_DAT3 MFP_CFG(GPIO41, AF4) +#define GPIO42_CAM_DAT2 MFP_CFG(GPIO42, AF4) +#define GPIO43_CAM_DAT1 MFP_CFG(GPIO43, AF4) +#define GPIO44_CAM_DAT0 MFP_CFG(GPIO44, AF4) + +#define GPIO46_CAM_VSYNC MFP_CFG(GPIO46, AF4) +#define GPIO48_CAM_HSYNC MFP_CFG(GPIO48, AF4) +#define GPIO54_CAM_MCLK MFP_CFG(GPIO54, AF4) +#define GPIO55_CAM_PCLK MFP_CFG(GPIO55, AF4) + +/* MSPRO */ +#define GPIO40_MSP_DAT1 MFP_CFG(GPIO40, AF2) +#define GPIO41_MSP_DAT0 MFP_CFG(GPIO41, AF2) +#define GPIO43_MSP_DAT2 MFP_CFG(GPIO43, AF2) +#define GPIO44_MSP_DAT3 MFP_CFG(GPIO44, AF2) +#define GPIO42_MSP_BS MFP_CFG(GPIO42, AF3) +#define GPIO50_MSP_SCLK MFP_CFG(GPIO50, AF3) + +#define GPIO84_MSP_CD MFP_CFG(GPIO84, AF0) + /* PWM */ #define GPIO96_PWM3_OUT MFP_CFG(GPIO96, AF1) #define GPIO97_PWM2_OUT MFP_CFG(GPIO97, AF1) diff --git a/arch/arm/mach-mmp/include/mach/mfp-pxa910.h b/arch/arm/mach-mmp/include/mach/mfp-pxa910.h index 7e8a80f25ddcfc..dcdc481e01925c 100644 --- a/arch/arm/mach-mmp/include/mach/mfp-pxa910.h +++ b/arch/arm/mach-mmp/include/mach/mfp-pxa910.h @@ -8,17 +8,28 @@ #define MFP_DRIVE_MEDIUM (0x4 << 13) #define MFP_DRIVE_FAST (0x8 << 13) +/* UART1 */ +#define GPIO43_UART1_RXD MFP_CFG(GPIO43, AF7) +#define GPIO44_UART1_TXD MFP_CFG(GPIO44, AF7) +#define GPIO51_UART1_RXD MFP_CFG(GPIO51, AF1) +#define GPIO52_UART1_TXD MFP_CFG(GPIO52, AF1) + /* UART2 */ #define GPIO47_UART2_RXD MFP_CFG(GPIO47, AF6) #define GPIO48_UART2_TXD MFP_CFG(GPIO48, AF6) /* UART3 */ -#define GPIO31_UART3_RXD MFP_CFG(GPIO31, AF4) -#define GPIO32_UART3_TXD MFP_CFG(GPIO32, AF4) +#define GPIO29_UART3_CTS MFP_CFG(GPIO29, AF4) +#define GPIO30_UART3_RTS MFP_CFG(GPIO30, AF4) +#define GPIO31_UART3_TXD MFP_CFG(GPIO31, AF4) +#define GPIO32_UART3_RXD MFP_CFG(GPIO32, AF4) /*IRDA*/ #define GPIO51_IRDA_SHDN MFP_CFG(GPIO51, AF0) +/* USB ID */ +#define GPIO44_USB_ID MFP_CFG(GPIO44, AF0) + /* SMC */ #define SM_nCS0_nCS0 MFP_CFG(SM_nCS0, AF0) #define SM_ADV_SM_ADV MFP_CFG(SM_ADV, AF0) @@ -27,6 +38,13 @@ #define SM_BE0_SM_BE0 MFP_CFG(SM_BE0, AF1) #define SM_BE1_SM_BE1 MFP_CFG(SM_BE1, AF1) +/* ETH */ +#define GPIO13_GPIO13 MFP_CFG(GPIO13, AF0) + +#define GPIO19_FM_RDS_IRQ MFP_CFG(GPIO19, AF0) +#define GPIO20_FM_RESET MFP_CFG(GPIO20, AF0) + + /* I2C */ #define GPIO53_CI2C_SCL MFP_CFG(GPIO53, AF2) #define GPIO54_CI2C_SDA MFP_CFG(GPIO54, AF2) @@ -116,6 +134,11 @@ #define GPIO108_LCD_DD22 MFP_CFG(GPIO108, AF1) #define GPIO109_LCD_DD23 MFP_CFG(GPIO109, AF1) +#define GPIO43_LCD_SCL MFP_CFG(GPIO43, AF6) +#define GPIO44_LCD_CS1 MFP_CFG(GPIO44, AF6) +#define GPIO45_LCD_SDI MFP_CFG(GPIO45, AF6) +#define GPIO46_LCD_SDO MFP_CFG(GPIO46, AF6) + #define GPIO104_LCD_SPIDOUT MFP_CFG(GPIO104, AF3) #define GPIO105_LCD_SPIDIN MFP_CFG(GPIO105, AF3) #define GPIO107_LCD_CS1 MFP_CFG(GPIO107, AF3) @@ -144,21 +167,75 @@ #define GPIO76_CAM_VSYNC MFP_CFG_DRV(GPIO76, AF1, MEDIUM) #define GPIO77_CAM_MCLK MFP_CFG_DRV(GPIO77, AF1, MEDIUM) #define GPIO78_CAM_PCLK MFP_CFG_DRV(GPIO78, AF1, MEDIUM) +#define GPIO16_CAM_PWR_SUB MFP_CFG_DRV(GPIO16, AF0, MEDIUM) +#define GPIO18_CAM_RESET_SUB MFP_CFG_DRV(GPIO18, AF0, MEDIUM) +#define GPIO15_CAM_PWR_MAIN MFP_CFG_DRV(GPIO15, AF0, MEDIUM) +#define GPIO17_CAM_RESET_MAIN MFP_CFG_DRV(GPIO17, AF0, MEDIUM) +#define GPIO49_CAM_AFEN MFP_CFG_DRV(GPIO49, AF0, MEDIUM) /* MMC1 */ -#define MMC1_DAT7_MMC1_DAT7 MFP_CFG_DRV(MMC1_DAT7, AF0, MEDIUM) -#define MMC1_DAT6_MMC1_DAT6 MFP_CFG_DRV(MMC1_DAT6, AF0, MEDIUM) -#define MMC1_DAT5_MMC1_DAT5 MFP_CFG_DRV(MMC1_DAT5, AF0, MEDIUM) -#define MMC1_DAT4_MMC1_DAT4 MFP_CFG_DRV(MMC1_DAT4, AF0, MEDIUM) -#define MMC1_DAT3_MMC1_DAT3 MFP_CFG_DRV(MMC1_DAT3, AF0, MEDIUM) -#define MMC1_DAT2_MMC1_DAT2 MFP_CFG_DRV(MMC1_DAT2, AF0, MEDIUM) -#define MMC1_DAT1_MMC1_DAT1 MFP_CFG_DRV(MMC1_DAT1, AF0, MEDIUM) -#define MMC1_DAT0_MMC1_DAT0 MFP_CFG_DRV(MMC1_DAT0, AF0, MEDIUM) -#define MMC1_CMD_MMC1_CMD MFP_CFG_DRV(MMC1_CMD, AF0, MEDIUM) +#define MMC1_DAT7_MMC1_DAT7 MFP_CFG_PULL_HIGH(MMC1_DAT7, AF0) +#define MMC1_DAT6_MMC1_DAT6 MFP_CFG_PULL_HIGH(MMC1_DAT6, AF0) +#define MMC1_DAT5_MMC1_DAT5 MFP_CFG_PULL_HIGH(MMC1_DAT5, AF0) +#define MMC1_DAT4_MMC1_DAT4 MFP_CFG_PULL_HIGH(MMC1_DAT4, AF0) +#define MMC1_DAT3_MMC1_DAT3 MFP_CFG_PULL_HIGH(MMC1_DAT3, AF0) +#define MMC1_DAT2_MMC1_DAT2 MFP_CFG_PULL_HIGH(MMC1_DAT2, AF0) +#define MMC1_DAT1_MMC1_DAT1 MFP_CFG_PULL_HIGH(MMC1_DAT1, AF0) +#define MMC1_DAT0_MMC1_DAT0 MFP_CFG_PULL_HIGH(MMC1_DAT0, AF0) +#define MMC1_CMD_MMC1_CMD MFP_CFG_PULL_HIGH(MMC1_CMD, AF0) #define MMC1_CLK_MMC1_CLK MFP_CFG_DRV(MMC1_CLK, AF0, MEDIUM) #define MMC1_CD_MMC1_CD MFP_CFG_DRV(MMC1_CD, AF0, MEDIUM) #define MMC1_WP_MMC1_WP MFP_CFG_DRV(MMC1_WP, AF0, MEDIUM) + +/* MMC2 */ +#define MMC2_DAT3_GPIO_37 MFP_CFG_DRV(GPIO37, AF1, MEDIUM) +#define MMC2_DAT2_GPIO_38 MFP_CFG_DRV(GPIO38, AF1, MEDIUM) +#define MMC2_DAT1_GPIO_39 MFP_CFG_DRV(GPIO39, AF1, MEDIUM) +#define MMC2_DAT0_GPIO_40 MFP_CFG_DRV(GPIO40, AF1, MEDIUM) +#define MMC2_CMD_GPIO_41 MFP_CFG_DRV(GPIO41, AF1, MEDIUM) +#define MMC2_CLK_GPIO_42 MFP_CFG_DRV(GPIO42, AF1, MEDIUM) + +/* wlan */ +#define WLAN_PD_GPIO_14 MFP_CFG(GPIO14, AF0) +#define WLAN_RESET_GPIO_20 MFP_CFG(GPIO20, AF0) +#define WLAN_BT_RESET_GPIO_34 MFP_CFG(GPIO34, AF0) +#define WLAN_MAC_WAKEUP_GPIO_35 MFP_CFG(GPIO35, AF0) +#define WLAN_LHC_GPIO_36 MFP_CFG(GPIO36, AF0) + +/*GPIO interupt*/ +#define GPIO0_GPIO0 MFP_CFG(GPIO0, AF0) +#define GPIO1_GPIO1 MFP_CFG(GPIO1, AF0) +#define GPIO2_GPIO2 MFP_CFG(GPIO2, AF0) +#define GPIO3_GPIO3 MFP_CFG(GPIO3, AF0) +#define GPIO4_GPIO4 MFP_CFG(GPIO4, AF0) +#define GPIO5_GPIO5 MFP_CFG(GPIO5, AF0) +#define GPIO6_GPIO6 MFP_CFG(GPIO6, AF0) +#define GPIO7_GPIO7 MFP_CFG(GPIO7, AF0) +#define GPIO8_GPIO8 MFP_CFG(GPIO8, AF0) +#define GPIO9_GPIO9 MFP_CFG(GPIO9, AF0) +#define GPIO15_GPIO15 MFP_CFG(GPIO15, AF0) +#define GPIO16_GPIO16 MFP_CFG(GPIO16, AF0) +#define GPIO43_GPIO43 MFP_CFG(GPIO43, AF0) +#define GPIO44_GPIO44 MFP_CFG(GPIO44, AF0) +#define GPIO45_GPIO45 MFP_CFG(GPIO45, AF0) +#define GPIO20_GPIO20 MFP_CFG(GPIO20, AF0) +#define GPIO105_GPIO105 MFP_CFG(GPIO105, AF0) +#define GPIO79_GPIO79 MFP_CFG(GPIO79, AF0) +#define GPIO46_GPIO46 MFP_CFG(GPIO46, AF0) +#define GPIO53_GPIO53 MFP_CFG(GPIO53, AF0) +#define GPIO54_GPIO54 MFP_CFG(GPIO54, AF0) +#define SM_BE0_GPIO126 MFP_CFG(SM_BE0, AF0) +#define GPIO49_GPIO49 MFP_CFG(GPIO49, AF0) +#define GPIO50_GPIO50 MFP_CFG(GPIO50, AF0) +#define GPIO81_GPIO81 MFP_CFG(GPIO81, AF0) +#define GPIO82_GPIO82 MFP_CFG(GPIO82, AF0) +#define GPIO84_GPIO84 MFP_CFG(GPIO84, AF0) +#define GPIO44_GPIO44 MFP_CFG(GPIO44, AF0) +#define GPIO124_GPIO124 MFP_CFG(GPIO124, AF0) + + + /* PWM */ #define GPIO27_PWM3_AF2 MFP_CFG(GPIO27, AF2) #define GPIO51_PWM2_OUT MFP_CFG(GPIO51, AF2) diff --git a/arch/arm/mach-mmp/include/mach/mfp-teton_bga.h b/arch/arm/mach-mmp/include/mach/mfp-teton_bga.h new file mode 100644 index 00000000000000..72cbc3fb1757ec --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/mfp-teton_bga.h @@ -0,0 +1,31 @@ +#ifndef __ASM_MACH_MFP_TETON_BGA_H +#define __ASM_MACH_MFP_PXA168_H + +#include +#include + +/*MMC Enable*/ +#define GPIO27_MMC1_EN MFP_CFG(GPIO27, AF5) + +/*MMC2*/ +#define GPIO117_MMC2_CMD MFP_CFG(GPIO117, AF4) +#define GPIO118_MMC2_CLK MFP_CFG(GPIO118, AF4) +#define GPIO119_MMC2_DAT0 MFP_CFG(GPIO119, AF4) +#define GPIO120_MMC2_DAT1 MFP_CFG(GPIO120, AF4) +#define GPIO121_MMC2_DAT2 MFP_CFG(GPIO121, AF4) +#define GPIO122_MMC2_DAT3 MFP_CFG(GPIO122, AF4) + +/*I2S*/ +#define GPIO116_I2S_TXD MFP_CFG(GPIO116, AF1) + +/*MSPRO*/ +#define GPIO42_MSP_BS MFP_CFG(GPIO42, AF3) +#define GPIO44_MSP_DAT1 MFP_CFG(GPIO44, AF3) +#define GPIO45_MSP_DAT0 MFP_CFG(GPIO45, AF3) +#define GPIO46_MSP_DAT2 MFP_CFG(GPIO46, AF3) +#define GPIO48_MSP_DAT3 MFP_CFG(GPIO48, AF3) +#define GPIO50_MSP_SCLK MFP_CFG(GPIO50, AF3) + +#define GPIO47_MSP_INS MFP_CFG(GPIO47, AF0) + +#endif /* __ASM_MACH_MFP_TETON_BGA_H */ diff --git a/arch/arm/mach-mmp/include/mach/mfp.h b/arch/arm/mach-mmp/include/mach/mfp.h index 62e510e80a5809..6a9d743ffa0531 100644 --- a/arch/arm/mach-mmp/include/mach/mfp.h +++ b/arch/arm/mach-mmp/include/mach/mfp.h @@ -31,4 +31,16 @@ #define MFP_CFG_DRV(pin, af, drv) \ (MFP_LPM_FLOAT | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_##drv) +#define MFP_CFG_PULL(pin, af, pull) \ + (MFP_LPM_FLOAT | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_MEDIUM | MFP_PULL_##pull) + +#define MFP_CFG_DRV_PULL(pin, af, drv, pull) \ + (MFP_LPM_FLOAT | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_##drv | MFP_LPM_PULL_##pull) + +#define MFP_CFG_PULL_HIGH(pin, af) \ + (MFP_LPM_PULL_HIGH | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_MEDIUM) + +#define MFP_CFG_PULL_LOW(pin, af) \ + (MFP_LPM_PULL_LOW | MFP_PIN(MFP_PIN_##pin) | MFP_##af | MFP_DRIVE_MEDIUM) + #endif /* __ASM_MACH_MFP_H */ diff --git a/arch/arm/mach-mmp/include/mach/micco.h b/arch/arm/mach-mmp/include/mach/micco.h new file mode 100644 index 00000000000000..58e670d04d15ee --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/micco.h @@ -0,0 +1,462 @@ +/* + * + * Copyright (C) 2006, Marvell Corporation. + * + * 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. + */ + +#ifndef __MACH_MICCO_H +#define __MACH_MICCO_H +#include + +#define MICCO_ADDRESS 0x34 + +#define MICCO_CHIP_ID 0x00 +#define MICCO_EVENT_A 0x01 +#define MICCO_EA_ONKEY (1) +#define MICCO_EA_EXTON (1 << 2) +#define MICCO_EA_CHDET (1 << 3) +#define MICCO_EA_TBAT (1 << 4) +#define MICCO_EA_VBATMON (1 << 5) +#define MICCO_EA_REV_IOVER (1 << 6) +#define MICCO_EA_IOVER (1 << 7) + +#define MICCO_EVENT_B 0x02 +#define MICCO_EB_CH_TCTO (1 << 0) +#define MICCO_EB_CH_CCTO (1 << 1) +#define MICCO_EB_USB_DEV (1 << 2) +#define MICCO_EB_OTGCP_IOVER (1 << 3) +#define MICCO_EB_VBUS_4P55 (1 << 4) +#define MICCO_EB_VBUS_3P8 (1 << 5) +#define MICCO_EB_SESSION_1P8 (1 << 6) +#define MICCO_EB_SRP_READY (1 << 7) + +#define MICCO_EVENT_C 0x03 +#define MICCO_EC_PEN_DOWN (1 << 4) +#define MICCO_EC_TSI_READY (1 << 5) + +#define MICCO_EVENT_D 0x04 +#define MICCO_ED_HEADSET (1 << 3) +#define MICCO_ED_HOOKSWITCH (1 << 4) + +#define MICCO_STATUS_A 0x05 +#define MICCO_STATUS_A_CHDET (1 << 3) + +#define MICCO_STATUS_B 0x06 +#define MICCO_STATUS_B_USBDEV (1 << 0) +#define MICCO_STATUS_B_HEADSET (1 << 1) +#define MICCO_STATUS_B_HOOKSWITCH (1 << 2) +#define MICCO_STATUS_B_VBUS_V_4P4 (1 << 4) +#define MICCO_STATUS_B_VBUS_V_3P8 (1 << 5) +#define MICCO_STATUS_B_SESS_V_1P8 (1 << 6) +#define MICCO_STATUS_B_SRP_READY (1 << 7) + +#define MICCO_IRQ_MASK_A 0x07 +#define IRQ_MACK_A_ONKEY (1) +#define IRQ_MASK_A_EXTON (1 << 2) +#define IRQ_MASK_A_CHDET (1 << 3) +#define IRQ_MASK_A_TBAT (1 << 4) +#define IRQ_MASK_A_VBATMON (1 << 5) +#define IRQ_MASK_A_REV_IOVER (1 << 6) +#define IRQ_MASK_A_CH_IOVER (1 << 7) + +#define MICCO_IRQ_MASK_B 0x08 +#define IRQ_MASK_B_CH_TCTO (1) +#define IRQ_MASK_B_CH_CCTO (1 << 1) +#define IRQ_MASK_B_USB_DEV (1 << 2) +#define IRQ_MASK_B_OTGCP_IOVER (1 << 3) +#define IRQ_MASK_B_VBUS_VALID_4_55 (1 << 4) +#define IRQ_MASK_B_VBUS_VALID_3_8 (1 << 5) +#define IRQ_MASK_B_SESSION_VALID_1_8 (1 << 6) +#define IRQ_MASK_B_SRP_READY_0_6 (1 << 7) + + +#define MICCO_IRQ_MASK_C 0x09 +#define IRQ_MASK_C_ADC_MAN (1) +#define IRQ_MASK_C_ADC_AUTO4 (1 << 1) +#define IRQ_MASK_C_ADC_AUTO5 (1 << 2) +#define IRQ_MASK_C_ADC_AUTO6 (1 << 3) +#define IRQ_MASK_C_PEN_DOWN (1 << 4) +#define IRQ_MASK_C_TSI_READY (1 << 5) + +#define MICCO_IRQ_MASK_D 0x0A +#define IRQ_MASK_D_HOOKSWITCH (1 << 4) +#define IRQ_MASK_D_HEADSET (1 << 3) + +#define MICCO_SYSCTRL_A 0x0B +#define MICCO_SYSCTRL_B 0x0C + +#define MICCO_FAULT_LOG 0x0D + +#define MICCO_OVER1 0x10 +#define MICCO_APP_OVER2 0x11 +#define MICCO_APP_OVER3 0x12 +#define MICCO_LDO643 0x13 +#define MICCO_LDO987 0x14 +#define MICCO_LDO1110 0x15 +#define MICCO_LDO1312 0x16 +#define MICCO_LDO1514 0x17 + +#define MICCO_BUCK_SLEEP 0x18 +#define MICCO_LDO_nSLEEP1 0x19 +#define MICCO_LDO_nSLEEP2 0x1A +#define MICCO_LDO_SLEEP_BIT1 0x1B +#define MICCO_LDO_SLEEP_BIT2 0x1C + +#define MICCO_VCC1 0x20 +#define MICCO_COMM_OVER2 0x21 +#define MICCO_COMM_OVER3 0x22 +#define MICCO_BUCK1_DVC1 0x23 +#define MICCO_BUCK1_DVC2 0x24 +#define MICCO_APPS_AVRC 0x25 +#define MICCO_COMM_CDTV1 0x26 +#define MICCO_COMM_CDTV2 0x27 +#define MICCO_COMM_CVRC 0x28 +#define MICCO_SRAM_DVC1 0x29 +#define MICCO_SRAM_DVC2 0x2A +#define MICCO_SRAM_SVRC 0x2B + +#define MICCO_LDO1_MDTV1 0x32 +#define MICCO_LDO1_MDTV2 0x33 +#define MICCO_LDO1_MVRC 0x34 + +#define MICCO_LED1_CONTROL 0x35 +#define MICCO_LED2_CONTROL 0x36 +#define MICCO_LEDPC_CONTROL1 0x37 +#define MICCO_LEDPC_CONTROL2 0x38 +#define MICCO_LEDPC_CONTROL3 0x39 +#define MICCO_LEDPC_CONTROL4 0x3A +#define MICCO_LEDPC_CONTROL5 0x3B + +#define MICCO_WLED_CONTROL1 0x3C +#define MICCO_WLED_CONTROL2 0x3D +#define MICCO_FLASH_CONTROL1 0x3E +#define MICCO_FLASH_CONTROL2 0x3F + +#define MICCO_VIBRA_CONTROL 0x40 + +#define MICCO_MISC 0x41 +#define MICCO_MISC_VBUS_COMPS_EN (1 << 7) +#define MICCO_MISC_USBSR_EN (1 << 6) +#define MICCO_MISC_USBCP_EN (1 << 5) +#define MICCO_MISC_I_TBAT_ON (1 << 4) +#define MICCO_MISC_REMCON_FILTER (1 << 2) +#define MICCO_MISC_REMCON_ENABLE (1 << 1) +#define MICCO_MISC_REMCON_AUTO (1 << 0) + +#define MICCO_CHARGE_CONTROL 0x42 + +#define MICCO_DAC_MSB 0x46 +#define MICCO_DAC_LSB 0x47 + +#define MICCO_ADC_MAN_CONTROL 0x50 +#define MICCO_ADC_MAN_CONT_LDOADC_EN (1 << 4) +#define MICCO_ADC_MAN_CONTROL_IDBAT_EN (1 << 5) +#define MICCO_ADC_MAN_CONT_CONV (1 << 3) + +#define MICCO_ADC_AUTO_CONTROL_1 0x51 +#define MICCO_ADC_AUTO_1_AUTOADC_SLEEP_EN (1) +#define MICCO_ADC_AUTO_1_VBAT_EN (1 << 1) +#define MICCO_ADC_AUTO_1_ICH_EN (1 << 2) +#define MICCO_ADC_AUTO_1_VBAT_EXTRA_EN (1 << 4) +#define MICCO_ADC_AUTO_1_TBAT_EN (1 << 5) +#define MICCO_ADC_AUTO_1_DEB_VBAT_MON (1 << 6) + +#define MICCO_ADC_AUTO_CONTROL_2 0x52 +#define MICCO_ADC_AUTO_2_ADC4_EN (1) +#define MICCO_ADC_AUTO_2_ADC5_EN (1 << 1) +#define MICCO_ADC_AUTO_2_ADC6_EN (1 << 2) +#define MICCO_ADC_AUTO_2_PENDET_EN (1 << 4) +#define MICCO_ADC_AUTO_2_TSI_EN (1 << 3) + + + +#define MICCO_TSI_CONTROL_1 0x53 +#define MICCO_TSI_CONTROL_2 0x54 + +#define MICCO_VBATMON 0x55 +#define MICCO_VBATHIGHP 0x56 +#define MICCO_TBATHIGHN 0x57 +#define MICCO_TBATLOW 0x58 +#define MICCO_AUTO4HIGH 0x59 +#define MICCO_AUTO4LOW 0x5A +#define MICCO_AUTO5HIGH 0x5B +#define MICCO_AUTO5LOW 0x5C +#define MICCO_AUTO6HIGH 0x5D +#define MICCO_AUTO6LOW 0x5E + +#define MICCO_MAN_RES_LSB 0x5F +#define MICCO_MAN_RES_MSB 0x60 +#define MICCO_VBAT_RES 0x61 +#define MICCO_VBATMIN_RES 0x62 +#define MICCO_ICHMAX_RES 0x63 +#define MICCO_ICHMIN_RES 0x64 +#define MICCO_ICHAVERAGE_RES 0x65 +#define MICCO_VCHMAX_RES 0x66 +#define MICCO_VCHMIN_RES 0x67 +#define MICCO_TBAT_RES 0x68 +#define MICCO_AUTO4_RES 0x69 +#define MICCO_AUTO5_RES 0x6A +#define MICCO_AUTO6_RES 0x6B + +/* Micco TSI registers */ +#define MICCO_TSI_X_MSB 0x6C +#define MICCO_TSI_Y_MSB 0x6D +#define MICCO_TSI_XY_MSB 0x6E + +/* Micco Audio control registers */ +#define MICCO_AUDIO_REG_BASE 0x70 +#define MICCO_AUDIO_REGS_NUM 22 + +#define MICCO_MUX_MONO 0x0 +#define MICCO_MUX_BEAR 0x1 +#define MICCO_MUX_LINE_OUT 0x2 +#define MICCO_MUX_STEREO_CH1 0x3 +#define MICCO_MUX_STEREO_CH2 0x4 + + +#define MICCO_AUDIO_LINE_AMP 0x5 +#define MICCO_AUDIO_LINE_AMP_EN (1 << 4) +#define LINE_OUT_MIN_VOL 0 +#define LINE_OUT_MAX_VOL 15 + +#define MICCO_STEREO_AMPLITUDE_CH1 0x6 +#define MICCO_STEREO_GAIN_SEPARATE (1 << 6) +#define MICCO_STEREO_EN (1 << 7) +#define STEREO_MIN_VOL 0 +#define STEREO_MAX_VOL 63 + +#define MICCO_STEREO_AMPLITUDE_CH2 0x7 +#define MICCO_STEREO_FAST_START (1 << 6) + +#define MICCO_HIFI_DAC_CONTROL 0x8 +#define MICCO_HIFI_DAC_ON (1 << 7) +#define MICCO_HIFI_HPF_BYPASS_2 (1 << 6) +#define MICCO_HIFI_HPF_BYPASS_1 (1 << 5) +#define MICCO_HIFI_DAC_MUTE2 (1 << 4) +#define MICCO_HIFI_DAC_MUTE1 (1 << 3) +#define MICCO_HIFI_DAC_INV_2 (1 << 2) +#define MICCO_HIFI_DAC_INV_1 (1 << 1) + +#define MICCO_MONO_VOL 0x9 +#define MICCO_MONO_FAST_START (1 << 7) +#define MICCO_MONO_EN (1 << 6) +#define MONO_MIN_VOL 0 +#define MONO_MAX_VOL 51 + + +#define MICCO_BEAR_VOL 0xA +#define MICCO_BEAR_FAST_START (1 << 7) +#define MICCO_BEAR_EN (1 << 6) +#define BEAR_MIN_VOL 0 +#define BEAR_MAX_VOL 51 + + +#define MICCO_I2S_CONTROL 0xB +#define MICCO_I2S_SYNC_OUTPUT (1 << 7) +#define MICCO_I2S_SRM_EN (1 << 6) +#define MICCO_I2S_DA_MASTER (1 << 5) +#define MICCO_I2S_MSB_JU_MODE (1 << 4) + + +#define MICCO_TX_PGA 0xC +#define MICCO_TX_PGA_DSP_0DB (1) + + +#define MICCO_MIC_PGA 0xD +#define MICCO_MIC_PGA_EXT_EN (1 << 6) +#define MICCO_MIC_PGA_INT_EN (1 << 5) +#define MICCO_MIC_PGA_SELMIC_2 (1 << 4) +#define MICCO_MIC_PGA_AMP_EN (1 << 3) +#define MIC_PGA_MIN_GAIN 0 +#define MIC_PGA_MAX_GAIN 7 + +#define MICCO_TX_PGA_MUX 0xE + +#define MICCO_VCODEC_ADC_CONTROL 0xF +#define MICCO_VCODEC_ADC_MUTE (1 << 7) +#define MICCO_VCODEC_ADC_ON_EN (1) + + +#define MICCO_VCODEC_VDAC_CONTROL 0x10 +#define MICCO_VDAC_PCM_LOOP (1 << 7) +#define MICCO_VDAC_PCM_SDOTRI (1 << 6) +#define MICCO_VDAC_ON (1 << 3) +#define MICCO_VDAC_HPF_BYPASS (1 << 2) +#define MICCO_VDAC_HPF_INV (1 << 1) +#define MICCO_VDAC_HPF_MUTE (1) + + +#define MICCO_SIDETONE 0x11 +#define MICCO_SIDETONE_EN (1 << 7) +#define MICCO_SIDETONE_GAIN_EN (1 << 6) +#define MICCO_SIDETONE_GAIN_STEREO (1 << 5) + +#define MICCO_PGA_AUX1_2 0x12 +#define MICCO_PGA_AUX1_EN (1 << 7) +#define MICCO_PGA_AUX2_EN (1 << 3) +#define AUX1_2_MIN_VOL 0 +#define AUX1_2_MAX_VOL 3 + + +#define MICCO_PGA_AUX3 0x13 +#define MICCO_PGA_AUX3_EN (1 << 3) +#define AUX3_MIN_VOL 0 +#define AUX3_MAX_VOL 3 + + +#define MICCO_PGA_DACS 0x14 +#define MICCO_SOFT_START_RAMP 0x15 +#define MICCO_REM_IN_POLLING_TIME 0x16 +#define PERIOD_125_MS 0 +#define PERIOD_250_MS 1 +#define PERIOD_500_MS 2 +#define PERIOD_1000_MS 3 + +#define MICCO_VBUCK1_BASE 725 +#define MICCO_VBUCK1_STEP 25 +#define MICCO_VBUCK1_MAX 1500 + +#define MICCO_VSRAM_BASE 725 +#define MICCO_VSRAM_STEP 25 +#define MICCO_VSRAM_MAX 1500 + +#define MICCO_VLDO1_BASE 1700 +#define MICCO_VLDO1_STEP 25 +#define MICCO_VLDO1_MAX 2075 + +#define MICCO_VLDO3_BASE 1800 +#define MICCO_VLDO3_STEP 100 +#define MICCO_VLDO3_MAX 3300 + +#define MICCO_VLDO6_BASE 2500 +#define MICCO_VLDO6_STEP 50 +#define MICCO_VLDO6_MAX 2850 + +#define MICCO_VLDO12_BASE 1700 +#define MICCO_VLDO12_BASE1 2700 +#define MICCO_VLDO12_STEP 50 +#define MICCO_VLDO12_MAX 3050 + +#define MICCO_VLDO13_BASE 1800 +#define MICCO_VLDO13_STEP 100 +#define MICCO_VLDO13_MAX 3300 + +#define MICCO_VLDO15_BASE 1800 +#define MICCO_VLDO15_STEP 100 +#define MICCO_VLDO15_MAX 3300 + +#define MICCO_VLDO14_BASE 1800 +#define MICCO_VLDO14_STEP 100 +#define MICCO_VLDO14_MAX 3300 + +#define MICCO_VLDO9_BASE 2700 +#define MICCO_VLDO9_STEP 50 +#define MICCO_VLDO9_MAX 3050 + +#define MICCO_VLDO10_BASE 2700 +#define MICCO_VLDO10_STEP 50 +#define MICCO_VLDO10_MAX 3050 + +#define MICCO_VLDO11_BASE 1800 +#define MICCO_VLDO11_STEP 100 +#define MICCO_VLDO11_MAX 3300 + + +/* Micco audio definition. TODO: move to micco codec + * driver. + */ + +#define MICCO_CODEC_INPUT_NUMBER 7 +enum { + CODEC_AUX1 = 0, + CODEC_AUX2, + CODEC_AUX3, + CODEC_MIC1, + CODEC_MIC2, + CODEC_PCM, + CODEC_HIFI, +}; + +#define MICCO_CODEC_OUPUT_NUMBER 5 +enum { + CODEC_BEAR = 0, + CODEC_MONO, + CODEC_STEREO, + CODEC_LINE_OUT, + CODEC_ADC, +}; + +enum { + MICCO_VOICE_PORT = 0, + MICCO_HIFI_PORT, +}; + +enum { + BUCK1 = 1, + BUCK2, + BUCK3, + LDO1, + LDO2, + LDO3, + LDO4, + LDO5, + LDO6, + LDO7, + LDO8, + LDO9, + LDO10, + LDO11, + LDO12, + LDO13, + LDO14, + LDO15, + USB_OTG, + LDO_GPADC, + LDO_AUDIO, + LDO_PMCORE, + LDO_BBAT, +}; + +#define MICCO_B0_ID 0x10 +#define MICCO_EA_ID 0x30 +#define MICCO_EB_ID 0x31 + +struct micco_platform_data { + int (*init_irq)(void); + int (*ack_irq)(void); + void (*platform_init)(void); + spinlock_t lock; + struct work_struct work; + struct power_chip *power_chips; +}; + +/* General */ +int micco_read(u8 reg, u8 *val); +int micco_write(u8 reg, u8 val); + +int micco_enable_pen_down_irq(int pen_en); +int micco_tsi_enable_pen(int pen_en); +int micco_tsi_enable_tsi(int tsi_en); +int micco_tsi_poweron(void); +int micco_tsi_poweroff(void); +int micco_tsi_readxy(u16 *x, u16 *y, int pen_state); + +/* For Audio */ +int micco_codec_read(u8 reg, u8 *val); +int micco_codec_write(u8 reg, u8 val); +int micco_codec_set_input_gain(int type, int gain); +int micco_codec_set_sample_rate(int port, int rate); +int micco_codec_enable_input(int type); +int micco_codec_disable_input(int type); +int micco_codec_enable_output(int type); +int micco_codec_disable_output(int type); +int micco_codec_enable_input(int type); +int micco_codec_disable_input(int type); +int micco_codec_set_output_vol(int type, int vol); +int micco_audio_init(void); + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/mmc.h b/arch/arm/mach-mmp/include/mach/mmc.h new file mode 100644 index 00000000000000..70f20c11c13893 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/mmc.h @@ -0,0 +1,69 @@ +#ifndef ASMARM_ARCH_MMC_H +#define ASMARM_ARCH_MMC_H + +#include +#include + +struct device; +struct mmc_host; + +struct pxasdh_platform_data { + unsigned int ocr_mask; /* available voltages */ + unsigned long detect_delay; /* delay in jiffies before detecting cards after interrupt */ + int (*init)(struct device *, irq_handler_t , void *); + int (*get_ro)(struct device *); + void (*setpower)(struct device *, unsigned int); + void (*exit)(struct device *, void *); + int (*mfp_config)(void); + struct pfn_cfg *pfn_table; + int (*get_cd)(struct device *); + unsigned int bus_width; + unsigned int max_speed; + + /* SD_CLOCK_AND_BURST_SIZE_SETUP register */ + unsigned int sd_clock; /* 1 for need to tuning sd clock */ + unsigned int sdclk_sel; + unsigned int sdclk_delay; + + unsigned int quirks; + unsigned int mrvl_quirks; + +#ifdef CONFIG_SD8XXX_RFKILL + /*for sd8688-rfkill device*/ + struct mmc_host **pmmc; +#endif +}; + +struct platform_mmc_slot { + int gpio_detect; /* 0 for controller detect, 1 for gpio detect */ + int no_wp; /* 0 for with write protect, 1 for without write protect */ + int gpio_cd; + int gpio_wp; +}; + +extern struct platform_mmc_slot pxa_mmc_slot[]; +extern int pxa_mci_ro(struct device *dev); +extern int pxa_mci_init(struct device *dev, irq_handler_t pxa_detect_int,void *data); +extern void pxa_mci_exit(struct device *dev, void *data); +extern int pxa_mci_get_cd(struct device *dev); + +/* Disable Free Running Clocks for SDIO */ +#define MRVL_QUIRK_SDIO_ENABLE_DYN_CLOCK_GATING (1<<0) + +/* pin enum */ +enum { + PIN_MMC_CMD, + PIN_MMC_CLK, + PIN_MMC_WP, + PIN_MMC_CD, + PIN_MMC_DAT0, + PIN_MMC_DAT1, + PIN_MMC_DAT2, + PIN_MMC_DAT3, + PIN_MMC_DAT4, + PIN_MMC_DAT5, + PIN_MMC_DAT6, + PIN_MMC_DAT7, + PIN_MMC_END +}; +#endif diff --git a/arch/arm/mach-mmp/include/mach/mspm_prof.h b/arch/arm/mach-mmp/include/mach/mspm_prof.h new file mode 100644 index 00000000000000..851225761fae18 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/mspm_prof.h @@ -0,0 +1,34 @@ +/* + * PXA Performance profiler and Idle profiler Routines + * + * Copyright (c) 2003 Intel Corporation. + * + * 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. + * + * (C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef MSPM_PROF_H +#define MSPM_PROF_H + +#ifdef __KERNEL__ + +#undef MAX_OP_NUM +#define MAX_OP_NUM 20 + +/* The minimum sample window is 20ms, the default window is 100ms */ +#define MIN_SAMPLE_WINDOW 20 +#define DEF_SAMPLE_WINDOW 100 + +#define DEF_HIGH_THRESHOLD 80 +#define DEF_LOW_THRESHOLD 20 + +extern int mspm_add_event(int op, int cpu_idle); +extern int mspm_prof_init(void); +extern void mspm_prof_exit(void); +#endif + +#endif diff --git a/arch/arm/mach-mmp/include/mach/nand_supported.h b/arch/arm/mach-mmp/include/mach/nand_supported.h new file mode 100644 index 00000000000000..e3879e1a53152d --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/nand_supported.h @@ -0,0 +1,398 @@ +#ifndef __MACH_NAND_SUPPORTED_H__ +#define __MACH_NAND_SUPPORTED_H__ + +static struct pxa3xx_nand_cmdset smallpage_cmdset = { + .read1 = 0x0000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_cmdset largepage_cmdset = { + .read1 = 0x3000, + .read2 = 0x0050, + .program = 0x1080, + .read_status = 0x0070, + .read_id = 0x0090, + .erase = 0xD060, + .reset = 0x00FF, + .lock = 0x002A, + .unlock = 0x2423, + .lock_status = 0x007A, +}; + +static struct pxa3xx_nand_timing common_timing = { + .tCH = 40, + .tCS = 80, + .tWH = 60, + .tWP = 100, + .tRH = 80, + .tRP = 100, + .tR = 90000, + .tWHR = 400, + .tAR = 40, +}; + +static struct pxa3xx_nand_timing samsung512MbX8_timing = { + .tCH = 10, + .tCS = 15, + .tWH = 20, + .tWP = 40, + .tRH = 30, + .tRP = 40, + .tR = 15000, + .tWHR = 110, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing samsung512MbX16_timing = { + .tCH = 10, + .tCS = 10, + .tWH = 20, + .tWP = 40, + .tRH = 30, + .tRP = 40, + .tR = 11123, + .tWHR = 110, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing samsung2GbX8_timing = { + .tCH = 10, + .tCS = 35, + .tWH = 15, + .tWP = 25, + .tRH = 20, + .tRP = 25, + .tR = 25000, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing samsung2GbX8_timing_fast = { + .tCH = 5, + .tCS = 10, + .tWH = 10, + .tWP = 0, + .tRH = 10, + .tRP = 12, + .tR = 25000, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing samsung8GbX8_timing = { + .tADL = 160, + .tCH = 5, + .tCS = 20, + .tWH = 10, + .tWP = 12, + .tRH = 10, + .tRP = 12, + .tR = 60000, + .tRHW = 20, + .tWHR = 80, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing samsung8GbX8_timing_fast = { + .tADL = 160, + .tCH = 5, + .tCS = 10, + .tWH = 10, + .tWP = 0, + .tRH = 10, + .tRP = 12, + .tR = 25000, + .tRHW = 20, + .tWHR = 80, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing samsung32GbX8_timing = { + .tADL = 200, + .tCH = 10, + .tCS = 15, + .tWH = 10, + .tWP = 12, + .tRH = 10, + .tRP = 8, + .tR = 60000, + .tRHW = 20, + .tWHR = 75, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing micron32GbX8_timing = { + .tADL = 200, + .tCH = 10, + .tCS = 35, + .tWH = 30, + .tWP = 50, + .tRH = 15, + .tRP = 50, + .tR = 50000, + .tRHW = 20, + .tWHR = 120, + .tAR = 25, +}; + +static struct pxa3xx_nand_timing micron_timing = { + .tADL = 200, + .tCH = 10, + .tCS = 25, + .tWH = 15, + .tWP = 25, + .tRH = 15, + .tRP = 25, + .tR = 25000, + .tRHW = 20, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_timing stm2GbX16_timing = { + .tADL = 200, + .tCH = 10, + .tCS = 35, + .tWH = 15, + .tWP = 25, + .tRH = 15, + .tRP = 25, + .tR = 25000, + .tRHW = 20, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_flash hynix4GbX16 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .name = "Hynix 4Gibx16", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 4096, + .chip_id = 0xbcad, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +struct pxa3xx_nand_flash nand_common = { + .timing = &common_timing, + .cmdset = &largepage_cmdset, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash samsung512MbX8 = { + .timing = &samsung512MbX8_timing, + .cmdset = &smallpage_cmdset, + .name = "Samsung 512Mibx8", + .page_per_block = 32, + .page_size = 512, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 4096, + .chip_id = 0x76ec, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash samsung512MbX16 = { + .timing = &samsung512MbX16_timing, + .cmdset = &smallpage_cmdset, + .name = "Samsung 512Mibx16", + .page_per_block = 32, + .page_size = 512, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 4096, + .chip_id = 0x46ec, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash samsung2GbX8 = { + .timing = &samsung2GbX8_timing, + .cmdset = &largepage_cmdset, + .name = "Samsung 2Gibx8", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 2048, + .chip_id = 0xdaec, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash samsung8GbX8 = { + .timing = &samsung8GbX8_timing, + .cmdset = &largepage_cmdset, + .name = "Samsung 8Gibx8", + .page_per_block = 128, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 4096, + .chip_id = 0xd3ec, + .chip_id_mask = 0xffff, + .ecc_type = ECC_BCH, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash samsung32GbX8 = { + .timing = &samsung32GbX8_timing, + .cmdset = &largepage_cmdset, + .name = "Samsung 32Gibx8", + .page_per_block = 128, + .page_size = 4096, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 8192, + .chip_id = 0xd7ec, + .chip_id_mask = 0xffff, + .ecc_type = ECC_BCH, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash micron1GbX8 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .name = "Micron 1Gibx8", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 1024, + .chip_id = 0xa12c, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash micron1GbX16 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .name = "Micron 1Gibx16", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 1024, + .chip_id = 0xb12c, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash micron2GbX16 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .name = "Micron 2Gibx16", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 2048, + .chip_id = 0xbaec, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash micron4GbX8 = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .name = "Micron 4Gibx8", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 4096, + .chip_id = 0xdc2c, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash micron2GbX16_ba2c = { + .timing = µn_timing, + .cmdset = &largepage_cmdset, + .name = "Micron 2Gibx16", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 2048, + .chip_id = 0xba2c, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash micron32GbX8 = { + .timing = µn32GbX8_timing, + .cmdset = &largepage_cmdset, + .name = "Micron 32Gibx8", + .page_per_block = 256, + .page_size = 4096, + .flash_width = 8, + .dfc_width = 8, + .num_blocks = 4096, + .chip_id = 0x682c, + .chip_id_mask = 0xffff, + .ecc_type = ECC_BCH, + .ecc_strength = 1, +}; + + +static struct pxa3xx_nand_flash stm2GbX16 = { + .timing = &stm2GbX16_timing, + .cmdset = &largepage_cmdset, + .name = "Stm 2Gibx16", + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 2048, + .chip_id = 0xba20, + .chip_id_mask = 0xffff, + .ecc_type = ECC_HAMMIN, + .ecc_strength = 1, +}; + +static struct pxa3xx_nand_flash *builtin_flash_types[] = { + &nand_common, + &samsung512MbX8, + &samsung512MbX16, + &samsung2GbX8, + &samsung8GbX8, + &samsung32GbX8, + µn1GbX8, + µn4GbX8, + µn1GbX16, + µn2GbX16, + µn2GbX16_ba2c, + µn32GbX8, + &stm2GbX16, + &hynix4GbX16, +}; + +#endif diff --git a/arch/arm/mach-mmp/include/mach/ov529.h b/arch/arm/mach-mmp/include/mach/ov529.h new file mode 100644 index 00000000000000..394e9b5683e4be --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/ov529.h @@ -0,0 +1,21 @@ +#ifndef __ASM_ARCH_OV529_H__ +#define __ASM_ARCH_OV529_H__ + +#define PCD_CMD 1 +#define PCD_DATA 0 + +struct ov529_platform_data { + char name[128]; + int (*init)(void); + void (*release)(void); + int (*power_on)(int on); + void (*turnon_sensor)(void); + void (*set_ptype)(int on); + int (*get_sel_uart)(void); + int (*get_pwait)(void); + void (*set_pcd)(int on); + void (*set_rts)(int on); +}; + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/portofino.h b/arch/arm/mach-mmp/include/mach/portofino.h new file mode 100644 index 00000000000000..4a96a502e7ab08 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/portofino.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2008, Marvell Corporation. + * Author: Bin Yang + * Yael Sheli Chemla + * + * 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. + */ + +#ifndef __MACH_PORTOFINO_H +#define __MACH_PORTOFINO_H +#include + +#define PORTOFINO_ADDRESS 0x11 /*88PM8606, (codenamed 'Portofino')*/ + +#define PORTOFINO_ID 0x17 +#define PORTOFINO_PREREG1 0x10 + +/* General */ +int portofino_read(u8 reg, u8 *val); +int portofino_write(u8 reg, u8 val); + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/power_button.h b/arch/arm/mach-mmp/include/mach/power_button.h new file mode 100644 index 00000000000000..81e94185111124 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/power_button.h @@ -0,0 +1,10 @@ +#ifndef _POWER_BUTTON_H +#define _POWER_BUTTON_H + +struct power_button_platform_data { + int (*init)(irq_handler_t pwrdwn_handler, irq_handler_t standby_handler); + void (*send_standby_ack)(void); + void (*send_powerdwn_ack)(void); +}; + +#endif diff --git a/arch/arm/mach-mmp/include/mach/power_mcu.h b/arch/arm/mach-mmp/include/mach/power_mcu.h new file mode 100644 index 00000000000000..944bac76e2a1cd --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/power_mcu.h @@ -0,0 +1,193 @@ +#ifndef __POWER_MCU_H__ +#define __POWER_MCU_H__ + +#include +#include + +#define EC_NO_CONNECTION (-123) + + +/* _W word */ +/* _B byte */ +#if 0 +enum { + MCU_VERSION_B = 0 , /* version of ec */ + MCU_STATUS_B, /* status */ + MCU_RARC_B, /* remianing active relavtie capacity */ + MCU_RSRC_B, /* remaining standby relative capacity */ + MCU_RAAC_W, /* remaining active absolute capacity */ + MCU_RSAC_W, /* remaining standby absolute capacity */ + MCU_IAVG_W, /* average current register */ + MCU_TEMP_W, /* temperature */ + MCU_VOLT_W, /* voltage */ + MCU_CURRENT_W, /* current */ + MCU_ACR_W, /* accumulated current */ + MCU_ACRL_W, /* low accumulated current */ + MCU_AS_B, /* age scalar */ + MCU_FULL_W, /* full capacity */ + MCU_AE_W, /* active empty */ + MCU_SE_W, /* standby empty */ + MCU_TIME_LOW_W, /* low 16bit of rtc */ + MCU_TIME_MID1_W, /* low mid 16bit of rtc */ + MCU_TIME_MID2_W, /* high mid 16bit of rtc */ + MCU_TIME_HIGH_W, /* high 16bit of rtc */ + MCU_END, +}; +#endif +#if 0 +enum I2C_MCU_DATA { + MCU_VERSION = 0x00, /* MCU VERSION */ + MCU_STATUS = 0x01, /* DS STATUS */ + MCU_RAAC_MSB = 0x02, + MCU_RAAC_LSB = 0x03, + MCU_RSAC_MSB = 0x04, + MCU_RSAC_LSB = 0x05, + MCU_RARC = 0x06, + MCU_RSRC = 0x07, + MCU_IAVG_MSB = 0x08, + MCU_IAVG_LSB = 0x09, + MCU_TEMP_MSB = 0x0a, + MCU_TEMP_LSB = 0x0b, + MCU_VOLT_MSB = 0x0c, + MCU_VOLT_LSB = 0x0d, + MCU_CURRENT_MSB = 0x0e, + MCU_CURRENT_LSB = 0x0f, /* Shut down command */ + MCU_ACR_MSB = 0x10, /* Sleep command */ + MCU_ACR_LSB = 0x11, + MCU_ACRL_MSB = 0x12, + MCU_ACRL_LSB = 0x13, + MCU_AS = 0x14, + MCU_SFR = 0x15, + MCU_FULL_MSB = 0x16, + MCU_FULL_LSB = 0x17, + MCU_AE_MSB = 0x18, + MCU_AE_LSB = 0x19, + MCU_SE_MSB = 0x1a, + MCU_SE_LSB = 0x1b, + + MCU_END = 0x61, +}; +#endif +enum I2C_MCU_DATA { + MCU_OFFSET_BYTE = 0x00, + MCU_VERSION = 0x00, /* MCU VERSION */ + MCU_DS_STATUS = 0x01, /* DS STATUS */ + MCU_DS_RARC = 0x02, + MCU_DS_RSRC = 0x03, + MCU_DS_AS = 0x04, + MCU_DS_SFR = 0x05, + MCU_DS_CONTROL = 0x06, + MCU_DS_AB = 0x07, + MCU_DS_VCHG = 0x08, + MCU_DS_IMIN = 0x09, + MCU_DS_VAE = 0x0a, + MCU_DS_IAE = 0x0b, + MCU_DS_RSNSP = 0x0c, + MCU_BATT_ABSENT = 0x0d, /* 1--battery is not avaiable 0--battery is avaiable */ + MCU_AC_IN = 0x0e, + MCU_BATT_PROTECT = 0x0f, + MCU_BATT_CYCLE = 0x10, + MCU_VCORE_FLAG = 0x11, + MCU_OFFSET_WORD = 0x12, + MCU_DS_RAAC = 0x12, + MCU_DS_IAVG = 0x16, + MCU_DS_VOLT1 = 0x18, + MCU_DS_CURRENT = 0x1a, + MCU_DS_ACR = 0x1c, + MCU_DS_ACRL = 0x1e, + MCU_DS_FULL = 0x20, + MCU_DS_AE = 0x22, + MCU_DS_SE = 0x24, + MCU_DS_AC = 0x26, + MCU_DS_TEMP = 0x28, + MCU_DS_FULL40 = 0x2c, + /* RTC */ + MCU_RTC_LSB = 0x2e, + MCU_RTC_MSB = 0x30, + MCU_BATT_FULL = 0x32, + MCU_BATT_FAULT = 0x33, + /* ALARM */ + MCU_ALARM_LSB = 0x36, + MCU_ALARM_MSB = 0x38, + MCU_LED_BLINK = 0x3a, + MCU_SYS_RST = 0x3b, + MCU_VCORE_ADJ = 0x3c, + MCU_DS_VOLT2 = 0x3e, + MCU_VCORE_VOLT = 0x40, + MCU_END, + +}; + +struct avengers_lite_device_info { + struct device *dev; + unsigned long update_time; /* jiffies when data read */ + int ec_status; + int full_counter; + + unsigned char VERSION; + int STATUS; + unsigned char RARC; + unsigned char RSRC; + unsigned char AS; + unsigned char SFR; + unsigned char CONTROL; + unsigned char AB; + unsigned char VCHG; + unsigned char IMIN; + unsigned char VAE; + unsigned char IAE; + unsigned char RSNSP; + unsigned char BATT_ABSENT; + unsigned char AC_IN; + unsigned char BATT_PROTECT; + unsigned char BATT_CYCLE; + unsigned short RAAC; + unsigned short RSAC; + unsigned short IAVG; + unsigned short VOLT1; + short CURRENT; + unsigned short ACR; + unsigned short ACRL; + unsigned short FULL; + unsigned short AE; + unsigned short SE; + unsigned short AC; + unsigned short TEMP; + unsigned short FULL40; + unsigned short RTC_LLSB; + unsigned short RTC_LMSB; + unsigned short RTC_HLSB; + unsigned short RTC_HMSB; + unsigned short ALARM_LLSB; + unsigned short ALARM_LMSB; + unsigned short ALARM_HLSB; + unsigned short ALARM_HMSB; + unsigned short VOLT2; + unsigned short BATT_FULL; + unsigned short BATT_FAULT; + struct rtc_time time; + unsigned long raw_time_low; + unsigned long raw_time_high; + + struct power_supply bat; + struct power_supply ac; + struct workqueue_struct *monitor_wqueue; + struct delayed_work monitor_work; +}; + +#define MASK_CHGTF 0x80 +#define MASK_AEF 0x40 +#define MASK_SEF 0x20 + +#define MASK_BATT_OV 0x80 + + +extern int power_mcu_read(unsigned char reg, void *pval); +/* extern int power_mcu_read_word(unsigned char reg, unsigned short *pval); */ +extern int power_mcu_write_byte(unsigned char reg, unsigned char val); +extern int power_mcu_write_word(unsigned char reg, unsigned short val); +void power_mcu_write_rtc(u32 rtc); +void power_mcu_read_rtc(u32 *rtc); +void power_mcu_write_alarm(u32 rtc); +void power_mcu_read_alarm(u32 *rtc); +#endif diff --git a/arch/arm/mach-mmp/include/mach/pxa168.h b/arch/arm/mach-mmp/include/mach/pxa168.h index 3ad612cbdf091a..9c003cdb56c2d4 100644 --- a/arch/arm/mach-mmp/include/mach/pxa168.h +++ b/arch/arm/mach-mmp/include/mach/pxa168.h @@ -1,20 +1,34 @@ #ifndef __ASM_MACH_PXA168_H #define __ASM_MACH_PXA168_H -#include #include +#include #include -#include - -extern struct pxa_device_desc pxa168_device_uart1; -extern struct pxa_device_desc pxa168_device_uart2; -extern struct pxa_device_desc pxa168_device_twsi0; -extern struct pxa_device_desc pxa168_device_twsi1; -extern struct pxa_device_desc pxa168_device_pwm1; -extern struct pxa_device_desc pxa168_device_pwm2; -extern struct pxa_device_desc pxa168_device_pwm3; -extern struct pxa_device_desc pxa168_device_pwm4; -extern struct pxa_device_desc pxa168_device_nand; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define VDD_IO_3P3V 0 +#define VDD_IO_1P8V 1 +typedef enum { + VDD_IO0, + VDD_IO1, + VDD_IO2, + VDD_IO3, + VDD_IO4, +} vdd_io_t; + +extern void pxa168_set_vdd_iox(vdd_io_t, int); +extern void pxa168_mfp_set_fastio_drive(int); static inline int pxa168_add_uart(int id) { @@ -23,14 +37,26 @@ static inline int pxa168_add_uart(int id) switch (id) { case 1: d = &pxa168_device_uart1; break; case 2: d = &pxa168_device_uart2; break; - } - - if (d == NULL) + case 3: d = &pxa168_device_uart3; break; + /* special case for avengers lite mapping */ + case 4: d = &pxa168_device_uart1b; break; + default: return -EINVAL; + } return pxa_register_device(d, NULL, 0); } +static inline int pxa168_add_msp(struct card_platform_data *data) +{ + return pxa_register_device(&pxa168_device_msp, data, sizeof(*data)); +} + +static inline int pxa168_add_cf(void) +{ + return pxa_register_device(&pxa168_device_cf, NULL, 0); +} + static inline int pxa168_add_twsi(int id, struct i2c_pxa_platform_data *data, struct i2c_board_info *info, unsigned size) { @@ -51,15 +77,36 @@ static inline int pxa168_add_twsi(int id, struct i2c_pxa_platform_data *data, return pxa_register_device(d, data, sizeof(*data)); } -static inline int pxa168_add_pwm(int id) +static inline int pxa168_add_fb(struct pxa168fb_mach_info *mi) +{ + return pxa_register_device(&pxa168_device_fb, mi, sizeof(*mi)); +} + +static inline int pxa168_add_fb_ovly(struct pxa168fb_mach_info *mi) +{ + return pxa_register_device(&pxa168_device_fb_ovly, mi, sizeof(*mi)); +} + +static inline int pxa168_add_cam(void) +{ + return pxa_register_device(&pxa168_device_camera, NULL, 0); +} + +static inline int pxa168_add_ov529(struct ov529_platform_data *pd) +{ + return pxa_register_device(&pxa168_device_ov529, pd, sizeof(*pd)); +} + +static inline int pxa168_add_ssp(int id) { struct pxa_device_desc *d = NULL; switch (id) { - case 1: d = &pxa168_device_pwm1; break; - case 2: d = &pxa168_device_pwm2; break; - case 3: d = &pxa168_device_pwm3; break; - case 4: d = &pxa168_device_pwm4; break; + case 0: d = &pxa168_device_ssp0; break; + case 1: d = &pxa168_device_ssp1; break; + case 2: d = &pxa168_device_ssp2; break; + case 3: d = &pxa168_device_ssp3; break; + case 4: d = &pxa168_device_ssp4; break; default: return -EINVAL; } @@ -67,8 +114,129 @@ static inline int pxa168_add_pwm(int id) return pxa_register_device(d, NULL, 0); } -static inline int pxa168_add_nand(struct pxa3xx_nand_platform_data *info) +static inline int pxa168_add_spi(int id, struct pxa2xx_spi_master *pdata) +{ + struct platform_device *pd; + + pd = platform_device_alloc("pxa2xx-spi", id); + if (pd == NULL) { + pr_err("pxa2xx-spi: failed to allocate device (id=%d)\n", id); + return -ENOMEM; + } + + platform_device_add_data(pd, pdata, sizeof(*pdata)); + + return platform_device_add(pd); +} + +static inline int pxa168_add_keypad(struct pxa27x_keypad_platform_data *data) +{ + return pxa_register_device(&pxa168_device_keypad, data, sizeof(*data)); +} + +static inline int pxa168_add_rtc(void *data) +{ + pxa910_device_rtc.dev.platform_data = data; + return platform_device_register(&pxa910_device_rtc); +} + +static inline int pxa168_add_nand(struct flash_platform_data *data) +{ + return pxa_register_device(&pxa168_device_nand, data, sizeof(*data)); +} + +static inline int pxa168_add_onenand(struct flash_platform_data *data) +{ + return pxa_register_device(&pxa168_device_onenand, data, sizeof(*data)); +} + +static inline int pxa168_add_u2o(void *data) +{ + pxa168_device_u2o.dev.platform_data = data; + return platform_device_register(&pxa168_device_u2o); +} + +static inline int pxa168_add_u2h(struct pxa_usb_plat_info *info) +{ + pxa168_device_u2h.dev.platform_data = info; + return platform_device_register(&pxa168_device_u2h); +} + +static inline int pxa168_add_u2ootg(struct pxa_usb_plat_info *info) +{ + pxa168_device_u2ootg.dev.platform_data = info; + return platform_device_register(&pxa168_device_u2ootg); +} + +static inline int pxa168_add_u2oehci(struct pxa_usb_plat_info *info) +{ + pxa168_device_u2oehci.dev.platform_data = info; + return platform_device_register(&pxa168_device_u2oehci); +} + +static inline int pxa168_add_mfu(struct pxa168_eth_platform_data *data) { - return pxa_register_device(&pxa168_device_nand, info, sizeof(*info)); +#if defined(CONFIG_PXA168_ETH) + return pxa_register_device(&pxa168_device_mfu, data, sizeof(*data)); +#else + return 0; +#endif } + +static inline int pxa168_add_pcie(struct pxa168_pcie_platform_data *data) +{ + return pxa_register_device(&pxa168_device_pcie, data, sizeof(*data)); +} + +static inline int pxa168_add_sdh(int id, struct pxasdh_platform_data *data) +{ + struct pxa_device_desc *d = NULL; + + switch (id) { + case 0: d = &pxa168_device_sdh0; break; + case 1: d = &pxa168_device_sdh1; break; + case 2: d = &pxa168_device_sdh2; break; + case 3: d = &pxa168_device_sdh3; break; + default: + return -EINVAL; + } + + return pxa_register_device(d, data, sizeof(*data)); +} + +static inline void pxa168_add_freq(void) +{ + int ret; + ret = platform_device_register(&pxa168_device_freq); + if (ret) + dev_err(&pxa168_device_freq.dev, + "unable to register device: %d\n", ret); +} + +static inline void pxa168_cir_init(void) +{ + int ret; + ret = platform_device_register(&pxa168_device_cir); + if (ret) + dev_err(&pxa168_device_cir.dev, + "unable to register device: %d\n", ret); +} + +static inline void pxa168_add_icr(void) +{ + int ret; + ret = pxa_register_device(&pxa168_device_icr, NULL, 0); + if (ret) + pr_err("unable to register ICR device: %d\n", ret); +} + +static inline void pxa168_add_cs4344(void) +{ + int ret; + ret = platform_device_register(&pxa168_device_cs4344); + if (ret) + dev_err(&pxa168_device_cs4344.dev, + "unable to register CS4344 device: %d\n", ret); +} + #endif /* __ASM_MACH_PXA168_H */ diff --git a/arch/arm/mach-mmp/include/mach/pxa168_dvfm.h b/arch/arm/mach-mmp/include/mach/pxa168_dvfm.h new file mode 100644 index 00000000000000..f4e9f7a2b8ad90 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa168_dvfm.h @@ -0,0 +1,211 @@ +/* + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef PXA168_DVFM_H +#define PXA168_DVFM_H + +#include + +#define OP_NAME_LEN 16 + + +struct pxa168_md_opt { + struct pxa168_opmode_md *dvfm_settings; /* reg vals for this mode */ + int index; /* legacy way to look up */ + int power_mode; /* active, idle, etc. */ + int vcc_core; /* core voltage, mV */ + int pclk; /* core clock */ + int baclk; /* bus interface clock */ + int xpclk; /* L2 cache interface clock */ + int dclk; /* DDR clock */ + int aclk; /* AXI fabric clock */ + int aclk2; /* AXI2 fabric clock */ + int lpj; + char name[OP_NAME_LEN]; +}; + +struct pxa168_opmode_md { + char name[OP_NAME_LEN]; + int power_mode; + unsigned int corepll_sel; /* 0=pll1 312, 1=pll1 624, 2=pll2 */ + unsigned int axipll_sel; /* 0=pll1 312, 1=pll1 624, 2=pll2 */ + unsigned int aclk2_div; /* axi2 clock */ + unsigned int aclk_div; /* axi clock */ + unsigned int dclk_div; /* ddr clock */ + unsigned int xpclk_div; /* xp clock */ + unsigned int baclk_div; /* ba clock */ + unsigned int pclk_div; /* processor clock */ + unsigned int pll2_refdiv; + unsigned int pll2_fbdiv; + unsigned int pll2_reg1; + unsigned int vcc_core; +}; + + +#define BIT_0 (1 << 0) +#define BIT_1 (1 << 1) +#define BIT_2 (1 << 2) +#define BIT_3 (1 << 3) +#define BIT_4 (1 << 4) +#define BIT_5 (1 << 5) +#define BIT_6 (1 << 6) +#define BIT_7 (1 << 7) +#define BIT_8 (1 << 8) +#define BIT_9 (1 << 9) +#define BIT_10 (1 << 10) +#define BIT_11 (1 << 11) +#define BIT_12 (1 << 12) +#define BIT_13 (1 << 13) +#define BIT_14 (1 << 14) +#define BIT_15 (1 << 15) +#define BIT_16 (1 << 16) +#define BIT_17 (1 << 17) +#define BIT_18 (1 << 18) +#define BIT_19 (1 << 19) +#define BIT_20 (1 << 20) +#define BIT_21 (1 << 21) +#define BIT_22 (1 << 22) +#define BIT_23 (1 << 23) +#define BIT_24 (1 << 24) +#define BIT_25 (1 << 25) +#define BIT_26 (1 << 26) +#define BIT_27 (1 << 27) +#define BIT_28 (1 << 28) +#define BIT_29 (1 << 29) +#define BIT_30 (1 << 30) +#define BIT_31 ((unsigned)1 << 31) + +#define SHIFT0(Val) (Val) +#define SHIFT1(Val) ((Val) << 1) +#define SHIFT2(Val) ((Val) << 2) +#define SHIFT3(Val) ((Val) << 3) +#define SHIFT4(Val) ((Val) << 4) +#define SHIFT5(Val) ((Val) << 5) +#define SHIFT6(Val) ((Val) << 6) +#define SHIFT7(Val) ((Val) << 7) +#define SHIFT8(Val) ((Val) << 8) +#define SHIFT9(Val) ((Val) << 9) +#define SHIFT10(Val) ((Val) << 10) +#define SHIFT11(Val) ((Val) << 11) +#define SHIFT12(Val) ((Val) << 12) +#define SHIFT13(Val) ((Val) << 13) +#define SHIFT14(Val) ((Val) << 14) +#define SHIFT15(Val) ((Val) << 15) +#define SHIFT16(Val) ((Val) << 16) +#define SHIFT17(Val) ((Val) << 17) +#define SHIFT18(Val) ((Val) << 18) +#define SHIFT19(Val) ((Val) << 19) +#define SHIFT20(Val) ((Val) << 20) +#define SHIFT21(Val) ((Val) << 21) +#define SHIFT22(Val) ((Val) << 22) +#define SHIFT23(Val) ((Val) << 23) +#define SHIFT24(Val) ((Val) << 24) +#define SHIFT25(Val) ((Val) << 25) +#define SHIFT26(Val) ((Val) << 26) +#define SHIFT27(Val) ((Val) << 27) +#define SHIFT28(Val) ((Val) << 28) +#define SHIFT29(Val) ((Val) << 29) +#define SHIFT30(Val) ((Val) << 30) +#define SHIFT31(Val) ((Val) << 31) + +/* + * apmu registers and bits definition + */ +#define APMU_CCR_OFF 0x0004 +#define APMU_CCSR_OFF 0x000C +#define APMU_IDLE_CFG_OFF 0x0018 +#define APMU_IMR_OFF 0x0098 +#define APMU_ISR_OFF 0x00A0 +#define APMU_CCR_ACLK_DYN_FC BIT_30 +#define APMU_CCR_DCLK_DYN_FC BIT_29 +#define APMU_CCR_CORE_DYN_FC BIT_28 +#define APMU_CCR_CORE_ALLOW_SPD_CHG BIT_27 +#define APMU_CCR_BUS_FREQ_CHG_REQ BIT_26 +#define APMU_CCR_DDR_FREQ_CHG_REQ BIT_25 +#define APMU_CCR_FREQ_CHG_REQ BIT_24 +#define APMU_CCR_BUS2_CLK_DIV_MSK SHIFT18(0x7) +#define APMU_CCR_BUS2_CLK_DIV_BASE 18 +#define APMU_CCR_BUS_CLK_DIV_MSK SHIFT15(0x7) +#define APMU_CCR_BUS_CLK_DIV_BASE 15 +#define APMU_CCR_DDR_CLK_DIV_MSK SHIFT12(0x7) +#define APMU_CCR_DDR_CLK_DIV_BASE 12 +#define APMU_CCR_XP_CLK_DIV_MSK SHIFT9(0x7) +#define APMU_CCR_XP_CLK_DIV_BASE 9 +#define APMU_CCR_BIU_CLK_DIV_MSK SHIFT6(0x7) +#define APMU_CCR_BIU_CLK_DIV_BASE 6 +#define APMU_CCR_CORE_CLK_DIV_MSK SHIFT0(0x7) +#define APMU_CCR_CORE_CLK_DIV_BASE 0 +#define APMU_CCSR_BUS2_CLK_DIV_MSK SHIFT18(0x7) +#define APMU_CCSR_BUS2_CLK_DIV_BASE 18 +#define APMU_CCSR_BUS_CLK_DIV_MSK SHIFT15(0x7) +#define APMU_CCSR_BUS_CLK_DIV_BASE 15 +#define APMU_CCSR_DDR_CLK_DIV_MSK SHIFT12(0x7) +#define APMU_CCSR_DDR_CLK_DIV_BASE 12 +#define APMU_CCSR_XP_CLK_DIV_MSK SHIFT9(0x7) +#define APMU_CCSR_XP_CLK_DIV_BASE 9 +#define APMU_CCSR_BIU_CLK_DIV_MSK SHIFT6(0x7) +#define APMU_CCSR_BIU_CLK_DIV_BASE 6 +#define APMU_CCSR_CORE_CLK_DIV_MSK SHIFT0(0x7) +#define APMU_CCSR_CORE_CLK_DIV_BASE 0 +#define APMU_IMR_FC_INTR_MASK BIT_1 +#define APMU_ISR_FC_ISR BIT_1 + +/* + * mpmu registers and bits definition + */ +#define MPMU_FCCR_OFF 0x0008 +#define MPMU_POSR_OFF 0x0010 +#define MPMU_PLL2CR_OFF 0x0034 +#define MPMU_ACGR_OFF 0x1024 +#define MPMU_PLL2_REG1_OFF 0x0060 +#define MPMU_PLL2_REG2_OFF 0x0064 + +#define MPMU_FCCR_CORECLKSEL_MSK SHIFT29(0x7) +#define MPMU_FCCR_CORECLKSEL_BASE 29 +#define MPMU_FCCR_AXICLKSEL_MSK SHIFT23(0x7) +#define MPMU_FCCR_AXICLKSEL_BASE 23 +#define MPMU_FCCR_MFC BIT_15 +#define MPMU_FCCR_PLL1CEN BIT_14 +#define MPMU_FCCR_PLL1REFD_MSK SHIFT9(0x1f) +#define MPMU_FCCR_PLL1REFD_BASE 9 +#define MPMU_FCCR_PLL1FBD_MSK SHIFT0(0x1ff) +#define MPMU_FCCR_PLL1FBD_BASE 0 + +#define MPMU_PLL2CR_FBDIV_MSK SHIFT10(0x1ff) +#define MPMU_PLL2CR_REFDIV_MSK SHIFT19(0x1f) + +#define MPMU_PLL2_REG1_VCODIV_SEL_DIFF_MSK SHIFT19(0xf) +#define MPMU_PLL2_REG1_VCODIV_SEL_DIFF_BASE 19 + +#define MPMU_POSR_PLL2REFD_MSK SHIFT23(0x1f) +#define MPMU_POSR_PLL2REFD_BASE 23 +#define MPMU_POSR_PLL2FBD_MSK SHIFT14(0x1ff) +#define MPMU_POSR_PLL2FBD_BASE 14 +#define MPMU_POSR_PLL1REFD_MSK SHIFT9(0x1f) +#define MPMU_POSR_PLL1REFD_BASE 9 +#define MPMU_POSR_PLL1FBD_MSK SHIFT0(0x1ff) +#define MPMU_POSR_PLL1FBD_BASE 0 + +struct pxa168_dvfm_info { + uint32_t cpuid; + unsigned char __iomem *pmum_base; + unsigned char __iomem *pmua_base; +}; + +extern void pxa168_op_machine_to_human(struct pxa168_opmode_md *opmode_md,\ + struct pxa168_md_opt *opmode_hu); + +extern int pxa168_trigger_dfc(uint32_t apmu_ccr); +extern unsigned long pxa168_dfc_get_lockcache_location(unsigned long *base, \ + unsigned int *size); + +extern void pxa168_get_current_opmode_md(struct pxa168_dvfm_info *driver_data,\ + struct pxa168_opmode_md *opmode_md); + + +#endif diff --git a/arch/arm/mach-mmp/include/mach/pxa168_eth.h b/arch/arm/mach-mmp/include/mach/pxa168_eth.h new file mode 100644 index 00000000000000..6ca3e59f2c3d13 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa168_eth.h @@ -0,0 +1,25 @@ +/* + *pxa168 ethernet platform device data definition file. + */ +#ifndef __LINUX_PXA168_ETH_H +#define __LINUX_PXA168_ETH_H + +struct pxa168_eth_platform_data { + u32 port_number; + u16 force_phy_addr; /* force override if phy_addr == 0 */ + u16 phy_addr; + + /* If speed is 0, then speed and duplex are autonegotiated. */ + u32 speed; /* 0, SPEED_10, SPEED_100, SPEED_1000 */ + u32 duplex; /* DUPLEX_HALF or DUPLEX_FULL */ + + /* non-zero values of the following fields override defaults */ + u32 tx_queue_size; + u32 rx_queue_size; + u8 mac_addr[6]; /* mac address if non-zero*/ + + int (*init)(void); + +}; + +#endif /* __LINUX_PXA168_ETH_H */ diff --git a/arch/arm/mach-mmp/include/mach/pxa168_pcie.h b/arch/arm/mach-mmp/include/mach/pxa168_pcie.h new file mode 100644 index 00000000000000..f0fd3d7ac5e609 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa168_pcie.h @@ -0,0 +1,20 @@ +/* + *pxa168 pcie platform device data definition file. + */ +#ifndef __LINUX_PXA168_PCIE_H +#define __LINUX_PXA168_PCIE_H + +struct pxa168_pcie_platform_data { + int (*init) (void); +}; + +extern u8 pxa168_pcie_read8(u32 addr); +extern u16 pxa168_pcie_read16(u32 addr); +extern u32 pxa168_pcie_read32(u32 addr); +extern void pxa168_pcie_write8(u8 val, u32 addr); +extern void pxa168_pcie_write16(u16 val, u32 addr); +extern void pxa168_pcie_write32(u32 val, u32 addr); +extern u32 __init pxa168_pcie_dev_id(void __iomem * base); +extern u32 __init pxa168_pcie_rev(void __iomem * base); + +#endif /* __LINUX_PXA168_PCIE_H */ diff --git a/arch/arm/mach-mmp/include/mach/pxa168_pm.h b/arch/arm/mach-mmp/include/mach/pxa168_pm.h new file mode 100644 index 00000000000000..8e0993e73a3742 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa168_pm.h @@ -0,0 +1,382 @@ +/* + * PXA168 Power Management Routines + * + * Copyright (C) 2004, Intel Corporation(chao.xie@intel.com). + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2009 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef __PXA3xx_PM_H__ +#define __PXA3xx_PM_H__ + +#include + +/* mode save flags */ +#define PM_MODE_SAVE_FLAG_SYS 0x1 +#define PM_MODE_SAVE_FLAG_IRQ 0x2 +#define PM_MODE_SAVE_FLAG_FIQ 0x4 +#define PM_MODE_SAVE_FLAG_ABT 0x8 +#define PM_MODE_SAVE_FLAG_UND 0x10 +#define PM_MODE_SAVE_FLAG_SVC 0x20 + +/* value for PWRMODE register */ +#define PXA3xx_PM_S2D3C4 0x06 +#define PXA3xx_PM_S0D2C2 0x03 +#define PXA3xx_PM_S3D4C4 0x07 +#define PXA3xx_PM_S0D1C2 0x02 +#define PXA3xx_PM_S0D0C1 0x01 + +/* CPSR Processor constants */ +#define CPSR_Mode_MASK (0x0000001F) +#define CPSR_Mode_USR (0x10) +#define CPSR_Mode_FIQ (0x11) +#define CPSR_Mode_IRQ (0x12) +#define CPSR_Mode_SVC (0x13) +#define CPSR_Mode_ABT (0x17) +#define CPSR_Mode_UND (0x1B) +#define CPSR_Mode_SYS (0x1F) +#define CPSR_I_Bit (0x80) +#define CPSR_F_Bit (0x40) + +/* Timer definitions */ +#define TMRnm(n, m) __REG_PXA910(0xd4014004+(n)*12+(m)*4) +#define TIERn(n) __REG_PXA910(0xd4014040+(n)*4) +#define TICRn(n) __REG_PXA910(0xd4014074+(n)*4) +#define TCVWRRn(n) __REG_PXA910(0xd40140a4+(n)*4) + +#define WORD_SIZE 4 + +/* the position of each data memeber */ +#define SleepState_begin 0x0 +#define SleepState_checksum 0x0 +#define SleepState_wordCount (SleepState_checksum + WORD_SIZE) +#define SleepState_areaAddress (SleepState_wordCount + WORD_SIZE) +#define SleepState_modeSaveFlags (SleepState_areaAddress + WORD_SIZE) + +/* save ARM registers */ +#define SleepState_ENTRY_REGS (SleepState_modeSaveFlags + WORD_SIZE) +#define SleepState_ENTRY_CPSR (SleepState_ENTRY_REGS) +#define SleepState_ENTRY_SPSR (SleepState_ENTRY_CPSR + WORD_SIZE) +#define SleepState_ENTRY_R0 (SleepState_ENTRY_SPSR + WORD_SIZE) +#define SleepState_ENTRY_R1 (SleepState_ENTRY_R0 + WORD_SIZE) +#define SleepState_SYS_REGS (SleepState_ENTRY_REGS + 17*WORD_SIZE) +#define SleepState_FIQ_REGS (SleepState_SYS_REGS + 2*WORD_SIZE) +#define SleepState_IRQ_REGS (SleepState_FIQ_REGS + 8*WORD_SIZE) +#define SleepState_ABT_REGS (SleepState_IRQ_REGS + 3*WORD_SIZE) +#define SleepState_UND_REGS (SleepState_ABT_REGS + 3*WORD_SIZE) +#define SleepState_SVC_REGS (SleepState_UND_REGS + 3*WORD_SIZE) + +/* save MMU settings */ +#define SleepState_Cp15_ACR_MMU (SleepState_SVC_REGS + 3*WORD_SIZE) +#define SleepState_Cp15_AUXCR_MMU (SleepState_Cp15_ACR_MMU + WORD_SIZE) +#define SleepState_Cp15_TTBR_MMU (SleepState_Cp15_AUXCR_MMU + WORD_SIZE) +#define SleepState_Cp15_DACR_MMU (SleepState_Cp15_TTBR_MMU + WORD_SIZE) +#define SleepState_Cp15_PID_MMU (SleepState_Cp15_DACR_MMU + WORD_SIZE) +#define SleepState_Cp15_CPAR (SleepState_Cp15_PID_MMU + WORD_SIZE) + +#define SleepState_extendedChecksumByteCount \ + (SleepState_Cp15_CPAR + WORD_SIZE) +#define SleepState_flushFunc \ + (SleepState_extendedChecksumByteCount + WORD_SIZE) +#define SleepState_end (SleepState_flushFunc + WORD_SIZE) +#define SleepState_size (SleepState_end - SleepState_begin) + +#ifndef __ASSEMBLY__ + +#ifdef __KERNEL__ +struct intc_regs { + unsigned int icu_int_conf[63]; + unsigned int icu_fiq_sel_int_num; + unsigned int icu_irq_sel_int_num; + unsigned int icu_gbl_irq_msk; + unsigned int icu_dma_int_msk; + unsigned int icu_dma_int_status; + unsigned int icu_int_status_0; + unsigned int icu_int_status_1; + unsigned int icu_ddr_arm_l2_int_msk; + unsigned int icu_ddr_arm_l2_int_status; +}; + +struct mpmu_regs { + unsigned int fccr; + unsigned int pocr; + unsigned int posr; + unsigned int succr; + unsigned int ohcr; + unsigned int gpcr; + unsigned int pll2cr; + unsigned int sccr; + unsigned int pll1_reg1; + unsigned int pll1_reg2; + unsigned int pll1_ssc; + unsigned int pll2_reg1; + unsigned int pll2_reg2; + unsigned int pll2_ssc; + unsigned int ts; + unsigned int wdtpcr; + unsigned int apcr; + unsigned int apsr; + unsigned int aprr; + unsigned int acgr; + unsigned int arsr; + unsigned int awucrs; + unsigned int awucrm; +}; + +struct apbclk_regs { + unsigned int uart0; + unsigned int uart1; + unsigned int gpio; + unsigned int pwm0; + unsigned int pwm1; + unsigned int pwm2; + unsigned int pwm3; + unsigned int rtc; + unsigned int twsi0; + unsigned int twsi1; + unsigned int kpc; + unsigned int timers; + unsigned int aib; + unsigned int sw_jtag; + unsigned int timer1; + unsigned int onewire; + unsigned int asfar; + unsigned int assar; + unsigned int uart2; + unsigned int timer2; + unsigned int ac97; + unsigned int ssp0; + unsigned int ssp1; + unsigned int ssp2; + unsigned int ssp3; + unsigned int ssp4; +}; + +struct apmu_regs { + unsigned int ccr; + unsigned int ccsr; + unsigned int fc_timer; + unsigned int idle_cfg; + unsigned int lcd_clk_res_ctrl; + unsigned int ccic_clk_res_ctrl; + unsigned int sdh0_clk_res_ctrl; + unsigned int sdh1_clk_res_ctrl; + unsigned int usb_clk_res_ctrl; + unsigned int nfc_clk_res_ctrl; + unsigned int dma_clk_res_ctrl; + unsigned int bus_clk_res_ctrl; + unsigned int wake_clk; + unsigned int core_status; + unsigned int res_frm_slp_clr; + unsigned int imr; + unsigned int irwc; + unsigned int isr; + unsigned int dtc_clk_res_ctrl; + unsigned int mc_hw_slp_type; + unsigned int mc_slp_req; + unsigned int mc_sw_slp_type; + unsigned int pll_sel_status; + unsigned int gc_clk_res_ctrl; + unsigned int smc_clk_res_ctrl; + unsigned int xd_clk_res_ctrl; + unsigned int sdh2_clk_res_ctrl; + unsigned int sdh3_clk_res_ctrl; + unsigned int cf_clk_res_ctrl; + unsigned int msp_clk_res_ctrl; + unsigned int cmu_clk_res_ctrl; + unsigned int mfu_clk_res_ctrl; +}; + +#define MAX_MFP_PINS 131 + +struct mfp_regs { + unsigned int mfp[MAX_MFP_PINS]; +}; + +struct gpio_regs { + unsigned int gpdr[4]; + unsigned int grer[4]; + unsigned int gfer[4]; + unsigned int gedr[4]; + unsigned int gapmask[4]; + +}; + +struct ciu_regs { + unsigned int chip_id; + unsigned int cpu_conf; + unsigned int cpu_sram_spd; + unsigned int cpu_l2c_sram_spd; + unsigned int mcb_conf; + unsigned int sys_boot_cntrl; + unsigned int sw_branch_addr; + unsigned int perf_count0_cntrl; + unsigned int perf_count1_cntrl; + unsigned int perf_count2_cntrl; + unsigned int perf_count0; + unsigned int perf_count1; + unsigned int perf_count2; + unsigned int mc_conf; + unsigned int mcb_sram_spd; + unsigned int axi_sram_spd; +}; + +struct axifab_regs { + unsigned int timeout; + unsigned int timeout_status; + unsigned int port[22]; +}; + +struct smc_regs { + unsigned int msc0; + unsigned int msc1; + unsigned int sxcnfg0; + unsigned int sxcnfg1; + unsigned int memclkcfg; + unsigned int csdficfg0; + unsigned int csdficfg1; + unsigned int clk_ret_del; + unsigned int adv_ret_del; + unsigned int csadrmap0; + unsigned int csadrmap1; + unsigned int we_ap0; + unsigned int we_ap1; + unsigned int oe_ap0; + unsigned int oe_ap1; + unsigned int adv_ap0; + unsigned int adv_ap1; +}; + +struct squ_regs { + unsigned int ctrl_0; + unsigned int ctrl_1; + unsigned int ctrl_2; + unsigned int fmbist_ctrl_0; + unsigned int fmbist_ctrl_1; + unsigned int fmbist_status_0; + unsigned int rsvd; + unsigned int perf_count_cntrl; + unsigned int perf_count_s1; + unsigned int perf_count_s4; + unsigned int perf_count_s8; + unsigned int cam_ent_bank0; + unsigned int cam_ent_bank1; + unsigned int cam_ent_bank2; + unsigned int cam_ent_bank3; + unsigned int cam_ent_bank4; + unsigned int cam_ent_bank5; + unsigned int logger_ent; + unsigned int chan_0_byte_cnt; + unsigned int chan_1_byte_cnt; + unsigned int chan_0_src_addr; + unsigned int chan_1_src_addr; + unsigned int chan_0_dest_addr; + unsigned int chan_1_dest_addr; + unsigned int chan_0_next_desc_ptr; + unsigned int chan_1_next_desc_ptr; + unsigned int chan_0_ctrl; + unsigned int chan_1_ctrl; + unsigned int chan_pri; + unsigned int chan_0_curr_desc_ptr; + unsigned int chan_1_curr_desc_ptr; + unsigned int chan_0_int_mask; + unsigned int chan_1_int_mask; + unsigned int chan_0_int_rst_sel; + unsigned int chan_1_int_rst_sel; + unsigned int chan_0_int_status; + unsigned int chan_1_int_status; +}; + +struct pm_save_data { + u32 checksum; + u32 wordCount; + u32 areaAddress; + u32 modeSaveFlags; + /* current mode registers cpsr, sprsr, r0-r12, lr, sp */ + u32 ENTRY_REGS[17]; + /* SYS mode registers:sp, lr */ + u32 SYS_REGS[2]; + /* FIQ mode registers:spsr, r8-r12, sp, lr */ + u32 FIQ_REGS[8]; + /* IRQ mode registers:spsr, sp, lr */ + u32 IRQ_REGS[3]; + /* ABT mode registers:spsr, sp, lr */ + u32 ABT_REGS[3]; + /* UND mode registers:spsr, sp, lr */ + u32 UND_REGS[3]; + /* SVC mode registers:spsr, sp, lr */ + u32 SVC_REGS[3]; + /* MMU registers */ + u32 CP15_ACR_MMU; + u32 CP15_AUXCR_MMU; + u32 CP15_TTBR_MMU; + u32 CP15_DACR_MMU; + u32 CP15_PID_MMU; + u32 CP15_CPAR; + + u32 extendedChecksumByteCount; + void (*flushFunc)(void); +}; + +struct pxa168_pm_regs { + /* It's used to save core registers. */ + struct pm_save_data pm_data; + struct mfp_regs mfp; + struct gpio_regs gpio; + struct intc_regs intc; + struct apmu_regs apmu; + struct mpmu_regs mpmu; + struct apbclk_regs apbclk; + struct ciu_regs ciu; + struct squ_regs squ; + struct axifab_regs axifab; + struct smc_regs smc; + /* It's the address of DDR that stores key information. + * Two words are used from the address. + */ + void *data_pool; + unsigned int word0; + unsigned int word1; + unsigned int word2; + +}; + + +enum { + POWER_MODE_ACTIVE = 0, + POWER_MODE_CORE_INTIDLE, + POWER_MODE_CORE_EXTIDLE, + POWER_MODE_APPS_IDLE, + POWER_MODE_APPS_SLEEP, + POWER_MODE_SYS_SLEEP, + POWER_MODE_HIBERNATE, +}; + +enum { + IDLE_ACTIVE = 0, + IDLE_CORE_INTIDLE = 1, + IDLE_CORE_EXTIDLE = 2, + IDLE_APPS_IDLE = 4, + IDLE_APPS_SLEEP = 8, + IDLE_SYS_SLEEP = 16, +}; + +extern int enable_deepidle; +extern struct kobject *power_kobj; +extern void pxa168_cpu_sleep(unsigned int, unsigned int); +extern void pxa168_cpu_resume(void); +extern void pxa168_pm_swi(void); +extern void pxa168_pm_enter_lowpower_mode(int state); +extern void set_idle_op(int, int); +extern void mspm_idle_load(void); +extern void mspm_idle_clean(void); +extern unsigned int read_timer(void); +#endif +#endif + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/pxa168fb.h b/arch/arm/mach-mmp/include/mach/pxa168fb.h new file mode 100644 index 00000000000000..63ad824e0d64c4 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa168fb.h @@ -0,0 +1,412 @@ +/* + * linux/arch/arm/mach-mmp/include/mach/pxa168fb.h + * + * Copyright (C) 2009 Marvell International Ltd. + * + * 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. + */ + +#ifndef __ASM_MACH_PXA168FB_H +#define __ASM_MACH_PXA168FB_H +/* ---------------------------------------------- */ +/* Header Files */ +/* ---------------------------------------------- */ +#include +/* ---------------------------------------------- */ +/* IOCTL Definition */ +/* ---------------------------------------------- */ +#define FB_IOC_MAGIC 'm' +#define FB_IOCTL_CONFIG_CURSOR _IO(FB_IOC_MAGIC, 0) +#define FB_IOCTL_DUMP_REGS _IO(FB_IOC_MAGIC, 1) +#define FB_IOCTL_CLEAR_IRQ _IO(FB_IOC_MAGIC, 2) + +/* + * There are many video mode supported. + */ +#define FB_IOCTL_SET_VIDEO_MODE _IO(FB_IOC_MAGIC, 3) +#define FB_IOCTL_GET_VIDEO_MODE _IO(FB_IOC_MAGIC, 4) +/* Request a new video buffer from driver. User program needs to free + * this memory. + */ +#define FB_IOCTL_CREATE_VID_BUFFER _IO(FB_IOC_MAGIC, 5) + +/* Configure viewport in driver. */ +#define FB_IOCTL_SET_VIEWPORT_INFO _IO(FB_IOC_MAGIC, 6) +#define FB_IOCTL_GET_VIEWPORT_INFO _IO(FB_IOC_MAGIC, 7) + +/* Flip the video buffer from user mode. Vide buffer can be separated into: + * a. Current-used buffer - user program put any data into it. It will be + * displayed immediately. + * b. Requested from driver but not current-used - user programe can put any + * data into it. It will be displayed after calling + * FB_IOCTL_FLIP_VID_BUFFER. + * User program should free this memory when they don't use it any more. + * c. User program alloated - user program can allocated a contiguos DMA + * buffer to store its video data. And flip it to driver. Notices that + * this momory should be free by user programs. Driver won't take care of + * this. + */ +#define FB_IOCTL_FLIP_VID_BUFFER _IO(FB_IOC_MAGIC, 8) + +/* Get the current buffer information. User program could use it to display + * anything directly. If developer wants to allocate multiple video layers, + * try to use FB_IOCTL_CREATE_VID_BUFFER to request a brand new video + * buffer. + */ +#define FB_IOCTL_GET_BUFF_ADDR _IO(FB_IOC_MAGIC, 9) + +/* Get/Set offset position of screen */ +#define FB_IOCTL_SET_VID_OFFSET _IO(FB_IOC_MAGIC, 10) +#define FB_IOCTL_GET_VID_OFFSET _IO(FB_IOC_MAGIC, 11) + +/* Turn on the memory toggle function to improve the frame rate while playing + * movie. + */ +#define FB_IOCTL_SET_MEMORY_TOGGLE _IO(FB_IOC_MAGIC, 12) + +/* Turn on the memory toggle function to improve the frame rate while playing + * movie. + */ +#define FB_IOCTL_SET_COLORKEYnALPHA _IO(FB_IOC_MAGIC, 13) +#define FB_IOCTL_GET_COLORKEYnALPHA _IO(FB_IOC_MAGIC, 14) +#define FB_IOCTL_SWITCH_GRA_OVLY _IO(FB_IOC_MAGIC, 15) +#define FB_IOCTL_SWITCH_VID_OVLY _IO(FB_IOC_MAGIC, 16) + +/* For VPro integration */ +#define FB_IOCTL_GET_FREELIST _IO(FB_IOC_MAGIC, 17) + +/* Wait for vsync happen. */ +#define FB_IOCTL_WAIT_VSYNC _IO(FB_IOC_MAGIC, 18) + +/* clear framebuffer: Makes resolution or color space changes look nicer */ +#define FBIO_CLEAR_FRAMEBUFFER _IO(FB_IOC_MAGIC, 19) + +/* Global alpha blend controls - Maintaining compatibility with existing + user programs. */ +#define FBIOPUT_VIDEO_ALPHABLEND 0xeb +#define FBIOPUT_GLOBAL_ALPHABLEND 0xe1 +#define FBIOPUT_GRAPHIC_ALPHABLEND 0xe2 + +/* color swapping */ +#define FBIOPUT_SWAP_GRAPHIC_RED_BLUE 0xe3 +#define FBIOPUT_SWAP_GRAPHIC_U_V 0xe4 +#define FBIOPUT_SWAP_GRAPHIC_Y_UV 0xe5 +#define FBIOPUT_SWAP_VIDEO_RED_BLUE 0xe6 +#define FBIOPUT_SWAP_VIDEO_U_V 0xe7 +#define FBIOPUT_SWAP_VIDEO_Y_UV 0xe8 + +/* colorkey compatibility */ +#define FBIOGET_CHROMAKEYS 0xe9 +#define FBIOPUT_CHROMAKEYS 0xea + + +#define FB_VMODE_RGB565 0x100 +#define FB_VMODE_BGR565 0x101 +#define FB_VMODE_RGB1555 0x102 +#define FB_VMODE_BGR1555 0x103 +#define FB_VMODE_RGB888PACK 0x104 +#define FB_VMODE_BGR888PACK 0x105 +#define FB_VMODE_RGB888UNPACK 0x106 +#define FB_VMODE_BGR888UNPACK 0x107 +#define FB_VMODE_RGBA888 0x108 +#define FB_VMODE_BGRA888 0x109 + +#define FB_VMODE_YUV422PACKED 0x0 +#define FB_VMODE_YUV422PACKED_SWAPUV 0x1 +#define FB_VMODE_YUV422PACKED_SWAPYUorV 0x2 +#define FB_VMODE_YUV422PLANAR 0x3 +#define FB_VMODE_YUV422PLANAR_SWAPUV 0x4 +#define FB_VMODE_YUV422PLANAR_SWAPYUorV 0x5 +#define FB_VMODE_YUV420PLANAR 0x6 +#define FB_VMODE_YUV420PLANAR_SWAPUV 0x7 +#define FB_VMODE_YUV420PLANAR_SWAPYUorV 0x8 +#define FB_VMODE_YUV422PACKED_IRE_90_270 0x9 + +#define FB_HWCMODE_1BITMODE 0x0 +#define FB_HWCMODE_2BITMODE 0x1 + +#define FB_DISABLE_COLORKEY_MODE 0x0 +#define FB_ENABLE_Y_COLORKEY_MODE 0x1 +#define FB_ENABLE_U_COLORKEY_MODE 0x2 +#define FB_ENABLE_V_COLORKEY_MODE 0x4 +#define FB_ENABLE_RGB_COLORKEY_MODE 0x3 +#define FB_ENABLE_R_COLORKEY_MODE 0x5 +#define FB_ENABLE_G_COLORKEY_MODE 0x6 +#define FB_ENABLE_B_COLORKEY_MODE 0x7 + +#define FB_VID_PATH_ALPHA 0x0 +#define FB_GRA_PATH_ALPHA 0x1 +#define FB_CONFIG_ALPHA 0x2 + +#define FB_SYNC_COLORKEY_TO_CHROMA 1 +#define FB_SYNC_CHROMA_TO_COLORKEY 2 + +#define PXA168FB_FB_NUM 2 +/* overlay max buffer number */ +#define MAX_QUEUE_NUM 30 + +/* ---------------------------------------------- */ +/* Data Structure */ +/* ---------------------------------------------- */ +/* + * The follow structures are used to pass data from + * user space into the kernel for the creation of + * overlay surfaces and setting the video mode. + */ + +#define FBVideoMode int + +struct _sViewPortInfo { + unsigned short srcWidth; /* video source size */ + unsigned short srcHeight; + unsigned short zoomXSize; /* size after zooming */ + unsigned short zoomYSize; + unsigned short yPitch; + unsigned short uPitch; + unsigned short vPitch; +}; + +struct _sViewPortOffset { + unsigned short xOffset; /* position on screen */ + unsigned short yOffset; +}; + +struct _sVideoBufferAddr { + unsigned char frameID; /* which frame wants */ + unsigned char *startAddr[3]; /* new buffer (PA). three addr for YUV planar */ + unsigned char *inputData; /* input buf address (VA) */ + unsigned int length; /* input data's length */ +}; + +/* The pxa168_fb_chroma structure is here for legacy compatibility with former + * PXA display driver usage. + */ + +struct pxa168_fb_chroma { + u_char mode; + u_char y_alpha; + u_char y; + u_char y1; + u_char y2; + u_char u_alpha; + u_char u; + u_char u1; + u_char u2; + u_char v_alpha; + u_char v; + u_char v1; + u_char v2; +}; + + +struct _sColorKeyNAlpha { + unsigned int mode; + unsigned int alphapath; + unsigned int config; + unsigned int Y_ColorAlpha; + unsigned int U_ColorAlpha; + unsigned int V_ColorAlpha; +}; + +struct _sOvlySurface { + FBVideoMode videoMode; + struct _sViewPortInfo viewPortInfo; + struct _sViewPortOffset viewPortOffset; + struct _sVideoBufferAddr videoBufferAddr; +}; + +struct _sCursorConfig { + unsigned char enable; /* enable cursor or not */ + unsigned char mode; /* 1bit or 2bit mode */ + unsigned int color1; /* foreground color */ + unsigned int color2; /* background color */ + unsigned short xoffset; + unsigned short yoffset; + unsigned short width; + unsigned short height; + unsigned char *pBuffer; /* cursor data */ +}; + +/* Dumb interface */ +#define PIN_MODE_DUMB_24 0 +#define PIN_MODE_DUMB_18_SPI 1 +#define PIN_MODE_DUMB_18_GPIO 2 +#define PIN_MODE_DUMB_16_SPI 3 +#define PIN_MODE_DUMB_16_GPIO 4 +#define PIN_MODE_DUMB_12_SPI_GPIO 5 +#define PIN_MODE_SMART_18_SPI 6 +#define PIN_MODE_SMART_16_SPI 7 +#define PIN_MODE_SMART_8_SPI_GPIO 8 +#define PIN_MODE_DUMB_18_SMART_8 9 +#define PIN_MODE_DUMB_16_SMART_8_SPI 10 +#define PIN_MODE_DUMB_16_SMART_8_GPIO 11 +#define PIN_MODE_DUMB_16_DUMB_16 12 + +/* Dumb interface pin allocation */ +#define DUMB_MODE_RGB565 0 +#define DUMB_MODE_RGB565_UPPER 1 +#define DUMB_MODE_RGB666 2 +#define DUMB_MODE_RGB666_UPPER 3 +#define DUMB_MODE_RGB444 4 +#define DUMB_MODE_RGB444_UPPER 5 +#define DUMB_MODE_RGB888 6 + +/* default fb buffer size WVGA-32bits */ +#define DEFAULT_FB_SIZE (800 * 480 * 4) + +/* + * Buffer pixel format + * bit0 is for rb swap. + * bit12 is for Y UorV swap + */ +#define PIX_FMT_RGB565 0 +#define PIX_FMT_BGR565 1 +#define PIX_FMT_RGB1555 2 +#define PIX_FMT_BGR1555 3 +#define PIX_FMT_RGB888PACK 4 +#define PIX_FMT_BGR888PACK 5 +#define PIX_FMT_RGB888UNPACK 6 +#define PIX_FMT_BGR888UNPACK 7 +#define PIX_FMT_RGBA888 8 +#define PIX_FMT_BGRA888 9 +#define PIX_FMT_YUV422PACK 10 +#define PIX_FMT_YVU422PACK 11 +#define PIX_FMT_YUV422PLANAR 12 +#define PIX_FMT_YVU422PLANAR 13 +#define PIX_FMT_YUV420PLANAR 14 +#define PIX_FMT_YVU420PLANAR 15 +#define PIX_FMT_PSEUDOCOLOR 20 +#define PIX_FMT_YUYV422PACK (0x1000|PIX_FMT_YUV422PACK) +#define PIX_FMT_YUV422PACK_IRE_90_270 (0x1000|PIX_FMT_RGB888UNPACK) + +/* + * panel interface + */ +#define DPI 0 +#define DSI2DPI 1 +#define DSI 2 + +#ifdef __KERNEL__ +#include + +/* + * PXA LCD controller private state. + */ +struct pxa168fb_info { + struct device *dev; + struct clk *clk; + int id; + void *reg_base; + void *dsi1_reg_base; + void *dsi2_reg_base; + unsigned long new_addr[3]; /* three addr for YUV planar */ + dma_addr_t fb_start_dma; + void *fb_start; + int fb_size; + atomic_t w_intr; + wait_queue_head_t w_intr_wq; + struct mutex access_ok; + struct _sOvlySurface surface; + struct _sColorKeyNAlpha ckey_alpha; + struct fb_videomode dft_vmode; + struct fb_videomode out_vmode; + int fixed_output; + unsigned char *hwc_buf; + unsigned int pseudo_palette[16]; + struct tasklet_struct tasklet; + char *mode_option; + struct fb_info *fb_info; + int io_pin_allocation; + int pix_fmt; + unsigned is_blanked:1; + unsigned edid:1; + unsigned cursor_enabled:1; + unsigned cursor_cfg:1; + unsigned panel_rbswap:1; + unsigned debug:1; + unsigned active:1; + unsigned enabled:1; + unsigned edid_en:1; + + /* + * 0: DMA mem is from DMA region. + * 1: DMA mem is from normal region. + */ + unsigned mem_status:1; + struct pxa168_fb_chroma chroma; +}; + +/* + * PXA fb machine information + */ +struct pxa168fb_mach_info { + char id[16]; + unsigned int sclk_clock; + + int num_modes; + struct fb_videomode *modes; + unsigned int max_fb_size; + + /* + * Pix_fmt + */ + unsigned pix_fmt; + + /* + * I/O pin allocation. + */ + unsigned io_pin_allocation_mode:4; + + /* + * Dumb panel -- assignment of R/G/B component info to the 24 + * available external data lanes. + */ + unsigned dumb_mode:4; + unsigned panel_rgb_reverse_lanes:1; + + /* + * Dumb panel -- GPIO output data. + */ + unsigned gpio_output_mask:8; + unsigned gpio_output_data:8; + + /* + * Dumb panel -- configurable output signal polarity. + */ + unsigned invert_composite_blank:1; + unsigned invert_pix_val_ena:1; + unsigned invert_pixclock:1; + unsigned invert_vsync:1; + unsigned invert_hsync:1; + unsigned panel_rbswap:1; + unsigned active:1; + unsigned enable_lcd:1; + /* + * SPI control + */ + unsigned int spi_ctrl; + unsigned int spi_gpio_cs; + unsigned int spi_gpio_reset; + /* + * panel interface + */ + unsigned int display_interface; + + /* + * power on/off function. + */ + void (*pxa168fb_lcd_power)(struct pxa168fb_info *, unsigned int, unsigned int, int); + /* + * dsi to dpi setting function + */ + void (*dsi2dpi_set)(struct pxa168fb_info *); +}; +extern void pxa168fb_spi_send(struct pxa168fb_info *fbi, void *cmd, int count, unsigned int spi_gpio_cs); +extern void pxa910fb_spi_send(struct pxa168fb_info *fbi, void *cmd, int count, unsigned int spi_gpio_cs); +extern unsigned int gra_dma_base_address; +extern int is_android(void); + +#endif /* __KERNEL__ */ +#endif /* __ASM_MACH_PXA168FB_H */ diff --git a/arch/arm/mach-mmp/include/mach/pxa3xx_bbm.h b/arch/arm/mach-mmp/include/mach/pxa3xx_bbm.h new file mode 100644 index 00000000000000..1f88d9e59f3ef8 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa3xx_bbm.h @@ -0,0 +1,130 @@ +#ifndef __PXA3XX_BBT_H__ +#define __PXA3XX_BBT_H__ + +#define PXA_RELOC_HEADER 0x524e +#define PXA_BEGIN_SLOT 2 + +/* New BBM scheme */ +#define BOOT_PRAT_MAX 10 +#define PXA_NEW_BBM_HEADER 0x4D424254 +#define PXA_PART_IDET_1 0x4D52564C +#define PXA_PART_IDET_2 0x204D5054 +#define BBM_FULL_MASK 0xffffffff +#define BBM_HALF_MASK 0x0000ffff +#define BBT_TYPE_FACT 0x46616374 +#define BBT_TYPE_RUNT 0x52756E74 +#define PART_PHYS 0x50687973 +#define PART_LOGI 0x4C6F6769 +#define RP_UPWD 0x55505744 +#define RP_DNWD 0x444E5755 + +enum bbm_type { + BBM_NONE = 0, + BBM_LEGACY, + BBM_NEW, +}; + +enum bbm_order { + ORDER_REVERSE = 0, + ORDER_POSITIVE, +}; + +enum bbt_state { + BBT_NOINIT = 0, + BBT_INITED, + BBT_FORCE_NOINIT, +}; + +struct reloc_item { + unsigned short from; + unsigned short to; +}; + +struct reloc_table { + unsigned short header; + unsigned short total; +}; + +struct pxa3xx_bbt { + uint32_t ident; + uint32_t ver; + uint32_t type; + uint32_t reserved_1; + uint32_t reserved_2; + uint32_t entry_num; + uint32_t bbt_loc; + uint32_t reserved_3; + uint32_t reserved_4; + uint32_t reserved_5; + union { + struct reloc_item *reloc; + uint32_t *fact_bad; + }; +}; + +struct pxa3xx_partinfo { + uint32_t type; // Logi or Phys + uint32_t usage; // Partition name + uint32_t identifier; // for distinguish same name + uint32_t attrs; // r/w and permisson control + uint64_t start_addr; // addr of LSB of start block + uint64_t end_addr; // addr of MSB of end block + uint64_t rp_start; + uint64_t rp_size; + uint32_t rp_algo; + uint32_t rbbt_type; + uint64_t rbbt_start; + uint64_t rbbt_start_back; + uint64_t reserved; +}; + +struct pxa3xx_part { + uint64_t identifier; + uint32_t version; + uint32_t part_num; + uint64_t reserved; +}; + +struct pxa3xx_legacy_bbm { + int current_slot; + int max_reloc_entry; + + struct reloc_table *table; + struct reloc_item *reloc; +}; + +struct pxa3xx_new_bbm { + int main_block; + int back_block; + int update_indicator; + + struct pxa3xx_part *part; + struct pxa3xx_bbt *fbbt; + struct pxa3xx_bbt *rbbt; + struct pxa3xx_partinfo *partinfo; + loff_t *rbbt_offset; + int *max_reloc_entry; +}; + +struct pxa3xx_bbm { + int bbm_type; + int is_init; + int no_sync; + void *data_buf; + char *rel_dist; + + void (*uninit)(struct mtd_info *mtd); + loff_t (*search)(struct mtd_info *mtd, loff_t ofs); + struct mtd_partition * (*check_partition) + (struct mtd_info *mtd, struct mtd_partition *part, int *num); +}; + +int pxa3xx_scan_bbt(struct mtd_info *mtd); +int pxa3xx_block_bad(struct mtd_info *mtd, loff_t ofs, int allowbbt); +int pxa3xx_update_bbt(struct mtd_info *mtd, loff_t offs); +int pxa3xx_block_markbad(struct mtd_info *mtd, loff_t ofs); +int pxa3xx_bbm_recovery(struct mtd_info *mtd, int bbm_type, struct reloc_item *item, + int num, int reserve_last_page); +int page_search(struct mtd_info *mtd, int start_page, int end_page, + int direction, unsigned int indicator, void *buf, unsigned int mask); +#endif diff --git a/arch/arm/mach-mmp/include/mach/pxa3xx_nand.h b/arch/arm/mach-mmp/include/mach/pxa3xx_nand.h new file mode 100644 index 00000000000000..12c1bf79e1ce87 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa3xx_nand.h @@ -0,0 +1,82 @@ +#ifndef __ASM_ARCH_PXA3XX_NAND_H +#define __ASM_ARCH_PXA3XX_NAND_H + +#ifdef __KERNEL__ +#include +#include +#endif + +#define NUM_CHIP_SELECT 2 +#define CMD_POOL_SIZE 5 + +/* error code and state */ +enum ecc_type { + ECC_NONE = 0, + ECC_HAMMIN, + ECC_BCH, +}; + +struct pxa3xx_nand_timing { + unsigned int tADL; /* Adress to Write Data delay */ + unsigned int tCH; /* Enable signal hold time */ + unsigned int tCS; /* Enable signal setup time */ + unsigned int tWH; /* ND_nWE high duration */ + unsigned int tWP; /* ND_nWE pulse time */ + unsigned int tRH; /* ND_nRE high duration */ + unsigned int tRP; /* ND_nRE pulse width */ + unsigned int tR; /* ND_nWE high to ND_nRE low for read */ + unsigned int tRHW; /* delay for next command issue */ + unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ + unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ +}; + +struct pxa3xx_nand_cmdset { + uint16_t read1; + uint16_t read2; + uint16_t program; + uint16_t read_status; + uint16_t read_id; + uint16_t erase; + uint16_t reset; + uint16_t lock; + uint16_t unlock; + uint16_t lock_status; +}; + +struct pxa3xx_nand_flash { + const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ + const struct pxa3xx_nand_cmdset *cmdset; + const char name[18]; + + uint32_t page_per_block; /* Pages per block (PG_PER_BLK) */ + uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ + uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ + uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */ + uint32_t num_blocks; /* Number of physical blocks in Flash */ + uint32_t chip_id; + uint32_t chip_id_mask; + uint32_t ecc_type; /* 0 for Hamming, 1 for BCH */ + /* + * how many times you want to apply ecc in one page, + * Note if you give this value more than 1, the page + * would be divided into several chunks to execute + */ + uint32_t ecc_strength; +}; + +struct pxa3xx_nand_platform_data { + + /* the data flash bus is shared between the Static Memory + * Controller and the Data Flash Controller, the arbiter + * controls the ownership of the bus + */ + int enable_arbiter; + int use_dma; /* use DMA ? */ + int RD_CNT_DEL; + + struct mtd_partition *parts[NUM_CHIP_SELECT]; + unsigned int nr_parts[NUM_CHIP_SELECT]; +}; + +extern void pxa3xx_set_nand_info(struct pxa3xx_nand_platform_data *info); +#endif /* __ASM_ARCH_PXA3XX_NAND_H */ diff --git a/arch/arm/mach-mmp/include/mach/pxa910-squ.h b/arch/arm/mach-mmp/include/mach/pxa910-squ.h new file mode 100644 index 00000000000000..f9ca846e1e2e27 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa910-squ.h @@ -0,0 +1,114 @@ +#ifndef __MACH_SQU_H +#define __MACH_SQU_H + +#include + +#define PXA910_SQU_REGS_VIRT (AXI_VIRT_BASE + 0xA0000) +#define PXA910_SQU_REG(x) (*((volatile u32 *)(PXA910_SQU_REGS_VIRT + (x)))) +#define SQU_REG(x) (PXA910_SQU_REGS_VIRT + (x)) + +/*SQU*/ +#define SQU_CTRL_0 SQU_REG(0x000) +#define SQU_CTRL_1 SQU_REG(0x008) +#define SQU_CTRL_2 SQU_REG(0x030) +#define SQU_FMBIST_CTRL_0 SQU_REG(0x010) +#define SQU_FMBIST_CTRL_1 SQU_REG(0x018) +#define SQU_FMBIST_STATUS_0 SQU_REG(0x020) +#define SQU_RSVD SQU_REG(0x028) +#define SQU_PERF_COUNT_CNTRL SQU_REG(0x040) +#define SQU_PERF_COUNT_S1 SQU_REG(0x048) +#define SQU_PERF_COUNT_S4 SQU_REG(0x050) +#define SQU_PERF_COUNT_S8 SQU_REG(0x058) + +#define SQU_CAM_ENT_BANK0 SQU_REG(0x200) +#define SQU_CAM_ENT_BANK1 SQU_REG(0x280) +#define SQU_CAM_ENT_BANK2 SQU_REG(0x300) +#define SQU_CAM_ENT_BANK3 SQU_REG(0x380) +#define SQU_CAM_ENT_BANK4 SQU_REG(0x400) +#define SQU_CAM_ENT_BANK5 SQU_REG(0x480) +#define SQU_LOGGER_ENT SQU_REG(0x700) + +#define SQU_CHAN_0_BYTE_CNT SQU_REG(0x800) +#define SQU_CHAN_1_BYTE_CNT SQU_REG(0x804) +#define SQU_CHAN_0_SRC_ADDR SQU_REG(0x810) +#define SQU_CHAN_1_SRC_ADDR SQU_REG(0x814) +#define SQU_CHAN_0_DEST_ADDR SQU_REG(0x820) +#define SQU_CHAN_1_DEST_ADDR SQU_REG(0x824) +#define SQU_CHAN_0_NEXT_DESC_PTR SQU_REG(0x830) +#define SQU_CHAN_1_NEXT_DESC_PTR SQU_REG(0x834) +#define SQU_CHAN_0_CTRL SQU_REG(0x840) +#define SQU_CHAN_1_CTRL SQU_REG(0x844) +#define SQU_CHAN_PRI SQU_REG(0x860) +#define SQU_CHAN_0_CURR_DESC_PTR SQU_REG(0x870) +#define SQU_CHAN_1_CURR_DESC_PTR SQU_REG(0x874) +#define SQU_CHAN_0_INT_MASK SQU_REG(0x880) +#define SQU_CHAN_1_INT_MASK SQU_REG(0x884) +#define SQU_CHAN_0_INT_RST_SEL SQU_REG(0x890) +#define SQU_CHAN_1_INT_RST_SEL SQU_REG(0x894) +#define SQU_CHAN_0_INT_STATUS SQU_REG(0x8a0) +#define SQU_CHAN_1_INT_STATUS SQU_REG(0x8a4) + +#define SDSAR(x) PXA910_SQU_REG(0x810 + ((x) << 2)) +#define SDDAR(x) PXA910_SQU_REG(0x820 + ((x) << 2)) +#define SDNDPR(x) PXA910_SQU_REG(0x830 + ((x) << 2)) +#define SDCR(x) PXA910_SQU_REG(0x840 + ((x) << 2)) +#define SDCDPR(x) PXA910_SQU_REG(0x870 + ((x) << 2)) +#define SDIMR(x) PXA910_SQU_REG(0x880 + ((x) << 2)) +#define SDISR(x) PXA910_SQU_REG(0x8a0 + ((x) << 2)) + +#define SDCR_SSPMOD (1 << 21) /* SSPMod */ +#define SDCR_ABR (1 << 20) /* Channel Abort */ +#define SDCR_CDE (1 << 17) +#define SDCR_SDA (1 << 15) +#define SDCR_CHANACT (1 << 14) /* DMA Channel Active */ +#define SDCR_FETCHND (1 << 13) +#define SDCR_CHANEN (1 << 12) /* Channel Enable */ +#define SDCR_TRANSMOD (1 << 11) /* TransMod */ +#define SDCR_INTMODE (1 << 10) /* Interrupt Mode */ +#define SDCR_CHAINMOD (1 << 9) /* Chain Mode */ +#define SDCR_BURSTLIMIT_MSK (0x7 << 6) +#define SDCR_DESTDIR_MSK (0x3 << 4) +#define SDCR_SRCDIR_MSK (0x3 << 2) /* Source Direction */ +#define SDCR_DESTDESCCONT (1 << 1) +#define SDCR_SRCDESTCONT (1 << 0) +#define SDCR_DST_ADDR_INC (0 << 4) +#define SDCR_DST_ADDR_HOLD (0x2 << 4) +#define SDCR_SRC_ADDR_INC (0 << 2) +#define SDCR_SRC_ADDR_HOLD (0x2 << 2) +#define SDCR_DMA_BURST_32B (0x7 << 6) +#define SDIMR_COMP (1 << 0) + + +/* + * Descriptor structure for PXA910's SQU engine + * Note: this structure must always be aligned to a 16-byte boundary. + */ + +typedef struct pxa910_squ_desc { + volatile u32 byte_cnt; // byte count + volatile u32 src_addr; // source address + volatile u32 dst_addr; // target address + volatile u32 nxt_desc; // next descriptor dress +} pxa910_squ_desc; + + +typedef enum { + SQU_PRIO_HIGH = 0, + SQU_PRIO_MEDIUM = 1, + SQU_PRIO_LOW = 2 +} pxa910_squ_prio; + +/* + * SQU registration + */ +int __init pxa910_init_squ(int num_ch); + +int pxa910_request_squ (char *name, + pxa910_squ_prio prio, + void (*irq_handler)(int, void *), + void *data); + +void pxa910_free_squ (int squ_ch); + + +#endif /* __MACH_SQU_H */ diff --git a/arch/arm/mach-mmp/include/mach/pxa910.h b/arch/arm/mach-mmp/include/mach/pxa910.h index 4f0b4ec6f5d053..cc74f8ed045054 100644 --- a/arch/arm/mach-mmp/include/mach/pxa910.h +++ b/arch/arm/mach-mmp/include/mach/pxa910.h @@ -1,13 +1,16 @@ #ifndef __ASM_MACH_PXA910_H #define __ASM_MACH_PXA910_H -#include #include +#include #include #include +/*TODO: add devices here for pxa910*/ + extern struct pxa_device_desc pxa910_device_uart1; extern struct pxa_device_desc pxa910_device_uart2; +extern struct pxa_device_desc pxa910_device_uart3; extern struct pxa_device_desc pxa910_device_twsi0; extern struct pxa_device_desc pxa910_device_twsi1; extern struct pxa_device_desc pxa910_device_pwm1; @@ -23,14 +26,61 @@ static inline int pxa910_add_uart(int id) switch (id) { case 1: d = &pxa910_device_uart1; break; case 2: d = &pxa910_device_uart2; break; - } - - if (d == NULL) + case 3: d = &pxa910_device_uart3; break; + default: return -EINVAL; + } return pxa_register_device(d, NULL, 0); } +static inline void pxa910_add_acipc(void) +{ + int ret; + ret = platform_device_register(&pxa910_device_acipc); + if (ret) + dev_err(&pxa910_device_acipc.dev, + "unable to register device: %d\n", ret); +} + +static inline int pxa910_add_ire(void) +{ + return pxa_register_device(&pxa910_device_ire, NULL, 0); +} + +static inline int pxa910_add_ssp(int id) +{ + struct pxa_device_desc *d = NULL; + + switch (id) { + case 0: d = &pxa910_device_ssp0; break; + case 1: d = &pxa910_device_ssp1; break; + case 2: d = &pxa910_device_ssp2; break; + default: + return -EINVAL; + } + + return pxa_register_device(d, NULL, 0); +} + +static inline void pxa910_add_imm(void) +{ + int ret; + ret = platform_device_register(&pxa910_device_imm); + if (ret) + dev_err(&pxa910_device_imm.dev, + "unable to register device: %d\n", ret); +} + +static inline void pxa910_add_rtc(void) +{ + int ret; + ret = platform_device_register(&pxa910_device_rtc); + if (ret) + dev_err(&pxa910_device_acipc.dev, + "unable to register device: %d\n", ret); +} + static inline int pxa910_add_twsi(int id, struct i2c_pxa_platform_data *data, struct i2c_board_info *info, unsigned size) { @@ -51,6 +101,16 @@ static inline int pxa910_add_twsi(int id, struct i2c_pxa_platform_data *data, return pxa_register_device(d, data, sizeof(*data)); } +static inline int pxa910_add_fb(struct pxa168fb_mach_info *mi) +{ + return pxa_register_device(&pxa910_device_fb, mi, sizeof(*mi)); +} + +static inline int pxa910_add_fb_ovly(struct pxa168fb_mach_info *mi) +{ + return pxa_register_device(&pxa910_device_fb_ovly, mi, sizeof(*mi)); +} + static inline int pxa910_add_pwm(int id) { struct pxa_device_desc *d = NULL; diff --git a/arch/arm/mach-mmp/include/mach/pxa910_dvfm.h b/arch/arm/mach-mmp/include/mach/pxa910_dvfm.h new file mode 100644 index 00000000000000..41039af1649a89 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa910_dvfm.h @@ -0,0 +1,282 @@ +/* + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef PXA910_DVFM_H +#define PXA910_DVFM_H + +#include + +#define OP_NAME_LEN 16 + +struct pxa910_md_opt { + int power_mode; + int vcc_core; /* core voltage */ + int pclk; /* core clock */ + int pdclk; /* DDR interface clock */ + int baclk; /* bus interface clock */ + int xpclk; /* L2 cache interface clock */ + int dclk; /* DDR clock */ + int aclk; /* bus clock */ + int lpj; + char name[OP_NAME_LEN]; +}; + +#define BIT_0 (1 << 0) +#define BIT_1 (1 << 1) +#define BIT_2 (1 << 2) +#define BIT_3 (1 << 3) +#define BIT_4 (1 << 4) +#define BIT_5 (1 << 5) +#define BIT_6 (1 << 6) +#define BIT_7 (1 << 7) +#define BIT_8 (1 << 8) +#define BIT_9 (1 << 9) +#define BIT_10 (1 << 10) +#define BIT_11 (1 << 11) +#define BIT_12 (1 << 12) +#define BIT_13 (1 << 13) +#define BIT_14 (1 << 14) +#define BIT_15 (1 << 15) +#define BIT_16 (1 << 16) +#define BIT_17 (1 << 17) +#define BIT_18 (1 << 18) +#define BIT_19 (1 << 19) +#define BIT_20 (1 << 20) +#define BIT_21 (1 << 21) +#define BIT_22 (1 << 22) +#define BIT_23 (1 << 23) +#define BIT_24 (1 << 24) +#define BIT_25 (1 << 25) +#define BIT_26 (1 << 26) +#define BIT_27 (1 << 27) +#define BIT_28 (1 << 28) +#define BIT_29 (1 << 29) +#define BIT_30 (1 << 30) +#define BIT_31 ((unsigned)1 << 31) + +#define SHIFT0(Val) (Val) +#define SHIFT1(Val) ((Val) << 1) +#define SHIFT2(Val) ((Val) << 2) +#define SHIFT3(Val) ((Val) << 3) +#define SHIFT4(Val) ((Val) << 4) +#define SHIFT5(Val) ((Val) << 5) +#define SHIFT6(Val) ((Val) << 6) +#define SHIFT7(Val) ((Val) << 7) +#define SHIFT8(Val) ((Val) << 8) +#define SHIFT9(Val) ((Val) << 9) +#define SHIFT10(Val) ((Val) << 10) +#define SHIFT11(Val) ((Val) << 11) +#define SHIFT12(Val) ((Val) << 12) +#define SHIFT13(Val) ((Val) << 13) +#define SHIFT14(Val) ((Val) << 14) +#define SHIFT15(Val) ((Val) << 15) +#define SHIFT16(Val) ((Val) << 16) +#define SHIFT17(Val) ((Val) << 17) +#define SHIFT18(Val) ((Val) << 18) +#define SHIFT19(Val) ((Val) << 19) +#define SHIFT20(Val) ((Val) << 20) +#define SHIFT21(Val) ((Val) << 21) +#define SHIFT22(Val) ((Val) << 22) +#define SHIFT23(Val) ((Val) << 23) +#define SHIFT24(Val) ((Val) << 24) +#define SHIFT25(Val) ((Val) << 25) +#define SHIFT26(Val) ((Val) << 26) +#define SHIFT27(Val) ((Val) << 27) +#define SHIFT28(Val) ((Val) << 28) +#define SHIFT29(Val) ((Val) << 29) +#define SHIFT30(Val) ((Val) << 30) +#define SHIFT31(Val) ((Val) << 31) + +/* + * pmua registers and bits definition + */ +#define CC_SEA_OFF 0x0000 +#define CC_MOH_OFF 0x0004 +#define DM_CC_SEA_OFF 0x0008 +#define DM_CC_MOH_OFF 0x000C +#define MOH_IMR_OFF 0x0098 +#define MOH_ISR_OFF 0x00A0 + +#define PMUA_CC_SEA_SEA_RD_ST_CLEAR BIT_31 +#define PMUA_CC_SEA_ACLK_DYN_FC BIT_30 +#define PMUA_CC_SEA_DCLK_DYN_FC BIT_29 +#define PMUA_CC_SEA_CORE_DYN_FC BIT_28 +#define PMUA_CC_SEA_SEA_ALLOW_SPD_CHG BIT_27 +#define PMUA_CC_SEA_BUS_FREQ_CHG_REQ BIT_26 +#define PMUA_CC_SEA_DDR_FREQ_CHG_REQ BIT_25 +#define PMUA_CC_SEA_SEA_FREQ_CHG_REQ BIT_24 +#define PMUA_CC_SEA_ASYNC5 BIT_23 +#define PMUA_CC_SEA_ASYNC4 BIT_22 +#define PMUA_CC_SEA_ASYNC3_1 BIT_21 +#define PMUA_CC_SEA_ASYNC3 BIT_20 +#define PMUA_CC_SEA_ASYNC2 BIT_19 +#define PMUA_CC_SEA_ASYNC1 BIT_18 +#define PMUA_CC_SEA_BUS_CLK_DIV_MSK SHIFT15(0x7) +#define PMUA_CC_SEA_BUS_CLK_DIV_BASE 15 +#define PMUA_CC_SEA_DDR_CLK_DIV_MSK SHIFT12(0x7) +#define PMUA_CC_SEA_DDR_CLK_DIV_BASE 12 +#define PMUA_CC_SEA_XP_CLK_DIV_MSK SHIFT9(0x7) +#define PMUA_CC_SEA_XP_CLK_DIV_BASE 9 +#define PMUA_CC_SEA_BIU_CLK_DIV_MSK SHIFT6(0x7) +#define PMUA_CC_SEA_BIU_CLK_DIV_BASE 6 +#define PMUA_CC_SEA_BUS_MC_CLK_DIV_MSK SHIFT3(0x7) +#define PMUA_CC_SEA_BUS_MC_CLK_DIV_BASE 3 +#define PMUA_CC_SEA_CORE_CLK_DIV_MSK SHIFT0(0x7) +#define PMUA_CC_SEA_CORE_CLK_DIV_BASE 0 + +#define PMUA_CC_MOH_MOH_RD_ST_CLEAR BIT_31 +#define PMUA_CC_MOH_ACLK_DYN_FC BIT_30 +#define PMUA_CC_MOH_DCLK_DYN_FC BIT_29 +#define PMUA_CC_MOH_CORE_DYN_FC BIT_28 +#define PMUA_CC_MOH_MOH_ALLOW_SPD_CHG BIT_27 +#define PMUA_CC_MOH_BUS_FREQ_CHG_REQ BIT_26 +#define PMUA_CC_MOH_DDR_FREQ_CHG_REQ BIT_25 +#define PMUA_CC_MOH_MOH_FREQ_CHG_REQ BIT_24 +#define PMUA_CC_MOH_ASYNC5 BIT_23 +#define PMUA_CC_MOH_ASYNC4 BIT_22 +#define PMUA_CC_MOH_ASYNC3_1 BIT_21 +#define PMUA_CC_MOH_ASYNC3 BIT_20 +#define PMUA_CC_MOH_ASYNC2 BIT_19 +#define PMUA_CC_MOH_ASYNC1 BIT_18 +#define PMUA_CC_MOH_BUS_2_CLK_DIV_BASE 18 +#define PMUA_CC_MOH_BUS_CLK_DIV_MSK SHIFT15(0x7) +#define PMUA_CC_MOH_BUS_CLK_DIV_BASE 15 +#define PMUA_CC_MOH_DDR_CLK_DIV_MSK SHIFT12(0x7) +#define PMUA_CC_MOH_DDR_CLK_DIV_BASE 12 +#define PMUA_CC_MOH_XP_CLK_DIV_MSK SHIFT9(0x7) +#define PMUA_CC_MOH_XP_CLK_DIV_BASE 9 +#define PMUA_CC_MOH_BIU_CLK_DIV_MSK SHIFT6(0x7) +#define PMUA_CC_MOH_BIU_CLK_DIV_BASE 6 +#define PMUA_CC_MOH_BUS_MC_CLK_DIV_MSK SHIFT3(0x7) +#define PMUA_CC_MOH_BUS_MC_CLK_DIV_BASE 3 +#define PMUA_CC_MOH_CORE_CLK_DIV_MSK SHIFT0(0x7) +#define PMUA_CC_MOH_CORE_CLK_DIV_BASE 0 + +#define PMUA_DM_CC_SEA_MOH_RD_STATUS BIT_25 +#define PMUA_DM_CC_SEA_SEA_RD_STATUS BIT_24 +#define PMUA_DM_CC_SEA_ASYNC5 BIT_23 +#define PMUA_DM_CC_SEA_ASYNC4 BIT_22 +#define PMUA_DM_CC_SEA_ASYNC3_1 BIT_21 +#define PMUA_DM_CC_SEA_ASYNC3 BIT_20 +#define PMUA_DM_CC_SEA_ASYNC2 BIT_19 +#define PMUA_DM_CC_SEA_ASYNC1 BIT_18 +#define PMUA_DM_CC_SEA_BUS_CLK_DIV_MSK SHIFT15(0x7) +#define PMUA_DM_CC_SEA_BUS_CLK_DIV_BASE 15 +#define PMUA_DM_CC_SEA_DDR_CLK_DIV_MSK SHIFT12(0x7) +#define PMUA_DM_CC_SEA_DDR_CLK_DIV_BASE 12 +#define PMUA_DM_CC_SEA_XP_CLK_DIV_MSK SHIFT9(0x7) +#define PMUA_DM_CC_SEA_XP_CLK_DIV_BASE 9 +#define PMUA_DM_CC_SEA_BIU_CLK_DIV_MSK SHIFT6(0x7) +#define PMUA_DM_CC_SEA_BIU_CLK_DIV_BASE 6 +#define PMUA_DM_CC_SEA_BUS_MC_CLK_DIV_MSK SHIFT3(0x7) +#define PMUA_DM_CC_SEA_BUS_MC_CLK_DIV_BASE 3 +#define PMUA_DM_CC_SEA_CORE_CLK_DIV_MSK SHIFT0(0x7) +#define PMUA_DM_CC_SEA_CORE_CLK_DIV_BASE 0 + +#define PMUA_DM_CC_MOH_MOH_RD_STATUS BIT_25 +#define PMUA_DM_CC_MOH_SEA_RD_STATUS BIT_24 +#define PMUA_DM_CC_MOH_ASYNC5 BIT_23 +#define PMUA_DM_CC_MOH_ASYNC4 BIT_22 +#define PMUA_DM_CC_MOH_ASYNC3_1 BIT_21 +#define PMUA_DM_CC_MOH_ASYNC3 BIT_20 +#define PMUA_DM_CC_MOH_ASYNC2 BIT_19 +#define PMUA_DM_CC_MOH_ASYNC1 BIT_18 +#define PMUA_DM_CC_MOH_BUS_CLK_DIV_MSK SHIFT15(0x7) +#define PMUA_DM_CC_MOH_BUS_CLK_DIV_BASE 15 +#define PMUA_DM_CC_MOH_DDR_CLK_DIV_MSK SHIFT12(0x7) +#define PMUA_DM_CC_MOH_DDR_CLK_DIV_BASE 12 +#define PMUA_DM_CC_MOH_XP_CLK_DIV_MSK SHIFT9(0x7) +#define PMUA_DM_CC_MOH_XP_CLK_DIV_BASE 9 +#define PMUA_DM_CC_MOH_BIU_CLK_DIV_MSK SHIFT6(0x7) +#define PMUA_DM_CC_MOH_BIU_CLK_DIV_BASE 6 +#define PMUA_DM_CC_MOH_BUS_MC_CLK_DIV_MSK SHIFT3(0x7) +#define PMUA_DM_CC_MOH_BUS_MC_CLK_DIV_BASE 3 +#define PMUA_DM_CC_MOH_CORE_CLK_DIV_MSK SHIFT0(0x7) +#define PMUA_DM_CC_MOH_CORE_CLK_DIV_BASE 0 + +#define PMUA_MOH_IMR_MOH_FC_INTR_MASK BIT_1 +#define PMUA_MOH_IMR_SEA_FC_INTR_MASK BIT_0 + +#define PMUA_MOH_ISR_MOH_FC_ISR BIT_1 +#define PMUA_MOH_ISR_SEA_FC_ISR BIT_0 + +#define PMUA_MOH_DIS_MC_SW_REQ BIT_21 +#define PMUA_MOH_MC_WAKE_EN BIT_20 +#define PMUA_MOH_SRAM_PWRDWN BIT_6 +#define PMUA_MOH_PWRDWN BIT_5 +#define PMUA_MOH_IDLE BIT_1 + +/* + * pmum registers and bits definition + */ +#define FCCR_OFF 0x0008 + +#define PMUM_FCCR_MOHCLKSEL_MSK SHIFT29(0x7) +#define PMUM_FCCR_MOHCLKSEL_BASE 29 +#define PMUM_FCCR_SEAGCLKSEL_MSK SHIFT26(0x7) +#define PMUM_FCCR_SEAGCLKSEL_BASE 26 +#define PMUM_FCCR_AXICLKSEL_MSK SHIFT23(0x7) +#define PMUM_FCCR_AXICLKSEL_BASE 23 +#define PMUM_FCCR_MFC BIT_15 +#define PMUM_FCCR_PLL1CEN BIT_14 +#define PMUM_FCCR_PLL1REFD_MSK SHIFT9(0x1f) +#define PMUM_FCCR_PLL1REFD_BASE 9 +#define PMUM_FCCR_PLL1FBD_MSK SHIFT0(0x1ff) +#define PMUM_FCCR_PLL1FBD_BASE 0 + +#define PMUM_AXISD BIT_31 +#define PMUM_DSPSD BIT_30 +#define PMUM_SLPEN BIT_29 +#define PMUM_DTCMSD BIT_28 +#define PMUM_DDRCORSD BIT_27 +#define PMUM_APBSD BIT_26 +#define PMUM_BBSD BIT_25 +#define PMUM_INTCLR BIT_24 +#define PMUM_SLPWP0 BIT_23 +#define PMUM_SLPWP1 BIT_22 +#define PMUM_SLPWP2 BIT_21 +#define PMUM_SLPWP3 BIT_20 +#define PMUM_VCTCXOSD BIT_19 +#define PMUM_SLPWP4 BIT_18 +#define PMUM_SLPWP5 BIT_17 +#define PMUM_SLPWP6 BIT_16 +#define PMUM_SLPWP7 BIT_15 +#define PMUM_MSASLPEN BIT_14 + +#define PMUM_GSM_WAKEUPWMX BIT_29 +#define PMUM_WCDMA_WAKEUPX BIT_28 +#define PMUM_GSM_WAKEUPWM BIT_27 +#define PMUM_WCDMA_WAKEUPWM BIT_26 +#define PMUM_AP_ASYNC_INT BIT_25 +#define PMUM_AP_FULL_IDLE BIT_24 +#define PMUM_SDH1 BIT_23 +#define PMUM_SDH2 BIT_22 +#define PMUM_KEYPRESS BIT_21 +#define PMUM_TRACKBALL BIT_20 +#define PMUM_NEWROTARY BIT_19 +#define PMUM_WDT BIT_18 +#define PMUM_RTC_ALARM BIT_17 +#define PMUM_CP_TIMER_3 BIT_16 +#define PMUM_CP_TIMER_2 BIT_15 +#define PMUM_CP_TIMER_1 BIT_14 +#define PMUM_AP2_TIMER_3 BIT_13 +#define PMUM_AP2_TIMER_2 BIT_12 +#define PMUM_AP2_TIMER_1 BIT_11 +#define PMUM_AP1_TIMER_3 BIT_10 +#define PMUM_AP1_TIMER_2 BIT_9 +#define PMUM_AP1_TIMER_1 BIT_8 +#define PMUM_WAKEUP7 BIT_7 +#define PMUM_WAKEUP6 BIT_6 +#define PMUM_WAKEUP5 BIT_5 +#define PMUM_WAKEUP4 BIT_4 +#define PMUM_WAKEUP3 BIT_3 +#define PMUM_WAKEUP2 BIT_2 +#define PMUM_WAKEUP1 BIT_1 +#define PMUM_WAKEUP0 BIT_0 + +#endif diff --git a/arch/arm/mach-mmp/include/mach/pxa910_ire.h b/arch/arm/mach-mmp/include/mach/pxa910_ire.h new file mode 100644 index 00000000000000..7c95afe2d6877a --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa910_ire.h @@ -0,0 +1,108 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IRE_H +#define _IRE_H + +#include + +#define MAX_IRE_BUF_COUNT 3 + +#define IREIO_REQUEST_MEM _IOW('2', 1, struct ire_mem_req *) +#define IREIO_RELEASE_MEM _IOW('2', 2, unsigned long) +#define IREIO_FLUSH_MEM _IOW('2', 3, unsigned long) +#define IREIO_S_FMT _IOW('2', 4, struct ire_fmt *) +#define IREIO_G_FMT _IOW('2', 5, struct ire_fmt *) +#define IREIO_SUBMIT _IOW('2', 6, struct ire_submit_req *) +#define IREIO_ENQUEUE _IOW('2', 7, int) +#define IREIO_DEQUEUE _IOW('2', 8, int) +#define IREIO_GET_BUS_ADDR _IOW('2', 9, unsigned long) +#define IREIO_ENABLE _IOW('2', 10, unsigned long) +#define IREIO_DISABLE _IOW('2', 11, unsigned long) +#define IREIO_DESUBMIT _IOW('2', 12, struct ire_submit_req *) + +#define IRE_GRAPHICS_MEM 0 +#define IRE_FRAME_BUFFER 1 + +#define IRE_ATTR_COHERENT 0x00 +#define IRE_ATTR_WRITECOMBINE 0x10 +#define IRE_ATTR_CACHEABLE 0x20 + +#define IRE_MEM_REQ_TYPE(f) (f & 0x0f) +#define IRE_MEM_REQ_ATTR(f) (f & 0xf0) + +#define IRE_PIXFMT(endian, yuv, mode, bits) \ + (((endian & 0x3) << 9) | ((yuv & 0x3) << 7) |\ + ((mode & 0x3) << 5) | ((bits & 0x7) << 2)) + +#define IRE_PIXFMT_BITS(fmt) (((fmt >> 2) & 0x7) == 4 ? 16 :\ + ((fmt >> 2) & 0x7) == 6 ? 32 :\ + ((fmt >> 7) & 0x3) == 1 ? 16 : \ + ((fmt >> 7) & 0x3) == 2 ? 12 : 0) + +#define IRE_PIXFMT_UV_FACTOR(fmt) (((fmt >> 5) & 0x3) == 0 ? 0 : \ + ((fmt >> 7) & 0x3) == 1 ? 4 : \ + ((fmt >> 7) & 0x3) == 2 ? 2 : 0) + +typedef enum { + IRE_PIXFMT_RGB565 = IRE_PIXFMT(0, 0, 0, 4), + IRE_PIXFMT_RGB888 = IRE_PIXFMT(0, 0, 0, 6), + IRE_PIXFMT_YUV420_PLANAR = IRE_PIXFMT(0, 2, 2, 3), + IRE_PIXFMT_YUV422_PACKED_VYUV = IRE_PIXFMT(0, 1, 1, 3), + IRE_PIXFMT_YUV422_PACKED_UYVY = IRE_PIXFMT(1, 1, 1, 3), + IRE_PIXFMT_YUV422_PACKED_YUYV = IRE_PIXFMT(2, 1, 1, 3), + IRE_PIXFMT_YUV422_PACKED_YVYU = IRE_PIXFMT(3, 1, 1, 3), +} IRE_PIXEL_FORMAT; + +typedef enum { + IRE_ROT_90 = 0, + IRE_ROT_270 = 1, + IRE_ROT_180 = 2, +} IRE_ROTATION; + +struct ire_fmt { + unsigned int fmt; + unsigned int rot_angle; + unsigned long width, height; + unsigned long y_pitch, uv_pitch; + unsigned long yp0_pitch, uvp0_pitch; +}; + +struct ire_mem_req { + unsigned int req_type; + unsigned int req_size; + unsigned long phys_addr; + unsigned long mmap_addr; + unsigned long mmap_size; +}; + +#define IRE_SUBMIT_MODE_NDELAY (1 << 0) +#define IRE_SUBMIT_MODE_DEQUEUE (1 << 1) + +struct ire_buffer { + unsigned long y_paddr, u_paddr, v_paddr; +}; + +struct ire_submit_req { + unsigned int buf_id; + struct ire_buffer src; + struct ire_buffer dst; +}; + +#endif + + + diff --git a/arch/arm/mach-mmp/include/mach/pxa910_pm.h b/arch/arm/mach-mmp/include/mach/pxa910_pm.h new file mode 100644 index 00000000000000..209f9ca2bbef8c --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa910_pm.h @@ -0,0 +1,16 @@ +/* + * PXA910 Power Management Routines + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2009 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef __PXA910_PM_H__ +#define __PXA910_PM_H__ + +extern void pxa910_pm_enter_lowpower_mode(int state); + +#endif diff --git a/arch/arm/mach-mmp/include/mach/pxa930_acipc.h b/arch/arm/mach-mmp/include/mach/pxa930_acipc.h new file mode 100644 index 00000000000000..a60869b8a822f4 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/pxa930_acipc.h @@ -0,0 +1,136 @@ +/* + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + + * (C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef _PXA930_ACIPC_H_ +#define _PXA930_ACIPC_H_ + +#define API_ALIGNMENT + +/* user level ioctl commands for accessing APIs */ +#define ACIPC_SET_EVENT 0 +#define ACIPC_GET_EVENT 1 +#define ACIPC_SEND_DATA 2 +#define ACIPC_READ_DATA 3 +#define ACIPC_BIND_EVENT 4 +#define ACIPC_UNBIND_EVENT 5 +#define ACIPC_GET_BIND_EVENT_ARG 6 + +#define ACIPC_NUMBER_OF_EVENTS (10) +#define ACIPC_NUMBER_OF_INTERRUPTS (3) +#define ACIPC_INT0_EVENTS (0xff) +#define PXA910_ACIPC_INT1_EVENTS (ACIPC_SHM_PACKET_NOTIFY) +#define PXA910_ACIPC_INT2_EVENTS (ACIPC_IPM) +#define ACIPC_INT1_EVENTS (ACIPC_DDR_260_READY_REQ) +#define ACIPC_INT2_EVENTS (ACIPC_DDR_READY_REQ) + +/* clients callback type*/ +/*ICAT EXPORTED FUNCTION_TYPEDEF*/ +typedef u32 (*acipc_rec_event_callback)(u32 events_status); +typedef u32 acipc_data; + + +enum DDR_mode { + DDR_NOREQ = 0, + DDR_208MHZ = 0x1, + DDR_260MHZ = 0x2, +}; + +struct DDR_status { + int mode; + int needed_modes; +}; + +/* this enum define the event type*/ +/*ICAT EXPORTED ENUM*/ +typedef enum { + ACIPC_DDR_RELQ_REQ = 0x00000001, + ACIPC_DDR_RELQ_ACK = 0x00000001, + ACIPC_DDR_260_RELQ_REQ = 0x00000002, + ACIPC_DDR_260_RELQ_ACK = 0x00000002, + ACIPC_MSL_SLEEP_ALLOW = 0x00000004, + ACIPC_MSL_WAKEUP_ACK = 0x00000008, + ACIPC_MSL_WAKEUP_REQ = 0x00000010, + ACIPC_DATA_IND = 0x00000020, + ACIPC_SPARE, + ACIPC_DDR_260_READY_REQ = 0x00000100, + ACIPC_DDR_260_READY_ACK = 0x00000100, + ACIPC_DDR_READY_REQ = 0x00000200, + ACIPC_DDR_READY_ACK = 0x00000200, + /*pxa910 specific*/ + ACIPC_RINGBUF_TX_STOP = 0x00000001, + ACIPC_RINGBUF_TX_RESUME = 0x00000002, + ACIPC_PORT_FLOWCONTROL = 0x00000004, + ACIPC_SHM_PACKET_NOTIFY = 0x00000100, + ACIPC_IPM = 0x00000200, +} acipc_events; + +typedef enum { + ACIPC_RC_OK = 0, + ACIPC_HISTORICAL_EVENT_OCCUR, + ACIPC_EVENT_ALREADY_BIND, + ACIPC_RC_FAILURE, + ACIPC_RC_API_FAILURE, + ACIPC_RC_WRONG_PARAM, +} acipc_return_code; + +/* used by clients when binding a callback to an event*/ +/*ICAT EXPORTED ENUM*/ +typedef enum { + ACIPC_CB_NORMAL = 0, /* callback will be called only if + *the DDR available + */ + ACIPC_CB_ALWAYS_NO_DDR /* callback will be called always , + *even if the DDR is not available + */ +} acipc_callback_mode; + +typedef struct { + acipc_events IIR_bit; + acipc_callback_mode mode; + u32 mask; /* add to support multiple events binding. + see ACIPCEventBind for more details*/ + acipc_rec_event_callback cb; +} acipc_database_cell; + +/*CAT EXPORTED STRUCT*/ +typedef struct { + acipc_database_cell event_db[ACIPC_NUMBER_OF_EVENTS]; + acipc_callback_mode driver_mode; + u32 int0_events_cnt; + u32 historical_event_status; /* hold status of events that occur + *before the clients bind their callback + */ +} acipc_database; + +/*#define ACIPC_DEBUG*/ +#ifdef ACIPC_DEBUG + #define IPCTRACE(format, args...) printk(KERN_INFO format, ## args) + #define IPC_ENTER() printk("IPC: ENTER %s\n", __FUNCTION__) + #define IPC_LEAVE() printk("IPC: LEAVE %s\n", __FUNCTION__) +#else + #define IPCTRACE(s...) do{}while(0) + #define IPC_ENTER() do{}while(0) + #define IPC_LEAVE() do{}while(0) +#endif + +struct acipc_ioctl_arg{ + u32 arg; + acipc_events set_event; + acipc_callback_mode cb_mode; +}; + +#ifdef API_ALIGNMENT +typedef u32 (*ACIPC_RecEventCB)(u32 eventsStatus); +typedef acipc_data ACIPC_Data; +typedef acipc_events ACIPC_EventsE; +typedef acipc_return_code ACIPC_ReturnCodeE; +typedef acipc_callback_mode ACIPC_CBModeE; +#endif + +#endif /* _PXA930_ACIPC_H_ */ + diff --git a/arch/arm/mach-mmp/include/mach/regs-apbc.h b/arch/arm/mach-mmp/include/mach/regs-apbc.h index 712af03fd1af1e..1510e8581342a1 100644 --- a/arch/arm/mach-mmp/include/mach/regs-apbc.h +++ b/arch/arm/mach-mmp/include/mach/regs-apbc.h @@ -14,35 +14,47 @@ #include #define APBC_VIRT_BASE (APB_VIRT_BASE + 0x015000) +#define APBCP_VIRT_BASE (APB_VIRT_BASE + 0x03B000) #define APBC_REG(x) (APBC_VIRT_BASE + (x)) +#define APBCP_REG(x) (APBCP_VIRT_BASE + (x)) /* - * APB clock register offsets for PXA168 + * APB clock registers PXA910 */ -#define APBC_PXA168_UART1 APBC_REG(0x000) -#define APBC_PXA168_UART2 APBC_REG(0x004) -#define APBC_PXA168_GPIO APBC_REG(0x008) -#define APBC_PXA168_PWM1 APBC_REG(0x00c) -#define APBC_PXA168_PWM2 APBC_REG(0x010) -#define APBC_PXA168_PWM3 APBC_REG(0x014) -#define APBC_PXA168_PWM4 APBC_REG(0x018) -#define APBC_PXA168_SSP1 APBC_REG(0x01c) -#define APBC_PXA168_SSP2 APBC_REG(0x020) -#define APBC_PXA168_RTC APBC_REG(0x028) -#define APBC_PXA168_TWSI0 APBC_REG(0x02c) -#define APBC_PXA168_KPC APBC_REG(0x030) -#define APBC_PXA168_TIMERS APBC_REG(0x034) -#define APBC_PXA168_AIB APBC_REG(0x03c) -#define APBC_PXA168_SW_JTAG APBC_REG(0x040) -#define APBC_PXA168_ONEWIRE APBC_REG(0x048) -#define APBC_PXA168_SSP3 APBC_REG(0x04c) -#define APBC_PXA168_ASFAR APBC_REG(0x050) -#define APBC_PXA168_ASSAR APBC_REG(0x054) -#define APBC_PXA168_SSP4 APBC_REG(0x058) -#define APBC_PXA168_SSP5 APBC_REG(0x05c) -#define APBC_PXA168_TWSI1 APBC_REG(0x06c) -#define APBC_PXA168_UART3 APBC_REG(0x070) -#define APBC_PXA168_AC97 APBC_REG(0x084) +#define APBC_PXA910_ACIPC APBC_REG(0x0024) +#define APBC_PXA910_SSP1 APBC_REG(0x0020) +#define APBC_PXA910_RIPC APBCP_REG(0x0038) +/* + * APB clock registers PXA168 + */ +#define APBC_PXA168_UART0 APBC_REG(0x0000) +#define APBC_PXA168_UART1 APBC_REG(0x0004) +#define APBC_PXA168_GPIO APBC_REG(0x0008) +#define APBC_PXA168_PWM0 APBC_REG(0x000c) +#define APBC_PXA168_PWM1 APBC_REG(0x0010) +#define APBC_PXA168_PWM2 APBC_REG(0x0014) +#define APBC_PXA168_PWM3 APBC_REG(0x0018) +#define APBC_PXA168_RTC APBC_REG(0x0028) +#define APBC_PXA168_TWSI0 APBC_REG(0x002c) +#define APBC_PXA168_TWSI1 APBC_REG(0x006c) /* Power I2C */ +#define APBC_PXA168_KPC APBC_REG(0x0030) +#define APBC_PXA168_TIMERS APBC_REG(0x0034) +#define APBC_PXA168_TB_ROTARY APBC_REG(0x0038) +#define APBC_PXA168_AIB APBC_REG(0x003c) +#define APBC_PXA168_SW_JTAG APBC_REG(0x0040) +#define APBC_PXA168_TIMER1 APBC_REG(0x0044) +#define APBC_PXA168_ONEWIRE APBC_REG(0x0048) +#define APBC_PXA168_ASFAR APBC_REG(0x0050) +#define APBC_PXA168_ASSAR APBC_REG(0x0054) +#define APBC_PXA168_UART2 APBC_REG(0x0070) +#define APBC_PXA168_TIMER2 APBC_REG(0x007c) +#define APBC_PXA168_AC97 APBC_REG(0x0084) + +#define APBC_PXA168_SSP0 APBC_REG(0x081c) +#define APBC_PXA168_SSP1 APBC_REG(0x0820) +#define APBC_PXA168_SSP2 APBC_REG(0x084c) +#define APBC_PXA168_SSP3 APBC_REG(0x0858) +#define APBC_PXA168_SSP4 APBC_REG(0x085c) /* * APB Clock register offsets for PXA910 @@ -54,13 +66,8 @@ #define APBC_PXA910_PWM2 APBC_REG(0x010) #define APBC_PXA910_PWM3 APBC_REG(0x014) #define APBC_PXA910_PWM4 APBC_REG(0x018) -#define APBC_PXA910_SSP1 APBC_REG(0x01c) -#define APBC_PXA910_SSP2 APBC_REG(0x020) -#define APBC_PXA910_IPC APBC_REG(0x024) -#define APBC_PXA910_TWSI0 APBC_REG(0x02c) #define APBC_PXA910_KPC APBC_REG(0x030) #define APBC_PXA910_TIMERS APBC_REG(0x034) -#define APBC_PXA910_TBROT APBC_REG(0x038) #define APBC_PXA910_AIB APBC_REG(0x03c) #define APBC_PXA910_SW_JTAG APBC_REG(0x040) #define APBC_PXA910_TIMERS1 APBC_REG(0x044) @@ -68,6 +75,9 @@ #define APBC_PXA910_SSP3 APBC_REG(0x04c) #define APBC_PXA910_ASFAR APBC_REG(0x050) #define APBC_PXA910_ASSAR APBC_REG(0x054) +#define APBC_PXA910_UART2 APBCP_REG(0x001c) +#define APBC_PXA910_TWSI0 APBC_REG(0x002c) +#define APBC_PXA910_TWSI1 APBCP_REG(0x0028) /* Power I2C */ /* * APB Clock register offsets for MMP2 @@ -118,4 +128,7 @@ /* Functional Clock Selection Mask */ #define APBC_FNCLKSEL(x) (((x) & 0xf) << 4) +/* UART High Speed Multiple of Regular Speed */ +#define APBC_UART_HS_MULTI 4 + #endif /* __ASM_MACH_REGS_APBC_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-apmu.h b/arch/arm/mach-mmp/include/mach/regs-apmu.h index 9190305141201c..82719455709e0f 100644 --- a/arch/arm/mach-mmp/include/mach/regs-apmu.h +++ b/arch/arm/mach-mmp/include/mach/regs-apmu.h @@ -19,18 +19,73 @@ /* Clock Reset Control */ #define APMU_IRE APMU_REG(0x048) #define APMU_LCD APMU_REG(0x04c) -#define APMU_CCIC APMU_REG(0x050) +#define APMU_CCIC_RST APMU_REG(0x050) #define APMU_SDH0 APMU_REG(0x054) #define APMU_SDH1 APMU_REG(0x058) +#define APMU_SDH2 APMU_REG(0x0e0) +#define APMU_SDH3 APMU_REG(0x0e4) #define APMU_USB APMU_REG(0x05c) #define APMU_NAND APMU_REG(0x060) #define APMU_DMA APMU_REG(0x064) #define APMU_GEU APMU_REG(0x068) #define APMU_BUS APMU_REG(0x06c) +#define APMU_GC APMU_REG(0x0cc) +#define APMU_GC_PD APMU_REG(0x0d0) +#define APMU_CF APMU_REG(0x0f0) +#define APMU_WAKE_CLR APMU_REG(0x07c) +#define APMU_ICR APMU_REG(0x0f8) +#define APMU_MFU APMU_REG(0x0fc) +#define APMU_CCIC_DBG APMU_REG(0x088) +#define APMU_CCIC_GATE APMU_REG(0x028) +#define APMU_SMC APMU_REG(0x0d4) + +#define APMU_PCR APMU_REG(0x0000) +#define APMU_CCR APMU_REG(0x0004) +#define APMU_CCSR APMU_REG(0x000c) +#define APMU_FC_TIMER APMU_REG(0x0010) +#define APMU_CP_IDLE_CFG APMU_REG(0x0014) +#define APMU_IDLE_CFG APMU_REG(0x0018) +#define APMU_LCD_CLK_RES_CTRL APMU_REG(0x004c) +#define APMU_CCIC_CLK_RES_CTRL APMU_REG(0x0050) +#define APMU_SDH0_CLK_RES_CTRL APMU_REG(0x0054) +#define APMU_SDH1_CLK_RES_CTRL APMU_REG(0x0058) +#define APMU_SDH2_CLK_RES_CTRL APMU_REG(0x00e0) +#define APMU_SDH3_CLK_RES_CTRL APMU_REG(0x00e4) +#define APMU_USB_CLK_RES_CTRL APMU_REG(0x005c) +#define APMU_NFC_CLK_RES_CTRL APMU_REG(0x0060) +#define APMU_DMA_CLK_RES_CTRL APMU_REG(0x0064) +#define APMU_BUS_CLK_RES_CTRL APMU_REG(0x006c) +#define APMU_WAKE_CLK APMU_REG(0x007c) +#define APMU_PWR_STBL_TIMER APMU_REG(0x0084) +#define APMU_SRAM_PWR_DWN APMU_REG(0x008c) +#define APMU_CORE_STATUS APMU_REG(0x0090) +#define APMU_RES_FRM_SLP_CLR APMU_REG(0x0094) +#define APMU_IMR APMU_REG(0x0098) +#define APMU_IRWC APMU_REG(0x009c) +#define APMU_ISR APMU_REG(0x00a0) +#define APMU_DX8_CLK_RES_CTRL APMU_REG(0x00a4) +#define APMU_DTC_CLK_RES_CTRL APMU_REG(0x00ac) +#define APMU_MC_HW_SLP_TYPE APMU_REG(0x00b0) +#define APMU_MC_SLP_REQ APMU_REG(0x00b4) +#define APMU_MC_SW_SLP_TYPE APMU_REG(0x00c0) +#define APMU_PLL_SEL_STATUS APMU_REG(0x00c4) +#define APMU_GC_CLK_RES_CTRL APMU_REG(0x00cc) +#define APMU_SMC_CLK_RES_CTRL APMU_REG(0x00d4) +#define APMU_XD_CLK_RES_CTRL APMU_REG(0x00dc) +#define APMU_CF_CLK_RES_CTRL APMU_REG(0x00f0) +#define APMU_MSP_CLK_RES_CTRL APMU_REG(0x00f4) +#define APMU_CMU_CLK_RES_CTRL APMU_REG(0x00f8) +#define APMU_MFU_CLK_RES_CTRL APMU_REG(0x00fc) #define APMU_FNCLK_EN (1 << 4) #define APMU_AXICLK_EN (1 << 3) #define APMU_FNRST_DIS (1 << 1) #define APMU_AXIRST_DIS (1 << 0) +#define APMU_GC_156M 0x0 +#define APMU_GC_312M 0x40 +#define APMU_GC_PLL2 0x80 +#define APMU_GC_PLL2_DIV2 0xc0 +#define APMU_GC_624M 0xc0 /* added according to Aspen SW spec v2.8*/ + #endif /* __ASM_MACH_REGS_APMU_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-ciu.h b/arch/arm/mach-mmp/include/mach/regs-ciu.h new file mode 100644 index 00000000000000..9f46227eae7ae2 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-ciu.h @@ -0,0 +1,51 @@ +/* + * linux/arch/arm/mach-mmp/include/mach/regs-ciu.h + * + * CPU Interface Unit Registers + * + * 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. + */ + +#ifndef __ASM_MACH_REGS_CIU_H +#define __ASM_MACH_REGS_CIU_H + +#include + +#define CIU_VIRT_BASE (AXI_VIRT_BASE + 0x82c00) +#define CIU_REG(x) (CIU_VIRT_BASE + (x)) + +#define CIU_CHIP_ID CIU_REG(0x0000) +#define CIU_CPU_CONF CIU_REG(0x0008) +#define CIU_CPU_SRAM_SPD CIU_REG(0x0010) +#define CIU_CPU_L2C_SRAM_SPD CIU_REG(0x0018) +#define CIU_MCB_CONF CIU_REG(0x001c) +#define CIU_SYS_BOOT_CNTRL CIU_REG(0x0020) +#define CIU_SW_BRANCH_ADDR CIU_REG(0x0024) +#define CIU_PERF_COUNT0_CNTRL CIU_REG(0x0028) +#define CIU_PERF_COUNT1_CNTRL CIU_REG(0x002c) +#define CIU_PERF_COUNT2_CNTRL CIU_REG(0x0030) +#define CIU_PERF_COUNT0 CIU_REG(0x0034) +#define CIU_PERF_COUNT1 CIU_REG(0x0038) +#define CIU_PERF_COUNT2 CIU_REG(0x003c) +#define CIU_MC_CONF CIU_REG(0x0040) +#define CIU_MCB_SRAM_SPD CIU_REG(0x0044) +#define CIU_AXI_SRAM_SPD CIU_REG(0x0048) + +/* + * may want to put these defines in a #ifdef [PXA168|PXA910|etc] + * block if use of these constants expands to other SoC + */ +#define CIU_SPEEDGRADE CIU_REG(0x0088) +#define CIU_SPEEDGRADE_SHIFT 7 +#define CIU_SPEEDGRADE_MASK (0xfu << CIU_SPEEDGRADE_SHIFT) +#define CIU_SPEEDGRADE_156 0x0001 +#define CIU_SPEEDGRADE_312 0x0002 +#define CIU_SPEEDGRADE_400 0x0003 +#define CIU_SPEEDGRADE_624 0x0004 +#define CIU_SPEEDGRADE_800 0x0005 +#define CIU_SPEEDGRADE_1060 0x0006 +#define CIU_SPEEDGRADE_1200 0x0007 + +#endif /* __ASM_MACH_REGS_CIU_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-icu.h b/arch/arm/mach-mmp/include/mach/regs-icu.h index f882d91894be59..47f9944321a932 100644 --- a/arch/arm/mach-mmp/include/mach/regs-icu.h +++ b/arch/arm/mach-mmp/include/mach/regs-icu.h @@ -17,18 +17,23 @@ #define ICU_REG(x) (ICU_VIRT_BASE + (x)) #define ICU_INT_CONF(n) ICU_REG((n) << 2) -#define ICU_INT_CONF_MASK (0xf) - -/************ PXA168/PXA910 (MMP) *********************/ #define ICU_INT_CONF_AP_INT (1 << 6) #define ICU_INT_CONF_CP_INT (1 << 5) #define ICU_INT_CONF_IRQ (1 << 4) +#define ICU_INT_CONF_MASK (0xf) + +#define ICU_AP_FIQ_SEL_INT_NUM ICU_REG(0x108) /* AP FIQ Selected Interrupt */ +#define ICU_AP_IRQ_SEL_INT_NUM ICU_REG(0x10C) /* AP IRQ Selected Interrupt */ +#define ICU_AP_GBL_IRQ_MSK ICU_REG(0x114) /* AP Global Interrupt Mask */ +#define ICU_DMA_INT_MSK ICU_REG(0x11C) /* */ +#define ICU_DMA_INT_STATUS ICU_REG(0x124) /* */ +#define ICU_INT_STATUS_0 ICU_REG(0x128) /* Interrupt Stuats 0 */ +#define ICU_INT_STATUS_1 ICU_REG(0x12C) /* Interrupt Status 1 */ +#define ICU_DDR_ARM_L2_INT_MSK ICU_REG(0x130) /* */ +#define ICU_DDR_ARM_L2_INT_STATUS ICU_REG(0x134) /* */ -#define ICU_AP_FIQ_SEL_INT_NUM ICU_REG(0x108) /* AP FIQ Selected Interrupt */ -#define ICU_AP_IRQ_SEL_INT_NUM ICU_REG(0x10C) /* AP IRQ Selected Interrupt */ -#define ICU_AP_GBL_IRQ_MSK ICU_REG(0x114) /* AP Global Interrupt Mask */ -#define ICU_INT_STATUS_0 ICU_REG(0x128) /* Interrupt Stuats 0 */ -#define ICU_INT_STATUS_1 ICU_REG(0x12C) /* Interrupt Status 1 */ +#define ICU_AP_GBL_IRQ_MSK_IRQ_MSK (1 << 1) +#define ICU_AP_GBL_IRQ_MSK_FIQ_MSK (1 << 0) /************************** MMP2 ***********************/ diff --git a/arch/arm/mach-mmp/include/mach/regs-mpmu.h b/arch/arm/mach-mmp/include/mach/regs-mpmu.h new file mode 100644 index 00000000000000..88b27f2963b17e --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-mpmu.h @@ -0,0 +1,46 @@ +/* + * linux/arch/arm/mach-mmp/include/mach/regs-mpmu.h + * + * Main Power Management Unit + * + * 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. + */ + +#ifndef __ASM_MACH_REGS_MPMU_H +#define __ASM_MACH_REGS_MPMU_H + +#include + +#define MPMU_VIRT_BASE (APB_VIRT_BASE + 0x50000) +#define MPMU_REG(off) (MPMU_VIRT_BASE + (off)) + +#define MPMU_CPCR MPMU_REG(0x0000) +#define MPMU_FCCR MPMU_REG(0x0008) +#define MPMU_POCR MPMU_REG(0x000c) +#define MPMU_POSR MPMU_REG(0x0010) +#define MPMU_SUCCR MPMU_REG(0x0014) +#define MPMU_VRCR MPMU_REG(0x0018) +#define MPMU_OHCR MPMU_REG(0x001c) +#define MPMU_GPCR MPMU_REG(0x0030) +#define MPMU_PLL2CR MPMU_REG(0x0034) +#define MPMU_SCCR MPMU_REG(0x0038) +#define MPMU_CWUCRM MPMU_REG(0x004c) +#define MPMU_PLL1_REG1 MPMU_REG(0x0050) +#define MPMU_PLL1_REG2 MPMU_REG(0x0054) +#define MPMU_PLL1_SSC MPMU_REG(0x0058) +#define MPMU_PLL2_REG1 MPMU_REG(0x0060) +#define MPMU_PLL2_REG2 MPMU_REG(0x0064) +#define MPMU_PLL2_SSC MPMU_REG(0x0068) +#define MPMU_TS MPMU_REG(0x0080) +#define MPMU_WDTPCR MPMU_REG(0x0200) +#define MPMU_APCR MPMU_REG(0x1000) +#define MPMU_APSR MPMU_REG(0x1004) +#define MPMU_APRR MPMU_REG(0x1020) +#define MPMU_ACGR MPMU_REG(0x1024) +#define MPMU_ARSR MPMU_REG(0x1028) +#define MPMU_AWUCRS MPMU_REG(0x1048) +#define MPMU_AWUCRM MPMU_REG(0x104c) + +#endif /* __ASM_MACH_REGS_APMU_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-pcie.h b/arch/arm/mach-mmp/include/mach/regs-pcie.h new file mode 100644 index 00000000000000..bf5eabb9556d61 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-pcie.h @@ -0,0 +1,195 @@ +/* + * linux/arch/arm/mach-mmp/include/mach/regs-pcie.h + * + * PCIe controler registers + * + * 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. + */ + +#ifndef __ASM_MACH_REGS_PCIE_H +#define __ASM_MACH_REGS_PCIE_H + +#include + + +#define PXA168_PCIE_PHYS_BASE 0xd1200000 +#define PXA168_PCIE_VIRT_BASE 0xfee00000 +#define PXA168_PCIE_SIZE SZ_1M + +#define PCIE_REG(x) ((void __iomem *)(PXA168_PCIE_VIRT_BASE + (x))) + +/* PCIe Register Offsets */ + +#define PCIE_DMA_CTRL_CH0 0x0 /* DMA Control */ +#define PCIE_DMA_CTRL_CH1 0x80 +#define PCIE_DMA_CTRL_CH2 0x100 +#define PCIE_DMA_CTRL_CH3 0x180 +#define PCIE_DMA_CTRL_BYTE_COUNT(x) (x && 0xFFF) + +#define PCIE_DMA_PCIE_CTRL_CH0 0x4 /* DMA PCIe Control */ +#define PCIE_DMA_PCIE_CTRL_CH1 0x84 +#define PCIE_DMA_PCIE_CTRL_CH2 0x104 +#define PCIE_DMA_PCIE_CTRL_CH3 0x184 +#define PCIE_DMA_PCIE_CTRL_PCIE_TYPE_MASK 0x1F +#define PCIE_DMA_PCIE_CTRL_TLP_MEM_RW 0x0 +#define PCIE_DMA_PCIE_CTRL_TLP_MEM_READ_LOCK 0x1 +#define PCIE_DMA_PCIE_CTRL_TLP_CFG_TYPE0_RW 0x4 +#define PCIE_DMA_PCIE_CTRL_TLP_CFG_TYPE1_RW 0x5 +#define PCIE_DMA_PCIE_CTRL_PCIETD_OFFSET 7 + +#define PCIE_DMA_START_CH0 0xC /* DMA Start */ +#define PCIE_DMA_START_CH1 0x8C +#define PCIE_DMA_START_CH2 0x10C +#define PCIE_DMA_START_CH3 0x18C +#define PCIE_DMA_START_TRANSFER 0x1 + +#define PCIE_DMA_SRC_ADDR_LS_CH0 0x10 /* DMA Channel Source Address upper */ +#define PCIE_DMA_SRC_ADDR_LS_CH1 0x90 +#define PCIE_DMA_SRC_ADDR_LS_CH2 0x110 +#define PCIE_DMA_SRC_ADDR_LS_CH3 0x190 + +#define PCIE_DMA_SRC_ADDR_MS_CH0 0x14 /* DMA Channel Source Address lower */ +#define PCIE_DMA_SRC_ADDR_MS_CH1 0x94 +#define PCIE_DMA_SRC_ADDR_MS_CH2 0x114 +#define PCIE_DMA_SRC_ADDR_MS_CH3 0x194 + +#define PCIE_DMA_DEST_ADDR_LS_CH0 0x18 /* DMA Channel Destination Address + upper */ +#define PCIE_DMA_DEST_ADDR_LS_CH1 0x98 +#define PCIE_DMA_DEST_ADDR_LS_CH2 0x118 +#define PCIE_DMA_DEST_ADDR_LS_CH3 0x198 + +#define PCIE_DMA_DEST_ADDR_MS_CH0 0x1C /* DMA Channel Destination Address + lower */ +#define PCIE_DMA_DEST_ADDR_MS_CH1 0x9C +#define PCIE_DMA_DEST_ADDR_MS_CH2 0x11C +#define PCIE_DMA_DEST_ADDR_MS_CH3 0x19C + +#define PCIE_DMA_NEXT_DESC_CH0 0x20 /* DMA Next Descriptor Pointer */ +#define PCIE_DMA_NEXT_DESC_CH1 0xA0 +#define PCIE_DMA_NEXT_DESC_CH2 0x120 +#define PCIE_DMA_NEXT_DESC_CH3 0x1A0 + +#define PCIE_DMA_MODE_CH0 0x24 /* DMA mode */ +#define PCIE_DMA_MODE_CH1 0xA4 +#define PCIE_DMA_MODE_CH2 0x124 +#define PCIE_DMA_MODE_CH3 0x1A4 +#define PCIE_DMA_MODE_NONCHAINED (0x0) +#define PCIE_DMA_MODE_CHAINED (0x1) +#define PCIE_DMA_MODE_PCIE_READ (0x1 << 1) +#define PCIE_DMA_MODE_PCIE_WRITE (0x0 << 1) + +#define PCIE_PIO_RD_DATA_CH0 0x30 /* PIO Read Data */ +#define PCIE_PIO_RD_DATA_CH1 0xB0 +#define PCIE_PIO_RD_DATA_CH2 0x130 +#define PCIE_PIO_RD_DATA_CH3 0x1B0 + +#define PCIE_PIO_START_CH0 0x34 /* PIO Start */ +#define PCIE_PIO_START_CH1 0xB4 +#define PCIE_PIO_START_CH2 0x134 +#define PCIE_PIO_START_CH3 0x1B4 + +#define PCIE_PIO_WR_DATA_CH0 0x38 /* PIO Write Data */ +#define PCIE_PIO_WR_DATA_CH1 0xB8 +#define PCIE_PIO_WR_DATA_CH2 0x138 +#define PCIE_PIO_WR_DATA_CH3 0x1B8 + +#define PCIE_PIO_WR_STRB_CH0 0x3C /* PIO Write Data Strobes */ +#define PCIE_PIO_WR_STRB_CH1 0xBC +#define PCIE_PIO_WR_STRB_CH2 0x13C +#define PCIE_PIO_WR_STRB_CH3 0x1BC + +#define PCIE_PIO_ADDR_LS_CH0 0x40 /* PIO address LSBs */ +#define PCIE_PIO_ADDR_LS_CH1 0xC0 +#define PCIE_PIO_ADDR_LS_CH2 0x140 +#define PCIE_PIO_ADDR_LS_CH3 0x1C0 + +#define PCIE_PIO_ADDR_MS_CH0 0x44 /* PIO address MSBs */ +#define PCIE_PIO_ADDR_MS_CH1 0xC4 +#define PCIE_PIO_ADDR_MS_CH2 0x144 +#define PCIE_PIO_ADDR_MS_CH3 0x1C4 + +#define PCIE_DMA_RD_RESP_STAT0 0x60 /* DMA Read Response Status */ +#define PCIE_DMA_RD_RESP_STAT1 0xE0 +#define PCIE_DMA_RD_RESP_STAT2 0x160 +#define PCIE_DMA_RD_RESP_STAT3 0x1E0 + +#define PCIE_DMA_WR_RESP_STAT0 0x64 /* DMA Write Response Status */ +#define PCIE_DMA_WR_RESP_STAT1 0xE4 +#define PCIE_DMA_WR_RESP_STAT2 0x164 +#define PCIE_DMA_WR_RESP_STAT3 0x1E4 + +#define PCIE_DMA_ISR 0xf80 /* DMA Channel Interrupt Status Register */ +#define PCIE_DMA_ISRM 0xf84 /* DMA Interrupt Status Register Mask */ +#define PCIE_DMA_AXI_SLVERR_ISR 0xf90 /* DMA Channel AXI Slave Error + Interrupt Status Register */ +#define PCIE_DMA_AXI_SLVERR_ISRM 0xf94 /* DMA Channel AXI Slave Error + Interrupt Status Register Mask */ +#define PCIE_DMA_AXI_DECERR_ISR 0xf98 /* DMA Channel AXI Decode Error + Interrupt Status Register */ +#define PCIE_DMA_AXI_DECERR_ISRM 0xf9c /* DMA Channel AXI Decode Error + Interrupt Status Register Mask */ +#define PCIE_DMA_COMPL_SC_ISR 0xfa0 /* DMA Channel PCIe Successful + Completion Interrupt Status + Register */ +#define PCIE_DMA_COMPL_SC_ISRM 0xfa4 /* DMA Channel PCIe Successful + Completion Interrupt Status + Register Mask */ +#define PCIE_DMA_COMPL_UR_ISR 0xfa8 /* DMA Channel PCIe Unsupported + Request Completion Interrupt + Status Register */ +#define PCIE_DMA_COMPL_UR_ISRM 0xfac /* DMA Channel PCIe Unsupported + REquest Completion Interrupt + Status Register Mask */ +#define PCIE_DMA_COMPL_CR_S_ISR 0xfb0 /* DMA Channel PCIe Configuration + Request Retry Completion Interrupt + Status Register */ +#define PCIE_DMA_COMPL_CR_S_ISRM 0xfb4 /* DMA Channel PCIe Configuration + Request Retry Completion Interrupt + Status Register Mask */ +#define PCIE_DMA_COMPL_CA_ISR 0xfb8 /* DMA Channel PCIe Completer Abort + Interrupt Status Register */ +#define PCIE_DMA_COMPL_CA_ISRM 0xfbc /* DMA Channel PCIe Completer Abort + Interrupt Status Register Mask */ +#define PCIE_MSI 0x1000 /* PCIe Control */ +#define PCIE_MSI_ISR_BASE 0x1400 /* MSI ISR - 0x1400, 0x1408...0x1438 */ +#define PCIE_MSI_ISRM_BASE 0x1404 /* MSI ISR Mask - 0x1404, + 0x140C...0x143C */ +#define PCIE_ISR0 0x1800 /* PCIe Control ISR 0 register */ +#define PCIE_ISRM0 0x1804 /* PCIe Control ISR 0 mask register */ +#define PCIE_ISR1 0x1808 /* PCIe Control ISR 1 register */ +#define PCIE_ISRM1 0x180C /* PCIe Control ISR 1 mask register */ +#define PCIE_PCI_LEGACY_ISR0 0x1820 /* PCIe legacy interrupt ISR */ +#define PCIE_PCI_LEGACY_ISRM0 0x1824 /* PCI legacy interrupt ISR mask */ +#define PCIE_CTRL0 0x1840 /* PCIe Control0 register */ +#define PCIE_CTRL1 0x1844 /* PCIe Control1 register */ +#define PCIE_STAT0 0x1880 /* PCIe Status0 register */ +#define PCIE_STAT1 0x1884 /* PCIe Status1 register */ + +#define PCIE_PHY_CONTROL 0x2000 +#define PCIE_PHY_ANALOG_CTRL (PCIE_PHY_CONTROL + (0x94 << 2)) + +/* Configuration capability registers */ +#define PCIE_STD_PCI_CFG 0x3000 +#define PCIE_CFG_PM_CAP 0x3040 +#define PCIE_CFG_MSI_CAP 0x3050 +#define PCIE_CFG_PCIE_CAP 0x3070 +#define PCIE_CFG_MSIX_CAP 0x30B0 +#define PCIE_CFG_SLOT_CAP 0x30C0 +#define PCIE_CFG_VPD_CAP 0x30D0 + +#define PCIE_CFG_PM_CAP_REG(x) ((void __iomem *)(PCIE_REG(PCIE_CFG_PM_CAP) \ + + (x))) +#define PCIE_CFG_MSI_CAP_REG(x) ((void __iomem *)(PCIE_REG(PCIE_CFG_MSI_CAP) \ + + (x))) +#define PCIE_CFG_PCIE_CAP_REG(x) ((void __iomem *)(PCIE_REG(PCIE_CFG_PCIE_CAP) \ + + (x))) +#define PCIE_CFG_MSIX_CAP_REG(x) ((void __iomem *)(PCIE_REG(PCIE_CFG_MSIX_CAP)\ + + (x))) +#define PCIE_CFG_SLOT_CAP_REG(x) ((void __iomem *)(PCIE_REG(PCIE_CFG_SLOT_CAP)\ + + (x))) +#define PCIE_CFG_VPD_CAP_REG(x) ((void __iomem *)(PCIE_REG(PCIE_CFG_VPD_CAP)\ + + (x))) +#endif /* __ASM_MACH_REGS_PCIE_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-rtc.h b/arch/arm/mach-mmp/include/mach/regs-rtc.h new file mode 100644 index 00000000000000..cc9f44b0faa77b --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-rtc.h @@ -0,0 +1,24 @@ +#ifndef __ASM_MACH_REGS_RTC_H +#define __ASM_MACH_REGS_RTC_H + +#include + +/* + * Real Time Clock + */ +#define RTC_REG(offset) (*((volatile u32 *)(rtc_base+(offset)))) + +#define RCNR RTC_REG(0x00) /* RTC Count Register */ +#define RTAR RTC_REG(0x04) /* RTC Alarm Register */ +#define RTSR RTC_REG(0x08) /* RTC Status Register */ +#define RTTR RTC_REG(0x0c) /* RTC Timer Trim Register */ +#define PIAR RTC_REG(0x38) /* Periodic Interrupt Alarm Register */ + +#define RTSR_PICE (1 << 15) /* Periodic interrupt count enable */ +#define RTSR_PIALE (1 << 14) /* Periodic interrupt Alarm enable */ +#define RTSR_HZE (1 << 3) /* HZ interrupt enable */ +#define RTSR_ALE (1 << 2) /* RTC alarm interrupt enable */ +#define RTSR_HZ (1 << 1) /* HZ rising-edge detected */ +#define RTSR_AL (1 << 0) /* RTC alarm detected */ + +#endif /* __ASM_MACH_REGS_RTC_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-smc.h b/arch/arm/mach-mmp/include/mach/regs-smc.h new file mode 100644 index 00000000000000..e484d40d71bd5f --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-smc.h @@ -0,0 +1,37 @@ +/* + * linux/arch/arm/mach-mmp/include/mach/regs-smc.h + * + * Static Memory Controller Registers + * + * 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. + */ + +#ifndef __ASM_MACH_REGS_SMC_H +#define __ASM_MACH_REGS_SMC_H + +#include + +#define SMC_VIRT_BASE (AXI_VIRT_BASE + 0x83800) +#define SMC_REG(x) (SMC_VIRT_BASE + (x)) + +#define SMC_MSC0 SMC_REG(0x0020) +#define SMC_MSC1 SMC_REG(0x0024) +#define SMC_SXCNFG0 SMC_REG(0x0030) +#define SMC_SXCNFG1 SMC_REG(0x0034) +#define SMC_MEMCLKCFG SMC_REG(0x0068) +#define SMC_CSDFICFG0 SMC_REG(0x0090) +#define SMC_CSDFICFG1 SMC_REG(0x0094) +#define SMC_CLK_RET_DEL SMC_REG(0x00b0) +#define SMC_ADV_RET_DEL SMC_REG(0x00b4) +#define SMC_CSADRMAP0 SMC_REG(0x00c0) +#define SMC_CSADRMAP1 SMC_REG(0x00c4) +#define SMC_WE_AP0 SMC_REG(0x00e0) +#define SMC_WE_AP1 SMC_REG(0x00e4) +#define SMC_OE_AP0 SMC_REG(0x00f0) +#define SMC_OE_AP1 SMC_REG(0x00f4) +#define SMC_ADV_AP0 SMC_REG(0x0100) +#define SMC_ADV_AP1 SMC_REG(0x0104) + +#endif /* __ASM_MACH_REGS_SMC_H */ diff --git a/arch/arm/mach-mmp/include/mach/regs-ssp.h b/arch/arm/mach-mmp/include/mach/regs-ssp.h new file mode 100644 index 00000000000000..5a2cbf84c3f6af --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/regs-ssp.h @@ -0,0 +1 @@ +#include diff --git a/arch/arm/mach-mmp/include/mach/regs-timers.h b/arch/arm/mach-mmp/include/mach/regs-timers.h index 45589fec9fc7d2..40fbaec444be12 100644 --- a/arch/arm/mach-mmp/include/mach/regs-timers.h +++ b/arch/arm/mach-mmp/include/mach/regs-timers.h @@ -15,6 +15,7 @@ #define TIMERS1_VIRT_BASE (APB_VIRT_BASE + 0x14000) #define TIMERS2_VIRT_BASE (APB_VIRT_BASE + 0x16000) +#define CP_TIMERS2_VIRT_BASE (APB_VIRT_BASE + 0x80000) #define TMR_CCR (0x0000) #define TMR_TN_MM(n, m) (0x0004 + ((n) << 3) + (((n) + (m)) << 2)) diff --git a/arch/arm/mach-mmp/include/mach/resource.h b/arch/arm/mach-mmp/include/mach/resource.h new file mode 100644 index 00000000000000..c21a1c78dc8b6d --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/resource.h @@ -0,0 +1,15 @@ +#ifndef __MACH_RESOURCE +#define __MACH_RESOURCE + +struct vibrator_data { + unsigned char max_v; + unsigned char min_v; + int (*set_vibrator)(unsigned char); +}; +void res_add_sanremo_vibrator(void); + + + + + +#endif /*__MACH_RESOURCE*/ diff --git a/arch/arm/mach-mmp/include/mach/rtc.h b/arch/arm/mach-mmp/include/mach/rtc.h new file mode 100644 index 00000000000000..957580d908312e --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/rtc.h @@ -0,0 +1,11 @@ +#ifndef __RTC_H__ +#define __RTC_H__ + +struct pxa168_rtc_platform_data { + void (*rtc_write)(u32 rtc); + void (*rtc_read)(u32 *rtc); + void (*alarm_write)(u32 rtc); +}; + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/sanremo.h b/arch/arm/mach-mmp/include/mach/sanremo.h new file mode 100644 index 00000000000000..16cc30b6f1d999 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/sanremo.h @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2008, Marvell Corporation. + * Author: Bin Yang + * Yael Sheli Chemla + * + * 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. + */ + +#ifndef __MACH_SANREMO_H +#define __MACH_SANREMO_H +#include + +#define SANREMO_ADDRESS 0x30 /*88PM8607, (codenamed 'SanRemo')*/ + +#define SANREMO_ID 0x00 +#define SANREMO_PDOWN_STATUS 0x01 +#define SANREMO_PDOWN_STATUS_B0 0x0e +#define SANREMO_OVER_TEMP_EVENT (1 << 0) +#define SANREMO_SW_PWDOWN_EVENT (1 << 2) +#define SANREMO_SYSEN_EVENT (1 << 3) +#define SANREMO_WD_EVENT (1 << 4) +#define SANREMO_LONG_ONKEY_EVENT (1 << 5) +#define SANREMO_PWR_HOLD_EVENT (1 << 6) +#define SANREMO_RTC_UNDER_VOLTAGE (1 << 7) + +#define SANREMO_STATUS 0x02 +#define SANREMO_STATUS_B0 0x01 +#define SANREMO_ONKEY_STATUS (1 << 0) +#define SANREMO_EXTON_STATUS (1 << 1) +#define SANREMO_CHG_STATUS (1 << 2) +#define SANREMO_BAT_STATUS (1 << 3) +#define SANREMO_PEN_STATUS (1 << 4) +#define SANREMO_HEADSET_STATUS (1 << 5) +#define SANREMO_HOOK_STATUS (1 << 6) +#define SANREMO_MICIN_STATUS (1 << 7) + +#define SANREMO_INTERRUPT_STATUS1 0x03 +#define SANREMO_ONKEY_INT (1 << 0) +#define SANREMO_EXTON_INT (1 << 1) +#define SANREMO_CHG_INT (1 << 2) +#define SANREMO_BAT_INT (1 << 3) +#define SANREMO_RTC_INT (1 << 4) + +#define SANREMO_INTERRUPT_STATUS2 0x04 +#define SANREMO_VBAT_INT (1 << 0) +#define SANREMO_VCHG_INT (1 << 1) +#define SANREMO_VSYS_INT (1 << 2) +#define SANREMO_TINT_INT (1 << 3) +#define SANREMO_GPADC0_INT (1 << 4) +#define SANREMO_GPADC1_INT (1 << 5) +#define SANREMO_GPADC2_INT (1 << 6) +#define SANREMO_GPADC3_INT (1 << 7) + +#define SANREMO_INTERRUPT_STATUS3 0x05 +#define SANREMO_PEN_INT (1 << 1) +#define SANREMO_HEADSET_INT (1 << 2) +#define SANREMO_HOOK_INT (1 << 3) +#define SANREMO_MICIN_INT (1 << 4) +#define SANREMO_CHG_FAIL_INT (1 << 5) +#define SANREMO_CHG_DONE_INT (1 << 6) +#define SANREMO_CHG_IOVER_INT (1 << 7) + +#define SANREMO_INTERRUPT_ENABLE1 0x06 +#define SANREMO_ONKEY_MASK (1 << 0) +#define SANREMO_EXTON_MASK (1 << 1) +#define SANREMO_CHG_MASK (1 << 2) +#define SANREMO_BAT_MASK (1 << 3) +#define SANREMO_RTC_MASK (1 << 4) + +#define SANREMO_INTERRUPT_ENABLE2 0x07 +#define SANREMO_VBAT_MASK (1 << 0) +#define SANREMO_VCHG_MASK (1 << 1) +#define SANREMO_VSYS_MASK (1 << 2) +#define SANREMO_TINT_MASK (1 << 3) +#define SANREMO_GPADC0_MASK (1 << 4) +#define SANREMO_GPADC1_MASK (1 << 5) +#define SANREMO_GPADC2_MASK (1 << 6) +#define SANREMO_GPADC3_MASK (1 << 7) + +#define SANREMO_INTERRUPT_ENABLE3 0x08 +#define SANREMO_PEN_MASK (1 << 1) +#define SANREMO_HEADSET_MASK (1 << 2) +#define SANREMO_HOOK_MASK (1 << 3) +#define SANREMO_MICIN_MASK (1 << 4) +#define SANREMO_CHG_FAIL_MASK (1 << 5) +#define SANREMO_CHG_DONE_MASK (1 << 6) +#define SANREMO_CHG_IOVER_MASK (1 << 7) + +#define SANREMO_RESET_OUT 0x09 +#define SANREMO_DEBOUNCE 0x0a +#define SANREMO_WAKEUP 0x0b +#define SANREMO_LDO1 0x10 +#define SANREMO_LDO2 0x11 +#define SANREMO_LDO3 0x12 +#define SANREMO_LDO4 0x13 +#define SANREMO_LDO5 0x14 +#define SANREMO_LDO6 0x15 +#define SANREMO_LDO7 0x16 +#define SANREMO_LDO8 0x17 +#define SANREMO_LDO9 0x18 +#define SANREMO_LDO10 0x19 +#define SANREMO_LDO12 0x1a +#define SANREMO_LDO14 0x1b +#define SANREMO_SLEEP_MODE1 0x1c +#define SANREMO_SLEEP_MODE2 0x1d +#define SANREMO_SLEEP_MODE3 0x1e +#define SANREMO_SLEEP_MODE4 0x1f +#define SANREMO_GO 0x20 +#define SANREMO_VBUCK1_SET_SLP 0x21 +#define SANREMO_VBUCK2_SET_SLP 0x22 +#define SANREMO_VBUCK3_SET_SLP 0x23 +#define SANREMO_VBUCK1_SET 0x24 +#define SANREMO_VBUCK2_SET 0x25 +#define SANREMO_VBUCK3_SET 0x26 +#define SANREMO_BUCK_CONTROLS 0x27 +#define SANREMO_VIBRA_SET 0x28 +#define SANREMO_VIBRA_PWM 0x29 +#define SANREMO_REF_GROUP 0x2a +#define SANREMO_SUPPLIES_EN11 0x2b +#define SANREMO_SUPPLIES_EN12 0x2c +#define SANREMO_GROUP1 0x2d +#define SANREMO_GROUP2 0x2e +#define SANREMO_GROUP3 0x2f +#define SANREMO_GROUP4 0x30 +#define SANREMO_GROUP5 0x31 +#define SANREMO_GROUP6 0x32 +#define SANREMO_SUPPLIES_EN21 0x33 +#define SANREMO_SUPPLIES_EN22 0x34 + +#define SANREMO_AUDIO_MIC_BUTTON_DETECTION_B0 0x37 +#define SANREMO_AUDIO_HEADSET_DETECTION_BO 0x38 +#define SANREMO_MISC1 0x40 +#define SANREMO_MISC1_GPIO1_DIR (1 << 3) +#define SANREMO_MISC1_GPIO1_VAL (1 << 4) +#define SANREMO_MISC1_GPIO2_DIR (1 << 5) +#define SANREMO_MISC1_GPIO2_VAL (1 << 6) + +#define SANREMO_MCLK 0x41 +#define SANREMO_MISC2 0x42 +#define SANREMO_PLL_CTRL1 0x43 +#define SANREMO_PLL_FRAC1 0x44 +#define SANREMO_PLL_FRAC2 0x45 +#define SANREMO_PLL_FRAC3 0x46 +#define SANREMO_PLL_CTRL2 0x47 +#define SANREMO_CHG_CTRL1 0x48 +#define SANREMO_CHG_CTRL2 0x49 +#define SANREMO_CHG_CTRL3 0x4a +#define SANREMO_CHG_CTRL4 0x4b +#define SANREMO_CHG_CTRL5 0x4c +#define SANREMO_CHG_CTRL6 0x4d +#define SANREMO_CHG_CTRL7 0x4e +#define SANREMO_GP_BIAS1 0x4f + +#define SANREMO_MEAS_ENABLE1 0x50 +#define SANREMO_MEAS_EN1_VBAT (1 << 0) +#define SANREMO_MEAS_EN1_VCHG (1 << 1) +#define SANREMO_MEAS_EN1_VSYS (1 << 2) +#define SANREMO_MEAS_EN1_TINT (1 << 3) +#define SANREMO_MEAS_EN1_RFTMP (1 << 4) +#define SANREMO_MEAS_EN1_TBAT (1 << 5) +#define SANREMO_MEAS_EN1_GPADC2 (1 << 6) +#define SANREMO_MEAS_EN1_GPADC3 (1 << 7) + +#define SANREMO_MEAS_ENABLE2 0x51 + +#define SANREMO_MEAS_ENABLE3 0x52 +#define SANREMO_MEAS_EN3_IBAT (1 << 0) +#define SANREMO_MEAS_EN3_IBAT_COMP (1 << 1) +#define SANREMO_MEASOFFTIME1_BAT_DET_EN_B0 (1<<1) +#define SANREMO_MEAS_EN3_COULOMB_COUNTER (1 << 2) +#define SANREMO_MEAS_EN3_PENDET (1 << 3) +#define SANREMO_MEAS_EN3_TSIX (1 << 4) +#define SANREMO_MEAS_EN3_TSIY (1 << 5) +#define SANREMO_MEAS_EN3_TSIZ1 (1 << 6) +#define SANREMO_MEAS_EN3_TSIZ2 (1 << 7) + +#define SANREMO_MEAS_OFF_TIME1 0x53 +#define SANREMO_MEASOFFTIME1_BAT_DET_EN (1 << 0) +#define SANREMO_MEASOFFTIME1_MEAS_OFF_TIME1 (1 << 1) +#define SANREMO_MEASOFFTIME1_MEAS_OFF_TIME1_MASK (0x3F << 1) +#define SANREMO_MEASOFFTIME1_MEAS_OFF_TIME1_BIT (1) + +#define SANREMO_MEAS_OFF_TIME2 0x54 +#define SANREMO_TSI_PREBIAS_TIME 0x55 +#define SANREMO_PD_PREBIAS_TIME 0x56 + +#define SANREMO_GPADC_MISC1 0x57 +#define SANREMO_GPADC_MISC1_GPFSM_EN (1 << 0) +#define SANREMO_GPPADC_GP_PREBIAS_TIME (01 << 1) +#define SANREMO_GPADC_MISC1_MASK (SANREMO_GPADC_MISC1_GPFSM_EN | SANREMO_GPPADC_GP_PREBIAS_TIME) + +#define SANREMO_SW_CAL 0x58 +#define SANREMO_GPADC_MISC2 0x59 +#define SANREMO_GP_BIAS2 0x5a +#define SANREMO_GP_BIAS3 //TBD +#define SANREMO_VBAT_LOW_TH 0x5b +#define SANREMO_VCHG_LOW_TH 0x5c +#define SANREMO_VSYS_LOW_TH 0x5d +#define SANREMO_TINT_LOW_TH 0x5e +#define SANREMO_TBAT_LOW_TH 0x5f +#define SANREMO_GPADC1_LOW_TH 0x60 +#define SANREMO_GPADC2_LOW_TH 0x61 +#define SANREMO_GPADC3_LOW_TH 0x62 +#define SANREMO_VBAT_UPP_TH 0x63 +#define SANREMO_VCHG_UPP_TH 0x64 +#define SANREMO_VSYS_UPP_TH 0x65 +#define SANREMO_TINT_UPP_TH 0x66 +#define SANREMO_TBAT_UPP_TH 0x67 +#define SANREMO_GPADC1_UPP_TH 0x68 +#define SANREMO_GPADC2_UPP_TH 0x69 +#define SANREMO_GPADC3_UPP_TH 0x6a +#define SANREMO_IBAT_MEAS1 0x6b +#define SANREMO_IBAT_MEAS2 0x6c +#define SANREMO_VBAT_MEAS1 0x6d +#define SANREMO_VBAT_MEAS2 0x6e +#define SANREMO_VCHG_MEAS1 0x6f +#define SANREMO_VCHG_MEAS2 0x70 +#define SANREMO_VSYS_MEAS1 0x71 +#define SANREMO_VSYS_MEAS2 0x72 +#define SANREMO_TINT_MEAS1 0x73 +#define SANREMO_TINT_MEAS2 0x74 +#define SANREMO_GPADC0_MEAS1 0x75 +#define SANREMO_GPADC0_MEAS2 0x76 +#define SANREMO_TBAT_MEAS1 0x77 +#define SANREMO_TBAT_MEAS2 0x78 +#define SANREMO_GPADC2_MEAS1 0x79 +#define SANREMO_GPADC2_MEAS2 0x7a +#define SANREMO_GPADC3_MEAS1 0x7b +#define SANREMO_GPADC3_MEAS2 0x7c +#define SANREMO_TSIX_MEAS1 0x8d +#define SANREMO_TSIX_MEAS2 0x8e +#define SANREMO_TSIY_MEAS1 0x8f +#define SANREMO_TSIY_MEAS2 0x90 +#define SANREMO_TSIZ1_MEAS1 0x91 +#define SANREMO_TSIZ1_MEAS2 0x92 +#define SANREMO_TSIZ2_MEAS1 0x93 +#define SANREMO_TSIZ2_MEAS2 0x94 +#define SANREMO_CCNT1 0x95 +#define SANREMO_CCNT2 0x96 +#define SANREMO_VBAT_AVG 0x97 +#define SANREMO_VCHG_AVG 0x98 +#define SANREMO_VSYS_AVG 0x99 +#define SANREMO_VBAT_MIN 0x9a +#define SANREMO_VCHG_MIN 0x9b +#define SANREMO_VSYS_MIN 0x9c +#define SANREMO_VBAT_MAX 0x9d +#define SANREMO_VCHG_MAX 0x9e +#define SANREMO_VSYS_MAX 0x9f +#define SANREMO_RTC1 0xa0 +#define SANREMO_RTC_COUNTER1 0xa1 +#define SANREMO_RTC_COUNTER2 0xa2 +#define SANREMO_RTC_COUNTER3 0xa3 +#define SANREMO_RTC_COUNTER4 0xa4 +#define SANREMO_RTC_EXPIRE1 0xa5 +#define SANREMO_RTC_EXPIRE2 0xa6 +#define SANREMO_RTC_EXPIRE3 0xa7 +#define SANREMO_RTC_EXPIRE4 0xa8 +#define SANREMO_RTC_TRIM_INT1 0xa9 +#define SANREMO_RTC_TRIM_INT2 0xaa +#define SANREMO_RTC_TRIM_FRAC1 0xab +#define SANREMO_RTC_TRIM_FRAC2 0xac +#define SANREMO_RTC_MISC1 0xad +#define SANREMO_RTC_MISC2 0xae +#define SANREMO_RTC_MISC3 0xaf + +/*Audio*/ +#define SANREMO_AUDIO_REG_BASE 0xb0 +#define SANREMO_AUDIO_REG_LEN 0x3b +#define SANREMO_AUDIO_PCM_INTERFACE_1 0x00 +#define SANREMO_AUDIO_PCM_INTERFACE_2 0x01 +#define SANREMO_AUDIO_PCM_INTERFACE_3 0x02 +#define SANREMO_AUDIO_ADC_PCM 0x03 +#define SANREMO_AUDIO_ADC_1 0x04 +#define SANREMO_AUDIO_ADC_2 0x05 +#define SANREMO_AUDIO_ADC_3 0x06 +#define SANREMO_AUDIO_ADC_4 0x07 +#define SANREMO_AUDIO_ADC_5 0x08 +#define SANREMO_AUDIO_ADC_6 0x09 +#define SANREMO_AUDIO_ADC_7 0x0a +#define SANREMO_AUDIO_I2S_INTERFACE_1 0x0b +#define SANREMO_AUDIO_I2S_INTERFACE_2 0x0c +#define SANREMO_AUDIO_I2S_INTERFACE_3 0x0d +#define SANREMO_AUDIO_Equalizer_N0_1 0x0e +#define SANREMO_AUDIO_Equalizer_N0_2 0x0f +#define SANREMO_AUDIO_Equalizer_N1_1 0x11 +#define SANREMO_AUDIO_Equalizer_N1_2 0x12 +#define SANREMO_AUDIO_Equalizer_D1_1 0x13 +#define SANREMO_AUDIO_Equalizer_D1_2 0x14 +#define SANREMO_AUDIO_Side_Tone_1 0x15 +#define SANREMO_AUDIO_Side_Tone_2 0x16 +#define SANREMO_AUDIO_Left_Gain1 0x17 +#define SANREMO_AUDIO_Left_Gain2 0x18 +#define SANREMO_AUDIO_Right_Gain1 0x19 +#define SANREMO_AUDIO_Right_Gain2 0x1a +#define SANREMO_AUDIO_DWA_OFFSET 0x1b +#define SANREMO_AUDIO_OFFSET_LEFT1 0x1c +#define SANREMO_AUDIO_OFFSET_LEFT2 0x1d +#define SANREMO_AUDIO_OFFSET_RIGHT1 0x1e +#define SANREMO_AUDIO_OFFSET_RIGHT2 0x1f +#define SANREMO_AUDIO_ADC_ANALOG_PROGRAM1 0x20 +#define SANREMO_AUDIO_ADC_ANALOG_PROGRAM2 0x21 +#define SANREMO_AUDIO_ADC_ANALOG_PROGRAM3 0x22 +#define SANREMO_AUDIO_ADC_ANALOG_PROGRAM4 0x23 +#define SANREMO_AUDIO_A2A_PATH_PROGRAMMING 0x24 +#define SANREMO_AUDIO_DAC_HS1_CTRL 0x25 +#define SANREMO_AUDIO_DAC_HS2_CTRL 0x26 +#define SANREMO_AUDIO_DAC_LO1_CTRL 0x27 +#define SANREMO_AUDIO_DAC_LO2_CTRL 0x28 +#define SANREMO_AUDIO_DAC_EAR_SPKRPHNE_GAIN 0x29 +#define SANREMO_AUDIO_MISC_AUDIO 0x2a +#define SANREMO_AUDIO_AUDIO_SUPPLIES1 0x2b +#define SANREMO_AUDIO_AUDIO_SUPPLIES2 0x2c +#define SANREMO_AUDIO_ADC_ANALOG_ENABLES 0x2d +#define SANREMO_AUDIO_ADC_DIGITAL_ENABLES 0x2e +#define SANREMO_AUDIO_DAC_ANALOG_ENABLES 0x2f +#define SANREMO_AUDIO_DAC_DIGITAL_ENABLES 0x31 +#define SANREMO_AUDIO_AUDIO_CAL1 0x32 +#define SANREMO_AUDIO_AUDIO_CAL2 0x33 +#define SANREMO_AUDIO_AUDIO_CAL3 0x34 +#define SANREMO_AUDIO_AUDIO_CAL4 0x35 +#define SANREMO_AUDIO_AUDIO_CAL5 0x36 +#define SANREMO_AUDIO_ANALOG_INPUT_SEL1 0x37 +#define SANREMO_AUDIO_ANALOG_INPUT_SEL2 0x38 +#define SANREMO_AUDIO_MIC_BUTTON_DETECTION 0x39 +#define SANREMO_AUDIO_HEADSET_DETECTION 0x3a +#define SANREMO_AUDIO_SHORTS 0x3b + +#define SANREMO_VBUCK1_CNT(x) ((x < 0) ? -1 : \ + ((x < 800) ? (x / 25 + 0x20) : \ + ((x < 1525) ? ((x - 800) / 25) \ + : -1))) +#define SANREMO_VBUCK1_MV(x) ((x < 0) ? -1 : \ + ((x < 0x1C) ? (x * 25 + 800) : \ + ((x < 0x20) ? 1500 : \ + ((x < 0x40) ? ((x - 0x20) * 25) \ + : -1)))) + +#define SANREMO_VBUCK2_CNT(x) ((x < 0) ? -1 : \ + ((x < 3050) ? (x / 50) : -1)) + +#define SANREMO_VBUCK2_MV(x) ((x < 0) ? -1 : \ + ((x < 0x3C) ? (x * 50) : \ + ((x < 0x40) ? 3000 : -1))) + +#define SANREMO_VBUCK3_CNT(x) ((x < 0) ? -1 : \ + ((x < 1525) ? (x / 25) : -1)) + +#define SANREMO_VBUCK3_MV(x) ((x < 0) ? -1 : \ + ((x < 0x3C) ? (x * 25) : \ + ((x < 0x40) ? 1500 : -1))) + +#define SANREMO_LDO1_CNT(x) ((x == 1800) ? 0 : \ + ((x == 1200) ? 1 : \ + ((x == 2800) ? 2 : -1))) + +#define SANREMO_LDO1_MV(x) ((x == 0) ? 1800 : \ + ((x == 1) ? 1200 : \ + ((x == 2) ? 2800 : -1))) + +#define SANREMO_LDO1_SLEEP_CNT(x) ((x == 1800) ? 0 : \ + ((x == 1200) ? 1 : -1)) + +#define SANREMO_LDO1_SLEEP_MV(x) ((x == 0) ? 1800 : \ + ((x == 1) ? 1200 : -1)) + +#define SANREMO_LDO2_CNT(x) ((x < 1800) ? -1 : \ + ((x < 1950) ? ((x - 1800) / 50) \ + : ((x < 2700) ? -1 : \ + ((x < 2950) ? ((x - 2550) / 50) \ + : -1)))) + +#define SANREMO_LDO2_MV(x) ((x < 0) ? -1 : \ + ((x < 3) ? (x * 50 + 1800) : \ + ((x < 8) ? (x * 50 + 2550) : -1))) + +#define SANREMO_LDO5_CNT(x) ((x < 2900) ? -1 : \ + ((x < 3200) ? ((x - 2900) / 100)\ + : ((x == 3300) ? 3 : -1))) + +#define SANREMO_LDO5_MV(x) ((x < 0) ? -1 : \ + ((x < 3) ? (x * 100 + 2900) : \ + ((x == 3) ? 3300 : -1))) + +#define SANREMO_LDO5_SLEEP_CNT(x) ((x == 2900) ? 0 : -1) + +#define SANREMO_LDO5_SLEEP_MV(x) ((x == 0) ? 2900 : -1) + +#define SANREMO_LDO6_CNT(x) ((x < 1800) ? -1 : \ + ((x < 1950) ? ((x - 1800) / 50) \ + : ((x < 2600) ? -1 : \ + ((x < 2850) ? ((x - 2450) / 50) \ + : -1)))) + +#define SANREMO_LDO6_MV(x) ((x < 0) ? -1 : \ + ((x < 3) ? (x * 50 + 1800) : \ + ((x < 8) ? (x * 50 + 2450) : -1))) + +#define SANREMO_LDO10_CNT(x) ((x == 1200) ? 8 : \ + ((x < 1800) ? -1 : \ + ((x < 1950) ? ((x - 1800) / 50) \ + : ((x < 2700) ? -1 \ + : ((x < 2950) ? ((x - 2550) / 50)\ + : -1))))) + +#define SANREMO_LDO10_MV(x) ((x < 0) ? -1 : \ + ((x < 3) ? (x * 50 + 1800) : \ + ((x < 8) ? (x * 50 + 2550) : 1200))) + +#define SANREMO_LDO12_CNT(x) ((x == 1200) ? 8 : \ + ((x < 1800) ? -1 : \ + ((x < 2000) ? ((x - 1800) / 100) :\ + ((x < 2700) ? -1 : \ + ((x < 3200) ? ((x - 2500) / 100) :\ + ((x == 3300) ? 7 : \ + -1)))))) + +#define SANREMO_LDO12_MV(x) ((x < 0) ? -1 : \ + ((x < 2) ? (x * 100 + 1800) : \ + ((x < 7) ? (x * 100 + 2500) : \ + ((x == 7) ? 3300 : 1200)))) + +enum { + SAN_BUCK1 = 1, + SAN_BUCK2, + SAN_BUCK3, + SAN_LDO1, + SAN_LDO2, + SAN_LDO3, + SAN_LDO4, + SAN_LDO5, + SAN_LDO6, + SAN_LDO7, + SAN_LDO8, + SAN_LDO9, + SAN_LDO10, + SAN_LDO12, + SAN_LDO14, + SAN_BUCK1_SLEEP, + SAN_BUCK2_SLEEP, + SAN_BUCK3_SLEEP, + SAN_LDO1_SLEEP, + SAN_LDO2_SLEEP, + SAN_LDO3_SLEEP, + SAN_LDO4_SLEEP, + SAN_LDO5_SLEEP, + SAN_LDO6_SLEEP, + SAN_LDO7_SLEEP, + SAN_LDO8_SLEEP, + SAN_LDO9_SLEEP, + SAN_LDO10_SLEEP, + SAN_LDO12_SLEEP, + SAN_LDO14_SLEEP, + SAN_LDO_MAX, +}; + +#define SANREMO_REG(x) ((x < SAN_BUCK1) ? -1 : \ + ((x < SAN_LDO1) ? ((x - SAN_BUCK1) \ + + 0x24) : ((x < SAN_BUCK1_SLEEP) ? \ + (x -SAN_LDO1 +0x10) : \ + ((x < SAN_LDO1_SLEEP) \ + ? (x - SAN_BUCK1_SLEEP + 0x21) :\ + ((x <= SAN_LDO14_SLEEP) ? \ + (x - SAN_LDO1_SLEEP + 0x10) : -1))))) + +struct sanremo_touch_platform_data { + unsigned int ts[4]; + int ts_revert[2]; +}; + +struct sanremo_platform_data { + int (*init_irq)(void); + int (*ack_irq)(void); + void (*platform_init)(void); + spinlock_t lock; + struct work_struct work; + struct power_chip *power_chips; + struct sanremo_touch_platform_data *tsi; +}; + +/* General */ +int sanremo_read(u8 reg, u8 *val); +int sanremo_write(u8 reg, u8 val); + +/* TSI */ +int sanremo_enable_pen_down_irq(int enable); +int sanremo_tsi_poweron(void); +int sanremo_tsi_poweroff(void); +int sanremo_tsi_readxy(u16 *x, u16 *y, int *pen_state); +int sanremo_tsi_enable_pen(int pen_en); +int sanremo_tsi_enable_tsi(int tsi_en); +int sanremo_enable_mic_irq(int enable); +int sanremo_enable_headset_irq(int enable); +int sanremo_tsi_enable_mic(int mic_en); +int sanremo_tsi_enable_headset(int headset_en); + +int sanremo_get_chip_id(void); +int sanremo_enable_headset_detect(void (*func)(unsigned long ), int enable); +int sanremo_get_headset_state(void); +/* For Audio */ +int sanremo_codec_read(u8 reg, u8 *val); +int sanremo_codec_write(u8 reg, u8 val); + +int sanremo_set_vibrator(unsigned char value); + +void sanremo_turn_off_power(void); +int sanremo_get_vbat(void); +int sanremo_usb_connect(void); + +#endif + diff --git a/arch/arm/mach-mmp/include/mach/ssp.h b/arch/arm/mach-mmp/include/mach/ssp.h new file mode 100644 index 00000000000000..af465127c093a4 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/ssp.h @@ -0,0 +1 @@ +#include diff --git a/arch/arm/mach-mmp/include/mach/system.h b/arch/arm/mach-mmp/include/mach/system.h index 4f5b0e0ce6cf8f..326568f9ddbecd 100644 --- a/arch/arm/mach-mmp/include/mach/system.h +++ b/arch/arm/mach-mmp/include/mach/system.h @@ -9,6 +9,12 @@ #ifndef __ASM_MACH_SYSTEM_H #define __ASM_MACH_SYSTEM_H +#include +#include +#include + +#define MPMU_APRR_WDTR (1<<4) + static inline void arch_idle(void) { cpu_do_idle(); @@ -16,6 +22,40 @@ static inline void arch_idle(void) static inline void arch_reset(char mode, const char *cmd) { - cpu_reset(0); + u32 reg; + u32 watchdog_virt_base; + + if (cpu_is_pxa168()) + watchdog_virt_base = TIMERS1_VIRT_BASE; + else if (cpu_is_pxa910()) + watchdog_virt_base = CP_TIMERS2_VIRT_BASE; + else + return ; + + /* negate hardware reset to the WDT after system reset */ + reg = readl(MPMU_APRR) | MPMU_APRR_WDTR; + writel(reg, MPMU_APRR); + + /*disable functional WDT clock */ + writel(0x1, MPMU_WDTPCR); + + /* clear previous WDT status */ + writel(0xbaba, watchdog_virt_base + TMR_WFAR); + writel(0xeb10, watchdog_virt_base + TMR_WSAR); + writel(0, watchdog_virt_base + TMR_WSR); + + /* set match counter */ + writel(0xbaba, watchdog_virt_base + TMR_WFAR); + writel(0xeb10, watchdog_virt_base + TMR_WSAR); + writel(0xf, watchdog_virt_base + TMR_WMR); + + /* enable WDT reset */ + writel(0xbaba, watchdog_virt_base + TMR_WFAR); + writel(0xeb10, watchdog_virt_base + TMR_WSAR); + writel(0x3, watchdog_virt_base + TMR_WMER); + + /*enable functional WDT clock */ + writel(0x3, MPMU_WDTPCR); } + #endif /* __ASM_MACH_SYSTEM_H */ diff --git a/arch/arm/mach-mmp/include/mach/timer_services.h b/arch/arm/mach-mmp/include/mach/timer_services.h new file mode 100644 index 00000000000000..daaedac906c935 --- /dev/null +++ b/arch/arm/mach-mmp/include/mach/timer_services.h @@ -0,0 +1,114 @@ +/* + * linux/arch/arm/mach-ttc/timer_services.c + * + * Support for the MMP Development Platform + * + * Copyright (C) 2008 Marvell International Ltd. + * + * 2009-08-1: Ofer Zaarur + * + * 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. + */ +#ifndef TIMER_SERV_H +#define TIMER_SERV_H 1 + +#define RESOLUTION_0 (CLOCK_TICK_RATE / 1500 + 1/2) /* Define resolution */ +#define RESOLUTION_1 0 +#define RESOLUTION_2 0 +#define COUNTER_0 0 +#define COUNTER_1 1 +#define COUNTER_2 2 + +#define MATCH_REGISTER_OFFSET sizeof(int) /* HW dependent */ + +struct timer_device { + struct platform_device *pdev; + struct list_head node; + void __iomem *mmio_base; + const char *label; + int counter_id; + int type; + int use_count; + int irq; + unsigned int jiff; + unsigned long resolution; +}; + + +struct timer_dev { + struct timer_device *timer; + u32 counter; + u32 resolution; + int irq; + +}; + +enum cpu_timer_type { + TIMER_UNDEFINED = 0, + MMP_TIMER, +}; + + +struct timer_device *timer_request(int counter, const char *label); +void timer_free(struct timer_device *timer); +void timer_services_disable(int id); +void timer_services_enable(int id, int period); +unsigned int timer_services_counter_read(int id); +unsigned long get_jiff(int id); + + +/*mapping the MMP timer registers */ +#define PHYS_IO_START 0xD4000000 /* Physical Address of IO */ +#define AP_APB_BASE_OFFSET 0x00010000 /* AP ABP base address offset */ +/* Timer definitions */ + +#define TMR_IO_BASE_OFFSET 0x00014000 /* Timers IO Base Offset */ +#define TMR2_IO_BASE_OFFSET 0x00016000 /* Timer2 IO Base Offset */ +#define TMR_CCR_OFFSET 0x0000 /* Timer Clock Control Register */ +#define TMR_T0_M0_OFFSET 0x0004 /* Timer Map Registers */ +#define TMR_T0_M1_OFFSET 0x0008 +#define TMR_T0_M2_OFFSET 0x000C +#define TMR_T1_M0_OFFSET 0x0010 +#define TMR_T1_M1_OFFSET 0x0014 +#define TMR_T1_M2_OFFSET 0x0018 +#define TMR_T2_M0_OFFSET 0x001C +#define TMR_T2_M1_OFFSET 0x0020 +#define TMR_T2_M2_OFFSET 0x0024 +#define TMR_CR0_OFFSET 0x0028 /* Timer Control Registers */ +#define TMR_CR1_OFFSET 0x002C +#define TMR_CR2_OFFSET 0x0030 +#define TMR_SR0_OFFSET 0x0034 /* Timer Status Registers */ +#define TMR_SR1_OFFSET 0x0038 +#define TMR_SR2_OFFSET 0x003C +#define TMR_IER0_OFFSET 0x0040 /* Timer Interrupt Enable Reg*/ +#define TMR_IER1_OFFSET 0x0044 +#define TMR_IER2_OFFSET 0x0048 +#define TMR_PLVR0_OFFSET 0x004C /* Timer Preload Value Registers */ +#define TMR_PLVR1_OFFSET 0x0050 +#define TMR_PLVR2_OFFSET 0x0054 +#define TMR_PLCR0_OFFSET 0x0058 /* Timer Preload Control Registers*/ +#define TMR_PLCR1_OFFSET 0x005C +#define TMR_PLCR2_OFFSET 0x0060 +#define TMR_WMER_OFFSET 0x0064 /* Timer Watchdog Match Enable reg*/ +#define TMR_WMR_OFFSET 0x0068 /* Timer Watchdog Match Register */ +#define TMR_WVR_OFFSET 0x006C /* Timer Watchdog Value Register */ +#define TMR_WSR_OFFSET 0x0070 /* Timer Watchdog Status Register */ +#define TMR_ICR0_OFFSET 0x0074 /* Timer Interrupt Clear Registers*/ +#define TMR_ICR1_OFFSET 0x0078 +#define TMR_ICR2_OFFSET 0x007C +#define TMR_WICR_OFFSET 0x0080 /*Timer Watchdog Interrupt Clr Reg*/ +#define TMR_CER_OFFSET 0x0084 /* Timer Count Enable Register */ +#define TMR_CMR_OFFSET 0x0088 /* Timer Count Mode Register */ +#define TMR_ILR0_OFFSET 0x008C /*Timer Interrupt Length Registers*/ +#define TMR_ILR1_OFFSET 0x0090 +#define TMR_ILR2_OFFSET 0x0094 +#define TMR_WCR_OFFSET 0x0098 /*Watchdog Counter Reset Register*/ +#define TMR_WFAR_OFFSET 0x009C /*Watchdog First Access Register*/ +#define TMR_WSAR_OFFSET 0x00A0 /*Watchdog Second Access Register*/ +#define TMR_CVWR0_OFFSET 0x00A4 /*Counters Value Write 4 Read Request*/ +#define TMR_CVWR1_OFFSET 0x00A8 +#define TMR_CVWR2_OFFSET 0x00AC + +#endif diff --git a/arch/arm/mach-mmp/include/mach/timex.h b/arch/arm/mach-mmp/include/mach/timex.h index 6cebbd0ca8f432..4dc4e2263c96a1 100644 --- a/arch/arm/mach-mmp/include/mach/timex.h +++ b/arch/arm/mach-mmp/include/mach/timex.h @@ -6,4 +6,8 @@ * published by the Free Software Foundation. */ +#ifdef CONFIG_PXA_32KTIMER +#define CLOCK_TICK_RATE 32768 +#else #define CLOCK_TICK_RATE 3250000 +#endif diff --git a/arch/arm/mach-mmp/include/mach/uncompress.h b/arch/arm/mach-mmp/include/mach/uncompress.h index 85bd8a2d84b5e4..9a500a8be3ba73 100644 --- a/arch/arm/mach-mmp/include/mach/uncompress.h +++ b/arch/arm/mach-mmp/include/mach/uncompress.h @@ -13,11 +13,19 @@ #define UART1_BASE (APB_PHYS_BASE + 0x36000) #define UART2_BASE (APB_PHYS_BASE + 0x17000) #define UART3_BASE (APB_PHYS_BASE + 0x18000) - -static volatile unsigned long *UART; +#define UART4_BASE (APB_PHYS_BASE + 0x26000) static inline void putc(char c) { + volatile unsigned long *UART; + + if (machine_is_ipcam()) + UART = (unsigned long *)UART4_BASE; + else if (machine_is_avengers_lite() || machine_is_edge()) + UART = (unsigned long *)UART3_BASE; + else + UART = (unsigned long *)UART2_BASE; + /* UART enabled? */ if (!(UART[UART_IER] & UART_IER_UUE)) return; @@ -35,17 +43,8 @@ static inline void flush(void) { } -static inline void arch_decomp_setup(void) -{ - /* default to UART2 */ - UART = (unsigned long *)UART2_BASE; - - if (machine_is_avengers_lite()) - UART = (unsigned long *)UART3_BASE; -} - /* * nothing to do */ - +#define arch_decomp_setup() #define arch_decomp_wdog() diff --git a/arch/arm/mach-mmp/io.c b/arch/arm/mach-mmp/io.c new file mode 100644 index 00000000000000..2b78a2c7aa806b --- /dev/null +++ b/arch/arm/mach-mmp/io.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include + + +extern u8 pxa168_pcie_read8(u32 addr); +extern void pxa168_pcie_write8(u8 val, u32 addr); + +void _pxa168_memcpy_toio(volatile void __iomem *to, const void *from, + size_t count) +{ + const unsigned char *f = from; + while (count) { + count--; + writeb(*f, to); + pxa168_pcie_write8(*f, (u32) to); + f++; + to++; + } +} +EXPORT_SYMBOL(_pxa168_memcpy_toio); + +void _pxa168_memcpy_fromio(void *to, const volatile void __iomem *from, + size_t count) +{ + unsigned char *t = to; + while (count) { + count--; + *t = pxa168_pcie_read8((u32) from) & 0xff; + t++; + from++; + } +} +EXPORT_SYMBOL(_pxa168_memcpy_fromio); + +void _pxa168_memset_io(volatile void __iomem *dst, int c, size_t count) +{ + while (count) { + count--; + pxa168_pcie_write8(c, (u32) dst); + dst++; + } +} +EXPORT_SYMBOL(_pxa168_memset_io); diff --git a/arch/arm/mach-mmp/ipcam.c b/arch/arm/mach-mmp/ipcam.c new file mode 100644 index 00000000000000..b706b9498f74c3 --- /dev/null +++ b/arch/arm/mach-mmp/ipcam.c @@ -0,0 +1,516 @@ +/* + * linux/arch/arm/mach-mmp/ipcam.c + * + * Support for the Marvell PXA168-based IPCAM Development Platform. + * + * 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 + * publishhed 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 "common.h" +#include + +/* MFP configurations */ +static unsigned long ipcam_pin_config[] __initdata = { + /* DEBUG UART3 */ + MFP_CFG(GPIO30, AF2), + MFP_CFG(GPIO31, AF2), + + /* MMC3_CD */ + MFP_CFG(GPIO2, AF5), + + /* MMC3 DAT3-0/CLK/CMD */ + MFP_CFG(GPIO4, AF6), + MFP_CFG(GPIO5, AF6), + MFP_CFG(GPIO6, AF6), + MFP_CFG(GPIO7, AF6), + MFP_CFG(GPIO35, AF6), + MFP_CFG(GPIO36, AF6), + + /* MMC4_CD */ + MFP_CFG(GPIO77, AF0), + + /* MMC4 DAT3-0/CLK/CMD */ + MFP_CFG(GPIO78, AF5), + MFP_CFG(GPIO79, AF5), + MFP_CFG(GPIO80, AF5), + MFP_CFG(GPIO81, AF5), + MFP_CFG(GPIO82, AF5), + MFP_CFG(GPIO83, AF5), + + /* SMC */ + MFP_CFG(GPIO8, AF0), + MFP_CFG(GPIO9, AF0), + MFP_CFG(GPIO10, AF0), + MFP_CFG(GPIO11, AF0), + MFP_CFG(GPIO12, AF0), + MFP_CFG(GPIO13, AF0), + MFP_CFG(GPIO14, AF0), + MFP_CFG(GPIO15, AF0), + MFP_CFG(GPIO19, AF0), + MFP_CFG(GPIO21, AF0), + MFP_CFG(GPIO22, AF0), + MFP_CFG(GPIO28, AF0), + MFP_CFG(GPIO33, AF5), + + /* Fast Ethernet */ + MFP_CFG(GPIO86, AF5), + MFP_CFG(GPIO87, AF5), + MFP_CFG(GPIO88, AF5), + MFP_CFG(GPIO89, AF5), + MFP_CFG(GPIO90, AF5), + MFP_CFG(GPIO91, AF5), + MFP_CFG(GPIO92, AF5), + MFP_CFG(GPIO93, AF5), + MFP_CFG(GPIO94, AF5), + MFP_CFG(GPIO95, AF5), + MFP_CFG(GPIO96, AF5), + MFP_CFG(GPIO97, AF5), + MFP_CFG(GPIO98, AF5), + MFP_CFG(GPIO99, AF5), + MFP_CFG(GPIO100, AF5), + MFP_CFG(GPIO101, AF5), + MFP_CFG(GPIO103, AF5), + /* ENET_RESET_N */ + MFP_CFG(GPIO102, AF0), + /* ENET_COMA_N */ + MFP_CFG(GPIO60, AF0), + + /* I2C */ + MFP_CFG(GPIO105, AF1), + MFP_CFG(GPIO106, AF1), + + /* USB_C_HPEN */ + MFP_CFG(GPIO85, AF7), + + /* board ID */ + /* + MFP_CFG(GPIO20, AF0), + MFP_CFG(GPIO84, AF0), + */ + /* not used */ + MFP_CFG(GPIO16, AF0), + MFP_CFG(GPIO17, AF0), + MFP_CFG(GPIO18, AF0), + MFP_CFG(GPIO23, AF0), + MFP_CFG(GPIO24, AF0), + MFP_CFG(GPIO25, AF0), + MFP_CFG(GPIO26, AF0), + MFP_CFG(GPIO27, AF0), + MFP_CFG(GPIO29, AF0), + MFP_CFG(GPIO32, AF0), + MFP_CFG(GPIO34, AF0), + MFP_CFG(GPIO43, AF0), + MFP_CFG(GPIO47, AF0), + MFP_CFG(GPIO49, AF0), + MFP_CFG(GPIO51, AF0), + MFP_CFG(GPIO52, AF0), + MFP_CFG(GPIO53, AF0), + MFP_CFG(GPIO104, AF0), + MFP_CFG(GPIO109, AF0), + MFP_CFG(GPIO110, AF0), + MFP_CFG(GPIO118, AF0), + MFP_CFG(GPIO119, AF0), + + /***** BOARD_ID *****/ + MFP_CFG_PULL_HIGH(GPIO120, AF0), + MFP_CFG_PULL_HIGH(GPIO121, AF0), + MFP_CFG_PULL_HIGH(GPIO122, AF0), + + /* Camera */ + MFP_CFG(GPIO37, AF4), + MFP_CFG(GPIO38, AF4), + MFP_CFG(GPIO39, AF4), + MFP_CFG(GPIO40, AF4), + MFP_CFG(GPIO41, AF4), + MFP_CFG(GPIO42, AF4), + MFP_CFG(GPIO44, AF4), + MFP_CFG(GPIO45, AF4), + MFP_CFG(GPIO46, AF4), + MFP_CFG(GPIO48, AF4), + MFP_CFG(GPIO50, AF4), + MFP_CFG(GPIO54, AF4), + MFP_CFG(GPIO55, AF4), + + /* SSP2, SPI NOR */ + GPIO107_SPI_NOR_RXD, + GPIO108_SPI_NOR_TXD, + GPIO110_GPIO, + GPIO111_SPI_NOR_CLK, + + /* SSP1, AUDIO */ + MFP_CFG(GPIO113, AF0), + MFP_CFG(GPIO114, AF1), + MFP_CFG(GPIO115, AF1), + MFP_CFG(GPIO116, AF2), + MFP_CFG(GPIO117, AF2), + + /* OV529 */ + MFP_CFG(GPIO20, AF0), + MFP_CFG(GPIO61, AF0), /* 529_RST */ + MFP_CFG(GPIO63, AF0), /* 529_SNAP */ + MFP_CFG(GPIO65, AF0), /* 529_RTS */ +}; + +#if defined(CONFIG_PXA168_CAMERA) && defined(CONFIG_VIDEO_OV7740) +/* sensor init */ +static int sensor_power_onoff(int on, int sensor) +{ + int ov7740_pwr_down; + + ov7740_pwr_down = mfp_to_gpio(MFP_PIN_GPIO62); + if (gpio_request(ov7740_pwr_down , "ov7740_pwr_down")) { + printk(KERN_INFO "gpio %d request failed\n", ov7740_pwr_down); + return -EIO; + } + + if (on) { + gpio_direction_output(ov7740_pwr_down, 0); + printk(KERN_ERR "ov7740 power on\n"); + } else { + gpio_direction_output(ov7740_pwr_down, 1); + printk(KERN_ERR "ov7740 power off\n"); + } + gpio_free(ov7740_pwr_down); + + return 0; +} + + +static struct sensor_platform_data ov7740_sensor_data = { + .id = SENSOR_LOW, + .power_on = sensor_power_onoff, +}; +#endif + +#if defined(CONFIG_VIDEO_OV529) +static int ov529_rst; +static int ov529_ptype; +static int ov529_sel_uart; +static int ov529_pwait; +static int ov529_pcd; +static int ov529_rts; + +static void ov529_power_on(int on) +{ + ov529_rst = mfp_to_gpio(MFP_PIN_GPIO61); + gpio_request(ov529_rst, "ov529_reset"); + if (on) { + /* Pull up ov529 reset pin */ + gpio_direction_output(ov529_rst, 1); + printk(KERN_INFO"ov529 power on\n"); + /* sensor_power_onoff(1, 0); */ + } else { + /* sensor_power_onoff(0, 0); */ + /* Pull down ov529 reset pin */ + gpio_direction_output(ov529_rst, 0); + printk(KERN_INFO"ov529 power off\n"); + } + gpio_free(ov529_rst); +} + +static void ov529_sensor_on(void) +{ + sensor_power_onoff(1, 0); +} +static void ov529_set_ptype(int on) +{ + printk(KERN_ERR "set ov529 ptype to %d\n", on); + gpio_direction_output(ov529_ptype, on); +} + +static int ov529_get_sel_uart(void) +{ + int ret; + + ret = gpio_get_value(ov529_sel_uart); + + return ret; +} + +static int ov529_get_pwait(void) +{ + int ret = 0; + + return ret & (1 << 28); +} + +static void ov529_set_pcd(int on) +{ + gpio_direction_output(ov529_pcd, on); +} + +static void ov529_set_rts(int on) +{ + gpio_direction_output(ov529_rts, on); +} +static int ov529_init(void) +{ + unsigned long temp, temp1, temp2; + + temp = (unsigned long)ioremap_nocache(0xD4283800, 0x200); + if (!temp) { + printk(KERN_ERR "Unable to ioremap smc registers\n"); + return -EINVAL; + } + temp1 = *(volatile unsigned long *)(temp + 0x90); + temp1 = 0x1000002; + *(volatile unsigned long *)(temp + 0x90) = temp1; + temp1 = *(volatile unsigned long *)(temp + 0x20); + temp1 = 0x42425242; + *(volatile unsigned long *)(temp + 0x20) = temp1; + temp2 = *(volatile unsigned long *)(temp + 0x20); + + iounmap(temp); + + ov529_ptype = mfp_to_gpio(MFP_PIN_GPIO63); + ov529_sel_uart = mfp_to_gpio(MFP_PIN_GPIO20); + ov529_pcd = mfp_to_gpio(MFP_PIN_GPIO33); + ov529_rts = mfp_to_gpio(MFP_PIN_GPIO65); + + if (gpio_request(ov529_ptype, "ov529_ptype")) { + printk(KERN_INFO "gpio %d request failed\n", ov529_ptype); + goto err_ptype; + } + if (gpio_request(ov529_sel_uart, "ov529_sel_uart")) { + printk(KERN_INFO "gpio %d request failed\n", ov529_rst); + goto err_sel_uart; + } + gpio_direction_input(ov529_sel_uart); + gpio_request(ov529_pcd, "ov529_pcd"); + gpio_request(ov529_rts, "ov520_rts"); + + return 0; + +err_sel_uart: + gpio_free(ov529_ptype); +err_ptype: + return -EIO; +} + +static void ov529_release(void) +{ + gpio_free(ov529_rst); + gpio_free(ov529_ptype); + gpio_free(ov529_sel_uart); + gpio_free(ov529_set_rts); +} + +struct ov529_platform_data ov529_encoder_data = { + .name = "ov529_magic", + .init = ov529_init, + .release = ov529_release, + .power_on = ov529_power_on, + .turnon_sensor = ov529_sensor_on, + .set_ptype = ov529_set_ptype, + .get_sel_uart = ov529_get_sel_uart, + .get_pwait = ov529_get_pwait, + .set_pcd = ov529_set_pcd, + .set_rts = ov529_set_rts, +}; +#endif + +static struct i2c_pxa_platform_data pwri2c_info __initdata = { + .use_pio = 1, +}; + + +/* i2c bus: audio codec */ +static struct i2c_board_info ipcam_i2c_board_info[] = { +}; + +/* power i2c bus: camera */ +static struct i2c_board_info pwri2c_board_info[] = +{ +#if defined(CONFIG_PXA168_CAMERA) && defined(CONFIG_VIDEO_OV7740) + { + .type = "ov7740", + .addr = 0x21, + .platform_data = &ov7740_sensor_data, + /* .irq = */ + }, +#endif +}; + +#if defined(CONFIG_MMC_PXA_SDH) + +/* quirks define should sync with drivers/mmc/host/sdhci.h */ + +static struct pxasdh_platform_data ipcam_sdh3_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .init = pxa_mci_init, + .exit = pxa_mci_exit, + .get_cd = pxa_mci_get_cd, +}; + +static struct pxasdh_platform_data ipcam_sdh4_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .init = pxa_mci_init, + .exit = pxa_mci_exit, + .get_cd = pxa_mci_get_cd +}; + +static void __init ipcam_init_mmc(void) +{ + /* mmc3, external mmc/sd socket */ + pxa_mmc_slot[2].gpio_detect = 1; + pxa_mmc_slot[2].no_wp = 1; + pxa_mmc_slot[2].gpio_cd = mfp_to_gpio(MFP_PIN_GPIO2); + pxa168_add_sdh(2, &ipcam_sdh3_platform_data); + + /* mmc4, internal mmc/sd socket */ + pxa_mmc_slot[3].gpio_detect = 1; + pxa_mmc_slot[3].no_wp = 1; + pxa_mmc_slot[3].gpio_cd = mfp_to_gpio(MFP_PIN_GPIO77); + pxa168_add_sdh(3, &ipcam_sdh4_platform_data); +} +#endif + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static int ipcam_u2o_vbus_status(unsigned base) +{ + return VBUS_HIGH; +} + +static struct pxa_usb_plat_info ipcam_u2o_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_status = ipcam_u2o_vbus_status, +}; +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H +static int ipcam_u2h_vbus_set(int enable) +{ + return 0; +} +static struct pxa_usb_plat_info ipcam_u2h_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_set = ipcam_u2h_vbus_set, +}; +#endif + +#define ENET_RESET_N mfp_to_gpio(MFP_PIN_GPIO102) +#define ENET_COMA_N mfp_to_gpio(MFP_PIN_GPIO60) + +static int ipcam_eth_init(void) +{ + if (gpio_request(ENET_RESET_N, "ENET_RESET_N")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", ENET_RESET_N); + return -EIO; + } + + if (gpio_request(ENET_COMA_N, "ENET_COMA_N")) { + gpio_free(ENET_RESET_N); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", ENET_COMA_N); + return -EIO; + } + + gpio_direction_output(ENET_RESET_N, 1); + gpio_direction_output(ENET_COMA_N, 1); + + gpio_free(ENET_RESET_N); + gpio_free(ENET_COMA_N); + + return 0; +} + +static struct pxa168_eth_platform_data pxa168_eth_data = { + .phy_addr = 0, /* phy addr depends on boards */ + .force_phy_addr = 1, + .init = ipcam_eth_init, +}; + + + +#if defined(CONFIG_SPI_PXA2XX) || defined(CONFIG_SPI_PXA2XX_MODULE) +extern void spi_flashinit(void); +static struct pxa2xx_spi_master pxa_ssp_master_info = { + .num_chipselect = 1, +}; + +static void __init ipcam_init_spi(void) +{ + pxa168_add_ssp(1); + pxa168_add_spi(2, &pxa_ssp_master_info); + spi_flashinit(); +} +#else +static inline void ipcam_init_spi(void) {} +#endif + +DECLARE_SPI_PARTITIONS(ipcam_spi_partitions); + +static void __init ipcam_init(void) +{ + pxa168_set_vdd_iox(VDD_IO0, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO1, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO2, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO3, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO4, VDD_IO_3P3V); + pxa168_mfp_set_fastio_drive(MFP_DS02X); + + mfp_config(ARRAY_AND_SIZE(ipcam_pin_config)); + + /* on-chip devices */ + pxa168_add_uart(3); + pxa168_add_ssp(0); + pxa168_add_twsi(0, &pwri2c_info, ARRAY_AND_SIZE(ipcam_i2c_board_info)); + pxa168_add_twsi(1, &pwri2c_info, ARRAY_AND_SIZE(pwri2c_board_info)); +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&ipcam_u2o_info); +#endif +#ifdef CONFIG_USB_EHCI_PXA_U2H + pxa168_add_u2h(&ipcam_u2h_info); +#endif +#if defined(CONFIG_MMC_PXA_SDH) + ipcam_init_mmc(); +#endif + pxa168_add_mfu(&pxa168_eth_data); + + /* off-chip devices */ +#if defined(CONFIG_PXA168_CAMERA) && defined(CONFIG_VIDEO_OV7740) + pxa168_add_cam(); +#endif +#if defined(CONFIG_VIDEO_OV529) + pxa168_add_ov529(&ov529_encoder_data); +#endif + ipcam_init_spi(); +} + +MACHINE_START(IPCAM, "PXA168 IPCAM Development Platform") + .phys_io = APB_PHYS_BASE, + .boot_params = 0x00000100, + .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, + .map_io = pxa_map_io, + .init_irq = pxa168_init_irq, + .timer = &pxa168_timer, + .init_machine = ipcam_init, +MACHINE_END diff --git a/arch/arm/mach-mmp/irq.c b/arch/arm/mach-mmp/irq.c new file mode 100644 index 00000000000000..58e833b36a08d8 --- /dev/null +++ b/arch/arm/mach-mmp/irq.c @@ -0,0 +1,55 @@ +/* + * linux/arch/arm/mach-mmp/irq.c + * + * Generic IRQ handling, GPIO IRQ demultiplexing, etc. + * + * Author: Bin Yang + * Created: Sep 30, 2008 + * Copyright: Marvell International Ltd. + * + * 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 "common.h" + +#define IRQ_ROUTE_TO_AP (ICU_INT_CONF_AP_INT | ICU_INT_CONF_IRQ) + +#define PRIORITY_DEFAULT 0x1 +#define PRIORITY_NONE 0x0 + +static void icu_mask_irq(unsigned int irq) +{ + __raw_writel(0, ICU_INT_CONF(irq)); +} + +static void icu_unmask_irq(unsigned int irq) +{ + __raw_writel(IRQ_ROUTE_TO_AP | PRIORITY_DEFAULT, ICU_INT_CONF(irq)); +} + +static struct irq_chip icu_irq_chip = { + .name = "icu_irq", + .ack = icu_mask_irq, + .mask = icu_mask_irq, + .unmask = icu_unmask_irq, +}; + +void __init icu_init_irq(void) +{ + int irq; + + for (irq = 0; irq < 64; irq++) { + __raw_writel(0, ICU_INT_CONF(irq)); + set_irq_chip(irq, &icu_irq_chip); + set_irq_handler(irq, handle_level_irq); + set_irq_flags(irq, IRQF_VALID); + } +} diff --git a/arch/arm/mach-mmp/mmc.c b/arch/arm/mach-mmp/mmc.c new file mode 100644 index 00000000000000..870f22161e5d44 --- /dev/null +++ b/arch/arm/mach-mmp/mmc.c @@ -0,0 +1,89 @@ +#include +#include +#include + +#define MAX_SLOTS 4 +struct platform_mmc_slot pxa_mmc_slot[MAX_SLOTS]; + +int pxa_mci_ro(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + return gpio_get_value(pxa_mmc_slot[pdev->id].gpio_wp); +} + +int pxa_mci_init(struct device *dev, + irq_handler_t pxa_detect_int, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int err, cd_irq, gpio_cd, gpio_wp; + + if (!pxa_mmc_slot[pdev->id].gpio_detect) + return 0; + + cd_irq = gpio_to_irq(pxa_mmc_slot[pdev->id].gpio_cd); + gpio_cd = pxa_mmc_slot[pdev->id].gpio_cd; + gpio_wp = -1; + + /* + * setup GPIO for MMC controller + */ + err = gpio_request(gpio_cd, "mmc card detect"); + if (err) + goto err_request_cd; + gpio_direction_input(gpio_cd); + + if (!pxa_mmc_slot[pdev->id].no_wp) { + gpio_wp = pxa_mmc_slot[pdev->id].gpio_wp; + err = gpio_request(gpio_wp, "mmc write protect"); + if (err) + goto err_request_wp; + gpio_direction_input(gpio_wp); + } + + err = request_irq(cd_irq, pxa_detect_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "MMC card detect", data); + if (err) { + printk(KERN_ERR "%s: MMC/SD/SDIO: " + "can't request card detect IRQ\n", __func__); + goto err_request_irq; + } + + return 0; + +err_request_irq: + if (!pxa_mmc_slot[pdev->id].no_wp && gpio_wp != -1) + gpio_free(gpio_wp); +err_request_wp: + gpio_free(gpio_cd); +err_request_cd: + return err; +} + +void pxa_mci_exit(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + int cd_irq, gpio_cd, gpio_wp; + + if (!pxa_mmc_slot[pdev->id].gpio_detect) + return; + + cd_irq = gpio_to_irq(pxa_mmc_slot[pdev->id].gpio_cd); + gpio_cd = pxa_mmc_slot[pdev->id].gpio_cd; + gpio_wp = pxa_mmc_slot[pdev->id].gpio_wp; + + free_irq(cd_irq, data); + gpio_free(gpio_cd); + if (!pxa_mmc_slot[pdev->id].no_wp) { + gpio_free(gpio_wp); + } +} + +int pxa_mci_get_cd(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + return gpio_get_value(pxa_mmc_slot[pdev->id].gpio_cd); +} diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c index 1873c821df9051..e3813ecc43bc02 100644 --- a/arch/arm/mach-mmp/pxa168.c +++ b/arch/arm/mach-mmp/pxa168.c @@ -13,18 +13,18 @@ #include #include #include -#include +#include -#include +#include #include -#include #include #include +#include #include #include #include -#include -#include +#include +#include #include "common.h" #include "clock.h" @@ -47,7 +47,7 @@ static void __init pxa168_init_gpio(void) { int i; - /* enable GPIO clock */ + /* enable GPIO unit clock */ __raw_writel(APBC_APBCLK | APBC_FNCLK, APBC_PXA168_GPIO); /* unmask GPIO edge detection for all 4 banks - APMASKx */ @@ -63,72 +63,289 @@ void __init pxa168_init_irq(void) pxa168_init_gpio(); } -/* APB peripheral clocks */ -static APBC_CLK(uart1, PXA168_UART1, 1, 14745600); -static APBC_CLK(uart2, PXA168_UART2, 1, 14745600); +struct gc_rate_table { + unsigned long rate; + unsigned int flag; +}; + +static struct gc_rate_table gc300_rates [] = { + /* put highest rate at the top of the table */ + { + .rate = 624000000, + .flag = APMU_GC_624M, + }, + { + .rate = 312000000, + .flag = APMU_GC_312M, + }, + { + .rate = 156000000, + .flag = APMU_GC_156M, + }, +}; + +static int gc_lookaround_rate(int target_rate, u32 *flag) +{ + int i; + + for (i=0; i= gc300_rates[i].rate) + break; + } + if (i==ARRAY_SIZE(gc300_rates)) i--; + *flag = gc300_rates[i].flag; + return gc300_rates[i].rate; +} + +static void gc300_clk_enable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst), flag; + + /* reset gc clock */ + __raw_writel(tmp & ~0x07, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); + udelay(1); + + /* select GC clock source */ + gc_lookaround_rate(clk->rate, &flag); + tmp &= ~0xc0; + tmp |= flag; + __raw_writel(tmp, clk->clk_rst); + + /* enable GC CLK EN */ + __raw_writel(tmp | 0x10, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); + + /* enable GC HCLK EN */ + __raw_writel(tmp | 0x08, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); + + /* enable GC ACLK EN */ + __raw_writel(tmp | 0x20, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); + + /* reset GC */ + __raw_writel(tmp & ~0x07, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); + + /* pull GC out of reset */ + __raw_writel(tmp | 0x2, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); + + /* delay 48 cycles */ + udelay(1); + + /* pull GC AXI/AHB out of reset */ + __raw_writel(tmp | 0x5, clk->clk_rst); + tmp = __raw_readl(clk->clk_rst); +} + +static void gc300_clk_disable(struct clk *clk) +{ + __raw_writel(0, clk->clk_rst); +} + +static int gc300_clk_setrate(struct clk *clk, unsigned long target_rate) +{ + u32 flag; + + clk->rate = gc_lookaround_rate(target_rate, &flag); + + return 0; +} + +static unsigned long gc300_clk_getrate(struct clk *clk) +{ + return clk->rate; +} + +struct clkops gc300_clk_ops = { + .enable = gc300_clk_enable, + .disable = gc300_clk_disable, + .setrate = gc300_clk_setrate, + .getrate = gc300_clk_getrate, +}; +static APBC_UART_CLK(uart1, PXA168_UART0, 14745600); +static APBC_UART_CLK(uart2, PXA168_UART1, 14745600); +static APBC_UART_CLK(uart3, PXA168_UART2, 14745600); static APBC_CLK(twsi0, PXA168_TWSI0, 1, 33000000); static APBC_CLK(twsi1, PXA168_TWSI1, 1, 33000000); -static APBC_CLK(pwm1, PXA168_PWM1, 1, 13000000); -static APBC_CLK(pwm2, PXA168_PWM2, 1, 13000000); -static APBC_CLK(pwm3, PXA168_PWM3, 1, 13000000); -static APBC_CLK(pwm4, PXA168_PWM4, 1, 13000000); +static APBC_CLK(ssp0, PXA168_SSP0, 4, 0); +static APBC_CLK(ssp1, PXA168_SSP1, 0, 6500000); +static APBC_CLK(keypad, PXA168_KPC, 0, 32000); +static APBC_PWM_CLK(pwm0, PXA168_PWM0, 0, 13000000); +static APBC_PWM_CLK(pwm1, PXA168_PWM1, 0, 13000000); +static APBC_PWM_CLK(pwm2, PXA168_PWM2, 0, 13000000); +static APBC_PWM_CLK(pwm3, PXA168_PWM3, 0, 13000000); -static APMU_CLK(nand, NAND, 0x01db, 208000000); +static APMU_CLK(nand, NAND, 0x19B, 156000000); +static APMU_CLK(lcd, LCD, 0x007f, 312000000); /* 312MHz, HCLK, CLK, AXICLK */ +static APMU_CLK(mfu, MFU, 0x9, 0); +static APMU_CLK_OPS(u2o, USB, 480000000, &u2o_clk_ops); /* 480MHz, AXICLK */ +static APMU_CLK_OPS(u2h, USB, 480000000, &u2h_clk_ops); /* 480MHz, AXICLK */ +static APMU_CLK_OPS(gc, GC, 0, &gc300_clk_ops); +static APMU_CLK_OPS(sdh0, SDH0, 48000000, &sdh_clk_ops); /* 48MHz, CLK, AXICLK */ +static APMU_CLK_OPS(sdh1, SDH1, 48000000, &sdh_clk_ops); /* 48MHz, CLK, AXICLK */ +static APMU_CLK_OPS(sdh2, SDH2, 48000000, &sdh_clk_ops); /* 48MHz, CLK, AXICLK */ +static APMU_CLK_OPS(sdh3, SDH3, 48000000, &sdh_clk_ops); /* 48MHz, CLK, AXICLK */ +static APMU_CLK(ccic_rst, CCIC_RST, 0x0, 312000000); +static APMU_CLK(ccic_gate, CCIC_GATE, 0xfff, 0); +static APMU_CLK_OPS(cf, CF, 104000000, &cf_clk_ops); +static APMU_CLK(icr, ICR, 0x3f, 0); +static APMU_CLK(smc, SMC, 0x5b, 0); -/* device and clock bindings */ static struct clk_lookup pxa168_clkregs[] = { INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL), INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL), + INIT_CLKREG(&clk_uart3, "pxa2xx-uart.2", NULL), + INIT_CLKREG(&clk_uart3, NULL, "UART3CLK"), /* clk for pxa168-ca */ + INIT_CLKREG(&clk_nand, "pxa3xx-nand", "NANDCLK"), INIT_CLKREG(&clk_twsi0, "pxa2xx-i2c.0", NULL), INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.1", NULL), - INIT_CLKREG(&clk_pwm1, "pxa168-pwm.0", NULL), - INIT_CLKREG(&clk_pwm2, "pxa168-pwm.1", NULL), - INIT_CLKREG(&clk_pwm3, "pxa168-pwm.2", NULL), - INIT_CLKREG(&clk_pwm4, "pxa168-pwm.3", NULL), - INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL), + INIT_CLKREG(&clk_ssp0, "pxa168-ssp.0", NULL), + INIT_CLKREG(&clk_ssp1, "pxa168-ssp.1", NULL), + INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL), + INIT_CLKREG(&clk_pwm0, "pxa168-pwm.0", "PWMCLK"), + INIT_CLKREG(&clk_pwm1, "pxa168-pwm.1", "PWMCLK"), + INIT_CLKREG(&clk_pwm2, "pxa168-pwm.2", "PWMCLK"), + INIT_CLKREG(&clk_pwm3, "pxa168-pwm.3", "PWMCLK"), + + INIT_CLKREG(&clk_lcd, NULL, "LCDCLK"), + INIT_CLKREG(&clk_u2o, NULL, "U2OCLK"), + INIT_CLKREG(&clk_u2h, NULL, "U2HCLK"), + INIT_CLKREG(&clk_gc, NULL, "GCCLK"), + INIT_CLKREG(&clk_mfu, "pxa168-mfu", "MFUCLK"), + INIT_CLKREG(&clk_sdh0, "pxa-sdh.0", "PXA-SDHCLK"), + INIT_CLKREG(&clk_sdh1, "pxa-sdh.1", "PXA-SDHCLK"), + INIT_CLKREG(&clk_sdh2, "pxa-sdh.2", "PXA-SDHCLK"), + INIT_CLKREG(&clk_sdh3, "pxa-sdh.3", "PXA-SDHCLK"), + INIT_CLKREG(&clk_ccic_rst, "pxa168-camera", "CCICRSTCLK"), + INIT_CLKREG(&clk_ccic_gate, "pxa168-camera", "CCICGATECLK"), + INIT_CLKREG(&clk_cf, "pxa168-cf", "CFCLK"), + INIT_CLKREG(&clk_icr, NULL, "ICRCLK"), + INIT_CLKREG(&clk_smc, "pxa168-ov529", "SMCCLK"), }; -static int __init pxa168_init(void) +void pxa168_mfp_set_fastio_drive(int type) { - if (cpu_is_pxa168()) { - mfp_init_base(MFPR_VIRT_BASE); - mfp_init_addr(pxa168_mfp_addr_map); - pxa_init_dma(IRQ_PXA168_DMA_INT0, 32); - clkdev_add_table(ARRAY_AND_SIZE(pxa168_clkregs)); + switch (type) { + /* pxa168 mfpr drive strength for fast IO pins[56:85]: + * ZR[0] = MFPR57[10] & MFPR59[10] + * ZR[1] = MFPR56[11] & MFPR58[11] + * ZR[2] = MFPR56[10] & MFPR58[10] + */ + case MFP_DS01X: + /* ZR[0] = ZR[1] = 0 */ + mfp_clr(57, 1<<10); + mfp_clr(59, 1<<10); + mfp_clr(56, 1<<11); + mfp_clr(58, 1<<11); + break; + case MFP_DS02X: + /* ZR[0] = 0, ZR[1] = 1 */ + mfp_clr(57, 1<<10); + mfp_clr(59, 1<<10); + mfp_set(56, 1<<11); + mfp_set(58, 1<<11); + break; + case MFP_DS03X: + /* ZR[0] = 1, ZR[1] = 0 */ + mfp_set(57, 1<<10); + mfp_set(59, 1<<10); + mfp_clr(56, 1<<11); + mfp_clr(58, 1<<11); + break; + case MFP_DS04X: + /* ZR[0] = ZR[1] = 1 */ + mfp_set(57, 1<<10); + mfp_set(59, 1<<10); + mfp_set(56, 1<<11); + mfp_set(58, 1<<11); + break; + default: + pr_err("drv type %d not supported\n", type); + break; } - return 0; + pr_info("%s config changed to %x\n", __func__, type); + return; } -postcore_initcall(pxa168_init); -/* system timer - clock enabled, 3.25MHz */ -#define TIMER_CLK_RST (APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3)) +#define MFP_VDD_IO_SET(type, pin, bit) \ + do { \ + if (cpu_is_pxa168_S0()) { \ + if (type == VDD_IO_3P3V) \ + mfp_clr(pin, 1<<(bit)); \ + else \ + mfp_set(pin, 1<<(bit)); \ + } else { \ + if (type == VDD_IO_3P3V) \ + mfp_set(pin, 1<<(bit)); \ + else \ + mfp_clr(pin, 1<<(bit)); \ + } \ + } while (0); -static void __init pxa168_timer_init(void) +void pxa168_set_vdd_iox(vdd_io_t vdd_io, int type) { - /* this is early, we have to initialize the CCU registers by - * ourselves instead of using clk_* API. Clock rate is defined - * by APBC_TIMERS_CLK_RST (3.25MHz) and enabled free-running - */ - __raw_writel(APBC_APBCLK | APBC_RST, APBC_PXA168_TIMERS); - - /* 3.25MHz, bus/functional clock enabled, release reset */ - __raw_writel(TIMER_CLK_RST, APBC_PXA168_TIMERS); - - timer_init(IRQ_PXA168_TIMER1); + switch (vdd_io) { + case VDD_IO0: + MFP_VDD_IO_SET(type, 60, 10); + break; + case VDD_IO1: + MFP_VDD_IO_SET(type, 60, 11); + break; + case VDD_IO2: + MFP_VDD_IO_SET(type, 61, 10); + break; + case VDD_IO3: + MFP_VDD_IO_SET(type, 61, 11); + break; + case VDD_IO4: + MFP_VDD_IO_SET(type, 62, 11); + break; + default: + pr_err("non-valid VDD_IO %d\n", vdd_io); + } } +#if defined(CONFIG_TIMER_SERVICES_MMP) +/****************************************************************/ +/* Timer services */ + +static struct resource mmp_resource_timer_services_0[] = { + /* regbase */ + [0] = { + .start = 0xD4014000, + .end = 0xD4014000, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_PXA168_TIMER2, + .end = IRQ_PXA168_TIMER2, + .flags = IORESOURCE_IRQ, + }, +}; -struct sys_timer pxa168_timer = { - .init = pxa168_timer_init, +static struct platform_device mmp_device_timer_services_0 = { + .name = "mmp-timer-services", + .id = 0, + .num_resources = ARRAY_SIZE(mmp_resource_timer_services_0), + .resource = mmp_resource_timer_services_0, }; +#endif -/* on-chip devices */ -PXA168_DEVICE(uart1, "pxa2xx-uart", 0, UART1, 0xd4017000, 0x30, 21, 22); -PXA168_DEVICE(uart2, "pxa2xx-uart", 1, UART2, 0xd4018000, 0x30, 23, 24); -PXA168_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28); -PXA168_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4025000, 0x28); -PXA168_DEVICE(pwm1, "pxa168-pwm", 0, NONE, 0xd401a000, 0x10); -PXA168_DEVICE(pwm2, "pxa168-pwm", 1, NONE, 0xd401a400, 0x10); -PXA168_DEVICE(pwm3, "pxa168-pwm", 2, NONE, 0xd401a800, 0x10); -PXA168_DEVICE(pwm4, "pxa168-pwm", 3, NONE, 0xd401ac00, 0x10); -PXA168_DEVICE(nand, "pxa3xx-nand", -1, NAND, 0xd4283000, 0x80, 97, 99); +static int __init pxa168_init(void) +{ + if (cpu_is_pxa168()) { + mfp_init_base(MFPR_VIRT_BASE); + mfp_init_addr(pxa168_mfp_addr_map); + pxa_init_dma(IRQ_PXA168_DMA_INT0, 32); + clks_register(ARRAY_AND_SIZE(pxa168_clkregs)); +#if defined(CONFIG_TIMER_SERVICES_MMP) + platform_device_register(&mmp_device_timer_services_0); +#endif + } + + return 0; +} +postcore_initcall(pxa168_init); diff --git a/arch/arm/mach-mmp/pxa168_dfc_ll.S b/arch/arm/mach-mmp/pxa168_dfc_ll.S new file mode 100644 index 00000000000000..cb377216ed02a5 --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_dfc_ll.S @@ -0,0 +1,378 @@ +/* + * Low-level PXA168 hibernate mode support + * + * Copyright (C) 2009, Marvell Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + */ +#include +#include +#include +#include +#include +/* pxa168_trigger_dfc() + * Entry point for trigger the DFC + * + */ + .text + .align 5 + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ r0: apmu_ccr value +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ trigger dfc +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#define DDR_SR_BY_APMU +#undef DDR_SR_BY_DMC + +#define RUN_DFC_IN_CACHE +#undef RUN_DFC_IN_SRAM + +ENTRY(pxa168_dfc_get_lockcache_location) + + stmfd sp!, {r3 - r12, lr} + + ldr r2, =lock_cache_start + str r2, [r0] + ldr r3, =lock_cache_end + sub r3, r3, r2 + str r3, [r1] + + mov r0, #0 + ldmfd sp!, {r3 - r12, pc} + +ENTRY(pxa168_trigger_dfc) + + stmfd sp!, {r3 - r12, lr} + mov r11, r0 + +#ifdef RUN_DFC_IN_SRAM + @ Step 1 + @ map L2 cache to SQU as SRAM + mcr p15, 1, r0, c7, c11, 0 /* clean L2 cache */ + mcr p15, 1, r0, c7, c7, 0 /* invalidate L2 cache */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 26) + mcr p15, 0, r0, c1, c0, 0 /* disable L2 cache */ + + ldr r0, =0xfe282c08 /* test L2 IDLE bit */ + ldr r0, [r0] +100: tst r0, #(1 << 16) + beq 100b + + ldr r0, =0xfe2a0030 /* enable SQU bank3 */ + ldr r1, [r0] + orr r1, #0x1 + str r1, [r0] + + ldr r0, =0xfe282c08 /* L2 cache to SQU */ + ldr r1, [r0] + orr r1, #0x10 + str r1, [r0] + + @ Step 2 + @ copy the DFC code to ISRAM + ldr r0, =sram_membase + ldr r0, [r0] + ldr r3, =dfc_start + ldr r4, =dfc_end + add r4, r4, #0x100 + +dfc_rel_ram: + ldmia r3!, {r5 - r10} + stmia r0!, {r5 - r10} + cmp r3, r4 + ble dfc_rel_ram + + ldr r0, =sram_membase + ldr r0, [r0] + + mov pc, r0 + +#endif + + +#ifdef RUN_DFC_IN_CACHE + @ Step 1 + @ lock Data&instruction into cache +@ ldr r0, =lock_cache_start +@ ldr r1, =lock_cache_end +@ sub r1, r1, r0 +@ bl remap_to_uncacheable + + ldr lr, =dfc_start + + ldr r0, =dfc_lock_cache_code_base + ldr r0, [r0] + + bx r0 + +lock_cache_start: + nop + nop + + @locking I cache, using direct-mapped I-cache procedure. + mrc p15, 0, r3, c9, c0, 1 + orr r3, r3, #0xF + mcr p15, 0, r3, c9, c0, 1 + + @locking D cache, using 4 way mapped procedure. + mrc p15, 0, r3, c9, c0, 0 + mov r2, r3 + bic r3, r3, #0x8 + orr r3, r3, #0x7 + mcr p15, 0, r3, c9, c0, 0 + + ldr r0, =dfc_start + ldr r1, =dfc_end + + bic r0, r0, #0x7F +lock_one_cacheline: + mcr p15, 0, r0, c7, c5, 1 @I cache line invalidate by MVA. + mcr p15, 0, r0, c7, c13, 1 @force a I cacheline line fill. + mcr p15, 0, r0, c7, c6, 1 @invalidate D-Cache line by MVA. + pld [r0] + + add r0, r0, #0x20 + cmp r0, r1 + blo lock_one_cacheline + + bic r3, r3, #0xF + mcr p15, 0, r3, c9, c0, 1 + + mov r3, r2 + orr r3, r3, #0x8 + mcr p15, 0, r3, c9, c0, 0 + + mov pc, lr +#endif + + + +lock_cache_end: +dfc_start: + b 1f + .align 5 +1: + + @ put some address into TLB + ldr r1, =0xFE282800 /* lock APUM register address into TLB */ + mcr p15, 0, r1, c8, c7, 1 + mrc p15, 0, r0, c10, c0, 0 + orr r0, r0, #1 + mcr p15, 0, r0, c10, c0, 0 + ldr r1, [r1] + mrc p15, 0, r0, c10, c0, 0 + bic r0, r0, #1 + mcr p15, 0, r0, c10, c0, 0 + + ldr r1, =0xFE050000 /* lock MPMU register addresses into TLB */ + mcr p15, 0, r1, c8, c7, 1 + mrc p15, 0, r0, c10, c0, 0 + orr r0, r0, #1 + mcr p15, 0, r0, c10, c0, 0 + ldr r1, [r1] + mrc p15, 0, r0, c10, c0, 0 + bic r0, r0, #1 + mcr p15, 0, r0, c10, c0, 0 + + + ldr r1, =dmc_membase /* lock MPMU register addresses into TLB */ + ldr r1, [r1] + mcr p15, 0, r1, c8, c7, 1 + mrc p15, 0, r0, c10, c0, 0 + orr r0, r0, #1 + mcr p15, 0, r0, c10, c0, 0 + ldr r1, [r1] + mrc p15, 0, r0, c10, c0, 0 + bic r0, r0, #1 + mcr p15, 0, r0, c10, c0, 0 + + @ Step 3 + @ put the DDR into self refresh +#ifdef DDR_SR_BY_APMU + @ put DDR into self-refresh by SW_SLP_TYPE + ldr r1, =0xFE282800 + mov r0, #0 + str r0, [r1, #0xC0] /* write SW_SLP_TYPE to 0, self-refresh */ + ldr r0, =0x1 + str r0, [r1, #0xB4] /* write SLP_REQ to 1 */ + +sr_ack_check: + ldr r0, [r1, #0xB4] + tst r0, #2 + beq sr_ack_check +#endif + +#ifdef DDR_SR_BY_DMC + @ block ddr data request + ldr r1, =dmc_membase + ldr r1, [r1] /* DMC base: 0xB000_0000 */ + mov r0, #1 + str r0, [r1, #0x07e0] + + @ ddr self refresh + ldr r0, [r1, #0x0120] + orr r0, #0x40 + str r0, [r1, #0x0120] +#endif + + @ Step 4 + @ trigger the DFC by apmu_ccr +trigger_freq_chg: + ldr r0, =0xFE282804 /* APMU_CCR 0xD4282804 */ + str r11, [r0] + + ldr r1, =0xFE2828A0 /* APMU_ISR 0xD42828A0 */ + +dfc_done_check: + ldr r0, [r1] + tst r0, #2 /* PMUA_ISR_FC_ISR */ + beq dfc_done_check + + mov r0, #0 /* clean the ISR */ + str r0, [r1] + + @wait for some cycle + nop + nop + nop + nop + nop + nop + nop + + @ Step 6 + @ re-initialize the DDR + + @DMC base: 0xB000_0000 + ldr r1, =dmc_membase + ldr r1, [r1] + + @ set PHY_SYNC_EN + @ resynchronize the new PHY clock + ldr r0, =0x80000000 + str r0, [r1, #0x240] + +#ifdef DDR_SR_BY_DMC + @get out of self refresh + ldr r0, =0x80 + str r0, [r1, #0x120] +#endif + + @ set DLL_RESET bit + ldr r0, =0x40 + str r0, [r1, #0x80] + + @send MRS command + ldr r0, =0x03000100 + str r0, [r1, #0x120] + + @clean DLL_RESET bit + ldr r0, =0x0 + str r0, [r1, #0x80] + + @set DLL_RESET_TIMER to 8 + ldr r0, [r1, #0x230] + and r0, r0, #0x0FFFFFFF + orr r0, r0, #0x80000000 + str r0, [r1, #0x230] + + @ set PHY_DLL_RESET + ldr r0, =0x20000000 + str r0, [r1, #0x240] + + @write bit 27 of PHY_CNTRL_14 and wait at least + ldr r0, =0x08000000 + str r0, [r1, #0x240] + + @512 dlck cycles at least to update DLL. + ldr r0, =512 +wait_512dclks: + subs r0, r0, #2 + bne wait_512dclks + + @write bit 27 of PHY_CNTRL_14 again to restore, + @since this bit is a write '1' to toggle bit. + ldr r0, =0x08000000 + str r0, [r1, #0x240] + +#ifdef DDR_SR_BY_APMU + @ Make DDR out of self-refresh + @ write 0 to SLP_REQ + ldr r2, =0xFE282800 + ldr r0, =0x0 + str r0, [r2, #0xB4] +#endif + + +#ifdef DDR_SR_BY_DMC + @unblocking the DDR access request + mov r0, #0 + str r0, [r1, #0x07e0] +#endif + + + + mov r0, #2560 +loop2: + subs r0, r0, #1 + bne loop2 + + + + ldr r1, =ddr_restart_add + mov pc, r1 + .ltorg /* ensure the data pool here */ + +ddr_restart_add: +#ifdef RUN_DFC_IN_SRAM + @ disable L2 cache to SQU mapping + ldr r1, =0xfe282c08 + ldr r2, [r1] + bic r2, r2, #0x10 + str r2, [r1] + @ disable SQU bank3 + ldr r1, =0xfe2a0030 + ldr r2, [r1] + bic r2, r2, #0x1 + str r2, [r1] + + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1 << 26) + mcr p15, 0, r0, c1, c0, 0 /* enable L2 cache */ + + @ invalidate I, D caches & BTB + mcr p15, 0, ip, c7, c7, 0 + @ Drain Write (& Fill) Buffer + mcr p15, 0, ip, c7, c10, 4 + @ Prefetch Flush + mcr p15, 0, ip, c7, c5, 4 + @ invalidate I, D TLBs + mcr p15, 0, ip, c8, c7, 0 + @ invalidate L2 cache + mcr p15, 1, ip, c7, c7, 0 +#endif + +#ifdef RUN_DFC_IN_CACHE + @unlock DCache way 3 + mrc p15, 0, r3, c9, c0, 0 + bic r3, r3, #0x8 + mcr p15, 0, r3, c9, c0, 0 + + @invalidate all the I-Cache and flush BPU + mcr p15, 0, r0, c7, c5, 0 +#endif + + + mov r0, #0 + ldmfd sp!, {r3 - r12, pc} + +dfc_end: + nop + nop + diff --git a/arch/arm/mach-mmp/pxa168_dvfm.c b/arch/arm/mach-mmp/pxa168_dvfm.c new file mode 100644 index 00000000000000..80634ea01fada5 --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_dvfm.c @@ -0,0 +1,1441 @@ +/* + * PXA168 DVFM Driver + * + * Copyright (C) 2008 Marvell Corporation + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#undef DEBUG +#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define CONFIG_PXA910_DVFM_ASYNC_MODE 1 +#define CONFIG_PXA910_AP_ALONE_MODE 1 + +unsigned long dfc_lock_cache_code_base; +EXPORT_SYMBOL(dfc_lock_cache_code_base); + + +static struct info_head pxa168_dvfm_op_list = { + .list = LIST_HEAD_INIT(pxa168_dvfm_op_list.list), + .lock = RW_LOCK_UNLOCKED, +}; + +/* the operating point preferred by policy maker or user */ +static int preferred_op; + + +static int dvfm_dev_id; + +static unsigned int pxa168_vcc_core_array_a_step[] = { + 1000, /* mode 0 */ + 1000, /* mode 1 */ + 1100, /* mode 2 */ + 1100, /* mode 2.3 */ + 1100, /* mode 3 */ + 1100, /* mode 3.1 */ + 1100, /* mode 4 */ + 1100, /* mode 4.1 */ + 1100, /* core_intidle */ + 1100, /* core_extidle */ + 1100, /* apps_idle */ + 1100, /* apps_sleep */ + 1100, /* sys_sleep */ + 1100, /* hibernate */ +}; + +static unsigned int pxa168_vcc_core_array_b_step[] = { + 945, /* mode 0 */ + 945, /* mode 1 */ + 1000, /* mode 2 */ + 1000, /* mode 2.3 */ + 1000, /* mode 3 */ + 1000, /* mode 3.1 */ + 1120, /* mode 4 */ + 1120, /* mode 4.1 */ + 1120, /* core_intidle */ + 1120, /* core_extidle */ + 1120, /* apps_idle */ + 1120, /* apps_sleep */ + 1120, /* sys_sleep */ + 1120, /* hibernate */ +}; + +static struct pxa168_opmode_md pxa168_opmode_md_array[] = { + /* pclk, dclk, xpclk, baclk, aclk, aclk2, vcc_core, pll, mode, index */ + /* 156, 156, 156, 156, 156, 156, 945, 312, 0, 0 */ + { + .name = "mode 0", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 0, + .axipll_sel = 0, + .aclk2_div = 1, + .aclk_div = 1, + .dclk_div = 0, + .xpclk_div = 0, + .baclk_div = 0, + .pclk_div = 1, + .pll2_refdiv = 0, + .pll2_fbdiv = 0, + .pll2_reg1 = 0x00000000, + .vcc_core = 945, + }, + /* 400, 200, 200, 200, 156, 156, 945, 404, 1, 1 */ + { + .name = "mode 1", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 2, + .axipll_sel = 0, + .aclk2_div = 1, + .aclk_div = 1, + .dclk_div = 0, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 11, + .pll2_fbdiv = 200, + .pll2_reg1 = 0x90000164, + .vcc_core = 945, + }, + /* 624, 312, 312, 156, 156, 156, 1000, 624 2, 2 */ + { + .name = "mode 2", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 1, + .axipll_sel = 0, + .aclk2_div = 1, + .aclk_div = 1, + .dclk_div = 0, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 0, + .pll2_fbdiv = 0, + .pll2_reg1 = 0x00000000, + .vcc_core = 1000, + }, + /* 624, 156, 312, 156, 156, 156, 1000, 624, 2_3, 3 */ + { + .name = "mode 2.3", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 1, + .axipll_sel = 1, + .aclk2_div = 3, + .aclk_div = 3, + .dclk_div = 1, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 0, + .pll2_fbdiv = 0, + .pll2_reg1 = 0x00000000, + .vcc_core = 1000, + }, + /* 800, 400, 400, 200, 156, 312, 1000, 806, 3, 4 */ + { + .name = "mode 3", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 2, + .axipll_sel = 0, + .aclk2_div = 0, + .aclk_div = 1, + .dclk_div = 0, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 1, + .pll2_fbdiv = 91, + .pll2_reg1 = 0x90020364, + .vcc_core = 1000, + }, + /* 800, 200, 400, 200, 156, 312, 1000, 806, 3_1, 5 */ + { + .name = "mode 3.1", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 2, + .axipll_sel = 0, + .aclk2_div = 0, + .aclk_div = 1, + .dclk_div = 1, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 1, + .pll2_fbdiv = 91, + .pll2_reg1 = 0x90020364, + .vcc_core = 1000, + }, + /* 1066, 533, 533, 266, 156, 312, 1120, 1083, 4, 6 */ + { + .name = "mode 4", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 2, + .axipll_sel = 0, + .aclk2_div = 0, + .aclk_div = 1, + .dclk_div = 0, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 1, + .pll2_fbdiv = 123, + .pll2_reg1 = 0x90040664, + .vcc_core = 1120, + }, + /* 1066, 355, 355, 266, 156, 312, 1120, 1083, 4_1, 7 */ + { + .name = "mode 4.1", + .power_mode = POWER_MODE_ACTIVE, + .corepll_sel = 2, + .axipll_sel = 0, + .aclk2_div = 0, + .aclk_div = 1, + .dclk_div = 0, + .xpclk_div = 1, + .baclk_div = 3, + .pclk_div = 0, + .pll2_refdiv = 1, + .pll2_fbdiv = 123, + .pll2_reg1 = 0x90040664, + .vcc_core = 1120, + }, + /* core internal idle */ + { + .power_mode = POWER_MODE_CORE_INTIDLE, + .name = "core_intidle", + }, + /* core external idle */ + { + .power_mode = POWER_MODE_CORE_EXTIDLE, + .name = "core_extidle", + }, + /* application subsystem idle */ + { + .power_mode = POWER_MODE_APPS_IDLE, + .name = "apps_idle", + }, + /* application subsystem sleep */ + { + .power_mode = POWER_MODE_APPS_SLEEP, + .name = "apps_sleep", + }, + /* system sleep */ + { + .power_mode = POWER_MODE_SYS_SLEEP, + .name = "sys_sleep", + }, + /* hibernate */ + { + .power_mode = POWER_MODE_HIBERNATE, + .name = "hibernate", + }, + + +}; + +static int aspen_vcc_core_convt[][3] = { + /* cnt, in, out */ + { 0x00, 725, 379 }, + { 0x01, 750, 408 }, + { 0x02, 775, 436 }, + { 0x03, 800, 464 }, + { 0x04, 825, 493 }, + { 0x05, 850, 521 }, + { 0x06, 875, 550 }, + { 0x07, 900, 578 }, + { 0x08, 925, 606 }, + { 0x09, 950, 635 }, + { 0x0A, 975, 663 }, + { 0x0B, 1000, 691 }, + { 0x0C, 1025, 720 }, + { 0x0D, 1050, 748 }, + { 0x0E, 1075, 776 }, + { 0x0F, 1100, 805 }, + { 0x10, 1125, 833 }, + { 0x11, 1150, 861 }, + { 0x12, 1175, 890 }, + { 0x13, 1200, 918 }, + { 0x14, 1225, 947 }, + { 0x15, 1250, 975 }, + { 0x16, 1275, 1003 }, + { 0x17, 1300, 1032 }, + { 0x18, 1325, 1060 }, + { 0x19, 1350, 1088 }, + { 0x1A, 1375, 1117 }, + { 0x1B, 1400, 1145 }, + { 0x1C, 1425, 1173 }, + { 0x1D, 1450, 1202 }, + { 0x1E, 1475, 1230 }, + { 0x1F, 1500, 1258 }, + { 0x20, 1525, 1287 }, + { 0x21, 1550, 1315 }, + { 0x22, 1575, 1343 }, + { 0x23, 1600, 1372 }, + { 0x24, 1625, 1400 }, + { 0x25, 1650, 1429 }, + { 0x26, 1675, 1457 }, + { 0x27, 1700, 1485 }, + { 0x28, 1725, 1514 }, + { 0x29, 1750, 1542 }, + { 0x2A, 1775, 1570 }, + { 0x2B, 1800, 1599 }, +}; + +#define ASPEN_ECO11_SIZE (sizeof(aspen_vcc_core_convt) / \ + sizeof(aspen_vcc_core_convt[0])) + +/* #####################Debug Function######################## */ +static int dump_op(void *driver_data, struct op_info *p, char *buf) +{ + int len, count, x, i, max, sum; + struct pxa168_md_opt *q = (struct pxa168_md_opt *)p->op; + /*struct pxa168_dvfm_info *info = driver_data; + struct pxa168_md_opt md_opt_temp; + struct pxa168_opmode_md opmode_md_temp;*/ + + if (q == NULL) + len = sprintf(buf, "Can't dump the op info\n"); + else { + /* calculate how much bits is set in device word */ + max = DVFM_MAX_CLIENT >> 5; + for (i = 0, sum = 0; i < max; i++) { + x = p->device[i]; + for (count = 0; x; x = x & (x - 1), count++); + sum += count; + } + len = sprintf(buf, "OP:%d name:%s [%s, %d]\n", + p->index, q->name, (sum)?"Disabled" + :"Enabled", sum); + if (q->power_mode == POWER_MODE_ACTIVE) + len += sprintf(buf + len, "pclk:%d baclk:%d xpclk:%d " + "dclk:%d aclk:%d aclk2:%d vcc_core:%d\n", + q->pclk, q->baclk, q->xpclk, + q->dclk, q->aclk, q->aclk2, q->vcc_core); + } + + /* pxa168_get_current_opmode_md(info, &opmode_md_temp); + pxa168_op_machine_to_human(&opmode_md_temp, &md_opt_temp); + len += sprintf(buf + len, "pclk:%d baclk:%d xpclk:%d " + "dclk:%d aclk:%d aclk2:%d vcc_core:%d\n", + md_opt_temp.pclk, md_opt_temp.baclk, + md_opt_temp.xpclk, md_opt_temp.dclk, + md_opt_temp.aclk, md_opt_temp.aclk2, + md_opt_temp.vcc_core);*/ + + return len; +} + +static int dump_op_list(void *driver_data, struct info_head *op_table) +{ + struct op_info *p = NULL; + struct list_head *list = NULL; + struct pxa168_dvfm_info *info = driver_data; + char buf[256]; + + if (!op_table || list_empty(&op_table->list)) { + printk(KERN_WARNING "op list is null\n"); + return -EINVAL; + } + memset(buf, 0, 256); + list_for_each(list, &op_table->list) { + p = list_entry(list, struct op_info, list); + dump_op(info, p, buf); + } + return 0; +} + +void pxa168_op_machine_to_human(struct pxa168_opmode_md *opmode_md, + struct pxa168_md_opt *opmode_hu) +{ + unsigned int corepll, axipll; + int nume_temp, deno_temp; + int post_div; + int post_div_ration_nume = 1; + int post_div_ration_deno = 1; + opmode_hu->power_mode = opmode_md->power_mode; + strncpy(opmode_hu->name, opmode_md->name, OP_NAME_LEN); + if (opmode_md->power_mode == POWER_MODE_ACTIVE) { + if (opmode_md->corepll_sel == 0) { + corepll = 312; + } else if (opmode_md->corepll_sel == 1) { + corepll = 624; + } else { + nume_temp = opmode_md->pll2_fbdiv; + deno_temp = opmode_md->pll2_refdiv; + switch (deno_temp) { + case 0x00: + deno_temp = 1; + break; + case 0x10: + deno_temp = 2; + break; + case 0x01: + deno_temp = 3; + break; + default: + deno_temp += 2; + break; + } + corepll = 26*nume_temp/deno_temp; + } + + if (opmode_md->axipll_sel == 0) { + axipll = 312; + } else if (opmode_md->axipll_sel == 1) { + axipll = 624; + } else { + nume_temp = opmode_md->pll2_fbdiv; + deno_temp = opmode_md->pll2_refdiv; + switch (deno_temp) { + case 0x00: + deno_temp = 1; + break; + case 0x10: + deno_temp = 2; + break; + case 0x01: + deno_temp = 3; + break; + default: + deno_temp += 2; + break; + } + axipll = 26*nume_temp/deno_temp; + } + + if (opmode_md->corepll_sel == 2) { + + post_div = (opmode_md->pll2_reg1 & + MPMU_PLL2_REG1_VCODIV_SEL_DIFF_MSK)>> + MPMU_PLL2_REG1_VCODIV_SEL_DIFF_BASE; + + switch (post_div) { + case 0x0: + post_div_ration_nume = 1; + break; + case 0x1: + post_div_ration_nume = 3; + post_div_ration_deno = 2; + break; + case 0x2: + post_div_ration_nume = 2; + break; + case 0x3: + post_div_ration_nume = 5; + post_div_ration_deno = 2; + break; + case 0x4: + post_div_ration_nume = 3; + break; + case 0x5: + post_div_ration_nume = 4; + break; + case 0x6: + post_div_ration_nume = 8; + break; + case 0x7: + post_div_ration_nume = 6; + break; + case 0x9: + post_div_ration_nume = 10; + break; + case 0xa: + post_div_ration_nume = 12; + break; + case 0xb: + post_div_ration_nume = 14; + break; + } + } + + opmode_hu->pclk = corepll/(opmode_md->pclk_div + 1); + opmode_hu->baclk = corepll/(opmode_md->baclk_div + 1); + opmode_hu->xpclk = corepll/(opmode_md->xpclk_div + 1); + opmode_hu->dclk = (corepll*post_div_ration_deno)/ + (2*(opmode_md->dclk_div + 1)*post_div_ration_nume); + opmode_hu->aclk = axipll/(opmode_md->aclk_div + 1); + opmode_hu->aclk2 = axipll/(opmode_md->aclk2_div + 1); + opmode_hu->vcc_core = opmode_md->vcc_core; + opmode_hu->lpj = opmode_hu->pclk * 5000; + } + return; +} + +uint32_t pxa168_enable_swdfc(struct pxa168_dvfm_info *driver_data) +{ + uint32_t mpmu_acgr; + uint32_t apmu_ccr; + struct pxa168_dvfm_info *info = driver_data; + + /* some clocks are needed to allow DFC. Ensure they are enabled. */ + mpmu_acgr = readl(info->pmum_base + MPMU_ACGR_OFF); + mpmu_acgr |= (0u<<20) | /* 52MHz for APB2 (1u select APB2@26MHz) */ + (1u<<15) | /* 624MHz */ + (1u<<14) | /* PLL2 */ + (1u<<13) | /* 312MHz */ + (1u<<9) | /* GPC */ + (1u<<4); /* 26Mhz to APB */ + writel(mpmu_acgr, info->pmum_base + MPMU_ACGR_OFF); + pr_debug(">>>>>%s: mpmu_acgr = %x\n", __func__, mpmu_acgr); + /****************************************************************/ + /* SWDFC Enable/Disabe: this is a new feature on A0. */ + /* Bit 21 is an nEnable to allow SW to initiate DFC. */ + /* Must make sure the bit is clear, so SW can initiate DFC.*/ + /****************************************************************/ + apmu_ccr = readl(info->pmua_base + APMU_CCR_OFF); + apmu_ccr &= ~(1u<<21); + apmu_ccr &= ~(0xffu<<24); + writel(apmu_ccr, info->pmua_base + APMU_CCR_OFF); + pr_debug(">>>>>%s: apmu_ccr = %x\n", __func__, apmu_ccr); + + return apmu_ccr; +} + +uint32_t pxa168_dfc_prepare(struct pxa168_dvfm_info *driver_data) +{ + uint32_t apmu_ccr; + uint32_t apmu_imr; + uint32_t apmu_isr; + uint32_t apmu_temp; + struct pxa168_dvfm_info *info = driver_data; + + /* enable notification of dynamic frequency change events */ + apmu_imr = readl(info->pmua_base + APMU_IMR_OFF); + /* enabling DFC done notification for pclk, dclk and aclk */ + apmu_imr |= 0x3a; + writel(apmu_imr, info->pmua_base + APMU_IMR_OFF); + pr_debug(">>>>>%s: apmu_imr = %x\n", __func__, apmu_imr); + + /* clear out any status bits left over from previous events. */ + apmu_isr = readl(info->pmua_base + APMU_ISR_OFF); + writel(apmu_isr, info->pmua_base + APMU_ISR_OFF); + pr_debug(">>>>>%s: apmu_isr = %x\n", __func__, apmu_isr); + + /* clear the initiate bits during this stage. */ + apmu_ccr = readl(info->pmua_base + APMU_CCR_OFF); + apmu_ccr &= ~(0xffu<<24); + writel(apmu_ccr, info->pmua_base + APMU_CCR_OFF); + pr_debug(">>>>>%s: apmu_ccr = %x\n", __func__, apmu_ccr); + + apmu_ccr = readl(info->pmua_base + APMU_CCR_OFF); + apmu_ccr &= ~(0xffu<<24); + apmu_ccr |= (0xfu<<24); + + apmu_temp = readl(info->pmua_base); + if(cpu_is_pxa168_A0()==0) + apmu_temp |= 0x28000000; /* not A stepping */ + else + { + /* these two bits (31&27) should be set in reg address 0xd4282800 */ + /* otherwise, no ir. ?? needed to check with DE */ + apmu_temp |= 0x88000000; + } + writel(apmu_temp, info->pmua_base); + pr_debug(">>>>>%s: PHY(%x) = %x\n", __func__,\ + 0xd4282800, apmu_temp); + + /* make sure the allow sw mc control bit is not set */ + writel(0x00000302, info->pmua_base + APMU_IDLE_CFG_OFF ); + + return apmu_ccr; +} + +void pxa168_set_opmode_md(struct pxa168_dvfm_info *driver_data, + struct pxa168_opmode_md *opmode_md) +{ + uint32_t apmu_ccr, mpmu_fccr, mpmu_pll2cr, mpmu_pll2_reg1, + mpmu_pll2_reg2; + struct pxa168_dvfm_info *info = driver_data; + + /* if this is a pll2 mode, set up the refdiv, + * fbdiv set, kvco, vrng and post dividers.*/ + + if (opmode_md->corepll_sel == 2) { + /* first must allow software to control pll2 activation */ + mpmu_pll2cr = readl(info->pmum_base + MPMU_PLL2CR_OFF); + mpmu_pll2cr |= (1u<<9); + writel(mpmu_pll2cr, info->pmum_base + MPMU_PLL2CR_OFF); + pr_debug(">>>>>%s: mpmu_pll2cr = %x\n", __func__, \ + mpmu_pll2cr); + + /* clear the pll2 enable bit to disable pll2 */ + mpmu_pll2cr &= ~(1u<<8); + writel(mpmu_pll2cr, info->pmum_base + MPMU_PLL2CR_OFF); + pr_debug(">>>>>%s: mpmu_pll2cr = %x\n", __func__, \ + mpmu_pll2cr); + + /* set the new pll2 frequencies. */ + mpmu_pll2cr = readl(info->pmum_base + MPMU_PLL2CR_OFF); + mpmu_pll2cr &= ~((0x1f<<19) | (0x1ff<<10)); + mpmu_pll2cr |= ((opmode_md->pll2_refdiv<<19) | \ + (opmode_md->pll2_fbdiv<<10)); + writel(mpmu_pll2cr, info->pmum_base + MPMU_PLL2CR_OFF); + pr_debug(">>>>>%s: mpmu_pll2cr = %x\n", __func__, \ + mpmu_pll2cr); + + /* set up the kvco, vrng and post divider values. */ + mpmu_pll2_reg1 = opmode_md->pll2_reg1; + writel(mpmu_pll2_reg1, info->pmum_base + MPMU_PLL2_REG1_OFF); + pr_debug(">>>>>%s: mpmu_pll2_reg1 = %x\n", __func__, \ + mpmu_pll2_reg1); + + /* ensure differential clock mode is set when using pll2 */ + mpmu_pll2_reg2 = readl(info->pmum_base + MPMU_PLL2_REG2_OFF); + mpmu_pll2_reg2 |= (1u<<6); + writel(mpmu_pll2_reg2, info->pmum_base + MPMU_PLL2_REG2_OFF); + pr_debug(">>>>>%s: mpmu_pll2_reg2 = %x\n", __func__, \ + mpmu_pll2_reg2); + + /* enable pll2 */ + mpmu_pll2cr = readl(info->pmum_base + MPMU_PLL2CR_OFF); + mpmu_pll2cr |= (1u<<8); + writel(mpmu_pll2cr, info->pmum_base + MPMU_PLL2CR_OFF); + pr_debug(">>>>>%s: mpmu_pll2cr = %x\n", __func__, \ + mpmu_pll2cr); + + } + + /* select the PLL sources, including for core/ddr/axi */ + mpmu_fccr = readl(info->pmum_base + MPMU_FCCR_OFF); + mpmu_fccr &= ~(7u<<29); + mpmu_fccr |= (opmode_md->corepll_sel<<29); + mpmu_fccr &= ~(7u<<23); + mpmu_fccr |= (opmode_md->axipll_sel<<23); + mpmu_fccr |= 0x0000888e; + writel(mpmu_fccr, info->pmum_base + MPMU_FCCR_OFF); + pr_debug(">>>>>%s: mpmu_fccr = %x\n", __func__, mpmu_fccr); + + /* select the divider for each clock */ + apmu_ccr = readl(info->pmua_base + APMU_CCR_OFF); + apmu_ccr &= 0xFFF00000; + apmu_ccr |= (opmode_md->aclk2_div<<18) | + (opmode_md->aclk_div<<15) | + (opmode_md->dclk_div<<12) | + (opmode_md->xpclk_div<<9) | + (opmode_md->baclk_div<<6) | + (opmode_md->pclk_div<<0); + writel(apmu_ccr, info->pmua_base + APMU_CCR_OFF); + pr_debug(">>>>>%s: apmu_ccr = %x\n", __func__, apmu_ccr); + return; + +} + +static int get_op_num(void *driver_data, struct info_head *op_table) +{ + struct list_head *entry = NULL; + int num = 0; + + if (!op_table) + goto out; + read_lock(&op_table->lock); + if (list_empty(&op_table->list)) { + read_unlock(&op_table->lock); + goto out; + } + list_for_each(entry, &op_table->list) { + num++; + } + read_unlock(&op_table->lock); +out: + return num; +} + +static char *get_op_name(void *driver_data, struct op_info *p) +{ + struct pxa168_md_opt *q = NULL; + if (p == NULL) + return NULL; + q = (struct pxa168_md_opt *)p->op; + return q->name; +} + +static void update_voltage(int vcc_core) +{ + int pmic_vcc_core = aspen_vcc_core_convt[0][1]; + int i; + + for (i = 0; i < ASPEN_ECO11_SIZE-1; i++) { + if ((aspen_vcc_core_convt[i][2] < vcc_core) && \ + (aspen_vcc_core_convt[i+1][2] > vcc_core)) + pmic_vcc_core = aspen_vcc_core_convt[i+1][1]; + } + pr_debug("vcc_core: %d, pmic setting = %d\n", vcc_core, pmic_vcc_core); + pxa3xx_pmic_set_voltage(VCC_CORE, pmic_vcc_core); +} + +static int set_freq(void *driver_data, struct op_info *old, struct op_info *new) +{ + struct pxa168_dvfm_info *info = driver_data; + struct pxa168_opmode_md *dvfm_old, *dvfm_new; + uint32_t apmu_ccr; + + dvfm_old = ((struct pxa168_md_opt *)old->op)->dvfm_settings; + dvfm_new = ((struct pxa168_md_opt *)new->op)->dvfm_settings; + + pr_debug("old_index = %d, new_index = %d\n", old->index, new->index); + + /* set up the core voltage */ + if (dvfm_old->vcc_core > dvfm_new->vcc_core) + update_voltage(dvfm_new->vcc_core); + + /* launch dfc */ + pxa168_enable_swdfc(info); + pxa168_set_opmode_md(info, dvfm_new); + apmu_ccr = pxa168_dfc_prepare(info); + pr_debug(">>>>>%s: apmu_ccr = %x\n", __func__, apmu_ccr); + __cpuc_flush_kern_all(); + pxa168_trigger_dfc(apmu_ccr); + + /* set up the core voltage */ + if (dvfm_old->vcc_core < dvfm_new->vcc_core) + update_voltage(dvfm_new->vcc_core); + + return 0; +} + +static int update_freq(void *driver_data, struct dvfm_freqs *freqs) +{ + struct pxa168_dvfm_info *info = driver_data; + struct op_info *p = NULL; + struct op_info *old = NULL, *new = NULL; + unsigned long flags; + int found = 0, new_op = cur_op; + + write_lock_irqsave(&pxa168_dvfm_op_list.lock, flags); + if (!list_empty(&pxa168_dvfm_op_list.list)) { + list_for_each_entry(p, &pxa168_dvfm_op_list.list, list) { + if (p->index == freqs->old) { + found++; + old = p; + } + if (p->index == freqs->new) { + found++; + new = p; + new_op = p->index; + } + if (found == 2) + break; + } + } + write_unlock_irqrestore(&pxa168_dvfm_op_list.lock, flags); + if (found != 2) + return -EINVAL; + + set_freq(info, old, new); + cur_op = new_op; + loops_per_jiffy = ((struct pxa168_md_opt *)new->op)->lpj; + return 0; +} + +static void do_freq_notify(void *driver_data, struct dvfm_freqs *freqs) +{ + struct pxa168_dvfm_info *info = driver_data; + + dvfm_notifier_frequency(freqs, DVFM_FREQ_PRECHANGE); + update_freq(info, freqs); + dvfm_notifier_frequency(freqs, DVFM_FREQ_POSTCHANGE); +} + +static void do_lowpower_notify(void *driver_data, struct dvfm_freqs *freqs, \ + unsigned int state) +{ + dvfm_notifier_frequency(freqs, DVFM_FREQ_PRECHANGE); + pxa168_pm_enter_lowpower_mode(state); + dvfm_notifier_frequency(freqs, DVFM_FREQ_POSTCHANGE); + +} + +/* Check whether any client blocks the current operating point */ +static int block_client(struct op_info *info) +{ + int i; + unsigned int ret = 0; + for (i = 0; i < (DVFM_MAX_CLIENT >> 5); i++) + ret |= info->device[i]; + return (int)ret; +} + +static int check_op(void *driver_data, struct dvfm_freqs *freqs, + unsigned int new, unsigned int relation) +{ + struct op_info *p = NULL; + int index, tmp_index = -1, found = 0; + int first_op = 0; + + freqs->new = -1; + if (!dvfm_find_op(new, &p)) { + index = p->index; + } else + return -EINVAL; + + read_lock(&pxa168_dvfm_op_list.lock); + if (relation == RELATION_LOW) { + /* Set the lowest usable op that is higher than specifed one */ + /* Note: we assume bigger index number is more 'usable' */ + list_for_each_entry(p, &pxa168_dvfm_op_list.list, list) { + if (!block_client(p) && (p->index >= index)) { + if (tmp_index == -1 || (tmp_index >= p->index)) { + if (first_op == 0) + first_op = p->index; + freqs->new = p->index; + tmp_index = p->index; + found = 1; + } + if (found && (new == p->index)) + break; + } + } + if (found && (first_op == 1) && (new != p->index)) + freqs->new = first_op; + } else if (relation == RELATION_HIGH) { + /* Set the highest usable op that is lower than specified one */ + list_for_each_entry(p, &pxa168_dvfm_op_list.list, list) { + if (!block_client(p) && (p->index <= index)) { + if (tmp_index == -1 || tmp_index < p->index) { + freqs->new = p->index; + tmp_index = p->index; + } + } + } + } else if (relation == RELATION_STICK) { + /* Set the specified frequency */ + list_for_each_entry(p, &pxa168_dvfm_op_list.list, list) { + if (!block_client(p) && (p->index == new)) { + freqs->new = p->index; + break; + } + } + } + read_unlock(&pxa168_dvfm_op_list.lock); + if (freqs->new == -1) + return -EINVAL; + return 0; +} + +static int pxa168_set_op(void *driver_data, struct dvfm_freqs *freqs, + unsigned int new, unsigned int relation) +{ + struct pxa168_dvfm_info *info = driver_data; + struct pxa168_md_opt *md = NULL; + struct op_info *p = NULL; + unsigned long flags; + int ret; + + local_fiq_disable(); + local_irq_save(flags); + + ret = dvfm_find_op(freqs->old, &p); + if (ret) + goto out; + + memcpy(&freqs->old_info, p, sizeof(struct op_info)); + ret = check_op(info, freqs, new, relation); + if (ret) + goto out; + + if (!dvfm_find_op(freqs->new, &p)) { + memcpy(&(freqs->new_info), p, sizeof(struct op_info)); + /* If find old op and new op is same, skip it. + * At here, ret should be zero. + */ + if (freqs->old_info.index == freqs->new_info.index) + goto out; + pr_debug("old op index = %d, new op index = %d\n", \ + freqs->old_info.index, freqs->new_info.index); + md = (struct pxa168_md_opt *)p->op; + switch (md->power_mode) { + case POWER_MODE_ACTIVE: + do_freq_notify(info, freqs); + break; + case POWER_MODE_CORE_INTIDLE: + case POWER_MODE_CORE_EXTIDLE: + case POWER_MODE_APPS_IDLE: + case POWER_MODE_APPS_SLEEP: + case POWER_MODE_SYS_SLEEP: + case POWER_MODE_HIBERNATE: + do_lowpower_notify(info, freqs, md->power_mode); + break; + } + } + + local_irq_restore(flags); + local_fiq_enable(); + return 0; +out: + local_irq_restore(flags); + local_fiq_enable(); + return ret; +} + +static int pxa168_request_op(void *driver_data, int index) +{ + struct dvfm_freqs freqs; + struct op_info *info = NULL; + struct pxa168_md_opt *md = NULL; + int relation, ret; + + ret = dvfm_find_op(index, &info); + if (ret) + goto out; + freqs.old = cur_op; + freqs.new = index; + md = (struct pxa168_md_opt *)(info->op); + relation = RELATION_LOW; + ret = pxa168_set_op(driver_data, &freqs, index, relation); + if (!ret) + preferred_op = index; +out: + return ret; +} + +/* + * The machine operation of dvfm_enable + */ +static int pxa168_enable_dvfm(void *driver_data, int dev_id) +{ + struct pxa168_dvfm_info *info = driver_data; + struct pxa168_md_opt *md = NULL; + struct op_info *p = NULL; + int i, num; + num = get_op_num(info, &pxa168_dvfm_op_list); + for (i = 0; i < num; i++) { + if (!dvfm_find_op(i, &p)) { + md = (struct pxa168_md_opt *)p->op; + dvfm_enable_op(i, dev_id); + } + } + return 0; +} + +/* + * The mach operation of dvfm_disable + */ +static int pxa168_disable_dvfm(void *driver_data, int dev_id) +{ + struct pxa168_dvfm_info *info = driver_data; + struct pxa168_md_opt *md = NULL; + struct op_info *p = NULL; + int i, num; + num = get_op_num(info, &pxa168_dvfm_op_list); + for (i = 0; i < num; i++) { + if (!dvfm_find_op(i, &p)) { + md = (struct pxa168_md_opt *)p->op; + dvfm_disable_op(i, dev_id); + } + } + return 0; +} + +static int pxa168_enable_op(void *driver_data, int index, int relation) +{ + /* + * Restore preferred_op. Because this op is sugguested by policy maker + * or user. + */ + return pxa168_request_op(driver_data, preferred_op); +} + +static int pxa168_disable_op(void *driver_data, int index, int relation) +{ + struct dvfm_freqs freqs; + if (cur_op == index) { + freqs.old = index; + freqs.new = -1; + dvfm_set_op(&freqs, freqs.old, relation); + } + return 0; +} + + +#ifdef CONFIG_MSPM_PXA168_STATS +/* Convert 32K ticks to microseconds */ +static unsigned int ticks_32k_to_usec(unsigned int ticks) +{ + return (ticks * 5 * 5 * 5 * 5 * 5 * 5) >> 9; +} + +static unsigned int ticks_32k_to_sec(unsigned int ticks) +{ + return ticks >> 15; +} + + +static unsigned int pxa168_read_32k_ticks(void) +{ + return read_timer()/100; +} +#else +#define pxa168_read_32k_ticks NULL +#define ticks_32k_to_usec NULL +#define ticks_32k_to_sec NULL +#endif + + +static struct dvfm_driver pxa168_driver = { + .count = get_op_num, + .set = pxa168_set_op, + .dump = dump_op, + .name = get_op_name, + .request_set = pxa168_request_op, + .enable_dvfm = pxa168_enable_dvfm, + .disable_dvfm = pxa168_disable_dvfm, + .enable_op = pxa168_enable_op, + .disable_op = pxa168_disable_op, + .ticks_to_usec = ticks_32k_to_usec, + .ticks_to_sec = ticks_32k_to_sec, + .read_time = pxa168_read_32k_ticks, +}; + +void pxa168_get_current_opmode_md(struct pxa168_dvfm_info *driver_data, + struct pxa168_opmode_md *opmode_md) +{ + uint32_t mpmu_fccr, apmu_ccsr, mpmu_posr, mpmu_pll2_reg1; + struct pxa168_dvfm_info *info = driver_data; + int pmic_vcc_core = aspen_vcc_core_convt[0][1]; + int i; + + mpmu_fccr = readl(info->pmum_base + MPMU_FCCR_OFF); + mpmu_posr = readl(info->pmum_base + MPMU_POSR_OFF); + mpmu_pll2_reg1 = readl(info->pmum_base + MPMU_PLL2_REG1_OFF); + apmu_ccsr = readl(info->pmua_base + APMU_CCSR_OFF); + + opmode_md->corepll_sel = (mpmu_fccr & MPMU_FCCR_CORECLKSEL_MSK)>> + MPMU_FCCR_CORECLKSEL_BASE; + opmode_md->axipll_sel = (mpmu_fccr & MPMU_FCCR_AXICLKSEL_MSK)>> + MPMU_FCCR_AXICLKSEL_BASE; + opmode_md->aclk2_div = (apmu_ccsr & APMU_CCSR_BUS2_CLK_DIV_MSK)>> + APMU_CCSR_BUS2_CLK_DIV_BASE; + opmode_md->aclk_div = (apmu_ccsr & APMU_CCSR_BUS_CLK_DIV_MSK)>> + APMU_CCSR_BUS_CLK_DIV_BASE; + opmode_md->dclk_div = (apmu_ccsr & APMU_CCSR_DDR_CLK_DIV_MSK)>> + APMU_CCSR_DDR_CLK_DIV_BASE; + opmode_md->xpclk_div = (apmu_ccsr & APMU_CCSR_XP_CLK_DIV_MSK)>> + APMU_CCSR_XP_CLK_DIV_BASE; + opmode_md->baclk_div = (apmu_ccsr & APMU_CCSR_BIU_CLK_DIV_MSK)>> + APMU_CCSR_BIU_CLK_DIV_BASE; + opmode_md->pclk_div = (apmu_ccsr & APMU_CCSR_CORE_CLK_DIV_MSK)>> + APMU_CCSR_CORE_CLK_DIV_BASE; + opmode_md->pll2_refdiv = (mpmu_posr & MPMU_POSR_PLL2REFD_MSK)>> + MPMU_POSR_PLL2REFD_BASE; + opmode_md->pll2_fbdiv = (mpmu_posr & MPMU_POSR_PLL2FBD_MSK)>> + MPMU_POSR_PLL2FBD_BASE; + opmode_md->pll2_reg1 = mpmu_pll2_reg1; + opmode_md->power_mode = POWER_MODE_ACTIVE; + strncpy(opmode_md->name, "dump op" , OP_NAME_LEN); + + opmode_md->vcc_core = 0; + pxa3xx_pmic_get_voltage(VCC_CORE, &pmic_vcc_core); + for (i = 0; i < ASPEN_ECO11_SIZE; i++) { + if ((aspen_vcc_core_convt[i][1] == pmic_vcc_core)) + opmode_md->vcc_core = aspen_vcc_core_convt[i][2]; + } + return; +} + +/* Produce a operating point table */ +static int op_init(struct pxa168_dvfm_info *driver_data, struct info_head *op_table) +{ + struct pxa168_dvfm_info *info = driver_data; + struct pxa168_md_opt *md, *smd = NULL; + struct pxa168_opmode_md *opmode_md_temp; + unsigned long flags; + int i, index; + struct op_info *p = NULL, *q = NULL; + + unsigned int *pxa168_vcc_core_array; + int array_size = ARRAY_SIZE(pxa168_opmode_md_array); + + /* the supported operating modes is stepping dependent */ + /* Vtyp for each operating mode is also stepping dependent. */ + /* select the correct operating mode set at runtime here: */ + if (cpu_is_pxa168_A0()) + pxa168_vcc_core_array = pxa168_vcc_core_array_a_step; + else + pxa168_vcc_core_array = pxa168_vcc_core_array_b_step; + + write_lock_irqsave(&op_table->lock, flags); + + /* create the op_info list, which contains the human readable */ + /* values (pxa168_md_opt structures) and the dvfm settings */ + /* (pxa168_opmode_md structures). */ + /* start with the fixed operating modes. later, after that, */ + /* add some dynamically defined operatimg modes to the list. */ + + /* add the hard coded operating modes to the op_info list */ + for (i = 0, index = 0; i < array_size; i++) { + + /* set the vcc_core field,which is stepping dependent */ + pxa168_opmode_md_array[i].vcc_core = pxa168_vcc_core_array[i]; + + /* Set index of operating point used in idle (lpm) */ + if (pxa168_opmode_md_array[i].power_mode != POWER_MODE_ACTIVE) + set_idle_op(index, pxa168_opmode_md_array[i].power_mode); + + /* allocate structures and build an op_table entry */ + p = kzalloc(sizeof(struct op_info), GFP_KERNEL); + if (!p) + return -ENOMEM; + + md = p->op = kzalloc(sizeof(struct pxa168_md_opt), GFP_KERNEL); + if (!(p->op)) + return -ENOMEM; + + pxa168_op_machine_to_human(&(pxa168_opmode_md_array[i]), p->op); + md->dvfm_settings = &pxa168_opmode_md_array[i]; + md->index = index; + p->index = index++; + list_add_tail(&(p->list), &(op_table->list)); + } + + /* add dynamically created operating modes to the list. */ + /* the boot operating mode is a dynamically created mode . */ + p = kzalloc(sizeof(struct op_info), GFP_KERNEL); + if (!(p)) + return -ENOMEM; + + p->op = kzalloc(sizeof(struct pxa168_md_opt), GFP_KERNEL); + if (!(p->op)) + return -ENOMEM; + + opmode_md_temp = kzalloc(sizeof(struct pxa168_opmode_md), GFP_KERNEL); + if (!opmode_md_temp) + return -ENOMEM; + + md = (struct pxa168_md_opt *)p->op; + /* capture the op info. start by getting the dvfm_settings */ + pxa168_get_current_opmode_md(info, opmode_md_temp); /* dvfm_settings */ + + /* convert the dvfm_settings to human readable format */ + pxa168_op_machine_to_human(opmode_md_temp, md); + + /* find out if the current mode is one of the hard coded modes */ + /* by comparing the human readable structures. */ + def_op = 0x5a5a; /* magic number */ + list_for_each_entry(q, &(op_table->list), list) { + smd = (struct pxa168_md_opt *)q->op; + if (md->pclk == smd->pclk && \ + md->aclk2 == smd->aclk2 && \ + md->baclk == smd->baclk && \ + md->xpclk == smd->xpclk && \ + md->dclk == smd->dclk && \ + md->aclk == smd->aclk) { + def_op = q->index; + break; + } + } + + if (def_op != 0x5a5a) { + md->dvfm_settings = smd->dvfm_settings; + md->vcc_core = smd->vcc_core; + kfree(opmode_md_temp); + } else { + opmode_md_temp->vcc_core = 1155; /* Vmax for pxa168 */ + md->dvfm_settings = opmode_md_temp; + md->vcc_core = opmode_md_temp->vcc_core; + } + md->power_mode = POWER_MODE_ACTIVE; + sprintf(md->name, "BOOT OP"); + md->index = index; + p->index = index++; + + /* Add BOOT OP into op list */ + list_add_tail(&p->list, &op_table->list); + + + /* create a place holder for temporary, custom operating modes */ + q = (struct op_info *)kzalloc(sizeof(struct op_info), GFP_KERNEL); + if (!(q)) + return -ENOMEM; + q->op = (struct pxa168_md_opt *)kzalloc\ + (sizeof(struct pxa168_md_opt), GFP_KERNEL); + if (!(q->op)) + return -ENOMEM; + + opmode_md_temp = kzalloc(sizeof(struct pxa168_opmode_md), GFP_KERNEL); + if (!opmode_md_temp) + return -ENOMEM; + + /* for now, initialize the custom operating mode data with boot mode */ + smd = (struct pxa168_md_opt *)q->op; + memcpy(smd, md, sizeof(struct pxa168_md_opt)); + sprintf(smd->name, "CUSTOM OP"); + memcpy(opmode_md_temp, md->dvfm_settings, + sizeof(struct pxa168_opmode_md)); + smd->dvfm_settings = opmode_md_temp; + smd->index = index; + q->index = index++; + + /* Add CUSTOM OP into op list */ + list_add_tail(&q->list, &op_table->list); + + /* BOOT op */ + if (def_op == 0x5a5a) { + cur_op = p->index; + def_op = p->index; + } else + cur_op = def_op; + + preferred_op = cur_op; + pr_debug("%s, def_op:%d, cur_op:%d\n", __func__, def_op, cur_op); + + /* set the operating point number */ + op_nums = array_size + 2; + + printk("Current Operating Point is %d\n", cur_op); + dump_op_list(info, op_table); + write_unlock_irqrestore(&op_table->lock, flags); + + return 0; +} + +unsigned long remap_to_uncacheable(unsigned long add, int size) +{ + unsigned long temp; + temp = (unsigned long)__pa(add); + temp = (unsigned long)ioremap(temp, size); + return temp; +} + +#ifdef CONFIG_PM +static int pxa168_freq_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int pxa168_freq_resume(struct platform_device *pdev) +{ + unsigned int prev_op; + + prev_op = cur_op; /* cur_op: opmode before hibernate */ + dvfm_request_op(0); /* go via mode 0 to simplify fcs */ + dvfm_request_op(prev_op); /* finally, restore prev op mode */ + + return 0; +} +#else +#define pxa168_freq_suspend NULL +#define pxa168_freq_resume NULL +#endif + +static int pxa168_speedgrade() +{ + unsigned long speedgrade_bits; + + speedgrade_bits = *(volatile unsigned long*)CIU_SPEEDGRADE; + speedgrade_bits = (speedgrade_bits & CIU_SPEEDGRADE_MASK) + >> CIU_SPEEDGRADE_SHIFT; + + return (int) speedgrade_bits; +} + + +static int pxa168_freq_probe(struct platform_device *pdev) +{ + struct resource *res; + struct pxa168_dvfm_info *info; + uint32_t apmu_temp; + + if (!(info = kzalloc(sizeof(struct pxa168_dvfm_info), GFP_KERNEL))) + goto err; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmum_regs"); + if (!res) goto err; + info->pmum_base = ioremap(res->start, res->end - res->start + 1); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmua_regs"); + if (!res) goto err; + info->pmua_base = ioremap(res->start, res->end - res->start + 1); + + pxa168_driver.priv = info; + + if (cpu_is_pxa168_A0() == 0) { /* checking for not A stepping */ + /* these bits (30:29) should be set to 0x10 in reg address 0xd4282800 */ + apmu_temp = readl(info->pmua_base); + apmu_temp |= 0x20000000; + writel(apmu_temp, info->pmua_base); + pr_debug(">>>>>%s: PHY(%x) = %x\n", __func__, + 0xd4282800, apmu_temp); + } + + if (op_init(info, &pxa168_dvfm_op_list)) + goto err; + + if (dvfm_register("PXA168-DVFM", &dvfm_dev_id)) + goto err; + + if (dvfm_register_driver(&pxa168_driver, &pxa168_dvfm_op_list)) + goto err; + + /* Only B steppings can support the higher operating modes: */ + if (cpu_is_pxa168_A0()) { + /* On Ax steppings, disable the higher operating modes */ + dvfm_disable_op_name("mode 4", dvfm_dev_id); + dvfm_disable_op_name("mode 4.1", dvfm_dev_id); + } else if (pxa168_speedgrade() <= CIU_SPEEDGRADE_800) { + /* On B stepping, the speed grade will set the limit */ + dvfm_disable_op_name("mode 4", dvfm_dev_id); + dvfm_disable_op_name("mode 4.1", dvfm_dev_id); + } + + return 0; +err: + printk(KERN_NOTICE "pxa168_dvfm init failed\n"); + return -EIO; +} + +static int pxa168_freq_remove(struct platform_device *pdev) +{ + kfree(pxa168_driver.priv); + dvfm_unregister("PXA168-DVFM", &dvfm_dev_id); + return dvfm_unregister_driver(&pxa168_driver); +} + +static struct platform_driver pxa168_freq_driver = { + .driver = { + .name = "pxa168-freq", + }, + .probe = pxa168_freq_probe, + .remove = pxa168_freq_remove, +#ifdef CONFIG_PM + .suspend = pxa168_freq_suspend, + .resume = pxa168_freq_resume, +#endif +}; + +#ifdef CONFIG_MSPM_PXA168_STATS +static unsigned int switch_lowpower_before, switch_lowpower_after; +/* It's invoked by PM functions. + * PM functions can store the accurate time of entering/exiting low power + * mode. + */ +int calc_switchtime(unsigned int end, unsigned int start) +{ + switch_lowpower_before = end; + switch_lowpower_after = start; + return 0; +} + +static int pxa168_stats_notifier_freq(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct dvfm_freqs *freqs = (struct dvfm_freqs *)data; + struct op_info *info = &(freqs->new_info); + struct pxa168_md_opt *md = NULL; + unsigned int ticks; + + ticks = pxa168_read_32k_ticks(); + md = (struct pxa168_md_opt *)(info->op); + if (md->power_mode == POWER_MODE_ACTIVE) { + switch (val) { + case DVFM_FREQ_PRECHANGE: + calc_switchtime_start(freqs->old, freqs->new, ticks); + break; + case DVFM_FREQ_POSTCHANGE: + /* Calculate the costed time on switching frequency */ + calc_switchtime_end(freqs->old, freqs->new, ticks); + dvfm_add_event(freqs->old, CPU_STATE_RUN, + freqs->new, CPU_STATE_RUN); + dvfm_add_timeslot(freqs->old, CPU_STATE_RUN); + mspm_add_event(freqs->old, CPU_STATE_RUN); + break; + } + } else if (md->power_mode == POWER_MODE_APPS_IDLE || + md->power_mode == POWER_MODE_APPS_SLEEP || + md->power_mode == POWER_MODE_SYS_SLEEP) { + switch (val) { + case DVFM_FREQ_PRECHANGE: + calc_switchtime_start(freqs->old, freqs->new, ticks); + /* Consider lowpower mode as idle mode */ + dvfm_add_event(freqs->old, CPU_STATE_RUN, + freqs->new, CPU_STATE_IDLE); + dvfm_add_timeslot(freqs->old, CPU_STATE_RUN); + mspm_add_event(freqs->old, CPU_STATE_RUN); + break; + case DVFM_FREQ_POSTCHANGE: + /* switch_lowpower_start before switch_lowpower_after + * is updated in calc_switchtime(). + * It's invoked in pm function. + */ + calc_switchtime_end(freqs->old, freqs->new, + switch_lowpower_before); + calc_switchtime_start(freqs->new, freqs->old, + switch_lowpower_after); + calc_switchtime_end(freqs->new, freqs->old, + ticks); + dvfm_add_event(freqs->new, CPU_STATE_IDLE, + freqs->old, CPU_STATE_RUN); + dvfm_add_timeslot(freqs->new, CPU_STATE_IDLE); + mspm_add_event(freqs->new, CPU_STATE_IDLE); + break; + } + } + return 0; +} +static struct notifier_block notifier_freq_block = { + .notifier_call = pxa168_stats_notifier_freq, +}; +#else +#endif + + +static int __init pxa168_freq_init(void) +{ + unsigned long base; + unsigned int size; + if (!cpu_is_pxa168()) + return -EIO; +#ifdef CONFIG_MSPM_PXA168_STATS + dvfm_register_notifier(¬ifier_freq_block, + DVFM_FREQUENCY_NOTIFIER); +#endif + pxa168_dfc_get_lockcache_location(&base, &size); + dfc_lock_cache_code_base = remap_to_uncacheable(base, size); + + return platform_driver_register(&pxa168_freq_driver); +} + +static void __exit pxa168_freq_exit(void) +{ +#ifdef CONFIG_MSPM_PXA168_STATS + dvfm_unregister_notifier(¬ifier_freq_block, + DVFM_FREQUENCY_NOTIFIER); +#endif + + platform_driver_unregister(&pxa168_freq_driver); +} + +module_init(pxa168_freq_init); +module_exit(pxa168_freq_exit); diff --git a/arch/arm/mach-mmp/pxa168_dvfm_stats.c b/arch/arm/mach-mmp/pxa168_dvfm_stats.c new file mode 100644 index 00000000000000..3274bd5e468d90 --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_dvfm_stats.c @@ -0,0 +1,660 @@ +/* + * DVFM Statistic Driver + * + * Copyright (C) 2007 Marvell Corporation + * Haojian Zhuang + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define OP_NUM 20 +#define JIFFIES_VALVE 2 + +#define EVENT_PAGE_NUM 32 +#define OP_STATS_PAGE_NUM 16 +#define EVENT_ARRAY_SIZE (EVENT_PAGE_NUM * PAGE_SIZE) +#define OP_STATS_ARRAY_SIZE (OP_STATS_PAGE_NUM * PAGE_SIZE) + +#define EVENT_BUF_LEN 64 +#define STATS_BUF_LEN 256 + +struct op_switch { + unsigned int last_tick; + unsigned int min_tick; + unsigned int max_tick; + unsigned int timestamp; + unsigned int count; +}; + +static struct op_switch op_switch[OP_NUM][OP_NUM]; + +struct event_info { + unsigned int event_idx; + unsigned int op_idx; +}; + +struct event_type { + unsigned int event; /* high halfword is src, low is dest */ + unsigned int timestamp; /* record time when the event happen */ +}; + +struct op_stats_type { + unsigned int timestamp; + unsigned int op_idx; + unsigned int runtime; + unsigned int idletime; + unsigned int overflow; /* high halfword is run, low is idle */ +}; + +struct op_cycle_type { + unsigned int op_idx; + unsigned int runtime; + unsigned int idletime; + unsigned int count; +}; + +/* relayfs interface */ +static struct rchan *event_chan, *op_stats_chan; + +/* Run/Idle state change, OPs change are all stored in event table. */ +static struct event_type *event_table_head, *event_p; +/* Detail time cost on all OPs are stored in op_stats table */ +static struct op_stats_type *op_stats_table_head, *op_stats_p; + +static struct event_info event_array[2 * OP_NUM]; +static int event_num; /* stores the number of all events */ + +/* Indicates the first timeslot should be skipped */ +static int dvfm_timeslot_init; + +/* Total ticks cost on all OPs. */ +static struct op_cycle_type op_ticks_array[OP_NUM]; +static spinlock_t stats_lock = SPIN_LOCK_UNLOCKED; + +static int cap_init(void); +static int cap_deinit(void); +static void write_event_chan(void); +static void write_stats_chan(void); + +/* Interface under SYSFS */ + +/* Display duty cycles on all operating points */ +static ssize_t duty_cycle_show(struct sys_device *sys_dev,\ + struct sysdev_attribute *attr, char *buf) +{ + int len, i; + unsigned int total_ticks; + + total_ticks = 0; + for (i = 0; i < (event_num / 2); i++) { + total_ticks += op_ticks_array[i].runtime + + op_ticks_array[i].idletime; + } + if (total_ticks == 0) { + len = sprintf(buf, "No OP change, no duty cycle info\n"); + return len; + } + len = sprintf(buf, "Duty cycle of operating point list:\n"); + for (i = 0; i < (event_num / 2); i++) { + len += sprintf(buf + len, "op%d run:%u%% idle:%u%%\n", + i, op_ticks_array[i].runtime * 100 / total_ticks, + op_ticks_array[i].idletime * 100 / total_ticks); + } + return len; +} +SYSDEV_ATTR(duty_cycle, 0444, duty_cycle_show, NULL); + +/* Display costed time on all operating points */ +static ssize_t ticks_show(struct sys_device *sys_dev,\ + struct sysdev_attribute *attr, char *buf) +{ + int len, i; + len = sprintf(buf, "Ticks of operating point list:\n"); + for (i = 0; i < (event_num / 2); i++) { + len += sprintf(buf + len, "op%d, run ticks:%u, idle ticks:%u " + "run second:%u, idle second:%u count:%u\n", + i, op_ticks_array[i].runtime, + op_ticks_array[i].idletime, + dvfm_driver->ticks_to_sec(op_ticks_array[i].runtime), + dvfm_driver->ticks_to_sec(op_ticks_array[i].idletime), + op_ticks_array[i].count); + } + return len; +} +SYSDEV_ATTR(ticks, 0444, ticks_show, NULL); + +static inline void __delay(unsigned long loops) +{ + __asm__ __volatile__ ("1:\n" "subs %0, %1, #1\n" + "bne 1b" : "=r" (loops) : "0"(loops)); +} + +#define CONFIG_SYS_HZ 3250000 +static ssize_t mips_show(struct sys_device *sys_dev, \ + struct sysdev_attribute *attr, char *buf) +{ + ulong oldtimer, timer, left; + + unsigned long loops_per_sec = 1; + printk(KERN_INFO "Calibrating delay loop.. "); + while (loops_per_sec <<= 1) { + oldtimer = read_timer(); + oldtimer = read_timer(); + __delay(loops_per_sec); + timer = read_timer(); + timer = read_timer(); + timer = timer - oldtimer; + if (timer >= CONFIG_SYS_HZ) { + left = loops_per_sec % timer; + loops_per_sec = loops_per_sec / timer * CONFIG_SYS_HZ; + while (left > 100000) { + loops_per_sec += CONFIG_SYS_HZ / (timer / left); + left = timer % left; + } + printk("ok - %lu.%02lu BogoMips\n", + loops_per_sec/500000, + (loops_per_sec/5000) % 100); + return 0; + } + } + printk(KERN_INFO "failed\n"); + return -1; + + +} +SYSDEV_ATTR(mips, 0444, mips_show, NULL); +/* Display costed time on switching operating point */ +static ssize_t switch_time_show(struct sys_device *sys_dev,\ + struct sysdev_attribute *attr, char *buf) +{ + int i, j, len, op_num; + + /* event_num is double size of op_num */ + op_num = event_num / 2; + len = sprintf(buf, "Time cost on switching operating point:"); + len += sprintf(buf + len, "(Unit is usec)\n"); + len += sprintf(buf + len, "(Left is source OP, right is dest OP)\n"); + for (i = 0; i < op_num; i++) + len += sprintf(buf + len, "\tOP%d", i); + len += sprintf(buf + len, "\n"); + for (i = 0; i < op_num; i++) { + len += sprintf(buf + len, "OP%d\t", i); + for (j = 0; j < op_num; j++) { + len += sprintf(buf + len, "%d(%d,%d)\t", + dvfm_driver->ticks_to_usec(op_switch[i][j].last_tick), + dvfm_driver->ticks_to_usec(op_switch[i][j].min_tick), + dvfm_driver->ticks_to_usec(op_switch[i][j].max_tick)); + } + len += sprintf(buf + len, "\n"); + } + return len; +} +SYSDEV_ATTR(switch_time, 0444, switch_time_show, NULL); + +/* Re-collect static information */ +static ssize_t stats_store(struct sys_device *sys_dev,\ + struct sysdev_attribute *attr, const char *buf, + size_t len) +{ + unsigned int cap_flag; + + sscanf(buf, "%u", &cap_flag); + if (cap_flag == 1) { + cap_init(); + } else if (cap_flag == 0) { + cap_deinit(); + write_event_chan(); + write_stats_chan(); + } + return len; +} +SYSDEV_ATTR(stats, 0200, NULL, stats_store); + +static struct attribute *dvfm_stats_attr[] = { + &attr_duty_cycle.attr, + &attr_ticks.attr, + &attr_switch_time.attr, + &attr_stats.attr, + &attr_mips.attr, +}; + +static void update_op_cycle(int op_idx, unsigned int runtime, + unsigned int idletime, int inc) +{ + op_ticks_array[op_idx].runtime += runtime; + op_ticks_array[op_idx].idletime += idletime; + if (inc) + op_ticks_array[op_idx].count++; +} + +/* Events are divided into two parts. One is the set of run mode of operating + * points, the other is the set of idle mode of operating points. + * The event number of these two sets is equal. + */ +static int dvfm_event_init(struct info_head *op_table) +{ + struct op_info *entry = NULL; + int num = 0, i; + + if (!op_table) + goto out; + memset(&event_array, 0, sizeof(struct event_info) * 2 * OP_NUM); + read_lock(&op_table->lock); + if (list_empty(&op_table->list)) { + read_unlock(&op_table->lock); + goto out; + } + list_for_each_entry(entry, &op_table->list, list) { +#if 1 + num++; +#else + if (entry->op.flag == OP_FLAG_FACTORY) { + num++; + event_array[i].op_idx = num; + event_array[i].event_idx = num; + } +#endif + } + for (i = 0; i < num; i++) { + event_array[i + num].op_idx = event_array[i].op_idx; + event_array[i + num].event_idx = event_array[i].event_idx + + num; + } + read_unlock(&op_table->lock); +out: + event_num = 2 * num; + return event_num; +} + +static struct dentry *dvfm_create_buf(const char *filename, + struct dentry *parent, int mode, + struct rchan_buf *buf, int *is_global) +{ + return debugfs_create_file(filename, S_IRUGO | S_IWUSR, parent, + buf, &relay_file_operations); +} + +static int dvfm_remove_buf(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static struct rchan_callbacks event_cb = { + .subbuf_start = NULL, + .buf_mapped = NULL, + .buf_unmapped = NULL, + .create_buf_file = dvfm_create_buf, + .remove_buf_file = dvfm_remove_buf, +}; + +static int dvfm_event_table_init(void) +{ + event_table_head = kzalloc(EVENT_ARRAY_SIZE, GFP_KERNEL); + if (!event_table_head) + goto out_table; + event_p = event_table_head; + return 0; + +out_table: + printk(KERN_WARNING "Not enough memory of event table.\n"); + event_p = NULL; + event_table_head = NULL; + return -ENOMEM; +} + +static void dvfm_event_table_deinit(void) +{ + if (event_table_head) + kfree(event_table_head); +} + +static int dvfm_timeslot_table_init(void) +{ + op_stats_table_head = kzalloc(OP_STATS_ARRAY_SIZE, GFP_KERNEL); + if (!op_stats_table_head) + goto out_table; + op_stats_p = op_stats_table_head; + return 0; + +out_table: + printk(KERN_WARNING "Not enough memory of op stats table.\n"); + op_stats_table_head = NULL; + op_stats_p = NULL; + return -ENOMEM; +} + +static void dvfm_timeslot_table_deinit(void) +{ + if (op_stats_table_head) + kfree(op_stats_table_head); +} + +/* write event to relay channel */ +static void write_event_chan(void) +{ + struct event_type *p = event_table_head; + int len, size; + char buf[EVENT_BUF_LEN]; + + if (p == NULL) { + printk(KERN_WARNING "%s: no event\n", __func__); + return; + } + + /* + * Close the relay channel in order to save new data. + */ + if (event_chan) + relay_close(event_chan); + event_chan = relay_open("event_table", NULL, + PAGE_SIZE, EVENT_PAGE_NUM, &event_cb, NULL); + if (event_chan == NULL) { + printk(KERN_WARNING "Can't open event_chan.\n"); + return; + } + + relay_reset(event_chan); + size = sizeof(struct event_type); + spin_lock(&stats_lock); + for (;;) { + memset(buf, 0, EVENT_BUF_LEN); + len = sprintf(buf, "E, %x, T, %u\n", p->event, p->timestamp); + relay_write(event_chan, buf, len); + p++; + if (p > (event_table_head + (EVENT_ARRAY_SIZE - size) + / size)) + break; + } + spin_unlock(&stats_lock); + /* Just flush, don't use relay_close() at here. */ + relay_flush(event_chan); +} + +/* write stats to relay channel */ +static void write_stats_chan(void) +{ + struct op_stats_type *p = op_stats_table_head; + int len, size; + char buf[STATS_BUF_LEN]; + + if (p == NULL) { + printk(KERN_WARNING "%s: no stats\n", __func__); + return; + } + + /* + * Close the relay channel in order to save new data. + */ + if (op_stats_chan) + relay_close(op_stats_chan); + op_stats_chan = relay_open("op_stats_table", NULL, + PAGE_SIZE, OP_STATS_PAGE_NUM, &event_cb, NULL); + if (op_stats_chan == NULL) { + printk(KERN_WARNING "Can't open stats_chan.\n"); + return; + } + relay_reset(op_stats_chan); + size = sizeof(struct op_stats_type); + spin_lock(&stats_lock); + for (;;) { + memset(buf, 0, STATS_BUF_LEN); + len = sprintf(buf, "T, %u, OP, %d, RT, %u, IT, %u, OV, %x\n", + p->timestamp, p->op_idx, p->runtime, + p->idletime, p->overflow); + relay_write(op_stats_chan, buf, len); + p++; + if (p > (op_stats_table_head + (OP_STATS_ARRAY_SIZE - size) + / size)) + break; + } + spin_unlock(&stats_lock); + /* Just flush, don't use relay_close() at here. */ + relay_flush(op_stats_chan); +} + +/* + * Add this event into event table when new event changes. + * New event can be transition between run mode and idle mode in same operating + * point, transition between different operating point. + * Event table will record two things. One is event, the other is time stamp. + * + * The format of event is in below. + * source event, dest event + * + */ +int dvfm_add_event(int src_op_idx, int src_cpu_state, int dst_op_idx, + int dst_cpu_state) +{ + unsigned int event, size; + unsigned long flags; + if (event_table_head == NULL || event_p == NULL) + return -EINVAL; + if (src_cpu_state == CPU_STATE_IDLE) + src_op_idx += (event_num >> 1); + if (dst_cpu_state == CPU_STATE_IDLE) + dst_op_idx += (event_num >> 1); + event = (src_op_idx << 16) + dst_op_idx; + spin_lock_irqsave(&stats_lock, flags); + event_p->event = event; + event_p->timestamp = dvfm_driver->read_time(); + /* increase the pointer to next one */ + event_p++; + size = sizeof(struct event_type); + if (event_p >= (event_table_head + + (EVENT_ARRAY_SIZE - size) / size)) { + event_p = event_table_head; + } + spin_unlock_irqrestore(&stats_lock, flags); + return 0; +} + +/* + * Add this information into timeslot table. + * This table records the time cost on different cpu state and different + * operating points. + */ +int dvfm_add_timeslot(int op_idx, int cpu_state) +{ + static unsigned int prev_timestamp; + unsigned int timestamp, time, size; + unsigned int idle_overflow = 0, run_overflow = 0; + unsigned long flags; + + spin_lock_irqsave(&stats_lock, flags); + if (op_stats_table_head == NULL || op_stats_p == NULL) { + spin_unlock_irqrestore(&stats_lock, flags); + return -EINVAL; + } + timestamp = dvfm_driver->read_time(); + time = (timestamp >= prev_timestamp) ? timestamp - prev_timestamp + : 0xFFFFFFFF - prev_timestamp + timestamp; + prev_timestamp = timestamp; + if (!dvfm_timeslot_init) { + /* It's the first snapshot */ + dvfm_timeslot_init = 1; + spin_unlock_irqrestore(&stats_lock, flags); + return -EAGAIN; + } + + if (op_idx != op_stats_p->op_idx) { + /* new operating point */ + update_op_cycle(op_stats_p->op_idx, 0, 0, 1); + op_stats_p++; /* Point to next one */ + size = sizeof(struct op_stats_type); + if (op_stats_p >= (op_stats_table_head + (OP_STATS_ARRAY_SIZE + - size) / size)) + op_stats_p = op_stats_table_head; + op_stats_p->op_idx = op_idx; + op_stats_p->timestamp = dvfm_driver->read_time(); + op_stats_p->runtime = 0; + op_stats_p->idletime = 0; + op_stats_p->overflow = 0; + } + if (cpu_state == CPU_STATE_IDLE) { + if (op_stats_p->idletime > (0xFFFFFFFF - time)) + idle_overflow = 1; + op_stats_p->idletime += time; + update_op_cycle(op_idx, 0, time, 0); + } else { + if (op_stats_p->runtime > (0xFFFFFFFF - time)) + run_overflow = 1; + op_stats_p->runtime += time; + update_op_cycle(op_idx, time, 0, 0); + } + op_stats_p->overflow = (run_overflow << 16) + idle_overflow; + spin_unlock_irqrestore(&stats_lock, flags); + return 0; +} + +/* Calculate time when begin to switch new operating point. */ +int calc_switchtime_start(int old, int new, unsigned int ticks) +{ + if (old == new) + return -EINVAL; + op_switch[old][new].timestamp = ticks; + return 0; +} + +/* Calculate time when end to switch new operating point. */ +int calc_switchtime_end(int old, int new, unsigned int ticks) +{ + unsigned int time; + struct op_switch *p = &op_switch[old][new]; + + if (old == new) + return -EINVAL; + time = (ticks > p->timestamp) ? ticks - p->timestamp + : 0xFFFFFFFF - p->timestamp + ticks; + p->last_tick = time; + if (time > p->max_tick) + p->max_tick = time; + if (time < p->min_tick || p->min_tick == 0) + p->min_tick = time; + return 0; +} + +static int cap_init(void) +{ + struct op_info *info = NULL; + unsigned long flags; + int curop; + + spin_lock_irqsave(&stats_lock, flags); + /* Initialize the pointer to tables */ + event_p = event_table_head; + op_stats_p = op_stats_table_head; + memset((char *)event_p, 0, EVENT_ARRAY_SIZE); + memset((char *)op_stats_p, 0, OP_STATS_ARRAY_SIZE); + + /* Update the op_idx in first entry of op_stats_table */ + op_stats_p->op_idx = dvfm_get_op(&info); + op_stats_p->timestamp = dvfm_driver->read_time(); + curop = op_stats_p->op_idx; + spin_unlock_irqrestore(&stats_lock, flags); + + /* Clear op_cycle array */ + memset(&op_ticks_array, 0, sizeof(struct op_cycle_type) * OP_NUM); + + dvfm_timeslot_init = 0; + + /* make a timestamp for the first snapshot */ + dvfm_add_timeslot(curop, CPU_STATE_RUN); + dvfm_add_event(curop, CPU_STATE_RUN, curop, CPU_STATE_RUN); + + return 0; +} + +static int cap_deinit(void) +{ + struct op_info *info = NULL; + int curop; + + /* make a timestamp for the last snapshot */ + curop = dvfm_get_op(&info); + dvfm_add_timeslot(curop, CPU_STATE_RUN); + dvfm_add_event(curop, CPU_STATE_RUN, curop, CPU_STATE_RUN); + dvfm_timeslot_init = 0; + return 0; +} + +static int stats_add(struct sys_device *sys_dev) +{ + int i, n, ret; + n = ARRAY_SIZE(dvfm_stats_attr); + for (i = 0; i < n; i++) { + ret = sysfs_create_file(&(sys_dev->kobj), dvfm_stats_attr[i]); + if (ret) + return ret; + } + return 0; +} + +static int stats_rm(struct sys_device *sys_dev) +{ + int i, n; + n = ARRAY_SIZE(dvfm_stats_attr); + for (i = 0; i < n; i++) + sysfs_remove_file(&(sys_dev->kobj), dvfm_stats_attr[i]); + return 0; +} + +static int stats_suspend(struct sys_device *sysdev, pm_message_t pmsg) +{ + return 0; +} + +static int stats_resume(struct sys_device *sysdev) +{ + return 0; +} + +static struct sysdev_driver dvfm_stats_driver = { + .add = stats_add, + .remove = stats_rm, + .suspend = stats_suspend, + .resume = stats_resume, +}; + +int __init dvfm_stats_init(void) +{ + int ret; + + dvfm_event_init(dvfm_op_list); + dvfm_event_table_init(); + dvfm_timeslot_table_init(); + cap_init(); + ret = sysdev_driver_register(&cpu_sysdev_class, &dvfm_stats_driver); + if (ret) + printk(KERN_ERR "Can't register DVFM STATS in sysfs\n"); + return ret; +} + +void __exit dvfm_stats_exit(void) +{ + sysdev_driver_unregister(&cpu_sysdev_class, &dvfm_stats_driver); + dvfm_timeslot_table_deinit(); + dvfm_event_table_deinit(); +} + +module_init(dvfm_stats_init); +module_exit(dvfm_stats_exit); + diff --git a/arch/arm/mach-mmp/pxa168_mspm_idle.c b/arch/arm/mach-mmp/pxa168_mspm_idle.c new file mode 100644 index 00000000000000..f23b436c393f0f --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_mspm_idle.c @@ -0,0 +1,361 @@ +/* + * PXA168 MSPM IDLE + * + * Copyright (c) 2003 Intel Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2008 Marvell International Ltd. + * All Rights Reserved + */ + +#undef DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static int core_intidle_opidx = -1, core_extidle_opidx = -1,\ + apps_idle_opidx = -1, apps_sleep_opidx = -1, sys_sleep_opidx = -1; + +#define LOWPOWER_CORE_INTIDLE_THRE 0 +#define LOWPOWER_CORE_EXTIDLE_THRE 0 +#define LOWPOWER_APPS_IDLE_THRE 10 +#define LOWPOWER_APPS_SLEEP_THRE 20 +#define LOWPOWER_SYS_SLEEP_THRE 1500 +#define IDLE_STATS_NUM 1000 + +struct idle_stats { + unsigned int index; + unsigned int ticks; +}; + +struct mspm_idle_stats { + struct idle_stats stats[IDLE_STATS_NUM]; + unsigned int stats_index; + spinlock_t lock; +}; + +static struct mspm_idle_stats mspm_stats = { + .stats_index = 0, + .lock = SPIN_LOCK_UNLOCKED, +}; + +static void (*orig_idle)(void); + +/* static void mspm_cpu_idle(void) +{ + struct op_info *info = NULL; + int op; + + op = dvfm_get_op(&info); + mspm_add_event(op, CPU_STATE_RUN); + cpu_do_idle(); + mspm_add_event(op, CPU_STATE_IDLE); +}*/ + +/* Collect statistic information before entering idle */ +static void record_idle_stats(void) +{ + int i; + + spin_lock(&mspm_stats.lock); + if (++mspm_stats.stats_index == IDLE_STATS_NUM) + mspm_stats.stats_index = 0; + i = mspm_stats.stats_index; + memset(&mspm_stats.stats[i], 0, sizeof(struct idle_stats)); + mspm_stats.stats[i].index = i; + mspm_stats.stats[i].ticks = read_timer(); + spin_unlock(&mspm_stats.lock); +} + +/* Collect statistic information after exiting idle. + */ +static void update_idle_stats(void) +{ + int i; + + spin_lock(&mspm_stats.lock); + i = mspm_stats.stats_index; + mspm_stats.stats[i].ticks = read_timer() + - mspm_stats.stats[i].ticks; + spin_unlock(&mspm_stats.lock); +} + +void set_idle_op(int idx, int mode) +{ + switch (mode) { + case POWER_MODE_CORE_INTIDLE: + core_intidle_opidx = idx; + break; + case POWER_MODE_CORE_EXTIDLE: + core_extidle_opidx = idx; + break; + case POWER_MODE_APPS_IDLE: + apps_idle_opidx = idx; + break; + case POWER_MODE_APPS_SLEEP: + apps_sleep_opidx = idx; + break; + case POWER_MODE_SYS_SLEEP: + sys_sleep_opidx = idx; + break; + } +} + + +static int lpidle_is_valid(int enable, unsigned int msec, + struct dvfm_freqs *freqs, int lp_idle) +{ + struct op_info *info = NULL; + struct pxa168_md_opt *op; + int prev_op; + int ret; + int threshold = 0; + if ((freqs == NULL) || (lp_idle == IDLE_CORE_EXTIDLE && \ + core_extidle_opidx == -1) + || (lp_idle == IDLE_APPS_IDLE && apps_idle_opidx == -1) + || (lp_idle == IDLE_APPS_SLEEP && apps_sleep_opidx == -1) + || (lp_idle == IDLE_SYS_SLEEP && sys_sleep_opidx == -1)) + return 0; + + if (enable & lp_idle) { + switch (lp_idle) { + case IDLE_CORE_INTIDLE: + threshold = LOWPOWER_CORE_INTIDLE_THRE; + break; + case IDLE_CORE_EXTIDLE: + threshold = LOWPOWER_CORE_EXTIDLE_THRE; + break; + case IDLE_APPS_IDLE: + threshold = LOWPOWER_APPS_IDLE_THRE; + break; + case IDLE_APPS_SLEEP: + threshold = LOWPOWER_APPS_SLEEP_THRE; + break; + case IDLE_SYS_SLEEP: + threshold = LOWPOWER_SYS_SLEEP_THRE; + break; + default: + BUG(); + break; + } + + /* Check dynamic tick flag && idle interval */ + if (msec < threshold) + return 0; + /* Check whether the specified low power mode is valid */ + ret = dvfm_get_op(&info); + if (info == NULL) + return 0; + op = (struct pxa168_md_opt *)info->op; + if (op == NULL) + return 0; + if ((ret >= 0) && (op->power_mode == POWER_MODE_ACTIVE)) { + prev_op = ret; + freqs->old = ret; + freqs->new = lp_idle == IDLE_CORE_EXTIDLE \ + ? core_extidle_opidx : + lp_idle == IDLE_APPS_IDLE ? apps_idle_opidx : + lp_idle == IDLE_APPS_SLEEP ? apps_sleep_opidx : + lp_idle == IDLE_SYS_SLEEP ? sys_sleep_opidx : + -1; + if (freqs->new < 0) + return 0; + ret = dvfm_get_opinfo(freqs->new, &info); + if ((ret >= 0) && find_first_bit(info->device, \ + DVFM_MAX_CLIENT) == DVFM_MAX_CLIENT) { + return 1; + } + } + } + return 0; +} + +/* + * IDLE Thread + */ + +unsigned int get_remain_slice(void) +{ + uint32_t val1 = 0; + uint32_t val2 = 0; + + val1 = __raw_readl(TIMERS1_VIRT_BASE + TMR_TN_MM(0, 0)); + val2 = read_timer(); + return (val1-val2)/3250; +} + + +void mspm_do_idle(void) +{ + struct dvfm_freqs freqs; + int ret = -EINVAL; + unsigned int msec; +#ifdef CONFIG_MSPM_PXA168_STATS + struct op_info *info = NULL; + int op; +#endif + local_irq_disable(); + if (!need_resched()) { + msec = get_remain_slice(); + record_idle_stats(); + + if (lpidle_is_valid(enable_deepidle, msec, &freqs, \ + IDLE_SYS_SLEEP)) { + ret = dvfm_set_op(&freqs, freqs.new, + RELATION_STICK); + if (ret == 0) + goto out; + } + if (lpidle_is_valid(enable_deepidle, msec, &freqs, \ + IDLE_APPS_SLEEP)) { + ret = dvfm_set_op(&freqs, freqs.new, + RELATION_STICK); + if (ret == 0) + goto out; + } + if (lpidle_is_valid(enable_deepidle, msec, &freqs, \ + IDLE_APPS_IDLE)) { + ret = dvfm_set_op(&freqs, freqs.new, + RELATION_STICK); + if (ret == 0) + goto out; + } + if (enable_deepidle & IDLE_CORE_EXTIDLE) { +#ifdef CONFIG_MSPM_PXA168_STATS + op = dvfm_get_op(&info); + dvfm_add_event(op, CPU_STATE_RUN, op, CPU_STATE_IDLE); + dvfm_add_timeslot(op, CPU_STATE_RUN); +#endif + + pxa168_pm_enter_lowpower_mode(POWER_MODE_CORE_EXTIDLE); + +#ifdef CONFIG_MSPM_PXA168_STATS + dvfm_add_event(op, CPU_STATE_IDLE, op, CPU_STATE_RUN); + dvfm_add_timeslot(op, CPU_STATE_IDLE); +#endif + } + +out: + update_idle_stats(); + } + local_irq_enable(); +} + +static struct proc_dir_entry *entry_dir; +static struct proc_dir_entry *entry_stats; + +static int stats_show(struct seq_file *s, void *v) +{ + struct idle_stats *p = NULL; + int i, ui; + unsigned long flags; + + spin_lock_irqsave(&mspm_stats.lock, flags); + ui = mspm_stats.stats_index; + for (i = 0; i < IDLE_STATS_NUM; i++) { + p = &mspm_stats.stats[ui++]; + seq_printf(s, "index:%u idle_ticks:%u\n", + p->index, p->ticks); + if (ui == IDLE_STATS_NUM) + ui = 0; + } + spin_unlock_irqrestore(&mspm_stats.lock, flags); + return 0; +} + +static int stats_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, &stats_show, NULL); +} + +static struct file_operations stats_seq_ops = { + .owner = THIS_MODULE, + .open = stats_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mspm_proc_init(void) +{ + entry_dir = proc_mkdir("driver/mspm", NULL); + if (entry_dir == NULL) + return -ENOMEM; + + entry_stats = create_proc_entry("stats", 0, entry_dir); + if (entry_stats) + entry_stats->proc_fops = &stats_seq_ops; + return 0; +} + +static void mspm_proc_cleanup(void) +{ + remove_proc_entry("stats", entry_dir); + remove_proc_entry("driver/mspm", NULL); +} + +void mspm_idle_load(void) +{ + if (pm_idle != mspm_do_idle) { + orig_idle = pm_idle; + pm_idle = mspm_do_idle; + } +} + +void mspm_idle_clean(void) +{ + if (pm_idle == mspm_do_idle) + pm_idle = orig_idle; +} + +static int __init mspm_init(void) +{ + /*if (!cpu_is_pxa910()) + return -EFAULT;*/ + + /* Create file in procfs */ + if (mspm_proc_init()) + return -EFAULT; + + mspm_prof_init(); + + pr_info("Initialize PXA910 MSPM\n"); + + return 0; +} + +static void __exit mspm_exit(void) +{ + /* Remove procfs */ + mspm_proc_cleanup(); + + mspm_prof_exit(); + + pr_info("Quit PXA910 MSPM\n"); +} + +module_init(mspm_init); +module_exit(mspm_exit); + +MODULE_DESCRIPTION("PXA910_MSPM"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-mmp/pxa168_mspm_prof.c b/arch/arm/mach-mmp/pxa168_mspm_prof.c new file mode 100644 index 00000000000000..7081ff55c1a799 --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_mspm_prof.c @@ -0,0 +1,437 @@ +/* + * PXA910 MSPM Profiler + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2008 Marvell International Ltd. + * All Rights Reserved + */ + +/* + * Behavior of profiler + * + * When sample window is finished, profiler calculates the mips in sample + * window. System will be adjusted to suitable OP. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct mspm_op_stats { + int op; + int idle; + unsigned int timestamp; + unsigned int jiffies; +}; + +struct mspm_mips { + int mips; + int h_thres; /* high threshold */ + int l_thres; /* low threshold */ +}; + +enum { + DISABLE = 0, + ENABLE, +}; + + + +static int mspm_ctrl_state = DISABLE, mspm_prof_state = DISABLE; +static int mspm_ctrl = DISABLE, mspm_prof = DISABLE; + +/* Store OP's MIPS in op_mips[]. The lowest frequency OP is the first entry */ +static struct mspm_mips op_mips[MAX_OP_NUM]; + +/* Store duration time of all OP in op_duration[] */ +static int op_duration[MAX_OP_NUM]; + +/* Store costed time in run_op_time[] & idle_op_time[] */ +static int run_op_time[MAX_OP_NUM], idle_op_time[MAX_OP_NUM]; + +/* + * Store the first timestamp of sample window in first_stats + * Store the current timestamp of sample window in cur_stats + */ +static struct mspm_op_stats first_stats, cur_stats; + +/* OP numbers used in IPM IDLE Profiler */ +static int mspm_op_num; + +static struct timer_list idle_prof_timer; +static int mspm_window = DEF_SAMPLE_WINDOW; +static int window_jif; + +/* DVFM notifier and index */ +static int mspm_prof_notifier_freq(struct notifier_block *nb, + unsigned long val, void *data); +static struct notifier_block notifier_freq_block = { + .notifier_call = mspm_prof_notifier_freq, +}; + +static int dvfm_dev_idx; + +/* + * Adjust to the most appropriate OP according to MIPS result of + * sample window + */ +static int mspm_request_tune(int mips) +{ + int i; + + for (i = mspm_op_num - 1; i >= 0; i--) { + if (mips >= (op_mips[i].l_thres * + op_mips[i].mips / 100)) + break; + } + dvfm_request_op(0); + dvfm_request_op(i); + return 0; +} + +/* + * Calculate the MIPS in sample window + */ +static int mspm_calc_mips(unsigned int first_time) +{ + int i, mips, curop; + unsigned int time, sum_time = 0, sum = 0; + struct op_info *info = NULL; + + curop = dvfm_get_op(&info); + + /* Store the last slot as RUN state */ + time = read_timer(); + run_op_time[cur_stats.op] += time - cur_stats.timestamp; + cur_stats.timestamp = time; + cur_stats.jiffies = jiffies; + cur_stats.op = curop; + cur_stats.idle = CPU_STATE_RUN; + /* Calculate total time costed in sample window */ + for (i = 0; i < mspm_op_num; i++) { + sum_time += run_op_time[i] + idle_op_time[i]; + sum += run_op_time[i] * op_mips[i].mips; + op_duration[i] = run_op_time[i] + idle_op_time[i]; + } + if (sum_time == 0) { + /* CPU usage is 100% in current operating point */ + sum_time = time - first_time; + sum = sum_time * op_mips[curop].mips; + op_duration[curop] = sum_time; + } + + /* + * Calculate MIPS in sample window + * Formula: run_op_time[i] / sum_time * op_mips[i].mips + */ + mips = sum / sum_time; + return mspm_request_tune(mips); +} + +/* + * Record the OP index and RUN/IDLE state. + */ +int mspm_add_event(int op, int cpu_idle) +{ + unsigned int time; + + if (mspm_prof_state == ENABLE) { + time = read_timer(); + /* sum the current sample window */ + if (cpu_idle == CPU_STATE_IDLE) + idle_op_time[cur_stats.op] += time - \ + cur_stats.timestamp; + else if (cpu_idle == CPU_STATE_RUN) + run_op_time[cur_stats.op] += time - cur_stats.timestamp; + /* update start point of current sample window */ + cur_stats.op = op; + cur_stats.idle = cpu_idle; + cur_stats.timestamp = time; + cur_stats.jiffies = jiffies; + } + return 0; +} +EXPORT_SYMBOL(mspm_add_event); + +/* + * Prepare to do a new sample. + * Clear the index in mspm_op_stats table. + */ +static int mspm_do_new_sample(void) +{ + /* clear previous sample window */ + memset(&run_op_time, 0, sizeof(int) * MAX_OP_NUM); + memset(&idle_op_time, 0, sizeof(int) * MAX_OP_NUM); + /* prepare for the new sample window */ + first_stats.op = cur_stats.op; + first_stats.idle = cur_stats.idle; + first_stats.timestamp = read_timer(); + first_stats.jiffies = jiffies; + return 0; +} + +/*************************************************************************** + * Idle Profiler + ***************************************************************************/ + +static int mspm_start_prof(void) +{ + struct op_info *info = NULL; + + window_jif = msecs_to_jiffies(mspm_window); + /* start next sample window */ + cur_stats.op = dvfm_get_op(&info); + cur_stats.idle = CPU_STATE_RUN; + cur_stats.timestamp = read_timer(); + cur_stats.jiffies = jiffies; + mspm_do_new_sample(); + mod_timer(&idle_prof_timer, jiffies + window_jif); + mspm_prof_state = ENABLE; + return 0; +} + +static int mspm_stop_prof(void) +{ + del_timer(&idle_prof_timer); + mspm_prof_state = DISABLE; + return 0; +} + +/* + * Handler of IDLE PROFILER + */ +static void idle_prof_handler(unsigned long data) +{ + mspm_calc_mips(first_stats.timestamp); + /* start next sample window */ + mspm_do_new_sample(); + mod_timer(&idle_prof_timer, jiffies + window_jif); +} + +/* + * Pause idle profiler when system enter Low Power mode. + * Continue it when system exit from Low Power mode. + */ +static int mspm_prof_notifier_freq(struct notifier_block *nb, + unsigned long val, void *data) +{ + /* will implement this when low power mode is enabled + * for pxa168, right now just return 0 + */ + return 0; +} + +/* It's invoked by initialization code & sysfs interface */ +static int launch_mspm(void) +{ + if ((mspm_ctrl_state == DISABLE) && + (mspm_ctrl == ENABLE)) { + /* enable mspm control */ + mspm_idle_load(); + /* check whether profiler should be launched. + * At here, we check mspm_prof value. + */ + if (mspm_prof == ENABLE) + mspm_start_prof(); + /* disable unused OP in MSPM */ + dvfm_disable_op_name("CUSTOM OP", dvfm_dev_idx); + dvfm_disable_op_name("BOOT OP", dvfm_dev_idx); + mspm_ctrl_state = ENABLE; + } else if ((mspm_ctrl_state == ENABLE) && + (mspm_ctrl == DISABLE)) { + /* disable mspm control */ + /* check whether profiler is launched. + * At here, we check mspm_prof_state. + */ + if (mspm_prof_state == ENABLE) + mspm_stop_prof(); + mspm_idle_clean(); + /* enable unused OP in MSPM */ + dvfm_enable_op_name("CUSTOM OP", dvfm_dev_idx); + dvfm_enable_op_name("BOOT OP", dvfm_dev_idx); + mspm_ctrl_state = DISABLE; + } + return 0; +} + +/************************************************************************ + * sysfs interface * + ************************************************************************/ + +#define mspm_attr(_name) \ +static struct kobj_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ + .store = _name##_store, \ +} + +/* + * Show whether MSPM is enabled + */ +static ssize_t mspm_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", mspm_ctrl); +} + +/* + * Configure MSPM + * When MSPM is enabled, mspm idle is loaded. + */ +static ssize_t mspm_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int data; + sscanf(buf, "%u", &data); + if (data) + mspm_ctrl = ENABLE; + else + mspm_ctrl = DISABLE; + launch_mspm(); + return len; +} +mspm_attr(mspm); + +/* + * Show whether MSPM profiler in kernel space is enabled + */ +static ssize_t prof_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", mspm_prof); +} + +/* + * Configure the MSPM profiler in kernel space + * This interface can't control deepidle + */ +static ssize_t prof_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int data; + sscanf(buf, "%u", &data); + if (data) + mspm_prof = ENABLE; + else + mspm_prof = DISABLE; + /* + * If mspm_ctrl_state is disabled, + * we needn't enable/disable profiler here. + */ + if (mspm_ctrl_state == DISABLE) + return len; + + if ((mspm_prof_state == DISABLE) && + (mspm_prof == ENABLE)) { + mspm_start_prof(); + } else if ((mspm_prof_state == ENABLE) && + (mspm_prof == DISABLE)) { + mspm_stop_prof(); + } + return len; +} +mspm_attr(prof); + +/* Show the length of sample window */ +static ssize_t window_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ums\n", mspm_window); +} + +static ssize_t window_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + sscanf(buf, "%u", &mspm_window); + return len; +} +mspm_attr(window); + +static struct attribute *g[] = { + &mspm_attr.attr, + &prof_attr.attr, + &window_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .name = "mspm", + .attrs = g, +}; + +/* + * Init MIPS of all OP + * Return OP numbers + */ +int __init mspm_init_mips(void) +{ + struct op_info *info = NULL; + struct pxa168_md_opt *md_op = NULL; + int i, ret; + memset(&op_mips, 0, MAX_OP_NUM * sizeof(struct mspm_mips)); + mspm_op_num = dvfm_op_count(); + for (i = 0; i < mspm_op_num; i++) { + ret = dvfm_get_opinfo(i, &info); + if (ret) + continue; + md_op = (struct pxa168_md_opt *)info->op; + op_mips[i].mips = md_op->pclk; + if (op_mips[i].mips) { + op_mips[i].h_thres = DEF_HIGH_THRESHOLD; + } else { + /* Low Power mode won't be considered */ + mspm_op_num = i; + break; + } + + } + for (i = 0; i < mspm_op_num - 1; i++) + op_mips[i + 1].l_thres = op_mips[i].h_thres * op_mips[i].mips + / op_mips[i + 1].mips; + return mspm_op_num; +} + +int __init mspm_prof_init(void) +{ + if (sysfs_create_group(power_kobj, &attr_group)) + return -EFAULT; + + /* It's used to trigger sample window. + * If system is idle, the timer could be deferred. + */ + init_timer_deferrable(&idle_prof_timer); + idle_prof_timer.function = idle_prof_handler; + idle_prof_timer.data = 0; + + mspm_op_num = mspm_init_mips(); + + dvfm_register_notifier(¬ifier_freq_block, + DVFM_FREQUENCY_NOTIFIER); + dvfm_register("MSPM PROF", &dvfm_dev_idx); + + launch_mspm(); + return 0; +} + +void __exit mspm_prof_exit(void) +{ + dvfm_unregister("MSPM PROF", &dvfm_dev_idx); + dvfm_unregister_notifier(¬ifier_freq_block, + DVFM_FREQUENCY_NOTIFIER); +} diff --git a/arch/arm/mach-mmp/pxa168_pcie.c b/arch/arm/mach-mmp/pxa168_pcie.c new file mode 100644 index 00000000000000..ba7e2a2823088a --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_pcie.c @@ -0,0 +1,671 @@ +/* + * arch/arm/mach-mmp/pxa168_pcie.c + * + * PCIe functions for Marvell PXA168 SoC + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_PROC_FS) +#include +#endif +#include "common.h" + +/* #define PCIE_DEBUG_CODE */ +#if defined(PCIE_DEBUG_CODE) +#define PCIE_DEBUG(fmt, arg...) \ + do { \ + printk(KERN_ERR \ + "[pcie debug: %s] " fmt , \ + __func__ , ##arg); \ + } while (0) +#else +#define PCIE_DEBUG(fmt, arg...) do { } while (0) +#endif + +/* + * PCIe unit register offsets. + */ +#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24) +#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19) +#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16) +#define PCIE_CONF_REG(r) (((r) & 0xfff)) +#define CONFIG_MEM 1 +#define IO_MEM 0 + +/* Size in Bytes */ +#define SIZE_8 1 +#define SIZE_16 2 +#define SIZE_32 4 + +static atomic_t ch = ATOMIC_INIT(-1); + +#define NUM_PCIE_PORTS 1 +#define NUM_PCIE_CHANNELS 4 +#define MAX_WAIT_LOOP 2000 +#define TIME_DELAY 2 + +#define LINKUP_TIMEOUT 100 +#define LINKUP_DELAY 100 + + +extern int pxa168_gpio_pcie_init(void); + +struct pxa168_pcie_channel_desc { + void __iomem *dma_pcie_ctrl_ch; + void __iomem *pio_addr_ls_ch; + void __iomem *pio_addr_ms_ch; + void __iomem *dma_mode_ch; + void __iomem *pio_wr_strb_ch; + void __iomem *pio_start_ch; + void __iomem *pio_rd_data_ch; + void __iomem *pio_wr_data_ch; +}; + +static struct pxa168_pcie_channel_desc pcie_channels[] = { + {PCIE_REG(PCIE_DMA_PCIE_CTRL_CH0), /* Channel 0 */ + PCIE_REG(PCIE_PIO_ADDR_LS_CH0), + PCIE_REG(PCIE_PIO_ADDR_MS_CH0), + PCIE_REG(PCIE_DMA_MODE_CH0), + PCIE_REG(PCIE_PIO_WR_STRB_CH0), + PCIE_REG(PCIE_PIO_START_CH0), + PCIE_REG(PCIE_PIO_RD_DATA_CH0), + PCIE_REG(PCIE_PIO_WR_DATA_CH0)}, + {PCIE_REG(PCIE_DMA_PCIE_CTRL_CH1), /* Channel 1 */ + PCIE_REG(PCIE_PIO_ADDR_LS_CH1), + PCIE_REG(PCIE_PIO_ADDR_MS_CH1), + PCIE_REG(PCIE_DMA_MODE_CH1), + PCIE_REG(PCIE_PIO_WR_STRB_CH1), + PCIE_REG(PCIE_PIO_START_CH1), + PCIE_REG(PCIE_PIO_RD_DATA_CH1), + PCIE_REG(PCIE_PIO_WR_DATA_CH1)}, + {PCIE_REG(PCIE_DMA_PCIE_CTRL_CH2), /* Channel 2 */ + PCIE_REG(PCIE_PIO_ADDR_LS_CH2), + PCIE_REG(PCIE_PIO_ADDR_MS_CH2), + PCIE_REG(PCIE_DMA_MODE_CH2), + PCIE_REG(PCIE_PIO_WR_STRB_CH2), + PCIE_REG(PCIE_PIO_START_CH2), + PCIE_REG(PCIE_PIO_RD_DATA_CH2), + PCIE_REG(PCIE_PIO_WR_DATA_CH2)}, + {PCIE_REG(PCIE_DMA_PCIE_CTRL_CH3), /* Channel 3 */ + PCIE_REG(PCIE_PIO_ADDR_LS_CH3), + PCIE_REG(PCIE_PIO_ADDR_MS_CH3), + PCIE_REG(PCIE_DMA_MODE_CH3), + PCIE_REG(PCIE_PIO_WR_STRB_CH3), + PCIE_REG(PCIE_PIO_START_CH3), + PCIE_REG(PCIE_PIO_RD_DATA_CH3), + PCIE_REG(PCIE_PIO_WR_DATA_CH3)}, +}; + +static spinlock_t channel_lock[NUM_PCIE_CHANNELS]; + + +static u32 +pxa168_pcie_read_reg(void __iomem * base, u8 is_conf_read, u32 addr, + u8 size) +{ + u32 val; + int i; + u8 data_strobe; + u32 aligned_addr; + unsigned long flags; + unsigned int channel; + + aligned_addr = addr & ~0x3; /* Align to DWORD */ + + switch (size) { + case SIZE_8: + data_strobe = (0x1 << (addr & 0x3)); + break; + case SIZE_16: + data_strobe = (0x3 << (addr & 0x3)); + break; + default: + data_strobe = 0xf; /* Size 32 */ + break; + } + /* Next channel */ + channel = atomic_inc_return(&ch) % NUM_PCIE_CHANNELS; + spin_lock_irqsave(&channel_lock[channel], flags); + + /* Configure Channel DMA PCI-E Control Register with + * Read Type (Configuration or Memory Read)*/ + if (is_conf_read) + __raw_writel(((1 << PCIE_DMA_PCIE_CTRL_PCIETD_OFFSET) | + (PCIE_DMA_PCIE_CTRL_TLP_CFG_TYPE0_RW & + 0x1F)), + pcie_channels[channel].dma_pcie_ctrl_ch); + else + __raw_writel(((1 << PCIE_DMA_PCIE_CTRL_PCIETD_OFFSET) | + (PCIE_DMA_PCIE_CTRL_TLP_MEM_RW & 0x1F)), + pcie_channels[channel].dma_pcie_ctrl_ch); + + /* Configure Channel PIO Address Register's */ + __raw_writel(aligned_addr, pcie_channels[channel].pio_addr_ls_ch); + if (!is_conf_read) + __raw_writel(0x0, pcie_channels[channel].pio_addr_ms_ch); + + /* Configure Channel DMA Mode register to Read/Non-chain Mode */ + __raw_writel((PCIE_DMA_MODE_NONCHAINED | + PCIE_DMA_MODE_PCIE_READ), + pcie_channels[channel].dma_mode_ch); + + /* Configure Channel PIO Write Data Strobes */ + __raw_writel(data_strobe, pcie_channels[channel].pio_wr_strb_ch); + + /* Start Channel Data Transfer */ + __raw_writel(PCIE_DMA_START_TRANSFER, + pcie_channels[channel].pio_start_ch); + + /* Wait for Start Bit to clear which indicates transfer + * completion + */ + for (i = 0; i < MAX_WAIT_LOOP; i++) { + if (!__raw_readl(pcie_channels[channel].pio_start_ch)) + break; + udelay(TIME_DELAY); + } + + if (likely(i < MAX_WAIT_LOOP)) { + val = __raw_readl(pcie_channels[channel].pio_rd_data_ch); + spin_unlock_irqrestore(&channel_lock[channel], flags); + + switch (size) { + case SIZE_8: + val = ((val >> (8 * (addr & 0x3))) & 0xff); + break; + case SIZE_16: + val = ((val >> (8 * (addr & 0x3))) & 0xffff); + break; + default: + break; + } + } else { + spin_unlock_irqrestore(&channel_lock[channel], flags); + PCIE_DEBUG("Error: PIO register read timeout\n"); + val = 0; + } + + return val; +} + +EXPORT_SYMBOL(pxa168_pcie_read_reg); + +static void pxa168_pcie_write_reg(void __iomem * base, u8 is_conf_write, + u32 addr, u8 size, u32 val) +{ + int i; + + u8 data_strobe; + u32 aligned_addr; + u32 aligned_val = val; + unsigned long flags; + unsigned int channel; + + aligned_addr = addr & ~0x3; /* Align to DWORD */ + + switch (size) { + case SIZE_8: + data_strobe = (0x1 << (addr & 0x3)); + /* Align val according to data strobe */ + aligned_val = ((val & 0xff) << (8 * (addr & 0x3))); + break; + case SIZE_16: + data_strobe = (0x3 << (addr & 0x3)); + /* Align val according to data strobe */ + aligned_val = ((val & 0xffff) << (8 * (addr & 0x3))); + break; + default: + data_strobe = 0xf; /* Size 32 */ + break; + } + /* Next channel */ + channel = atomic_inc_return(&ch) % NUM_PCIE_CHANNELS; + spin_lock_irqsave(&channel_lock[channel], flags); + + /* Configure Channel DMA PCI-E Control Register with + * Write Type (Configuration or Memory Write)*/ + if (is_conf_write) + __raw_writel(((1 << PCIE_DMA_PCIE_CTRL_PCIETD_OFFSET) | + (PCIE_DMA_PCIE_CTRL_TLP_CFG_TYPE0_RW & + 0x1F)), + pcie_channels[channel].dma_pcie_ctrl_ch); + else + __raw_writel(((1 << PCIE_DMA_PCIE_CTRL_PCIETD_OFFSET) | + (PCIE_DMA_PCIE_CTRL_TLP_MEM_RW & 0x1F)), + pcie_channels[channel].dma_pcie_ctrl_ch); + + /* Configure Channel PIO Address Register's */ + __raw_writel(aligned_addr, pcie_channels[channel].pio_addr_ls_ch); + + if (!is_conf_write) + writel(0x0, pcie_channels[channel].pio_addr_ms_ch); + + /* Configure Channel DMA Mode register to Write/Non-chain Mode */ + __raw_writel((PCIE_DMA_MODE_NONCHAINED | + PCIE_DMA_MODE_PCIE_WRITE), + pcie_channels[channel].dma_mode_ch); + + /* Configure Channel PIO Write Data Strobes */ + __raw_writel(data_strobe, pcie_channels[channel].pio_wr_strb_ch); + + /* Write data to Channel PIO Write Data Register */ + __raw_writel(aligned_val, pcie_channels[channel].pio_wr_data_ch); + + /* Start Channel Data Transfer */ + __raw_writel(PCIE_DMA_START_TRANSFER, + pcie_channels[channel].pio_start_ch); + + /* Wait for Start Bit to clear which indicates transfer + * completion + */ + for (i = 0; i < MAX_WAIT_LOOP; i++) { + if (!__raw_readl(pcie_channels[channel].pio_start_ch)) + break; + udelay(TIME_DELAY); + } + + if (i >= MAX_WAIT_LOOP) + PCIE_DEBUG("PIO register write timeout\n"); + + spin_unlock_irqrestore(&channel_lock[channel], flags); +} + +EXPORT_SYMBOL(pxa168_pcie_write_reg); + +u8 pxa168_pcie_read8(u32 addr) +{ + return (u8) pxa168_pcie_read_reg(0, IO_MEM, addr, SIZE_8); +} + +u16 pxa168_pcie_read16(u32 addr) +{ + return (u16) pxa168_pcie_read_reg(0, IO_MEM, addr, SIZE_16); +} + +u32 pxa168_pcie_read32(u32 addr) +{ + return pxa168_pcie_read_reg(0, IO_MEM, addr, SIZE_32); +} + +void pxa168_pcie_write8(u8 val, u32 addr) +{ + pxa168_pcie_write_reg(0, IO_MEM, addr, SIZE_8, (u32) val); +} + +void pxa168_pcie_write16(u16 val, u32 addr) +{ + pxa168_pcie_write_reg(0, IO_MEM, addr, SIZE_16, (u32) val); +} + +void pxa168_pcie_write32(u32 val, u32 addr) +{ + pxa168_pcie_write_reg(0, IO_MEM, addr, SIZE_32, val); +} + +static void pxa168_pcie_clk_enable(void) +{ + + /* Enable Clock to PCIe Controller */ + __raw_writel(0xFF, AXI_VIRT_BASE + 0x82900); + mdelay(1); +} + +static void pxa168_pcie_enable_link(void) +{ + unsigned int temp; + + pxa168_pcie_clk_enable(); + + /* + * Enable ANALOG_CTRL register in PCI-E PHY. This + * should supposedly be the only register among the PCI-E + * PHY registers based on DE's. Set bit 7:6 to 0x01 (0.9 ns) + * However this setting isn't part of XDB Test Scripts. + */ + temp = __raw_readl(PCIE_REG(PCIE_PHY_ANALOG_CTRL)); + temp &= ~0x000000f0; + temp |= 0x50; + __raw_writel(temp, PCIE_REG(PCIE_PHY_ANALOG_CTRL)); /* 0.9ns */ + + /* Advertise itself as Gen 1 at Link Capability Register */ + __raw_writel((__raw_readl(PCIE_REG(0x307C)) & 0xFFFFFFF0) | 0x1, + PCIE_REG(0x307C)); + + /* + * Magic Register settings to init PCI-E Link + * (based on some test scripts) + */ + + /* Program RC memory limit and base */ + __raw_writel(0x90008000, PCIE_REG(0x3020)); + /* Program RC prefechable memory limit and base */ + __raw_writel(0xB000A000, PCIE_REG(0x3024)); + + /* Sets the default link number to zero since we have just one port */ + __raw_writel((__raw_readl(PCIE_REG(0x3708)) & 0xFFFFFF00), + PCIE_REG(0x3708)); + /* Enable fast training */ + __raw_writel((__raw_readl(PCIE_REG(0x3710)) | 0x00000080), + PCIE_REG(0x3710)); + /* Enable ECRC checking and generation */ + __raw_writel((__raw_readl(PCIE_REG(0x3118)) | 0x00000140), + PCIE_REG(0x3118)); + + /* Disable L0s - Power Management */ + __raw_writel(0x00000000, PCIE_REG(0x3080)); + + /* Set device into active mode - D0 state */ + __raw_writel((__raw_readl(PCIE_REG(0x3044)) & 0xFFFFFFFC), + PCIE_REG(0x3044)); + + /* Enable the memory space and bus master on Control Register */ + __raw_writel(0x6, PCIE_REG(0x3004)); + + /* Bus number register */ + __raw_writel(0x00010100, PCIE_REG(0x3018)); + + /* Enable LTSSM */ + __raw_writel(0x1, PCIE_REG(PCIE_CTRL0)); +} + +int pxa168_pcie_link_up(void) +{ + unsigned int count = 0; + unsigned int ret = 1; + + while (!(((__raw_readl(PCIE_REG(PCIE_ISR0)) & 0xc0000000) + == 0xc0000000) ? 1 : 0)) { + udelay(LINKUP_DELAY); + count++; + if (count == LINKUP_TIMEOUT) { + ret = 0; /* Failed to get link */ + break; + } + } + if (count == LINKUP_TIMEOUT) + PCIE_DEBUG("Link up timeout at %dus\n", + (LINKUP_TIMEOUT * LINKUP_DELAY)); + + if (count && (count < LINKUP_TIMEOUT)) + PCIE_DEBUG("Link up time = %dus\n", (count * LINKUP_DELAY)); + + return ret; +} + +#if 0 +static int __init pxa168_pcie_x4_mode(void __iomem * base) +{ + /* TODO - Read CFG_PCIE_CAP + 0x0C to + * determine Linkwidth */ + return 0; +} +#endif + +#if 0 +static int pxa168_pcie_get_local_bus_nr(void __iomem * base) +{ + u8 val; + + /* Read Configuration Space Type1 Header (Offset 0x18) + * through DBI */ + val = __raw_readb(PCIE_REG(0x3018)); + + return val; +} +#endif + +static void pxa168_pcie_set_local_bus_nr(int nr) +{ + /* Write Configuration Space Type1 Header (Offset 0x18) + * through DBI */ + __raw_writeb((u8) (nr & 0xff), PCIE_REG(0x3018)); + + return; +} + +int pxa168_pcie_rd_conf(void __iomem * base, struct pci_bus *bus, + u32 devfn, int where, int size, u32 * val) +{ + u32 conf_addr; + u32 reg_val; + + if (devfn > 15) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + conf_addr = PCIE_CONF_BUS(bus->number) | + PCIE_CONF_DEV(PCI_SLOT(devfn)) | + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where); + + reg_val = pxa168_pcie_read_reg(base, CONFIG_MEM, conf_addr, 4); + + switch (size) { + case SIZE_8: + reg_val = (reg_val >> (8 * (where & 3))) & 0xff; + break; + case SIZE_16: + reg_val = (reg_val >> (8 * (where & 3))) & 0xffff; + break; + default: + break; + } + + *val = reg_val; + + return PCIBIOS_SUCCESSFUL; +} + +int pxa168_pcie_wr_conf(void __iomem * base, struct pci_bus *bus, + u32 devfn, int where, int size, u32 val) +{ + int ret = PCIBIOS_SUCCESSFUL; + u32 conf_addr; + + if (devfn > 15) + return PCIBIOS_FUNC_NOT_SUPPORTED; + + conf_addr = PCIE_CONF_BUS(bus->number) | + PCIE_CONF_DEV(PCI_SLOT(devfn)) | + PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where); + + pxa168_pcie_write_reg(base, CONFIG_MEM, conf_addr, size, val); + + return ret; +} + +static int __init pxa168_pcie_setup(int nr, struct pci_sys_data *sys) +{ + if (nr >= NUM_PCIE_PORTS) + return 0; + + pxa168_pcie_set_local_bus_nr(nr); + + return 1; +} + +static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, + int size, u32 * val) +{ + int ret; + + if (!pxa168_pcie_link_up()) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + ret = pxa168_pcie_rd_conf(0, bus, devfn, where, size, val); + + return ret; +} + +static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, + int where, int size, u32 val) +{ + int ret; + + if (!pxa168_pcie_link_up()) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + + ret = pxa168_pcie_wr_conf(0, bus, devfn, where, size, val); + + return ret; +} + +static struct pci_ops pcie_ops = { + .read = pcie_rd_conf, + .write = pcie_wr_conf, +}; + +static void __devinit rc_pci_fixup(struct pci_dev *dev) +{ + /* + * Prevent enumeration of root complex. + */ + if (dev->bus->parent == NULL && dev->devfn == 0) { + int i; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + dev->resource[i].start = 0; + dev->resource[i].end = 0; + dev->resource[i].flags = 0; + } + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); + +static struct pci_bus __init *pxa168_pcie_scan_bus(int nr, struct pci_sys_data + *sys) +{ + struct pci_bus *bus; + + if (nr < NUM_PCIE_PORTS) { + bus = pci_scan_bus(sys->busnr, &pcie_ops, sys); + } else { + bus = NULL; + BUG(); + } + + return bus; +} + +static int pxa168_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + int irq = -1; + + if (slot == 1) { + /* Unmask the Legacy Interrupts */ + __raw_writel(0x3, PCIE_REG(PCIE_PCI_LEGACY_ISRM0)); + /* Enable the Interrupt Pin Register in RC Config Space */ + __raw_writeb(0x1, PCIE_REG(0x303D)); + irq = IRQ_PXA168_PCIE_CORE; + } + + return irq; +} + +static struct hw_pci pxa168_pci __initdata = { + .nr_controllers = 1, + .swizzle = pci_std_swizzle, + .setup = pxa168_pcie_setup, + .scan = pxa168_pcie_scan_bus, + .map_irq = pxa168_pcie_map_irq, +}; + +static void __init pcie_channel_data_init(void) +{ + unsigned int i; + + for (i = 0; i < NUM_PCIE_CHANNELS; i++) + spin_lock_init(&(channel_lock[i])); +} + +static void __init add_pcie_port(void) +{ + pxa168_pcie_enable_link(); + + pcie_channel_data_init(); +} + +u32 __init pxa168_pcie_dev_id(void __iomem * base) +{ + return pxa168_pcie_read_reg(base, CONFIG_MEM, 0x0, 4); +} + +u32 __init pxa168_pcie_rev(void __iomem * base) +{ + return pxa168_pcie_read_reg(base, CONFIG_MEM, 0x8, 4); +} + +#if defined(CONFIG_PM) +static int pxa168_pcie_suspend(struct platform_device *pdev, pm_message_t msg) +{ + return 0; +} + +static int pxa168_pcie_resume(struct platform_device *pdev) +{ + /* TODO: Get this from platform data instead */ + if (pxa168_gpio_pcie_init() < 0) { + printk(KERN_ERR "pcie: GPIO initialization failed.\n"); + return 1; + } + + pxa168_pcie_clk_enable(); + pxa168_pcie_enable_link(); + pxa168_pcie_map_irq(NULL, 1, 0); + + return 0; +} + +static struct platform_driver pxa168_pcie_driver = { + .suspend = pxa168_pcie_suspend, + .resume_early = pxa168_pcie_resume, + .driver = { + .name = "pxa168-pcie", + .owner = THIS_MODULE, + }, +}; +#endif /* CONFIG_PM */ + + +static int __init pxa168_pcie_init(void) +{ + int ret; + + /* TODO: Get this from platform data instead */ + if (pxa168_gpio_pcie_init() < 0) { + printk(KERN_ERR "pcie: GPIO initialization failed.\n"); + return 1; + } + +#if defined(CONFIG_PM) + ret = platform_driver_register(&pxa168_pcie_driver); + if (ret) { + printk(KERN_ERR "pcie: unable to register driver\n"); + return 1; + } +#endif + + add_pcie_port(); + pci_common_init(&pxa168_pci); + return 0; +} + +device_initcall(pxa168_pcie_init); diff --git a/arch/arm/mach-mmp/pxa168_pm.c b/arch/arm/mach-mmp/pxa168_pm.c new file mode 100644 index 00000000000000..6a2fa546fec658 --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_pm.c @@ -0,0 +1,1291 @@ +/* + * PXA168 Power Management Routines + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2009 Marvell International Ltd. + * All Rights Reserved + */ + +#undef DEBUG +#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 +#include +#include +#include +#include +#include +#include + +/* +#define APB_PHYS_BASE 0xd4000000 +#define APB_VIRT_BASE 0xfe000000 +#define APB_PHYS_SIZE 0x00200000 + +#define AXI_PHYS_BASE 0xd4200000 +#define AXI_VIRT_BASE 0xfe200000 +#define AXI_PHYS_SIZE 0x00200000 + +#define DMAC_REGS_VIRT (APB_VIRT_BASE + 0x00000) + +#define TIMERS1_VIRT_BASE (APB_VIRT_BASE + 0x14000) + +#define APBC_VIRT_BASE (APB_VIRT_BASE + 0x15000) + +#define GPIO_REGS_VIRT (APB_VIRT_BASE + 0x19000) + +#define MPMU_VIRT_BASE (APB_VIRT_BASE + 0x50000) + +#define ICU_VIRT_BASE (AXI_VIRT_BASE + 0x82000) + +#define APMU_VIRT_BASE (AXI_VIRT_BASE + 0x82800) + +#define PXA910_SQU_REGS_VIRT (AXI_VIRT_BASE + 0xA0000) + +*/ + +#define APMASK(i) (GPIO_REGS_VIRT + BANK_OFF(i) + 0x09c) + +/*used by expander max7312, 16 pins gpio expander */ +#define GPIO_EXT0(x) (NR_BUILTIN_GPIO + (x)) +#define GPIO_EXT1(x) (NR_BUILTIN_GPIO + 16 + (x)) + + +/* How long we will in sleep mode if duty cycle. */ +unsigned int pm_sleeptime = 0; /* In seconds. */ +EXPORT_SYMBOL(pm_sleeptime); + +int enable_deepidle = 0x2; /* IDLE_D0 -- 0 */ + +static struct pxa168_pm_regs pxa168_pm_regs; + +static unsigned long pm_state; + + +unsigned char __iomem *dmc_membase; +EXPORT_SYMBOL(dmc_membase); +unsigned char __iomem *sram_membase; +EXPORT_SYMBOL(sram_membase); + +extern struct wake_lock main_wake_lock; +/*************************************************************************/ +static void flush_cpu_cache(void) +{ + __cpuc_flush_kern_all(); + //__cpuc_flush_l2cache_all(); +} + +static void pxa168_intc_save(struct intc_regs *context) +{ + unsigned int i; + + for (i = 0 ;i < 64;i++) + context->icu_int_conf[i] = __raw_readl(ICU_INT_CONF(i)); + context->icu_fiq_sel_int_num = __raw_readl(ICU_AP_FIQ_SEL_INT_NUM); + context->icu_irq_sel_int_num = __raw_readl(ICU_AP_IRQ_SEL_INT_NUM); + context->icu_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + context->icu_dma_int_msk = __raw_readl(ICU_DMA_INT_MSK); + context->icu_dma_int_status = __raw_readl(ICU_DMA_INT_STATUS); + context->icu_int_status_0 = __raw_readl(ICU_INT_STATUS_0); + context->icu_int_status_1 = __raw_readl(ICU_INT_STATUS_1); + context->icu_ddr_arm_l2_int_msk = __raw_readl(ICU_DDR_ARM_L2_INT_MSK); + context->icu_ddr_arm_l2_int_status = __raw_readl(ICU_DDR_ARM_L2_INT_STATUS); +} + +static void pxa168_intc_restore(struct intc_regs *context) +{ + unsigned int i; + for (i = 0 ;i < 64;i++) + __raw_writel(context->icu_int_conf[i], ICU_INT_CONF(i)); + __raw_writel(context->icu_fiq_sel_int_num, ICU_AP_FIQ_SEL_INT_NUM); + __raw_writel(context->icu_irq_sel_int_num, ICU_AP_IRQ_SEL_INT_NUM); + __raw_writel(context->icu_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + __raw_writel(context->icu_dma_int_msk, ICU_DMA_INT_MSK); + __raw_writel(context->icu_dma_int_status, ICU_DMA_INT_STATUS); + __raw_writel(context->icu_int_status_0, ICU_INT_STATUS_0); + __raw_writel(context->icu_int_status_1, ICU_INT_STATUS_1); + __raw_writel(context->icu_ddr_arm_l2_int_msk, ICU_DDR_ARM_L2_INT_MSK); + __raw_writel(context->icu_ddr_arm_l2_int_status, ICU_DDR_ARM_L2_INT_STATUS); +} + +#define MFPR_VIRT_BASE (APB_VIRT_BASE + 0x1e000) + +static void pxa168_mfp_save(struct mfp_regs *context) +{ + unsigned int i; + + for (i = 0; i < MAX_MFP_PINS; i++) + context->mfp[i] = __raw_readl(MFPR_VIRT_BASE + (i << 2)); +} + +static void pxa168_mfp_restore(struct mfp_regs *context) +{ + unsigned int i; + + for (i = 0; i < MAX_MFP_PINS; i++) + __raw_writel(context->mfp[i], MFPR_VIRT_BASE + (i << 2)); +} + +#define GAPMASK_OFFSET 0x9C + +static void pxa168_gpio_save(struct gpio_regs *context) +{ + unsigned int i; + + for (i = 0; i < 4; i++) { + context->gpdr[i] = __raw_readl(GPIO_BANK(i) + GPDR_OFFSET); + context->grer[i] = __raw_readl(GPIO_BANK(i) + GRER_OFFSET); + context->gfer[i] = __raw_readl(GPIO_BANK(i) + GFER_OFFSET); + context->gedr[i] = __raw_readl(GPIO_BANK(i) + GEDR_OFFSET); + context->gapmask[i] = __raw_readl(GPIO_BANK(i) + GAPMASK_OFFSET); + } +} + +static void pxa168_gpio_restore(struct gpio_regs *context) +{ + unsigned int i; + + for (i = 0; i < 4; i++) { + __raw_writel(context->gpdr[i], GPIO_BANK(i) + GPDR_OFFSET); + __raw_writel(context->grer[i], GPIO_BANK(i) + GRER_OFFSET); + __raw_writel(context->gfer[i], GPIO_BANK(i) + GFER_OFFSET); + __raw_writel(context->gedr[i], GPIO_BANK(i) + GEDR_OFFSET); + __raw_writel(context->gapmask[i], GPIO_BANK(i) + GAPMASK_OFFSET); + } +} + +static void pxa168_mpmu_save(struct mpmu_regs *context) +{ + context->fccr = __raw_readl(MPMU_FCCR); + context->pocr = __raw_readl(MPMU_POCR); + context->posr = __raw_readl(MPMU_POSR); + context->succr = __raw_readl(MPMU_SUCCR); + context->ohcr = __raw_readl(MPMU_OHCR); + context->gpcr = __raw_readl(MPMU_GPCR); + context->pll2cr = __raw_readl(MPMU_PLL2CR); + context->sccr = __raw_readl(MPMU_SCCR); + context->pll1_reg1 = __raw_readl(MPMU_PLL1_REG1); + context->pll1_reg2 = __raw_readl(MPMU_PLL1_REG2); + context->pll1_ssc = __raw_readl(MPMU_PLL1_SSC); + context->pll2_reg1 = __raw_readl(MPMU_PLL2_REG1); + context->pll2_reg2 = __raw_readl(MPMU_PLL2_REG2); + context->pll2_ssc = __raw_readl(MPMU_PLL2_SSC); + context->ts = __raw_readl(MPMU_TS); + context->wdtpcr = __raw_readl(MPMU_WDTPCR); + context->apcr = __raw_readl(MPMU_APCR); + context->apsr = __raw_readl(MPMU_APSR); + context->aprr = __raw_readl(MPMU_APRR); + context->acgr = __raw_readl(MPMU_ACGR); + context->arsr = __raw_readl(MPMU_ARSR); + context->awucrs = __raw_readl(MPMU_AWUCRS); + context->awucrm = __raw_readl(MPMU_AWUCRM); +} + +static void pxa168_mpmu_restore(struct mpmu_regs *context) +{ + __raw_writel(context->fccr, MPMU_FCCR); + __raw_writel(context->pocr, MPMU_POCR); + __raw_writel(context->posr, MPMU_POSR); + __raw_writel(context->succr, MPMU_SUCCR); + __raw_writel(context->ohcr, MPMU_OHCR); + __raw_writel(context->gpcr, MPMU_GPCR); + __raw_writel(context->pll2cr, MPMU_PLL2CR); + __raw_writel(context->sccr, MPMU_SCCR); + __raw_writel(context->pll1_reg1, MPMU_PLL1_REG1); + __raw_writel(context->pll1_reg2, MPMU_PLL1_REG2); + __raw_writel(context->pll1_ssc, MPMU_PLL1_SSC); + __raw_writel(context->pll2_reg1, MPMU_PLL2_REG1); + __raw_writel(context->pll2_reg2, MPMU_PLL2_REG2); + __raw_writel(context->pll2_ssc, MPMU_PLL2_SSC); + __raw_writel(context->ts, MPMU_TS); + __raw_writel(context->wdtpcr, MPMU_WDTPCR); + __raw_writel(context->apcr, MPMU_APCR); + __raw_writel(context->apsr, MPMU_APSR); + __raw_writel(context->aprr, MPMU_APRR); + __raw_writel(context->acgr, MPMU_ACGR); + __raw_writel(context->arsr, MPMU_ARSR); + __raw_writel(context->awucrs, MPMU_AWUCRS); + __raw_writel(context->awucrm, MPMU_AWUCRM); +} + +static void pxa168_apmu_save(struct apmu_regs *context) +{ + context->ccr = __raw_readl(APMU_CCR); + context->ccsr = __raw_readl(APMU_CCSR); + context->fc_timer = __raw_readl(APMU_FC_TIMER); + context->idle_cfg = __raw_readl(APMU_IDLE_CFG); + context->lcd_clk_res_ctrl = __raw_readl(APMU_LCD_CLK_RES_CTRL); + context->ccic_clk_res_ctrl = __raw_readl(APMU_CCIC_CLK_RES_CTRL); + context->sdh0_clk_res_ctrl = __raw_readl(APMU_SDH0_CLK_RES_CTRL); + context->sdh1_clk_res_ctrl = __raw_readl(APMU_SDH1_CLK_RES_CTRL); + context->sdh2_clk_res_ctrl = __raw_readl(APMU_SDH2_CLK_RES_CTRL); + context->sdh3_clk_res_ctrl = __raw_readl(APMU_SDH3_CLK_RES_CTRL); + context->usb_clk_res_ctrl = __raw_readl(APMU_USB_CLK_RES_CTRL); + context->nfc_clk_res_ctrl = __raw_readl(APMU_NFC_CLK_RES_CTRL); + context->dma_clk_res_ctrl = __raw_readl(APMU_DMA_CLK_RES_CTRL); + context->bus_clk_res_ctrl = __raw_readl(APMU_BUS_CLK_RES_CTRL); + context->wake_clk = __raw_readl(APMU_WAKE_CLK); + context->core_status = __raw_readl(APMU_CORE_STATUS); + context->res_frm_slp_clr = __raw_readl(APMU_RES_FRM_SLP_CLR); + context->imr = __raw_readl(APMU_IMR); + context->irwc = __raw_readl(APMU_IRWC); + context->isr = __raw_readl(APMU_ISR); + context->dtc_clk_res_ctrl = __raw_readl(APMU_DTC_CLK_RES_CTRL); + context->mc_hw_slp_type = __raw_readl(APMU_MC_HW_SLP_TYPE); + context->mc_slp_req = __raw_readl(APMU_MC_SLP_REQ); + context->mc_sw_slp_type = __raw_readl(APMU_MC_SW_SLP_TYPE); + context->pll_sel_status = __raw_readl(APMU_PLL_SEL_STATUS); + context->gc_clk_res_ctrl = __raw_readl(APMU_GC_CLK_RES_CTRL); + context->smc_clk_res_ctrl = __raw_readl(APMU_SMC_CLK_RES_CTRL); + context->xd_clk_res_ctrl = __raw_readl(APMU_XD_CLK_RES_CTRL); + context->cf_clk_res_ctrl = __raw_readl(APMU_CF_CLK_RES_CTRL); + context->msp_clk_res_ctrl = __raw_readl(APMU_MSP_CLK_RES_CTRL); + context->cmu_clk_res_ctrl = __raw_readl(APMU_CMU_CLK_RES_CTRL); + context->mfu_clk_res_ctrl = __raw_readl(APMU_MFU_CLK_RES_CTRL); +} + +static void pxa168_apmu_restore(struct apmu_regs *context) +{ + __raw_writel(context->ccr, APMU_CCR); + __raw_writel(context->ccsr, APMU_CCSR); + __raw_writel(context->fc_timer, APMU_FC_TIMER); + __raw_writel(context->idle_cfg, APMU_IDLE_CFG); + __raw_writel(context->lcd_clk_res_ctrl, APMU_LCD_CLK_RES_CTRL); + __raw_writel(context->ccic_clk_res_ctrl, APMU_CCIC_CLK_RES_CTRL); + __raw_writel(context->sdh0_clk_res_ctrl, APMU_SDH0_CLK_RES_CTRL); + __raw_writel(context->sdh1_clk_res_ctrl, APMU_SDH1_CLK_RES_CTRL); + __raw_writel(context->sdh2_clk_res_ctrl, APMU_SDH2_CLK_RES_CTRL); + __raw_writel(context->sdh3_clk_res_ctrl, APMU_SDH3_CLK_RES_CTRL); + __raw_writel(context->usb_clk_res_ctrl, APMU_USB_CLK_RES_CTRL); + __raw_writel(context->nfc_clk_res_ctrl, APMU_NFC_CLK_RES_CTRL); + __raw_writel(context->dma_clk_res_ctrl, APMU_DMA_CLK_RES_CTRL); + __raw_writel(context->bus_clk_res_ctrl, APMU_BUS_CLK_RES_CTRL); + __raw_writel(context->wake_clk, APMU_WAKE_CLK); + __raw_writel(context->core_status, APMU_CORE_STATUS); + __raw_writel(context->res_frm_slp_clr, APMU_RES_FRM_SLP_CLR); + __raw_writel(context->imr, APMU_IMR); + __raw_writel(context->irwc, APMU_IRWC); + __raw_writel(context->isr, APMU_ISR); + __raw_writel(context->dtc_clk_res_ctrl, APMU_DTC_CLK_RES_CTRL); + __raw_writel(context->mc_hw_slp_type, APMU_MC_HW_SLP_TYPE); + __raw_writel(context->mc_slp_req, APMU_MC_SLP_REQ); + __raw_writel(context->mc_sw_slp_type, APMU_MC_SW_SLP_TYPE); + __raw_writel(context->pll_sel_status, APMU_PLL_SEL_STATUS); + __raw_writel(context->gc_clk_res_ctrl, APMU_GC_CLK_RES_CTRL); + __raw_writel(context->smc_clk_res_ctrl, APMU_SMC_CLK_RES_CTRL); + __raw_writel(context->xd_clk_res_ctrl, APMU_XD_CLK_RES_CTRL); + __raw_writel(context->cf_clk_res_ctrl, APMU_CF_CLK_RES_CTRL); + __raw_writel(context->msp_clk_res_ctrl, APMU_MSP_CLK_RES_CTRL); + __raw_writel(context->cmu_clk_res_ctrl, APMU_CMU_CLK_RES_CTRL); + __raw_writel(context->mfu_clk_res_ctrl, APMU_MFU_CLK_RES_CTRL); +} + +static void pxa168_apbclk_save(struct apbclk_regs *context) +{ + context->uart0 = __raw_readl(APBC_PXA168_UART0); + context->uart1 = __raw_readl(APBC_PXA168_UART1); + context->gpio = __raw_readl(APBC_PXA168_GPIO); + context->pwm0 = __raw_readl(APBC_PXA168_PWM0); + context->pwm1 = __raw_readl(APBC_PXA168_PWM1); + context->pwm2 = __raw_readl(APBC_PXA168_PWM2); + context->pwm3 = __raw_readl(APBC_PXA168_PWM3); + context->rtc = __raw_readl(APBC_PXA168_RTC); + context->twsi0 = __raw_readl(APBC_PXA168_TWSI0); + context->twsi1 = __raw_readl(APBC_PXA168_TWSI1); + context->kpc = __raw_readl(APBC_PXA168_KPC); + context->timers = __raw_readl(APBC_PXA168_TIMERS); + context->aib = __raw_readl(APBC_PXA168_AIB); + context->sw_jtag = __raw_readl(APBC_PXA168_SW_JTAG); + context->timer1 = __raw_readl(APBC_PXA168_TIMER1); + context->onewire = __raw_readl(APBC_PXA168_ONEWIRE); + context->asfar = __raw_readl(APBC_PXA168_ASFAR); + context->assar = __raw_readl(APBC_PXA168_ASSAR); + context->uart2 = __raw_readl(APBC_PXA168_UART2); + context->timer2 = __raw_readl(APBC_PXA168_TIMER2); + context->ac97 = __raw_readl(APBC_PXA168_AC97); + context->ssp0 = __raw_readl(APBC_PXA168_SSP0); + context->ssp1 = __raw_readl(APBC_PXA168_SSP1); + context->ssp2 = __raw_readl(APBC_PXA168_SSP2); + context->ssp3 = __raw_readl(APBC_PXA168_SSP3); + context->ssp4 = __raw_readl(APBC_PXA168_SSP4); +} + +static void pxa168_apbclk_restore(struct apbclk_regs *context) +{ + __raw_writel(context->uart0, APBC_PXA168_UART0); + __raw_writel(context->uart1, APBC_PXA168_UART1); + __raw_writel(context->gpio, APBC_PXA168_GPIO); + __raw_writel(context->pwm0, APBC_PXA168_PWM0); + __raw_writel(context->pwm1, APBC_PXA168_PWM1); + __raw_writel(context->pwm2, APBC_PXA168_PWM2); + __raw_writel(context->pwm3, APBC_PXA168_PWM3); + __raw_writel(context->rtc, APBC_PXA168_RTC); + __raw_writel(context->twsi0, APBC_PXA168_TWSI0); + __raw_writel(context->twsi1, APBC_PXA168_TWSI1); + __raw_writel(context->kpc, APBC_PXA168_KPC); + __raw_writel(context->timers, APBC_PXA168_TIMERS); + __raw_writel(context->aib, APBC_PXA168_AIB); + __raw_writel(context->sw_jtag, APBC_PXA168_SW_JTAG); + __raw_writel(context->timer1, APBC_PXA168_TIMER1); + __raw_writel(context->onewire, APBC_PXA168_ONEWIRE); + __raw_writel(context->asfar, APBC_PXA168_ASFAR); + __raw_writel(context->assar, APBC_PXA168_ASSAR); + __raw_writel(context->uart2, APBC_PXA168_UART2); + __raw_writel(context->timer2, APBC_PXA168_TIMER2); + __raw_writel(context->ac97, APBC_PXA168_AC97); + __raw_writel(context->ssp0, APBC_PXA168_SSP0); + __raw_writel(context->ssp1, APBC_PXA168_SSP1); + __raw_writel(context->ssp2, APBC_PXA168_SSP2); + __raw_writel(context->ssp3, APBC_PXA168_SSP3); + __raw_writel(context->ssp4, APBC_PXA168_SSP4); +} + +static void pxa168_ciu_save(struct ciu_regs *context) +{ + context->chip_id = __raw_readl(CIU_CHIP_ID); + context->cpu_conf = __raw_readl(CIU_CPU_CONF); + context->cpu_sram_spd = __raw_readl(CIU_CPU_SRAM_SPD); + context->cpu_l2c_sram_spd = __raw_readl(CIU_CPU_L2C_SRAM_SPD); + context->mcb_conf = __raw_readl(CIU_MCB_CONF); + context->sys_boot_cntrl = __raw_readl(CIU_SYS_BOOT_CNTRL); + context->sw_branch_addr = __raw_readl(CIU_SW_BRANCH_ADDR); + context->perf_count0_cntrl = __raw_readl(CIU_PERF_COUNT0_CNTRL); + context->perf_count1_cntrl = __raw_readl(CIU_PERF_COUNT0_CNTRL); + context->perf_count2_cntrl = __raw_readl(CIU_PERF_COUNT0_CNTRL); + context->perf_count0 = __raw_readl(CIU_PERF_COUNT0); + context->perf_count1 = __raw_readl(CIU_PERF_COUNT1); + context->perf_count2 = __raw_readl(CIU_PERF_COUNT2); + context->mc_conf = __raw_readl(CIU_MC_CONF); + context->mcb_sram_spd = __raw_readl(CIU_MCB_SRAM_SPD); + context->axi_sram_spd = __raw_readl(CIU_AXI_SRAM_SPD); +} + +static void pxa168_ciu_restore(struct ciu_regs *context) +{ + __raw_writel(context->chip_id, CIU_CHIP_ID); + __raw_writel(context->cpu_conf, CIU_CPU_CONF); + __raw_writel(context->cpu_sram_spd, CIU_CPU_SRAM_SPD); + __raw_writel(context->cpu_l2c_sram_spd, CIU_CPU_L2C_SRAM_SPD); + __raw_writel(context->mcb_conf, CIU_MCB_CONF); + __raw_writel(context->sys_boot_cntrl, CIU_SYS_BOOT_CNTRL); + __raw_writel(context->sw_branch_addr, CIU_SW_BRANCH_ADDR); + __raw_writel(context->perf_count0_cntrl, CIU_PERF_COUNT0_CNTRL); + __raw_writel(context->perf_count1_cntrl, CIU_PERF_COUNT0_CNTRL); + __raw_writel(context->perf_count2_cntrl, CIU_PERF_COUNT0_CNTRL); + __raw_writel(context->perf_count0, CIU_PERF_COUNT0); + __raw_writel(context->perf_count1, CIU_PERF_COUNT1); + __raw_writel(context->perf_count2, CIU_PERF_COUNT2); + __raw_writel(context->mc_conf, CIU_MC_CONF); + __raw_writel(context->mcb_sram_spd, CIU_MCB_SRAM_SPD); + __raw_writel(context->axi_sram_spd, CIU_AXI_SRAM_SPD); +} + +static void pxa168_squ_save(struct squ_regs *context) +{ + context->ctrl_0 = __raw_readl(SQU_CTRL_0); + context->ctrl_1 = __raw_readl(SQU_CTRL_1); + context->ctrl_2 = __raw_readl(SQU_CTRL_2); + context->fmbist_ctrl_0 = __raw_readl(SQU_FMBIST_CTRL_0); + context->fmbist_ctrl_1 = __raw_readl(SQU_FMBIST_CTRL_1); + context->perf_count_cntrl = __raw_readl(SQU_PERF_COUNT_CNTRL); + context->cam_ent_bank0 = __raw_readl(SQU_CAM_ENT_BANK0); + context->cam_ent_bank1 = __raw_readl(SQU_CAM_ENT_BANK1); + context->cam_ent_bank2 = __raw_readl(SQU_CAM_ENT_BANK2); + context->cam_ent_bank3 = __raw_readl(SQU_CAM_ENT_BANK3); + context->cam_ent_bank4 = __raw_readl(SQU_CAM_ENT_BANK4); + context->cam_ent_bank5 = __raw_readl(SQU_CAM_ENT_BANK5); + context->chan_0_ctrl = __raw_readl(SQU_CHAN_0_CTRL); + context->chan_1_ctrl = __raw_readl(SQU_CHAN_1_CTRL); + context->chan_pri = __raw_readl(SQU_CHAN_PRI); +} + +static void pxa168_squ_restore(struct squ_regs *context) +{ + __raw_writel(context->ctrl_0, SQU_CTRL_0); + __raw_writel(context->ctrl_1, SQU_CTRL_1); + __raw_writel(context->ctrl_2, SQU_CTRL_2); + __raw_writel(context->fmbist_ctrl_0, SQU_FMBIST_CTRL_0); + __raw_writel(context->fmbist_ctrl_1, SQU_FMBIST_CTRL_1); + __raw_writel(context->perf_count_cntrl, SQU_PERF_COUNT_CNTRL); + __raw_writel(context->cam_ent_bank0, SQU_CAM_ENT_BANK0); + __raw_writel(context->cam_ent_bank1, SQU_CAM_ENT_BANK1); + __raw_writel(context->cam_ent_bank2, SQU_CAM_ENT_BANK2); + __raw_writel(context->cam_ent_bank3, SQU_CAM_ENT_BANK3); + __raw_writel(context->cam_ent_bank4, SQU_CAM_ENT_BANK4); + __raw_writel(context->cam_ent_bank5, SQU_CAM_ENT_BANK5); + __raw_writel(context->chan_0_ctrl, SQU_CHAN_0_CTRL); + __raw_writel(context->chan_1_ctrl, SQU_CHAN_1_CTRL); + __raw_writel(context->chan_pri, SQU_CHAN_PRI); +} + +#define AXIFAB_REGS_VIRT (AXI_VIRT_BASE + 0x10000) + +static void pxa168_axifab_save(struct axifab_regs *context) +{ + int i; + + context->timeout = __raw_readl(AXIFAB_REGS_VIRT + 0x220); + context->timeout_status = __raw_readl(AXIFAB_REGS_VIRT + 0x240); + for (i = 0; i < 4; i++) { + context->port[4*i] = __raw_readl(AXIFAB_REGS_VIRT + 0x1000 * i + 0x408); + context->port[4*i+1] = __raw_readl(AXIFAB_REGS_VIRT + 0x1000 * i + 0x40c); + context->port[4*i+2] = __raw_readl(AXIFAB_REGS_VIRT + 0x1000 * i + 0x428); + context->port[4*i+3] = __raw_readl(AXIFAB_REGS_VIRT + 0x1000 * i + 0x42c); + } + for (i = 0; i < 3; i++) { + context->port[16+2*i] = __raw_readl(AXIFAB_REGS_VIRT + 0x20 * i + 0x448); + context->port[16+2*i+1] = __raw_readl(AXIFAB_REGS_VIRT + 0x20 * i + 0x44c); + } +} + +static void pxa168_axifab_restore(struct axifab_regs *context) +{ + int i; + + __raw_writel(context->timeout, AXIFAB_REGS_VIRT + 0x220); + __raw_writel(context->timeout_status, AXIFAB_REGS_VIRT + 0x240); + for (i = 0; i < 4; i++) { + __raw_writel(context->port[4*i], AXIFAB_REGS_VIRT + 0x1000 * i + 0x408); + __raw_writel(context->port[4*i+1], AXIFAB_REGS_VIRT + 0x1000 * i + 0x40c); + __raw_writel(context->port[4*i+2], AXIFAB_REGS_VIRT + 0x1000 * i + 0x428); + __raw_writel(context->port[4*i+3], AXIFAB_REGS_VIRT + 0x1000 * i + 0x42c); + } + for (i = 0; i < 3; i++) { + __raw_writel(context->port[16+2*i], AXIFAB_REGS_VIRT + 0x20 * i + 0x448); + __raw_writel(context->port[16+2*i+1], AXIFAB_REGS_VIRT + 0x20 * i + 0x44c); + } +} + +static void pxa168_smc_save(struct smc_regs *context) +{ + context->msc0 = __raw_readl(SMC_MSC0); + context->msc1 = __raw_readl(SMC_MSC1); + context->sxcnfg0 = __raw_readl(SMC_SXCNFG0); + context->sxcnfg1 = __raw_readl(SMC_SXCNFG1); + context->memclkcfg = __raw_readl(SMC_MEMCLKCFG); + context->csdficfg0 = __raw_readl(SMC_CSDFICFG0); + context->csdficfg1 = __raw_readl(SMC_CSDFICFG1); + context->clk_ret_del = __raw_readl(SMC_CLK_RET_DEL); + context->adv_ret_del = __raw_readl(SMC_ADV_RET_DEL); + context->csadrmap0 = __raw_readl(SMC_CSADRMAP0); + context->csadrmap1 = __raw_readl(SMC_CSADRMAP1); + context->we_ap0 = __raw_readl(SMC_WE_AP0); + context->we_ap1 = __raw_readl(SMC_WE_AP1); + context->oe_ap0 = __raw_readl(SMC_OE_AP0); + context->oe_ap1 = __raw_readl(SMC_OE_AP1); + context->adv_ap0 = __raw_readl(SMC_ADV_AP0); + context->adv_ap1 = __raw_readl(SMC_ADV_AP1); +} + +static void pxa168_smc_restore(struct smc_regs *context) +{ + __raw_writel(context->msc0, SMC_MSC0); + __raw_writel(context->msc1, SMC_MSC1); + __raw_writel(context->sxcnfg0, SMC_SXCNFG0); + __raw_writel(context->sxcnfg1, SMC_SXCNFG1); + __raw_writel(context->memclkcfg, SMC_MEMCLKCFG); + __raw_writel(context->csdficfg0, SMC_CSDFICFG0); + __raw_writel(context->csdficfg1, SMC_CSDFICFG1); + __raw_writel(context->clk_ret_del, SMC_CLK_RET_DEL); + __raw_writel(context->adv_ret_del, SMC_ADV_RET_DEL); + __raw_writel(context->csadrmap0, SMC_CSADRMAP0); + __raw_writel(context->csadrmap1, SMC_CSADRMAP1); + __raw_writel(context->we_ap0, SMC_WE_AP0); + __raw_writel(context->we_ap1, SMC_WE_AP1); + __raw_writel(context->oe_ap0, SMC_OE_AP0); + __raw_writel(context->oe_ap1, SMC_OE_AP1); + __raw_writel(context->adv_ap0, SMC_ADV_AP0); + __raw_writel(context->adv_ap1, SMC_ADV_AP1); +} + +static void pxa168_sysbus_save(struct pxa168_pm_regs *context) +{ + pxa168_ciu_save(&(context->ciu)); + pxa168_squ_save(&(context->squ)); + pxa168_axifab_save(&(context->axifab)); + pxa168_smc_save(&(context->smc)); + pxa168_intc_save(&(context->intc)); + pxa168_apbclk_save(&(context->apbclk)); + pxa168_mfp_save(&(context->mfp)); + pxa168_gpio_save(&(context->gpio)); + pxa168_apmu_save(&(context->apmu)); + pxa168_mpmu_save(&(context->mpmu)); +} + +static void pxa168_sysbus_restore(struct pxa168_pm_regs *context) +{ + pxa168_ciu_restore(&(context->ciu)); + pxa168_squ_restore(&(context->squ)); + pxa168_axifab_restore(&(context->axifab)); + pxa168_mpmu_restore(&(context->mpmu)); + pxa168_apmu_restore(&(context->apmu)); + pxa168_intc_restore(&(context->intc)); + pxa168_apbclk_restore(&(context->apbclk)); + pxa168_smc_restore(&(context->smc)); + pxa168_mfp_restore(&(context->mfp)); + pxa168_gpio_restore(&(context->gpio)); +} + +/* +static int hibernate_gpio_init(void) +{ + gpio_direction_output(ex_gpio, 1); + mdelay(100); + return 0; +}*/ + +static int pxa168_pm_enter_sleep(struct pxa168_pm_regs *pm_regs) +{ + int i; + + pm_regs->data_pool = (unsigned char *)0xC0000000; + + pxa168_sysbus_save(pm_regs); + + /* should set:modeSaveFlags, areaAddress, flushFunc, psprAddress, + * extendedChecksumByteCount */ + pm_regs->pm_data.modeSaveFlags = 0x3f; /* PM_MODE_SAVE_FLAG_SVC; */ + pm_regs->pm_data.flushFunc = flush_cpu_cache; + pm_regs->pm_data.areaAddress = (unsigned int)&(pm_regs->pm_data); + pm_regs->pm_data.extendedChecksumByteCount = + sizeof(struct pxa168_pm_regs) - sizeof(struct pm_save_data); + printk("ext size:%d, save size%d\n", + pm_regs->pm_data.extendedChecksumByteCount, + sizeof(struct pm_save_data)); + + pm_regs->word0 = __raw_readl(pm_regs->data_pool); + pm_regs->word1 = __raw_readl(pm_regs->data_pool + 4); + pm_regs->word2 = __raw_readl(pm_regs->data_pool + 8); + + /* Write resume back address to SDRAM */ + __raw_writel(virt_to_phys(pxa168_cpu_resume), pm_regs->data_pool); + __raw_writel(virt_to_phys(&(pm_regs->pm_data)), pm_regs->data_pool + 4); + + /* Write Hibernate mode indicator to SDRAM */ + if(cpu_is_pxa168_S0()) + __raw_writel(0xa, pm_regs->data_pool + 8); + else + __raw_writel(0x55AA55AA, pm_regs->data_pool + 8); + + pxa168_cpu_sleep((unsigned int)&(pm_regs->pm_data), + virt_to_phys(&(pm_regs->pm_data))); + + /* come back */ + __raw_writel(pm_regs->word0, pm_regs->data_pool); + __raw_writel(pm_regs->word1, pm_regs->data_pool + 4); + __raw_writel(pm_regs->word2, pm_regs->data_pool + 8); + + pxa168_sysbus_restore(pm_regs); + + /* unmask GPIO edge detection for all 4 banks - APMASKx */ + for (i = 0; i < 4; i++) + __raw_writel(0xffffffff, APMASK(i)); + + //printk("*** made it back from sleep\n"); + + return 0; +} + + + +/* + * Called after processes are frozen, but before we shut down devices. + */ +static int pxa168_pm_prepare(void) +{ + return 0; +} + +/* + * Called after devices are re-setup, but before processes are thawed. + */ +static void pxa168_pm_finish(void) +{ + pm_state = PM_SUSPEND_ON; +} + +static int pxa168_pm_valid(suspend_state_t state) +{ + int ret = 1; + + if (state == PM_SUSPEND_MEM) { + pm_state = PM_SUSPEND_MEM; + } else if (state == PM_SUSPEND_STANDBY) { + pm_state = PM_SUSPEND_STANDBY; + } else { + ret = 0; + } + return ret; +} + +static ssize_t sleeptime_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", pm_sleeptime); +} + +static ssize_t sleeptime_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + sscanf(buf, "%u", &pm_sleeptime); + return len; +} + +#define pxa168_pm_enter_core_intidle() do {} while (0) + +void pxa168_pm_enter_core_extidle(void) +{ + uint32_t icu_ap_gbl_irq_msk; + + /* step 1: set the wakeup source */ + /* It should be woke up by ICU interrupt */ + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + /********************************************************************* + * set: DIS_MC_SW_REQ(21), MC_WAKE_EN(20), L2_RESETn(9), and IDLE(1) + * *******************************************************************/ + if(cpu_is_pxa168_A0()==0) + __raw_writel(0x28000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + else + __raw_writel(0x08000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + + __raw_writel(0x00300302, APMU_IDLE_CFG); + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + /********************************************************************* + * set: SetAlways(28), + * SetAlways(25), + * SetAlways(14) + *********************************************************************/ + __raw_writel(0x12004000, MPMU_APCR); + + pxa168_pm_swi(); +} + + + +void pxa168_pm_enter_apps_idle(void) +{ + uint32_t icu_ap_gbl_irq_msk; + + /* step 1: set the wakeup source */ + /* It should be woke up by ICU interrupt */ + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + /********************************************************************* + * set: DIS_MC_SW_REQ(21), MC_WAKE_EN(20), L2_RESETn(9), and IDLE(1) + * *******************************************************************/ + if(cpu_is_pxa168_A0()==0) + __raw_writel(0x28000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + else + __raw_writel(0x08000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + + __raw_writel(0x300202, APMU_IDLE_CFG); + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + /********************************************************************* + * set: AXISDD(31), SetAlways(28), DDRCORESD(27), + * SetAlways(25), + * SetAlways(14) + *********************************************************************/ + __raw_writel(0x9a004000, MPMU_APCR); + + pxa168_pm_swi(); +} + +void pxa168_pm_enter_apps_sleep(void) +{ + uint32_t icu_ap_gbl_irq_msk; + + /* step 1: set the wakeup source */ + /********************************************************************* + * unmask: Keypress(21), + * RTC_ALARM(17), AP1_TIMER_3(10), AP1_TIMER2(9), + * AP1_TIMER1(8), WAKEUP4,3(4,3) + * note: must mask WAKEUP5(5) and WAKEUP1(1)(for USB port) + *********************************************************************/ + __raw_writel(0x0220718, MPMU_AWUCRM); + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + /********************************************************************* + * set: DIS_MC_SW_REQ(21), MC_WAKE_EN(20), L2_RESETn(9), and IDLE(1) + * *******************************************************************/ + if(cpu_is_pxa168_A0()==0) + __raw_writel(0x28000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + else + __raw_writel(0x08000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + + __raw_writel(0x300202, APMU_IDLE_CFG); + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + /********************************************************************* + * set: AXISDD(31), SLPEN(29), SetAlways(28), DDRCORESD(27), + * SetAlways(25), SLPWP0,1,2,5,6,7(23,22,21,17,16,15), + * SetAlways(14) + *********************************************************************/ + __raw_writel(0xbae3c000, MPMU_APCR); + + pxa168_pm_swi(); +} + +void pxa168_pm_enter_sys_sleep(void) +{ + uint32_t icu_ap_gbl_irq_msk; + + /* step 1: set the wakeup source */ + /********************************************************************* + * unmask: Keypress(21), + * RTC_ALARM(17), AP1_TIMER_3(10), AP1_TIMER2(9), + * AP1_TIMER1(8), WAKEUP4,3(4,3) + * note: must mask WAKEUP5(5) and WAKEUP1(1)(for USB port) + *********************************************************************/ + __raw_writel(0x220718, MPMU_AWUCRM); + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + /********************************************************************* + * set: DIS_MC_SW_REQ(21), MC_WAKE_EN(20), L2_RESETn(9), and IDLE(1) + * *******************************************************************/ + __raw_writel(0x300202, APMU_IDLE_CFG); + if(cpu_is_pxa168_A0()==0) + __raw_writel(0x28000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + else + __raw_writel(0x08000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + /********************************************************************* + * set: AXISDD(31), SLPEN(29), SetAlways(28), DDRCORESD(27), APBSD(26) + * SetAlways(25), SLPWP0,1,2,5,6,7(23,22,21,17,16,15), + * SetAlways(14) + *********************************************************************/ + __raw_writel(0xbee3c000, MPMU_APCR); + + pxa168_pm_swi(); +} + +void pxa168_pm_enter_sys_sleep_edge(void) +{ + uint32_t icu_ap_gbl_irq_msk; + unsigned int icu_int_conf[64]; + int i; + for (i = 0; i < 64; i++) { + icu_int_conf[i] = __raw_readl(ICU_INT_CONF(i)); + if (IRQ_PXA168_KEYPAD != i) + __raw_writel(icu_int_conf[i]&0xfffffff8, \ + ICU_INT_CONF(i)); + } + /* step 1: set the wakeup source */ + /*__raw_writel(0x002207dd, MPMU_AWUCRM);*/ + __raw_writel(0x200008, MPMU_AWUCRM); + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + if(cpu_is_pxa168_A0()==0) + __raw_writel(0x28000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + else + __raw_writel(0x08000000, APMU_PCR ); /* ensure SETALWAYS bits = 1 */ + + __raw_writel(0x300202, APMU_IDLE_CFG); + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + __raw_writel(0xac000000, MPMU_APCR); + + pxa168_pm_swi(); + /* retain the ext idle config as default */ + __raw_writel(0x300202, APMU_IDLE_CFG); + __raw_writel(0x00000000, MPMU_APCR); + for (i = 0; i < 64; i++) { + if (IRQ_PXA168_KEYPAD != i) + __raw_writel(icu_int_conf[i], ICU_INT_CONF(i)); + } + +} + + + +#define pxa168_pm_enter_hibernate() do {} while (0) + + + +void pxa168_pm_enter_lowpower_mode(int state) +{ + struct op_info *info = NULL; + int op; + op = dvfm_get_op(&info); + switch (state) { + case POWER_MODE_CORE_INTIDLE: + mspm_add_event(op, CPU_STATE_RUN); + pxa168_pm_enter_core_intidle(); + mspm_add_event(op, CPU_STATE_IDLE); + break; + case POWER_MODE_CORE_EXTIDLE: + mspm_add_event(op, CPU_STATE_RUN); + pxa168_pm_enter_core_extidle(); + mspm_add_event(op, CPU_STATE_IDLE); + break; + case POWER_MODE_APPS_IDLE: + mspm_add_event(op, CPU_STATE_RUN); + pxa168_pm_enter_apps_idle(); + mspm_add_event(op, CPU_STATE_IDLE); + break; + case POWER_MODE_APPS_SLEEP: + mspm_add_event(op, CPU_STATE_RUN); + pxa168_pm_enter_apps_sleep(); + mspm_add_event(op, CPU_STATE_IDLE); + break; + case POWER_MODE_SYS_SLEEP: + mspm_add_event(op, CPU_STATE_RUN); + pxa168_pm_enter_sys_sleep(); + mspm_add_event(op, CPU_STATE_IDLE); + break; + case POWER_MODE_HIBERNATE: + pxa168_pm_enter_hibernate(); + break; + } +} + +static int pxa168_pm_enter(suspend_state_t state) +{ + int op; + struct op_info *info = NULL; + if (state == PM_SUSPEND_MEM) + if (machine_is_edge()) { + op = dvfm_get_op(&info); + dvfm_request_op(0); + pxa168_pm_enter_sys_sleep_edge(); + dvfm_request_op(op); + return 0; + } else { + return pxa168_pm_enter_sleep(&pxa168_pm_regs); + } + else + return -EINVAL; +} + +/* + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. + */ +static struct platform_suspend_ops pxa168_pm_ops = { + .valid = pxa168_pm_valid, + .prepare = pxa168_pm_prepare, + .enter = pxa168_pm_enter, + .finish = pxa168_pm_finish, +}; + + +static int tokenizer(char **tbuf, const char *userbuf, ssize_t n, + char **tokptrs, int maxtoks) +{ + char *cp, *tok; + char *whitespace = " \t\r\n"; + int ntoks = 0; + cp = kmalloc(n + 1, GFP_KERNEL); + if (!cp) + return -ENOMEM; + + *tbuf = cp; + memcpy(cp, userbuf, n); + cp[n] = '\0'; + + do { + cp = cp + strspn(cp, whitespace); + tok = strsep(&cp, whitespace); + if ((*tok == '\0') || (ntoks == maxtoks)) + break; + tokptrs[ntoks++] = tok; + } while (cp); + + return ntoks; +} + + +static ssize_t deepidle_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int len = 0; + + if (enable_deepidle & IDLE_CORE_INTIDLE) + len += sprintf(buf + len, "core_intidle, "); + if (enable_deepidle & IDLE_CORE_EXTIDLE) + len += sprintf(buf + len, "core_extidle, "); + if (enable_deepidle & IDLE_APPS_IDLE) + len += sprintf(buf + len, "apps_idle, "); + if (enable_deepidle & IDLE_APPS_SLEEP) + len += sprintf(buf + len, "apps_sleep, "); + if (enable_deepidle & IDLE_SYS_SLEEP) + len += sprintf(buf + len, "sys_sleep\n"); + len += sprintf(buf + len, "Command: echo [set|unset]\ + [core_intidle|core_extidle|apps_idle|apps_sleep|sys_sleep] " + "> deepidle\n"); + return len; +} +#define MAXTOKENS 80 +static ssize_t deepidle_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int error = 0; + char *tbuf = NULL; + char *token[MAXTOKENS]; + int ntoks = tokenizer(&tbuf, buf, len, (char **)&token, MAXTOKENS); + + if (ntoks <= 0) { + error = ntoks; + goto out; + } + + if (strcmp(token[0], "set") == 0) { + if (strcmp(token[1], "core_intidle") == 0) + enable_deepidle |= IDLE_CORE_INTIDLE; + else if (strcmp(token[1], "core_extidle") == 0) + enable_deepidle |= IDLE_CORE_EXTIDLE; + else if (strcmp(token[1], "apps_idle") == 0) + enable_deepidle |= IDLE_APPS_IDLE; + else if (strcmp(token[1], "apps_sleep") == 0) + enable_deepidle |= IDLE_APPS_SLEEP; + else if (strcmp(token[1], "sys_sleep") == 0) + enable_deepidle |= IDLE_SYS_SLEEP; + else + error = -EINVAL; + } else if (strcmp(token[0], "unset") == 0) { + if (strcmp(token[1], "core_intidle") == 0) + enable_deepidle &= ~IDLE_CORE_INTIDLE; + else if (strcmp(token[1], "core_extidle") == 0) + enable_deepidle &= ~IDLE_CORE_EXTIDLE; + else if (strcmp(token[1], "apps_idle") == 0) + enable_deepidle &= ~IDLE_APPS_IDLE; + else if (strcmp(token[1], "apps_sleep") == 0) + enable_deepidle &= ~IDLE_APPS_SLEEP; + else if (strcmp(token[1], "sys_sleep") == 0) + enable_deepidle &= ~IDLE_SYS_SLEEP; + else + error = -EINVAL; + } else { + if (strcmp(token[0], "0") == 0) + enable_deepidle = IDLE_ACTIVE; + else + error = -EINVAL; + } +out: + kfree(tbuf); + return error ? error : len; +} +static struct kobj_attribute sleeptime_attr = { + .attr = { + .name = __stringify(sleeptime), + .mode = 0644, + }, + .show = sleeptime_show, + .store = sleeptime_store, +}; + +void pxa168_pm_enter_apps_sleep_test(void) +{ + uint32_t icu_ap_gbl_irq_msk; + + /* step 1: set the wakeup source */ + /********************************************************************* + * unmask: Keypress(21), + * RTC_ALARM(17), AP1_TIMER_3(10), AP1_TIMER2(9), + * AP1_TIMER1(8), WAKEUP4,3(4,3) + * note: must mask WAKEUP5(5) and WAKEUP1(1)(for USB port) + *********************************************************************/ + __raw_writel(0x0220018, MPMU_AWUCRM); + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + /********************************************************************* + * set: DIS_MC_SW_REQ(21), MC_WAKE_EN(20), L2_RESETn(9), and IDLE(1) + * *******************************************************************/ + __raw_writel(0x300202, APMU_IDLE_CFG); + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + /********************************************************************* + * set: AXISDD(31), SLPEN(29), SetAlways(28), DDRCORESD(27), + * SetAlways(25), SLPWP0,1,2,5,6,7(23,22,21,17,16,15), + * SetAlways(14) + *********************************************************************/ + __raw_writel(0xbae3c000, MPMU_APCR); + + pxa168_pm_swi(); +} + +void pxa168_pm_enter_sys_sleep_test(void) +{ + uint32_t icu_ap_gbl_irq_msk; + + /* step 1: set the wakeup source */ + /********************************************************************* + * unmask: Keypress(21), + * RTC_ALARM(17), AP1_TIMER_3(10), AP1_TIMER2(9), + * AP1_TIMER1(8), WAKEUP4,3(4,3) + * note: must mask WAKEUP5(5) and WAKEUP1(1)(for USB port) + *********************************************************************/ + __raw_writel(0x220018, MPMU_AWUCRM); + + /* step 2: set the IDLE bit in the AP idle configuration register*/ + /********************************************************************* + * set: DIS_MC_SW_REQ(21), MC_WAKE_EN(20), L2_RESETn(9), and IDLE(1) + * *******************************************************************/ + __raw_writel(0x300202, APMU_IDLE_CFG); + + /* step 3: set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + icu_ap_gbl_irq_msk = __raw_readl(ICU_AP_GBL_IRQ_MSK); + icu_ap_gbl_irq_msk |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(icu_ap_gbl_irq_msk, ICU_AP_GBL_IRQ_MSK); + + /* step 4: set the AXISDD bit in the AP power control register to 1*/ + + /* step 5: set the DDRCORSD and APBSD bits in the MPMU_APCR to 1 */ + + /* step 6: set the SLPEN bit in the MPMU_APCR to 1 */ + + /* step 7: program the memory controller hardware sleep type */ + /********************************************************************* + * set: AXISDD(31), SLPEN(29), SetAlways(28), DDRCORESD(27), APBSD(26) + * SetAlways(25), SLPWP0,1,2,5,6,7(23,22,21,17,16,15), + * SetAlways(14) + *********************************************************************/ + __raw_writel(0xbee3c000, MPMU_APCR); + + pxa168_pm_swi(); +} + + + +void pxa168_pm_enter_lowpower_mode_test(int state) +{ + + unsigned int icu_int_conf[64]; + int i; + if (state == POWER_MODE_CORE_INTIDLE || \ + state == POWER_MODE_CORE_EXTIDLE || \ + state == POWER_MODE_APPS_IDLE) { + for (i = 0; i < 64; i++) { + icu_int_conf[i] = __raw_readl(ICU_INT_CONF(i)); + if (IRQ_PXA168_KEYPAD != i) + __raw_writel(icu_int_conf[i]&0xfffffff8, \ + ICU_INT_CONF(i)); + } + } + switch (state) { + case POWER_MODE_CORE_INTIDLE: + pxa168_pm_enter_core_intidle(); + break; + case POWER_MODE_CORE_EXTIDLE: + pxa168_pm_enter_core_extidle(); + break; + case POWER_MODE_APPS_IDLE: + pxa168_pm_enter_apps_idle(); + break; + case POWER_MODE_APPS_SLEEP: + pxa168_pm_enter_apps_sleep_test(); + break; + case POWER_MODE_SYS_SLEEP: + pxa168_pm_enter_sys_sleep_test(); + break; + case POWER_MODE_HIBERNATE: + pxa168_pm_enter_hibernate(); + break; + } + if (state == POWER_MODE_CORE_INTIDLE || \ + state == POWER_MODE_CORE_EXTIDLE || \ + state == POWER_MODE_APPS_IDLE) { + for (i = 0; i < 64; i++) { + if (IRQ_PXA168_KEYPAD != i) + __raw_writel(icu_int_conf[i], ICU_INT_CONF(i)); + } + } +} + +static ssize_t lpidle_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int error = 0; + char *tbuf = NULL; + char *token[MAXTOKENS]; + int ntoks = tokenizer(&tbuf, buf, len, (char **)&token, MAXTOKENS); + + if (ntoks <= 0) { + error = ntoks; + goto out; + } + if (strcmp(token[0], "core_extidle") == 0) + pxa168_pm_enter_lowpower_mode_test(POWER_MODE_CORE_EXTIDLE); + else if (strcmp(token[0], "apps_idle") == 0) + pxa168_pm_enter_lowpower_mode_test(POWER_MODE_APPS_IDLE); + else if (strcmp(token[0], "apps_sleep") == 0) + pxa168_pm_enter_lowpower_mode_test(POWER_MODE_APPS_SLEEP); + else if (strcmp(token[0], "sys_sleep") == 0) + pxa168_pm_enter_lowpower_mode_test(POWER_MODE_SYS_SLEEP); + else + error = -EINVAL; +out: + return error ? error : len; +} + +static ssize_t lpidle_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return 0; +} + +static struct kobj_attribute lpidle_attr = { + .attr = { + .name = __stringify(lpidle), + .mode = 0644, + }, + .show = lpidle_show, + .store = lpidle_store, +}; + + + + + +static struct kobj_attribute deepidle_attr = { + .attr = { + .name = __stringify(deepidle), + .mode = 0644, + }, + .show = deepidle_show, + .store = deepidle_store, +}; +static struct attribute * g[] = { + &sleeptime_attr.attr, + &deepidle_attr.attr, + &lpidle_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = g, +}; + + +static int __init pxa168_pm_init(void) +{ + if (!cpu_is_pxa168()) + return -EIO; + + if (sysfs_create_group(power_kobj, &attr_group)) + return -1; + + dmc_membase = ioremap(0xb0000000, 0x00001000); + sram_membase = ioremap(0xd1020000, 0x00020000); + +/* ex_gpio = GPIO_EXT0(1); + if (gpio_request(ex_gpio, "EXP2_SYS_DIS_N")) { + printk("EXP2_SYS_DIS_N Request Failed\n"); + return -EIO; + } currently not required */ + + suspend_set_ops(&pxa168_pm_ops); + return 0; +} + +late_initcall(pxa168_pm_init); diff --git a/arch/arm/mach-mmp/pxa168_pm_ll.S b/arch/arm/mach-mmp/pxa168_pm_ll.S new file mode 100644 index 00000000000000..01ec946b41f729 --- /dev/null +++ b/arch/arm/mach-mmp/pxa168_pm_ll.S @@ -0,0 +1,892 @@ +/* + * Low-level PXA168 hibernate mode support + * + * Copyright (C) 2009, Marvell Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include +#include + +/* pxa168_cpu_resume() + * Entry point for bootloader resume to kernel + * + * It will invoke pm_resume_from_sleep which use area_phy_address as parameter + */ +/* Note: The following code is located into the .data section. + * This is to allow area_phy_address to be accessed with a relative load + * while we can't rely on any MMU translation. We could have put + * area_phy_address in the .text section as well, but some setups might + * insist on it to be truly read-only. + */ + .text + .align 5 +ENTRY(pxa168_cpu_resume) + nop + nop + nop + nop + nop +/* +__resumedebug: + nop + nop + b __resumedebug +*/ + mov r1, #0x4 + ldr r0, [r1] + bl pm_resume_from_sleep + cmp r0, #1 + @ maybe turn on some lights for warning +error_ret: + nop + beq error_ret + +/* pxa168_cpu_sleep(unsigned int a, unsigned int b) + * + * Entry point for entering sleep mode(S2D3C4). + * a: + * vitual address of the data save area for Monahans content + * b: + * physical address of the data save area for Monahans content + * + * The API pm_enter_sleep will use the first parameter "a". The "b" will + * be stored in area_phy_adress which will be used by pm_resume_from_sleep. + */ + .text + .align 5 +ENTRY(pxa168_cpu_sleep) + b pm_enter_sleep + +@***************************************************************************** +@ pm_checksum_calculate +@ +@ Calculate checksum +@ +@ Inputs: +@ r0: the virutal address of the data area which will be calculated the +@ checksum +@ r1: the toltal word of the data area. Checksum is done on 4-byte word +@ +@ Output: +@ checksum +@ +@ Registers used +@ r0, r1, r2, r3 +@ + +pm_checksum_calculate: + + @ pick a non-zero seed + ldr r2, =(0x5A72) +calculate: + @ get value and increment pointer + ldr r3, [r0], #4 + add r2, r2, r3 + @ rotate left by one bit position + mov r2, r2, ROR #31 + subs r1, r1, #1 + bne calculate + mov r0, r2 + mov pc, lr + +@****************************************************************************** +@ +@ pm_resume_from_sleep +@ +@ Restore saved content and return back +@ +@ Inputs: +@ r0: The physical address of the saved data area +@ +@ Outputs: +@ None +@ + +pm_resume_from_sleep: + @ make sure that we are in SVC mode with irq and fiq off + mov r1, #(CPSR_Mode_SVC | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r1 + + @ Step 1 + @ validate checksum + @ get the address of the first word that is checksumable + mov r9, r0 + ldr r1, [r9, #SleepState_wordCount] + mov r8, lr + add r0, r9, #4 +1: + @b 1b + bl pm_checksum_calculate + ldr r3, [r9, #SleepState_checksum] + subs r1, r3, r0 + mov r0, #0 + movne r0, #1 + @ return if checksum is wrong + movne pc, r8 + mov r0, r9 + + @ enable PMU user access + mov r9, #0x1 + mrc p15, 0, r9, c15, c9, 0 + orr r9, r9, #0x1 + mcr p15, 0, r9, c15, c9, 0 + + @ Step 2 + ldr r9, [r0, #SleepState_Cp15_ACR_MMU] + ldr r8, [r0, #SleepState_Cp15_AUXCR_MMU] + ldr r7, [r0, #SleepState_Cp15_TTBR_MMU] + ldr r6, [r0, #SleepState_Cp15_DACR_MMU] + ldr r5, [r0, #SleepState_Cp15_PID_MMU] + ldr r4, [r0, #SleepState_Cp15_CPAR] + ldr r0, [r0, #SleepState_areaAddress] + + @ disable L2 cache to SQU mapping + ldr r1, =0xd4282c08 + ldr r2, [r1] + bic r2, r2, #0x10 + str r2, [r1] + + @ disable SQU bank3 + ldr r1, =0xd42a0030 + ldr r2, [r1] + bic r2, r2, #0x1 + str r2, [r1] + + @ invalidate I, D caches & BTB + mcr p15, 0, ip, c7, c7, 0 + @ Drain Write (& Fill) Buffer + mcr p15, 0, ip, c7, c10, 4 + @ Prefetch Flush + mcr p15, 0, ip, c7, c5, 4 + @ invalidate I, D TLBs + mcr p15, 0, ip, c8, c7, 0 + @ invalidate L2 cache + mcr p15, 1, ip, c7, c7, 0 + + @ Step 3 + @ Rrestore MMU settings and turn on MMU + mcr p15, 0, r4, c15, c1, 0 + mcr p15, 0, r5, c13, c0, 0 + mcr p15, 0, r6, c3, c0, 0 + mcr p15, 0, r7, c2, c0, 0 + mcr p15, 0, r8, c1, c0, 1 + + @ Get page table address + mrc p15, 0, r1, c2, c0, 0 + bic r1, r1, #0xff + bic r1, r1, #0x3f00 + ldr r2, =0x542e + + @ Mapping resume_turn_on_mmu in the pagetable + adr r3, resume_turn_on_mmu + mov r3, r3, lsr #20 + orr r4, r2, r3, lsl #20 + ldr r5, [r1, r3, lsl #2] + str r4, [r1, r3, lsl #2] + + @ Mapping page table address in the page table + mov r6, r1, lsr #20 + orr r7, r2, r6, lsl #20 + ldr r8, [r1, r6, lsl #2] + str r7, [r1, r6, lsl #2] + + ldr r10, =resume_after_turn_on_mmu + mov r10, r10 + b resume_turn_on_mmu + + .align 5 + +resume_turn_on_mmu: + mcr p15, 0, r9, c1, c0, 0 + + @ cp_wait + mrc p15, 0, r2, c2, c0, 0 + mov r2, r2 + mov r2, r2 + mov pc, r10 + nop + nop + nop + nop + +resume_after_turn_on_mmu: + @ Restore the Mappings in page table + str r5, [r1, r3, lsl #2] + str r8, [r1, r6, lsl #2] + + @ Step 4 + @ r0 stores the virtual address of the content save area + @ compare "modeSaveFlag" to decide which mode will be saved + ldr r6, [r0, #SleepState_modeSaveFlags] +1: + @ restore SVC content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_SVC) + beq 2f + add r7, r0, #SleepState_SVC_REGS + ldmia r7, {r2, sp, lr} + msr spsr, r2 + +2: + @ restore UND mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_UND) + beq 3f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_UND | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + add r7, r0, #SleepState_UND_REGS + ldmia r7, {r2, sp, lr} + msr spsr, r2 + +3: + @ restore ABT mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_ABT) + beq 4f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_ABT | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + add r7, r0, #SleepState_ABT_REGS + ldmia r7, {r2, sp, lr} + msr spsr, r2 + +4: + @ restore IRQ mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_IRQ) + beq 5f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_IRQ | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + add r7, r0, #SleepState_IRQ_REGS + ldmia r7, {r2, sp, lr} + msr spsr, r2 + +5: + @ restore FIQ mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_FIQ) + beq 6f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_FIQ | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + add r7, r0, #SleepState_FIQ_REGS + ldmia r7, {r2, r8-r12, sp, lr} + msr spsr, r2 + +6: + @ restore SYS mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_SYS) + beq 7f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_SYS | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + add r7, r0, #SleepState_SYS_REGS + ldmia r7, {sp, lr} + +7: + @ Step 5 + @ Re-establish whatever mode was in use at the time pm_enter_sleep() + @ was invoked and restore complete register context. Before restoring + @ the SPSR, make sure that the entry mode was not SYS mode, which has + @ no SPSR. + + @ Load CPSR, sp and (if not SYS mode) SPSR + ldr r3, [r0, #SleepState_ENTRY_CPSR] + msr cpsr, r3 + ldr r2, =CPSR_Mode_SYS + and r3, r3, r2 + cmp r3, r2 + ldrne r2, [r0, #SleepState_ENTRY_SPSR] + msrne spsr, r2 + add r0, r0, #SleepState_ENTRY_R0 + @ use "increase after" to skip r0 register restore, + ldmib r0, {r1 - r12, sp, lr} + @ restore r0 reigster + ldr r0, [r0] + + @ return to next instruction after pm_enter_sleep + mov pc, lr + +@***************************************************************************** +@ pm_enter_sleep_or_deep_sleep +@ +@ Put the system into S2D3C4 or S3D4C4 state +@ +@ Inputs: +@ r0: the virutal address of the data area to save the content of core +@ r1: sleep type, 6(sleep), 7(deep sleep) +@ +@ Outputs: +@ None +@ +@ Notes: +@ r1 should be saved previously +@ + +pm_enter_sleep_or_deep_sleep: + + @ Step 1 + @ store registers(r0-r12), sp, lr of current mode in the data array + @ ENTRY_REGS + @ the r0 changes to be virutal address of ENTRY_REGS + add r0, r0, #SleepState_ENTRY_R0 + @ skip r0 and r1 save + add r0, r0, #4 + stmib r0, {r2 - r12, sp, lr} + sub r0, r0, #4 + mov r11, r1 @save the sleep type + @ save r0 register + sub r5, r0, #SleepState_ENTRY_R0 + str r5, [r0] + + @ store cpsr of current mode in the data array ENTRY_REGS. + mrs r3, cpsr + str r3, [r5, #SleepState_ENTRY_CPSR] + + @ store spsr(if not SYS mode) of current mode in the content area + ldr r2, =CPSR_Mode_SYS + and r1, r3, r2 + cmp r1, r2 + mrsne r2, spsr + strne r2, [r5, #SleepState_ENTRY_SPSR] + + @ Step 2 + @ compare "modeSaveFlag" to decide which mode will be saved + @ the private registers are saved in an array. the consequence should + @ be "spsr", "r8-r12", sp, lr + @ the data array stores registers from low address to high address. + ldr r6, [r5, #SleepState_modeSaveFlags] + +1: + @ save SYS mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_SYS) + beq 2f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_SYS | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + add r7, r5, #SleepState_SYS_REGS + stmia r7, {sp, lr} + +2: + @ save FIQ mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_FIQ) + beq 3f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_FIQ | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + mrs r2, spsr + add r7, r5, #SleepState_FIQ_REGS + stmia r7, {r2, r8 - r12, sp, lr} + +3: + @ save IRQ mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_IRQ) + beq 4f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_IRQ | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + mrs r2, spsr + add r7, r5, #SleepState_IRQ_REGS + stmia r7, {r2, sp, lr} + +4: + @ save ABT mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_ABT) + beq 5f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_ABT | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + mrs r2, spsr + add r7, r5, #SleepState_ABT_REGS + stmia r7, {r2, sp, lr} + +5: + @ save UND mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_UND) + beq 6f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_UND | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + mrs r2, spsr + add r7, r5, #SleepState_UND_REGS + stmia r7, {r2, sp, lr} + +6: + @ save SVC mode content? + ands r1, r6, #(PM_MODE_SAVE_FLAG_SVC) + beq 7f + bic r3, r3, #(CPSR_Mode_MASK) + orr r3, r3, #(CPSR_Mode_SVC | CPSR_I_Bit | CPSR_F_Bit) + msr cpsr_c, r3 + mrs r2, spsr + add r7, r5, #SleepState_SVC_REGS + stmia r7, {r2, sp, lr} + +7: + @ Step 3 + @ save MMU settings + @ r5 is pointer to sleep save data area + + @ Cp15_ACR_MMU + mrc p15, 0, r0, c1, c0, 0 + str r0, [r5, #SleepState_Cp15_ACR_MMU] + + @ Cp15_AUXCR_MMU; + mrc p15, 0, r0, c1, c0, 1 + str r0, [r5, #SleepState_Cp15_AUXCR_MMU] + + @ Cp15_TTBR_MMU; + mrc p15, 0, r0, c2, c0, 0 + str r0, [r5, #SleepState_Cp15_TTBR_MMU] + + @ Cp15_DACR_MMU; + mrc p15, 0, r0, c3, c0, 0 + str r0, [r5, #SleepState_Cp15_DACR_MMU] + + @ Cp15_PID_MMU; + mrc p15, 0, r0, c13, c0, 0 + str r0, [r5, #SleepState_Cp15_PID_MMU] + + @ Cp15_CPAR; + mrc p15, 0, r0, c15, c1, 0 + str r0, [r5, #SleepState_Cp15_CPAR] + + @ Now enable access to all valid coprocessors + mcr p15, 0, r1, c15, c1, 0 + + @ cp_wait + mrc p15, 0, r0, c2, c0, 0 + mov r0, r0 + sub pc, pc, #4 + + @ Step 4 + @ The block 0 of nand flash should be copied to SRAM 0x5c014000 + @ The OS should save the resume back address and the content save area address + @ load current pspr to r12 register + @ldr r12, [r5, #SleepState_psprAddress] + @ Store 0x5c014000 to PSPR + @ldr r1, =0x5c014000 + @str r1, [r12] + + @ Step 5 + @ calculate checksum + @ get total word count for ckecksum and should not include "checksum" + mov r1, #SleepState_size - 4 + ldr r2, [r5, #SleepState_extendedChecksumByteCount] + add r1, r1, r2 + @ get the word count by /4 + mov r1, r1, lsr #2 + mov r0, r5 + str r1, [r0, #SleepState_wordCount]! + bl pm_checksum_calculate + str r0, [r5, #SleepState_checksum] + @ Step 6 + @ invoke user flush function + ldr r0, [r5, #SleepState_flushFunc] + cmp r0, #0 + movne lr, pc + movne pc, r0 + + b 1f + .align 5 +1: + @ Step 7 + @ map L2 cache to SQU as SRAM + mcr p15, 1, r0, c7, c11, 0 /* clean L2 cache */ + mcr p15, 1, r0, c7, c7, 0 /* invalidate L2 cache */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #(1 << 26) + mcr p15, 0, r0, c1, c0, 0 /* disable L2 cache */ + + ldr r0, =0xfe282c08 /* test L2 IDLE bit */ + ldr r0, [r0] +100: tst r0, #(1 << 16) + beq 100b + + ldr r0, =0xfe2a0030 /* enable SQU bank3 */ + ldr r1, [r0] + orr r1, #0x1 + str r1, [r0] + + ldr r0, =0xfe282c08 /* L2 cache to SQU */ + ldr r1, [r0] + orr r1, #0x10 + str r1, [r0] + + @ power down + b hibernate_pwr_down + + @ wait for sleep + @ should never go here +20: + nop + b 20b + +@***************************************************************************** +@ pm_enter_sleep +@ +@ Put the system into S2D3C4 state +@ +@ Inputs: +@ r0: the virutal address of the data area to save the content of core +@ +@ Outputs: +@ None +@ + +pm_enter_sleep: + str r1, [r0, #SleepState_ENTRY_R1] + mov r1, #PXA3xx_PM_S2D3C4 + b pm_enter_sleep_or_deep_sleep + +@***************************************************************************** +@ +@ +@ Pull EXP2_SYS_DIS_N low +@ +@ Inputs: +@ nONE +@ +@ Outputs: +@ None +@ + +.macro delay, counter + mov \counter, #0x80000 @ Initialize counter +1: subs \counter, \counter, #1 @ Decrement counter + bne 1b @ No, then loop again +.endm + +hibernate_pwr_down: + + ldr r0, =sram_membase + ldr r0, [r0] + ldr r3, =hibernate_sram_start + ldr r4, =hibernate_sram_end + add r4, r4, #0x100 + +hibernate_rel_ram: + ldmia r3!, {r5 - r12} + stmia r0!, {r5 - r12} + cmp r3, r4 + ble hibernate_rel_ram + + ldr r0, =sram_membase + ldr r0, [r0] + ldr r4, =dmc_membase + ldr r4, [r4] + + mov pc, r0 + +hibernate_sram_start: + b 1f + .align 5 +1: + + + ldr r10, =__machine_arch_type + ldr r10, [r10] + + @ block ddr data request + mov r7, #1 + str r7, [r4, #0x07e0] + + @ ddr self refresh + ldr r7, [r4, #0x0120] + orr r7, #0x40 + str r7, [r4, #0x0120] + + @ ddr self refresh + @mov r7, #0 + @str r7, [r6, #0x00c0] + @mov r7, #1 + @str r7, [r6, #0x00b4] + + ldr r11, =MACH_TYPE_ASPENITE + cmp r10, r11 + beq aspenite_power_down + + ldr r11, =MACH_TYPE_AVENGERS_LITE + cmp r10, r11 + beq avenger_power_down + +aspenite_power_down: + bl init_i2c + mov r4, #0x20 + mov r5, #0x06 + bl readi2c + mov r4, #0x20 + mov r5, #0x06 + and r6, r0, #0xfd /* set IO[1] output */ + bl writei2c + mov r4, #0x20 + mov r5, #0x02 + bl readi2c + mov r4, #0x20 + mov r5, #0x02 + orr r6, r0, #0x2 /* set IO[1] high */ + bl writei2c + delay r0 + mov r4, #0x20 + mov r5, #0x02 + bl readi2c + mov r4, #0x20 + mov r5, #0x02 + and r6, r0, #0xfd /* set IO[1] low */ + bl writei2c + b __loop + +avenger_power_down: + ldr r8, =0xfe019004 + ldr r0, [r8, #0xc] + orr r0, r0, #0x200000 + str r0 , [r8, #0xc] + + ldr r8, =0xfe019004 + ldr r0, [r8, #0x24] + mov r0, #0x200000 + str r0 , [r8, #0x24] + +__loop: + nop + nop + b __loop + +@***************************************************************************** +@ +@ +@ Init I2C +@ +@ Inputs: +@ nONE +@ +@ Outputs: +@ None +@ +init_i2c: + /* setup MFPRs for I2C */ + ldr r0, =0xa841 + ldr r1, =0xfe01e1a8 + str r0, [r1] + ldr r1, =0xfe01e1a4 + str r0, [r1] + + /* enable i2c clock */ + ldr r1, =0xfe051024 /* ACGR */ + ldr r0, [r1] + orr r0, r0, #0x40 /* enable i2c clock */ + str r0, [r1] + ldr r1, =0xfe01502c /* TWSI_CLK */ + mov r0, #0x3 + str r0, [r1] + + /* Initialize I2C Unit */ + ldr r1, =0xfe011000 /* I2C register start address */ + mov r0, #0x0 + str r0, [r1, #0x20] /* TWSI_SAR */ + mov r0, #0x4000 /* reset I2C */ + str r0, [r1, #0x10] /* TWSI_CR */ + mov r0, #0x60 /* set IUE and SCLE */ + str r0, [r1, #0x10] + mov pc, lr + +@***************************************************************************** +@ +@ +@ read I2C +@ +@ Inputs: +@ r4 - slave address, r5 - register in slave +@ +@ Outputs: +@ None +@ +readi2c: + /* Initialize I2C Unit */ + ldr r3, =0xfe011000 /* I2C register start address */ + + /* Set slave I2C address */ + str r4, [r3, #0x8] /* IDBR */ + + /* send a start condition */ + ldr r0, [r3, #0x10] + orr r0, r0, #0x9 /* START & TB */ + ldr r2, =0xffffeffd + and r0, r0, r2 /* ~(STOP | ALDIE) */ + str r0, [r3, #0x10] /* ICR */ + + /* wait until tx buffer empty */ + mov r8, #0x80000 /* time out */ +1: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x40 /* ITE in ISR */ + beq 1b + str r2, [r3, #0x18] /* clear status */ + + /* send first byte(register address) */ + str r5, [r3, #0x8] /* IDBR */ + ldr r0, [r3, #0x10] /* read ICR */ + ldr r2, =0x1008 + orr r0, r0, r2 /* ALDIE, TB */ + ldr r2, =0xfffffffe + and r0, r0, r2 /* ~START */ + orr r0, r0, #0x2 /* STOP */ + str r0, [r3, #0x10] + + /* wait until tx buffer empty */ + mov r8, #0x80000 /* time out */ +2: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x40 /* ITE in ISR */ + beq 2b + str r2, [r3, #0x18] /* clear status */ + + /* send second byte (slave read address) */ + orr r4, r4, #0x1 + str r4, [r3, #0x8] /* IDBR */ + ldr r0, [r3, #0x10] /* read ICR */ + ldr r2, =0x1008 + orr r0, r0, r2 /* ALDIE, TB */ + orr r0, r0, #0x1 /* START */ + ldr r2, =0xfffffffd + and r0, r0, r2 /* ~STOP */ + str r0, [r3, #0x10] + + /* wait until tx buffer empty */ + mov r8, #0x80000 /* time out */ +3: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x40 /* ITE in ISR */ + beq 3b + str r2, [r3, #0x18] /* clear status */ + + /* send stop signal and read data */ + ldr r0, [r3, #0x10] /* read ICR */ + ldr r2, =0x1008 + orr r0, r0, r2 /* ALDIE, TB */ + ldr r2, =0xfffffffe + and r0, r0, r2 /* ~START */ + orr r0, r0, #0x4 /* ACKNACK */ + orr r0, r0, #0x2 /* STOP */ + str r0, [r3, #0x10] + + /* wait until rx buffer full */ + mov r8, #0x80000 /* time out */ +3: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x80 /* IRF in ISR */ + beq 3b + str r2, [r3, #0x18] /* clear status */ + + ldr r0, [r3, #0x8] + + mov pc, lr + +@***************************************************************************** +@ +@ +@ write I2C +@ +@ Inputs: +@ nONE +@ +@ Outputs: +@ None +@ +writei2c: + /* Initialize I2C Unit */ + ldr r3, =0xfe011000 /* I2C register start address */ + + /* Set slave I2C address */ + str r4, [r3, #0x8] /* IDBR */ + + /* send a start condition */ + ldr r0, [r3, #0x10] + orr r0, r0, #0x9 /* START & TB */ + ldr r2, =0xffffeffd + and r0, r0, r2 /* ~(STOP | ALDIE) */ + str r0, [r3, #0x10] /* ICR */ + + /* wait until tx buffer empty */ + mov r8, #0x80000 /* time out */ +1: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x40 /* ITE in ISR */ + beq 1b + str r2, [r3, #0x18] /* clear status */ + + /* send first byte(register address) */ + str r5, [r3, #0x8] /* IDBR */ + ldr r0, [r3, #0x10] /* read ICR */ + ldr r2, =0x1008 + orr r0, r0, r2 /* ALDIE, TB */ + ldr r2, =0xfffffffe + and r0, r0, r2 /* ~START */ + ldr r2, =0xfffffffd + and r0, r0, r2 /* ~STOP */ + str r0, [r3, #0x10] + + /* wait until tx buffer empty */ + mov r8, #0x80000 /* time out */ +2: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x40 /* ITE in ISR */ + beq 2b + str r2, [r3, #0x18] /* clear status */ + + /* send second byte (register value) */ + str r6, [r3, #0x8] /* IDBR */ + ldr r0, [r3, #0x10] /* read ICR */ + ldr r2, =0x1008 + orr r0, r0, r2 /* ALDIE, TB */ + ldr r2, =0xfffffffe + and r0, r0, r2 /* ~START */ + orr r0, r0, #0x2 /* STOP */ + str r0, [r3, #0x10] + + /* wait until tx buffer empty */ + mov r8, #0x80000 /* time out */ +3: + ldr r2, [r3, #0x18] /* ISR */ + subs r8, r8, #1 + moveq pc, lr + ands r2, r2, #0x40 /* ITE in ISR */ + beq 3b + str r2, [r3, #0x18] /* clear status */ + + mov pc, lr + +hibernate_sram_end: + nop +@****************************************************************************** +@ +@ pxa168_pm_swi +@ +@ Issue SWI +@ +@ Inputs: +@ None +@ Outputs: +@ None +@ + +ENTRY(pxa168_pm_swi) + stmfd sp!, {r3 - r12, lr} + mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt + ldmfd sp!, {r3 - r12, pc} diff --git a/arch/arm/mach-mmp/pxa910-squ.c b/arch/arm/mach-mmp/pxa910-squ.c new file mode 100644 index 00000000000000..8cb8c34ac7d9a5 --- /dev/null +++ b/arch/arm/mach-mmp/pxa910-squ.c @@ -0,0 +1,144 @@ +/* + * linux/arch/arm/mach-pxa/pxa910_squ.c + * + * PXA910 SQU registration and IRQ dispatching + * + * Author: Nicolas Pitre + * Created: Nov 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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 + + +struct squ_channel { + char *name; + pxa910_squ_prio prio; + void (*irq_handler)(int, void *); + void *data; +}; + +static struct squ_channel *squ_channels; +static int num_squ_channels; + +int pxa910_request_squ (char *name, pxa910_squ_prio prio, + void (*irq_handler)(int, void *), + void *data) +{ + unsigned long flags; + int i, found = 0; + + printk(KERN_ERR "pxa910_request_squ: name : %s, prio: %d\n", name, prio); + /* basic sanity checks */ + if (!name || !irq_handler) + return -EINVAL; + + local_irq_save(flags); + + do { + printk(KERN_ERR "pxa910_request_squ: do\n"); + /* try grabbing a SQU channel with the requested priority */ + for (i = 0; i < num_squ_channels; i++) { + if ((squ_channels[i].prio == prio) && + !squ_channels[i].name) { + printk(KERN_ERR "founded channels : %d,prio:%d \n", i, prio); + found = 1; + break; + } + } + /* if requested prio group is full, try a hier priority */ + } while (!found && prio--); + + if (found) { + SDCR(i) = 0; + squ_channels[i].name = name; + squ_channels[i].irq_handler = irq_handler; + squ_channels[i].data = data; + } else { + printk (KERN_WARNING "No more available SQU channels for %s\n", name); + i = -ENODEV; + } + + local_irq_restore(flags); + return i; +} + +void pxa910_free_squ (int squ_ch) +{ + unsigned long flags; + + if (!squ_channels[squ_ch].name) { + printk (KERN_CRIT + "%s: trying to free channel %d which is already freed\n", + __func__, squ_ch); + return; + } + + local_irq_save(flags); + SDCR(squ_ch) = 0; + squ_channels[squ_ch].name = NULL; + local_irq_restore(flags); +} + +static irqreturn_t squ_irq_handler(int irq, void *dev_id) +{ + int i, dint; + + for (i = 0; i < num_squ_channels; i++) { + dint = SDISR(i); + if (dint) { + struct squ_channel *channel = &squ_channels[i]; + if (channel->name && channel->irq_handler) { + channel->irq_handler(i, channel->data); + SDISR(i)=0; + } else { + /* + * IRQ for an unregistered SQU channel: + * let's clear the interrupts and disable it. + */ + printk (KERN_WARNING "spurious IRQ for SQU channel %d\n", i); + SDCR(i) = 0; + } + } + } + return IRQ_HANDLED; +} + +int __init pxa910_init_squ(int num_ch) +{ + int i, ret; + + squ_channels = kzalloc(sizeof(struct squ_channel) * num_ch, GFP_KERNEL); + if (squ_channels == NULL) + return -ENOMEM; + + ret = request_irq(IRQ_PXA168_HIFI_DMA, squ_irq_handler, IRQF_DISABLED, "SQU", NULL); + if (ret) { + printk (KERN_CRIT "Wow! Can't register IRQ for SQU\n"); + kfree(squ_channels); + return ret; + } + + for (i = 0; i < num_ch; i++) + squ_channels[i].prio = min((i & 0xf) >> 2, SQU_PRIO_LOW); + + num_squ_channels = num_ch; + return 0; +} + +EXPORT_SYMBOL(pxa910_request_squ); +EXPORT_SYMBOL(pxa910_free_squ); diff --git a/arch/arm/mach-mmp/pxa910.c b/arch/arm/mach-mmp/pxa910.c index 46f2d69bef3cf0..50c0b2bb072120 100644 --- a/arch/arm/mach-mmp/pxa910.c +++ b/arch/arm/mach-mmp/pxa910.c @@ -13,22 +13,25 @@ #include #include #include +#include -#include #include #include #include +#include #include #include #include #include #include -#include +#include +#include #include "common.h" #include "clock.h" #define MFPR_VIRT_BASE (APB_VIRT_BASE + 0x1e000) +#define FAB_CTRL (AXI_VIRT_BASE + 0x260) static struct mfp_addr_map pxa910_mfp_addr_map[] __initdata = { @@ -78,103 +81,185 @@ static struct mfp_addr_map pxa910_mfp_addr_map[] __initdata = MFP_ADDR_END, }; -#define APMASK(i) (GPIO_REGS_VIRT + BANK_OFF(i) + 0x09c) +struct gc_rate_table { + unsigned long rate; + unsigned int flag; +}; + +static struct gc_rate_table gc500_rates [] = { + /* put highest rate at the top of the table */ + { + .rate = 400000000, + .flag = APMU_GC_PLL2, + }, + { + .rate = 312000000, + .flag = APMU_GC_312M, + }, + { + .rate = 200000000, + .flag = APMU_GC_PLL2_DIV2, + }, + { + .rate = 156000000, + .flag = APMU_GC_156M, + }, +}; + +static void gc500_clk_enable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); + + //__raw_writel(0x1f0f0f, APMU_XD_CLK_RES_CTRL); /*it will hang on old stepping*/ + __raw_writel(tmp | 0x38, clk->clk_rst); + __raw_writel(tmp | 0x238, clk->clk_rst); + udelay(200); /* at least 200 us*/ + __raw_writel(tmp | 0x638, clk->clk_rst); + __raw_writel(tmp | 0x63a, clk->clk_rst); + udelay(100); /* at least 48 cycles)*/ + __raw_writel(tmp | 0x73f, clk->clk_rst); + __raw_writel(0x1f, APMU_GC_PD); +} + +static void gc500_clk_disable(struct clk *clk) +{ + u32 tmp = __raw_readl(clk->clk_rst); -static void __init pxa910_init_gpio(void) + __raw_writel(tmp | 0x63f, clk->clk_rst); + __raw_writel(tmp | 0x23f, clk->clk_rst); + __raw_writel(tmp | 0x03f, clk->clk_rst); +} + +static int gc_lookaround_rate(unsigned long target_rate, u32 *flag) { int i; - /* enable GPIO clock */ - __raw_writel(APBC_APBCLK | APBC_FNCLK, APBC_PXA910_GPIO); + for (i=0; i= gc500_rates[i].rate) + break; + } + if (i==ARRAY_SIZE(gc500_rates)) i--; + *flag = gc500_rates[i].flag; + return gc500_rates[i].rate; +} - /* unmask GPIO edge detection for all 4 banks - APMASKx */ - for (i = 0; i < 4; i++) - __raw_writel(0xffffffff, APMASK(i)); +static int gc500_clk_setrate(struct clk *clk, unsigned long target_rate) +{ + u32 tmp, flag; + int rate = gc_lookaround_rate(target_rate, &flag); - pxa_init_gpio(IRQ_PXA910_AP_GPIO, 0, 127, NULL); + clk->rate = rate; + __raw_writel(0xf, APMU_GC_PD); + tmp = __raw_readl(clk->clk_rst); + tmp &= ~0xc0; + tmp |= flag; + __raw_writel(tmp, clk->clk_rst); + return 0; } -void __init pxa910_init_irq(void) +static unsigned long gc500_clk_getrate(struct clk *clk) { - icu_init_irq(); - pxa910_init_gpio(); + return clk->rate; } -/* APB peripheral clocks */ -static APBC_CLK(uart1, PXA910_UART0, 1, 14745600); -static APBC_CLK(uart2, PXA910_UART1, 1, 14745600); -static APBC_CLK(twsi0, PXA168_TWSI0, 1, 33000000); -static APBC_CLK(twsi1, PXA168_TWSI1, 1, 33000000); -static APBC_CLK(pwm1, PXA910_PWM1, 1, 13000000); -static APBC_CLK(pwm2, PXA910_PWM2, 1, 13000000); -static APBC_CLK(pwm3, PXA910_PWM3, 1, 13000000); -static APBC_CLK(pwm4, PXA910_PWM4, 1, 13000000); +struct clkops gc500_clk_ops = { + .enable = gc500_clk_enable, + .disable = gc500_clk_disable, + .setrate = gc500_clk_setrate, + .getrate = gc500_clk_getrate, +}; + +static APBC_UART_CLK(uart1, PXA168_UART0, 14745600); +static APBC_UART_CLK(uart2, PXA168_UART1, 14745600); +static APBC_UART_CLK(pxa910_uart3, PXA910_UART2, 14745600); +static APBC_CLK(twsi0, PXA910_TWSI0, 1, 33000000); +static APBC_CLK(twsi1, PXA910_TWSI1, 1, 33000000); +static APBC_CLK(keypad, PXA168_KPC, 0, 32000); +static APBC_CLK(ssp1, PXA910_SSP1, 0, 0); + +static PSEUDO_CLK(iscclk, 0, 0, 0); /* pseudo clock for imm */ -static APMU_CLK(nand, NAND, 0x01db, 208000000); +static APMU_CLK(lcd, LCD, 0x003f, 312000000); /* 312MHz, HCLK, CLK, AXICLK */ +static APMU_CLK(nand, NAND, 0x1DB, 208000000); +static APMU_CLK_OPS(u2o, USB, 480000000, &u2o_clk_ops); /* 480MHz, AXICLK */ +static APMU_CLK(ire, IRE, 0x8, 0); +static APMU_CLK_OPS(gc, GC, 0, &gc500_clk_ops); +static APMU_CLK(sdh0, SDH0, 0x001b, 48000000); /* 48MHz, CLK, AXICLK */ +static APMU_CLK(sdh1, SDH1, 0x001b, 48000000); /* 48MHz, CLK, AXICLK */ +static APMU_CLK(sdh2, SDH2, 0x001b, 48000000); /* 48MHz, CLK, AXICLK */ +static APMU_CLK(sdh3, SDH3, 0x001b, 48000000); /* 48MHz, CLK, AXICLK */ +static APMU_CLK(ccic_rst, CCIC_RST, 0x0, 312000000); +static APMU_CLK(ccic_gate, CCIC_GATE, 0xfff, 0); -/* device and clock bindings */ static struct clk_lookup pxa910_clkregs[] = { INIT_CLKREG(&clk_uart1, "pxa2xx-uart.0", NULL), INIT_CLKREG(&clk_uart2, "pxa2xx-uart.1", NULL), + INIT_CLKREG(&clk_pxa910_uart3, "pxa2xx-uart.2", NULL), INIT_CLKREG(&clk_twsi0, "pxa2xx-i2c.0", NULL), INIT_CLKREG(&clk_twsi1, "pxa2xx-i2c.1", NULL), - INIT_CLKREG(&clk_pwm1, "pxa910-pwm.0", NULL), - INIT_CLKREG(&clk_pwm2, "pxa910-pwm.1", NULL), - INIT_CLKREG(&clk_pwm3, "pxa910-pwm.2", NULL), - INIT_CLKREG(&clk_pwm4, "pxa910-pwm.3", NULL), - INIT_CLKREG(&clk_nand, "pxa3xx-nand", NULL), + INIT_CLKREG(&clk_lcd, "pxa168-fb", "LCDCLK"), + INIT_CLKREG(&clk_lcd, "pxa910-fb", NULL), + INIT_CLKREG(&clk_nand, "pxa3xx-nand", "NANDCLK"), + INIT_CLKREG(&clk_u2o, NULL, "U2OCLK"), + INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL), + INIT_CLKREG(&clk_ire, "pxa910-ire", NULL), + INIT_CLKREG(&clk_ssp1, "pxa168-ssp.1", NULL), + INIT_CLKREG(&clk_gc, NULL, "GCCLK"), + INIT_CLKREG(&clk_ssp1, "pxa910-ssp.1", NULL), + INIT_CLKREG(&clk_iscclk, NULL, "ISCCLK"), + INIT_CLKREG(&clk_sdh0, "pxa-sdh.0", "PXA-SDHCLK"), + INIT_CLKREG(&clk_sdh1, "pxa-sdh.1", "PXA-SDHCLK"), + INIT_CLKREG(&clk_sdh2, "pxa-sdh.2", "PXA-SDHCLK"), + INIT_CLKREG(&clk_sdh3, "pxa-sdh.3", "PXA-SDHCLK"), + INIT_CLKREG(&clk_ccic_rst, "pxa168-camera", "CCICRSTCLK"), + INIT_CLKREG(&clk_ccic_gate, "pxa168-camera", "CCICGATECLK"), }; -static int __init pxa910_init(void) +void pxa910_set_pll2(void) { - if (cpu_is_pxa910()) { - mfp_init_base(MFPR_VIRT_BASE); - mfp_init_addr(pxa910_mfp_addr_map); - pxa_init_dma(IRQ_PXA910_DMA_INT0, 32); - clkdev_add_table(ARRAY_AND_SIZE(pxa910_clkregs)); - } + u32 tmp = __raw_readl(MPMU_PLL2CR); - return 0; + /* FIXME default set to 400MHz */ + tmp &= ~((0x1f<<19) | (0x1ff<<10)); + tmp |= ((4<<19) | (61<<10)); + tmp |= (1<<9); + + __raw_writel(tmp, MPMU_PLL2CR); } -postcore_initcall(pxa910_init); -/* system timer - clock enabled, 3.25MHz */ -#define TIMER_CLK_RST (APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3)) +/* ACIPC clock is initialized by CP, enable the clock by default + * and this clock is always enabled. + */ +static void pxa910_init_acipc_clock(void) +{ + __raw_writel(0x3, APBC_PXA910_ACIPC); +} +static void pxa910_init_ripc_clock(void) +{ + __raw_writel(0x2, APBC_PXA910_RIPC); +} -static void __init pxa910_timer_init(void) +static int __init pxa910_init(void) { - /* reset and configure */ - __raw_writel(APBC_APBCLK | APBC_RST, APBC_PXA910_TIMERS); - __raw_writel(TIMER_CLK_RST, APBC_PXA910_TIMERS); + u32 tmp; - timer_init(IRQ_PXA910_AP1_TIMER1); -} + if (cpu_is_pxa910()) { + mfp_init_base(MFPR_VIRT_BASE); + mfp_init_addr(pxa910_mfp_addr_map); + pxa_init_dma(IRQ_PXA168_DMA_INT0, 32); + pxa910_init_squ(2); -struct sys_timer pxa910_timer = { - .init = pxa910_timer_init, -}; + /* FIXME set PLL2 here temporarily */ + pxa910_set_pll2(); + pxa910_init_acipc_clock(); + pxa910_init_ripc_clock(); + /* Enable AXI write request for gc500 */ + tmp = __raw_readl(FAB_CTRL); + __raw_writel(tmp | 0x8, FAB_CTRL); -/* on-chip devices */ + clks_register(ARRAY_AND_SIZE(pxa910_clkregs)); + } -/* NOTE: there are totally 3 UARTs on PXA910: - * - * UART1 - Slow UART (can be used both by AP and CP) - * UART2/3 - Fast UART - * - * To be backward compatible with the legacy FFUART/BTUART/STUART sequence, - * they are re-ordered as: - * - * pxa910_device_uart1 - UART2 as FFUART - * pxa910_device_uart2 - UART3 as BTUART - * - * UART1 is not used by AP for the moment. - */ -PXA910_DEVICE(uart1, "pxa2xx-uart", 0, UART2, 0xd4017000, 0x30, 21, 22); -PXA910_DEVICE(uart2, "pxa2xx-uart", 1, UART3, 0xd4018000, 0x30, 23, 24); -PXA910_DEVICE(twsi0, "pxa2xx-i2c", 0, TWSI0, 0xd4011000, 0x28); -PXA910_DEVICE(twsi1, "pxa2xx-i2c", 1, TWSI1, 0xd4025000, 0x28); -PXA910_DEVICE(pwm1, "pxa910-pwm", 0, NONE, 0xd401a000, 0x10); -PXA910_DEVICE(pwm2, "pxa910-pwm", 1, NONE, 0xd401a400, 0x10); -PXA910_DEVICE(pwm3, "pxa910-pwm", 2, NONE, 0xd401a800, 0x10); -PXA910_DEVICE(pwm4, "pxa910-pwm", 3, NONE, 0xd401ac00, 0x10); -PXA910_DEVICE(nand, "pxa3xx-nand", -1, NAND, 0xd4283000, 0x80, 97, 99); + return 0; +} +postcore_initcall(pxa910_init); diff --git a/arch/arm/mach-mmp/pxa910_dvfm.c b/arch/arm/mach-mmp/pxa910_dvfm.c new file mode 100644 index 00000000000000..5b2cf4701995ee --- /dev/null +++ b/arch/arm/mach-mmp/pxa910_dvfm.c @@ -0,0 +1,953 @@ +/* + * PXA910 DVFM Driver + * + * Copyright (C) 2008 Marvell Corporation + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define CONFIG_PXA910_DVFM_ASYNC_MODE 1 +#define CONFIG_PXA910_AP_ALONE_MODE 1 + +struct pxa910_dvfm_info { + uint32_t cpuid; + unsigned char __iomem *pmum_base; + unsigned char __iomem *pmua_base; +}; + +static struct info_head pxa910_dvfm_op_list = { + .list = LIST_HEAD_INIT(pxa910_dvfm_op_list.list), + .lock = RW_LOCK_UNLOCKED, +}; + +/* the operating point preferred by policy maker or user */ +static int preferred_op; + +extern unsigned int cur_op; /* current operating point */ +extern unsigned int def_op; /* default operating point */ + +static int dvfm_dev_id; + +static struct pxa910_md_opt pxa910_op_array[] = { + /* core 26MHz ddr 13MHz bus 26MHz */ + { + .power_mode = POWER_MODE_ACTIVE, + .vcc_core = 1400, + .pclk = 26, + .pdclk = 26, + .baclk = 26, + .xpclk = 26, + .dclk = 13, + .aclk = 26, + .lpj = 26*(500000)/HZ, + .name = "26MHz", + }, + /* core 78MHz ddr 78MHz bus 78MHz */ + { + .power_mode = POWER_MODE_ACTIVE, + .vcc_core = 1400, + .pclk = 78, + .pdclk = 78, + .baclk = 78, + .xpclk = 78, + .dclk = 78, + .aclk = 78, + .lpj = 78*(500000)/HZ, + .name = "78MHz", + }, + /* core 156MHz ddr 104MHz bus 104MHz */ + { + .power_mode = POWER_MODE_ACTIVE, + .vcc_core = 1400, + .pclk = 156, + .pdclk = 104, + .baclk = 104, + .xpclk = 104, + .dclk = 104, + .aclk = 104, + .lpj = 156*500000/HZ, + .name = "156MHz", + }, + /* core 312MHz ddr 156MHz bus 156MHz */ + { + .power_mode = POWER_MODE_ACTIVE, + .vcc_core = 1400, + .pclk = 312, + .pdclk = 156, + .baclk = 156, + .xpclk = 156, + .dclk = 156, + .aclk = 156, + .lpj = 312*500000/HZ, + .name = "312MHz", + }, + /* core 624MHz ddr 156MHz bus 156MHz */ + { + .power_mode = POWER_MODE_ACTIVE, + .vcc_core = 1400, + .pclk = 624, + .pdclk = 156, + .baclk = 156, + .xpclk = 312, + .dclk = 156, + .aclk = 156, + .lpj = 624*500000/HZ, + .name = "624MHz", + }, + /* core internal idle */ + { + .power_mode = POWER_MODE_CORE_INTIDLE, + .vcc_core = 1400, + .name = "core_intidle", + }, + /* core external idle */ + { + .power_mode = POWER_MODE_CORE_EXTIDLE, + .vcc_core = 1400, + .name = "core_extidle", + }, + /* application subsystem idle */ + { + .power_mode = POWER_MODE_APPS_IDLE, + .vcc_core = 1400, + .name = "apps_idle", + }, + /* application subsystem sleep */ + { + .power_mode = POWER_MODE_APPS_SLEEP, + .vcc_core = 1400, + .name = "apps_sleep", + }, + /* system sleep */ + { + .power_mode = POWER_MODE_SYS_SLEEP, + .vcc_core = 1400, + .name = "sys_sleep", + }, +}; + +struct proc_op_array { + unsigned int cpuid; + char *cpu_name; + struct pxa910_md_opt *op_array; + unsigned int nr_op; +}; + +#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) +static struct proc_op_array proc_op_arrays[] = { + {0x8000, "PXA910", ARRAY_AND_SIZE(pxa910_op_array)}, +}; + +/* #####################Debug Function######################## */ +static int dump_op(void *driver_data, struct op_info *p, char *buf) +{ + int len, count, x, i, max, sum; + struct pxa910_md_opt *q = (struct pxa910_md_opt *)p->op; + + if (q == NULL) + len = sprintf(buf, "Can't dump the op info\n"); + else { + /* calculate how much bits is set in device word */ + max = DVFM_MAX_CLIENT >> 5; + for (i = 0, sum = 0; i < max; i++) { + x = p->device[i]; + for (count = 0; x; x = x & (x - 1), count++); + sum += count; + } + len = sprintf(buf, "OP:%d name:%s [%s, %d]\n", + p->index, q->name, (sum)?"Disabled" + :"Enabled", sum); + len += sprintf(buf + len, "pclk:%d pdclk:%d baclk:%d xpclk:%d " + "dclk:%d aclk:%d vcc_core:%d\n", + q->pclk, q->pdclk, q->baclk, q->xpclk, + q->dclk, q->aclk, q->vcc_core); + } + return len; +} + +static int dump_op_list(void *driver_data, struct info_head *op_table) +{ + struct op_info *p = NULL; + struct list_head *list = NULL; + struct pxa910_dvfm_info *info = driver_data; + char buf[256]; + + if (!op_table || list_empty(&op_table->list)) { + printk(KERN_WARNING "op list is null\n"); + return -EINVAL; + } + memset(buf, 0, 256); + list_for_each(list, &op_table->list) { + p = list_entry(list, struct op_info, list); + dump_op(info, p, buf); + } + return 0; +} + +/* Get current setting, and record it in fv_info structure + */ +static int capture_op_info(void *driver_data, struct pxa910_md_opt *fv_info) +{ + struct pxa910_dvfm_info *info = driver_data; + int res = -EFAULT; + uint32_t fccr, core2, pll_ap = 0, pll_da = 0, temp; + + if (fv_info) { + memset(fv_info, 0, sizeof(struct pxa910_md_opt)); + temp = readl(info->pmua_base + CC_MOH_OFF); + temp &= ~PMUA_CC_MOH_MOH_RD_ST_CLEAR; + writel(temp, info->pmua_base + CC_MOH_OFF); + core2 = readl(info->pmua_base + DM_CC_MOH_OFF); + while (core2 & PMUA_DM_CC_MOH_SEA_RD_STATUS) + core2 = readl(info->pmua_base + DM_CC_MOH_OFF); + fccr = readl(info->pmum_base + FCCR_OFF); + temp = (fccr & PMUM_FCCR_MOHCLKSEL_MSK) >> PMUM_FCCR_MOHCLKSEL_BASE; + if (temp == 0x0) + pll_ap = 312; + else if (temp == 0x1) + pll_ap = 624; + else if (temp == 0x3) + pll_ap = 26; + temp = (fccr & PMUM_FCCR_AXICLKSEL_MSK) >> PMUM_FCCR_AXICLKSEL_BASE; + if (temp == 0x0) + pll_da = 312; + else if (temp == 0x1) + pll_da = 624; + else if (temp == 0x3) + pll_da = 26; + fv_info->pclk = pll_ap/(((core2 & PMUA_DM_CC_MOH_CORE_CLK_DIV_MSK) + >> PMUA_DM_CC_MOH_CORE_CLK_DIV_BASE) + 1); + fv_info->pdclk = pll_ap/(((core2 & PMUA_DM_CC_MOH_BUS_MC_CLK_DIV_MSK) + >> PMUA_DM_CC_MOH_BUS_MC_CLK_DIV_BASE) + 1); + fv_info->baclk = pll_ap/(((core2 & PMUA_DM_CC_MOH_BIU_CLK_DIV_MSK) + >> PMUA_DM_CC_MOH_BIU_CLK_DIV_BASE) + 1); + fv_info->xpclk = pll_ap/(((core2 & PMUA_DM_CC_MOH_XP_CLK_DIV_MSK) + >> PMUA_DM_CC_MOH_XP_CLK_DIV_BASE) + 1); + fv_info->dclk = pll_da/(((core2 & PMUA_DM_CC_MOH_DDR_CLK_DIV_MSK) + >> PMUA_DM_CC_MOH_DDR_CLK_DIV_BASE) + 1); + fv_info->dclk /= 2; + fv_info->aclk = pll_da/(((core2 & PMUA_DM_CC_MOH_BUS_CLK_DIV_MSK) + >> PMUA_DM_CC_MOH_BUS_CLK_DIV_BASE) + 1); + pxa3xx_pmic_get_voltage(VCC_CORE, &fv_info->vcc_core); + temp |= PMUA_CC_MOH_MOH_RD_ST_CLEAR; + writel(temp, info->pmua_base + CC_MOH_OFF); + return 0; + } + return res; +} + +static int get_op_num(void *driver_data, struct info_head *op_table) +{ + struct list_head *entry = NULL; + int num = 0; + + if (!op_table) + goto out; + read_lock(&op_table->lock); + if (list_empty(&op_table->list)) { + read_unlock(&op_table->lock); + goto out; + } + list_for_each(entry, &op_table->list) { + num++; + } + read_unlock(&op_table->lock); +out: + return num; +} + +static char *get_op_name(void *driver_data, struct op_info *p) +{ + struct pxa910_md_opt *q = NULL; + if (p == NULL) + return NULL; + q = (struct pxa910_md_opt *)p->op; + return q->name; +} + +static void update_voltage(void *driver_data, struct pxa910_md_opt *old, + struct pxa910_md_opt *new) +{ + pxa3xx_pmic_set_voltage(VCC_CORE, new->vcc_core); +} + +static void PMUcore2_fc_seq(void *driver_data, uint32_t cp_pdiv, uint32_t cp_bdiv, + uint32_t cp_mdiv, uint32_t cp_xpdiv, uint32_t d_div, uint32_t a_div) +{ + struct pxa910_dvfm_info *info = driver_data; + uint32_t cc_reg = 0, temp; + uint32_t fccr, rdata_core1, rdata_core2; + uint32_t pll_da = 0, pll_ap = 0, pll_cp = 0; + + temp = readl(info->pmua_base + CC_MOH_OFF); + temp &= ~PMUA_CC_MOH_MOH_RD_ST_CLEAR; + writel(temp, info->pmua_base + CC_MOH_OFF); + + /*Read dummy before sending the command*/ + rdata_core2 = readl(info->pmua_base + DM_CC_MOH_OFF); + while (rdata_core2 & PMUA_DM_CC_MOH_SEA_RD_STATUS) + rdata_core2 = readl(info->pmua_base + DM_CC_MOH_OFF); + + rdata_core1 = readl(info->pmua_base + DM_CC_SEA_OFF); + + if (cp_pdiv == 26) + writel(0x61800000, info->pmum_base + FCCR_OFF); + else + writel(0x20800000, info->pmum_base + FCCR_OFF); + + fccr = readl(info->pmum_base + FCCR_OFF); + + temp = (fccr & PMUM_FCCR_SEAGCLKSEL_MSK) >> PMUM_FCCR_SEAGCLKSEL_BASE; + if (temp == 0x0) + pll_cp = 312; + else if (temp == 0x1) + pll_cp = 624; + else if (temp == 0x3) + pll_cp = 26; + + temp = (fccr & PMUM_FCCR_MOHCLKSEL_MSK) >> PMUM_FCCR_MOHCLKSEL_BASE; + if (temp == 0x0) + pll_ap = 312; + else if (temp == 0x1) + pll_ap = 624; + else if (temp == 0x3) + pll_ap = 26; + + temp = (fccr & PMUM_FCCR_AXICLKSEL_MSK) >> PMUM_FCCR_AXICLKSEL_BASE; + if (temp == 0x0) + pll_da = 312; + else if (temp == 0x1) + pll_da = 624; + else if (temp == 0x3) + pll_da = 26; + + /* pclk divider */ + if (cp_pdiv != 0) + cc_reg |= (((pll_ap / cp_pdiv) - 1) + << PMUA_CC_MOH_CORE_CLK_DIV_BASE); + + /* pdclock divider */ + if (cp_mdiv != 0) + cc_reg |= ((((pll_ap) / cp_bdiv) - 1) + << PMUA_CC_MOH_BIU_CLK_DIV_BASE); + + /* xp clock divider */ + if (cp_xpdiv != 0) + cc_reg |= ((((pll_ap) / cp_xpdiv) - 1) + << PMUA_CC_MOH_XP_CLK_DIV_BASE); + + /* bus clock divider */ + if (cp_bdiv != 0) + cc_reg |= ((((pll_ap) / cp_mdiv) - 1) + << PMUA_CC_MOH_BUS_MC_CLK_DIV_BASE); + + /* D clock divider */ + if (d_div != 0) + cc_reg |= (((pll_da / d_div) - 1) + << PMUA_CC_MOH_DDR_CLK_DIV_BASE); + + /* A clock divider : fabric clock */ + if (a_div != 0) + cc_reg |= (((pll_da / a_div) - 1) + << PMUA_CC_MOH_BUS_CLK_DIV_BASE); + +#ifndef CONFIG_PXA910_DVFM_ASYNC_MODE + { + uint32_t temp_ap, temp_cp, temp_a, temp_d; + /* Async 2 Check : pdclock2 - DDRClk */ + temp_ap = ((((cc_reg & PMUA_CC_MOH_BUS_MC_CLK_DIV_MSK) + >> PMUA_CC_MOH_BUS_MC_CLK_DIV_BASE) + 1)); + temp_cp = (((cc_reg & PMUA_CC_MOH_DDR_CLK_DIV_MSK) + >> PMUA_CC_MOH_DDR_CLK_DIV_BASE) + 1) ; + /* ddr clk has an extra div by 2 */ + if ((pll_da/(temp_cp*2)) != (pll_ap/temp_ap)) + cc_reg |= PMUA_CC_MOH_ASYNC2; + else + cc_reg &= ~PMUA_CC_MOH_ASYNC2; + + /* Async 5 Check : baclk2-Aclk */ + temp_ap = ((((cc_reg & PMUA_CC_MOH_BIU_CLK_DIV_MSK) + >>PMUA_CC_MOH_BIU_CLK_DIV_BASE) + 1)); + temp_cp = ((cc_reg & PMUA_CC_MOH_BUS_CLK_DIV_MSK) + >> PMUA_CC_MOH_BUS_CLK_DIV_BASE) + 1; + if ((pll_da/temp_cp) != (pll_ap/temp_ap)) + cc_reg |= PMUA_CC_MOH_ASYNC5; + else + cc_reg &= ~PMUA_CC_MOH_ASYNC5; + + /* async 3 Aclk - DDR Clock */ + temp_a = (((cc_reg & PMUA_CC_MOH_BUS_CLK_DIV_MSK) + >>PMUA_CC_MOH_BUS_CLK_DIV_BASE) + 1); + temp_d = (((cc_reg & PMUA_CC_MOH_DDR_CLK_DIV_MSK) + >>PMUA_CC_MOH_DDR_CLK_DIV_BASE) + 1); + if ((temp_d*2) != temp_a) { + cc_reg |= PMUA_CC_MOH_ASYNC3; + cc_reg |= PMUA_CC_MOH_ASYNC3_1; + } else { + cc_reg &= (~PMUA_CC_MOH_ASYNC3_1); + cc_reg &= (~PMUA_CC_MOH_ASYNC3); + } + + /* Async 1 Check : pdclock1 - DDRClk */ + temp_ap = ((((rdata_core1 & PMUA_CC_SEA_BUS_MC_CLK_DIV_MSK) + >>PMUA_CC_SEA_BUS_MC_CLK_DIV_BASE) + 1)); + temp_cp = (((cc_reg & PMUA_CC_MOH_DDR_CLK_DIV_MSK) + >>PMUA_CC_MOH_DDR_CLK_DIV_BASE) + 1); + /* ddr clk has an extra div by 2 */ + if ((pll_da/(temp_cp*2)) != (pll_ap/temp_ap)) + cc_reg |= PMUA_CC_MOH_ASYNC1; + else + cc_reg &= ~PMUA_CC_MOH_ASYNC1; + + /* Async 4 Check : baclk1-Aclk */ + temp_ap = ((((rdata_core1 & PMUA_CC_SEA_BIU_CLK_DIV_MSK) + >> PMUA_CC_SEA_BIU_CLK_DIV_BASE) + 1)); + temp_cp = ((cc_reg & PMUA_CC_SEA_BUS_CLK_DIV_MSK) + >> PMUA_CC_SEA_BUS_CLK_DIV_BASE) + 1 ; + if ((pll_da/temp_cp) != (pll_ap/temp_ap)) + cc_reg |= PMUA_CC_MOH_ASYNC4; + else + cc_reg &= ~PMUA_CC_MOH_ASYNC4; + } +#else + cc_reg |= (PMUA_CC_MOH_ASYNC4 | PMUA_CC_MOH_ASYNC1 | + PMUA_CC_MOH_ASYNC3 | PMUA_CC_MOH_ASYNC3_1 | + PMUA_CC_MOH_ASYNC5 | PMUA_CC_MOH_ASYNC2); +#endif + + writel(cc_reg, info->pmua_base + CC_MOH_OFF); +} + +static void core2freqchgcmd(void *driver_data, int pclk, int dclk, int aclk) +{ + struct pxa910_dvfm_info *info = driver_data; + volatile unsigned long freqchg,coremsk; + + coremsk = readl(info->pmua_base + MOH_IMR_OFF); + coremsk |= (PMUA_MOH_IMR_MOH_FC_INTR_MASK); + writel(coremsk, info->pmua_base + MOH_IMR_OFF); + + freqchg = readl(info->pmua_base + CC_MOH_OFF); + + freqchg &= ~(PMUA_CC_MOH_MOH_ALLOW_SPD_CHG | + PMUA_CC_MOH_BUS_FREQ_CHG_REQ | + PMUA_CC_MOH_DDR_FREQ_CHG_REQ | + PMUA_CC_MOH_MOH_FREQ_CHG_REQ); + +#ifdef CONFIG_PXA910_AP_ALONE_MODE + writel(readl(info->pmua_base + CC_SEA_OFF) | + PMUA_CC_SEA_SEA_ALLOW_SPD_CHG, + info->pmua_base + CC_SEA_OFF); +#else + if (!(readl(info->pmua_base + CC_SEA_OFF) & + PMUA_CC_SEA_SEA_ALLOW_SPD_CHG)) + return; +#endif + + if ( pclk || dclk || aclk ) + freqchg |= (PMUA_CC_MOH_MOH_ALLOW_SPD_CHG); + if ( aclk ) + freqchg |= (PMUA_CC_SEA_BUS_FREQ_CHG_REQ); + if ( dclk ) + freqchg |= (PMUA_CC_SEA_DDR_FREQ_CHG_REQ); + if ( pclk ) + freqchg |= (PMUA_CC_MOH_MOH_FREQ_CHG_REQ); + + writel(freqchg, info->pmua_base + CC_MOH_OFF); + + /* Check 4 the cmd 2 go thru */ + while (!(PMUA_MOH_ISR_MOH_FC_ISR & + readl(info->pmua_base + MOH_ISR_OFF))) + ; + + /* Clear the PMU ISR */ + writel(0x0, info->pmua_base + MOH_ISR_OFF); + + freqchg |= PMUA_CC_MOH_MOH_RD_ST_CLEAR; + + /* Clear the bits */ + freqchg &= ~( PMUA_CC_MOH_MOH_ALLOW_SPD_CHG | + PMUA_CC_MOH_BUS_FREQ_CHG_REQ | + PMUA_CC_MOH_DDR_FREQ_CHG_REQ | + PMUA_CC_MOH_MOH_FREQ_CHG_REQ); + + /* clear the cmds bit */ + writel(freqchg, info->pmua_base + CC_MOH_OFF); +} + +static int set_freq(void *driver_data, struct pxa910_md_opt *old, + struct pxa910_md_opt *new) +{ + int core = 0, ddr = 0, bus = 0; + + if (new->pclk != old->pclk) + core = 1; + if (new->dclk != old->dclk) + ddr = 1; + if (new->aclk != old->aclk) + bus = 1; + if (!core && !ddr && !bus) + return 0; + if (new->vcc_core > old->vcc_core) + update_voltage(driver_data, old, new); + PMUcore2_fc_seq(driver_data, new->pclk, new->baclk, new->pdclk, + new->xpclk, new->dclk * 2, new->aclk); + core2freqchgcmd(driver_data, core, ddr, bus); + if (new->vcc_core < old->vcc_core) + update_voltage(driver_data, old, new); + return 0; +} + +static int update_freq(void *driver_data, struct dvfm_freqs *freqs) +{ + struct pxa910_dvfm_info *info = driver_data; + struct pxa910_md_opt *old = NULL, *new = NULL; + struct op_info *p = NULL; + unsigned long flags; + int found = 0, new_op = cur_op; + + write_lock_irqsave(&pxa910_dvfm_op_list.lock, flags); + if (!list_empty(&pxa910_dvfm_op_list.list)) { + list_for_each_entry(p, &pxa910_dvfm_op_list.list, list) { + if (p->index == freqs->old) { + found++; + old = (struct pxa910_md_opt *)p->op; + } + if (p->index == freqs->new) { + found++; + new = (struct pxa910_md_opt *)p->op; + new_op = p->index; + } + if (found == 2) + break; + } + } + write_unlock_irqrestore(&pxa910_dvfm_op_list.lock, flags); + if (found != 2) + return -EINVAL; + + set_freq(info, old, new); + cur_op = new_op; + loops_per_jiffy = new->lpj; + return 0; +} + +static void do_freq_notify(void *driver_data, struct dvfm_freqs *freqs) +{ + struct pxa910_dvfm_info *info = driver_data; + + dvfm_notifier_frequency(freqs, DVFM_FREQ_PRECHANGE); + update_freq(info, freqs); + dvfm_notifier_frequency(freqs, DVFM_FREQ_POSTCHANGE); +} + +static void do_lowpower_notify(void *driver_data, struct dvfm_freqs *freqs, + unsigned int state) +{ + dvfm_notifier_frequency(freqs, DVFM_FREQ_PRECHANGE); + pxa910_pm_enter_lowpower_mode(state); + dvfm_notifier_frequency(freqs, DVFM_FREQ_POSTCHANGE); +} + +/* Check whether any client blocks the current operating point */ +static int block_client(struct op_info *info) +{ + int i; + unsigned int ret = 0; + for (i = 0; i < (DVFM_MAX_CLIENT >> 5); i++) + ret |= info->device[i]; + return (int)ret; +} + +static int check_op(void *driver_data, struct dvfm_freqs *freqs, + unsigned int new, unsigned int relation) +{ + struct op_info *p = NULL; + int index, tmp_index = -1, found = 0; + int first_op = 0; + + freqs->new = -1; + if (!dvfm_find_op(new, &p)) { + index = p->index; + } else + return -EINVAL; + + read_lock(&pxa910_dvfm_op_list.lock); + if (relation == RELATION_LOW) { + /* Set the lowest usable op that is higher than specifed one */ + /* Note: we assume bigger index number is more 'usable' */ + list_for_each_entry(p, &pxa910_dvfm_op_list.list, list) { + if (!block_client(p) && (p->index >= index)) { + if (tmp_index == -1 || (tmp_index >= p->index)) { + if (first_op == 0) + first_op = p->index; + freqs->new = p->index; + tmp_index = p->index; + found = 1; + } + if (found && (new == p->index)) + break; + } + } + if (found && (first_op == 1) && (new != p->index)) + freqs->new = first_op; + } else if (relation == RELATION_HIGH) { + /* Set the highest usable op that is lower than specified one */ + list_for_each_entry(p, &pxa910_dvfm_op_list.list, list) { + if (!block_client(p) && (p->index <= index)) { + if (tmp_index == -1 || tmp_index < p->index) { + freqs->new = p->index; + tmp_index = p->index; + } + } + } + } else if (relation == RELATION_STICK) { + /* Set the specified frequency */ + list_for_each_entry(p, &pxa910_dvfm_op_list.list, list) { + if (!block_client(p) && (p->index == new)) { + freqs->new = p->index; + break; + } + } + } + read_unlock(&pxa910_dvfm_op_list.lock); + if (freqs->new == -1) + return -EINVAL; + return 0; +} + +static int pxa910_set_op(void *driver_data, struct dvfm_freqs *freqs, + unsigned int new, unsigned int relation) +{ + struct pxa910_dvfm_info *info = driver_data; + struct pxa910_md_opt *md = NULL; + struct op_info *p = NULL; + unsigned long flags; + int ret; + + local_fiq_disable(); + local_irq_save(flags); + + ret = dvfm_find_op(freqs->old, &p); + if (ret) + goto out; + + memcpy(&freqs->old_info, p, sizeof(struct op_info)); + ret = check_op(info, freqs, new, relation); + if (ret) + goto out; + + if (!dvfm_find_op(freqs->new, &p)) { + memcpy(&(freqs->new_info), p, sizeof(struct op_info)); + /* If find old op and new op is same, skip it. + * At here, ret should be zero. + */ + if (freqs->old_info.index == freqs->new_info.index) + goto out; + md = (struct pxa910_md_opt *)p->op; + switch (md->power_mode) { + case POWER_MODE_ACTIVE: + do_freq_notify(info, freqs); + break; + case POWER_MODE_CORE_INTIDLE: + case POWER_MODE_CORE_EXTIDLE: + case POWER_MODE_APPS_IDLE: + case POWER_MODE_APPS_SLEEP: + case POWER_MODE_SYS_SLEEP: + do_lowpower_notify(info, freqs, md->power_mode); + break; + } + } + + local_irq_restore(flags); + local_fiq_enable(); + return 0; +out: + local_irq_restore(flags); + local_fiq_enable(); + return ret; +} + +static int pxa910_request_op(void *driver_data, int index) +{ + struct dvfm_freqs freqs; + struct op_info *info = NULL; + struct pxa910_md_opt *md = NULL; + int relation, ret; + + ret = dvfm_find_op(index, &info); + if (ret) + goto out; + freqs.old = cur_op; + freqs.new = index; + md = (struct pxa910_md_opt *)(info->op); + relation = RELATION_LOW; + ret = pxa910_set_op(driver_data, &freqs, index, relation); + if (!ret) + preferred_op = index; +out: + return ret; +} + +/* + * The machine operation of dvfm_enable + */ +static int pxa910_enable_dvfm(void *driver_data, int dev_id) +{ + struct pxa910_dvfm_info *info = driver_data; + struct pxa910_md_opt *md = NULL; + struct op_info *p = NULL; + int i, num; + num = get_op_num(info, &pxa910_dvfm_op_list); + for (i = 0; i < num; i++) { + if (!dvfm_find_op(i, &p)) { + md = (struct pxa910_md_opt *)p->op; + dvfm_enable_op(i, dev_id); + } + } + return 0; +} + +/* + * The mach operation of dvfm_disable + */ +static int pxa910_disable_dvfm(void *driver_data, int dev_id) +{ + struct pxa910_dvfm_info *info = driver_data; + struct pxa910_md_opt *md = NULL; + struct op_info *p = NULL; + int i, num; + num = get_op_num(info, &pxa910_dvfm_op_list); + for (i = 0; i < num; i++) { + if (!dvfm_find_op(i, &p)) { + md = (struct pxa910_md_opt *)p->op; + dvfm_disable_op(i, dev_id); + } + } + return 0; +} + +static int pxa910_enable_op(void *driver_data, int index, int relation) +{ + /* + * Restore preferred_op. Because this op is sugguested by policy maker + * or user. + */ + return pxa910_request_op(driver_data, preferred_op); +} + +static int pxa910_disable_op(void *driver_data, int index, int relation) +{ + struct dvfm_freqs freqs; + if (cur_op == index) { + freqs.old = index; + freqs.new = -1; + dvfm_set_op(&freqs, freqs.old, relation); + } + return 0; +} + +static struct dvfm_driver pxa910_driver = { + .count = get_op_num, + .set = pxa910_set_op, + .dump = dump_op, + .name = get_op_name, + .request_set = pxa910_request_op, + .enable_dvfm = pxa910_enable_dvfm, + .disable_dvfm = pxa910_disable_dvfm, + .enable_op = pxa910_enable_op, + .disable_op = pxa910_disable_op, +}; + +/* Produce a operating point table */ +static int op_init(struct pxa910_dvfm_info *driver_data, struct info_head *op_table) +{ + struct pxa910_dvfm_info *info = driver_data; + struct pxa910_md_opt *md, *smd; + unsigned long flags; + int i, index; + struct op_info *p = NULL, *q = NULL; + struct proc_op_array *proc = NULL; + + proc = proc_op_arrays; + printk("initializing op table for %s\n", proc->cpu_name); + for (i = 0, index = 0; i < proc->nr_op; i++) { + /* Set index of operating point used in idle */ + if (proc->op_array[i].power_mode != POWER_MODE_ACTIVE) + set_idle_op(index, proc->op_array[i].power_mode); + + if (!(p = (struct op_info *)kzalloc(sizeof(struct op_info), + GFP_KERNEL))) + return -ENOMEM; + if (!(p->op = (struct pxa910_md_opt *)kzalloc(sizeof(struct pxa910_md_opt), + GFP_KERNEL))) + return -ENOMEM; + memcpy(p->op, &proc->op_array[i], sizeof(struct pxa910_md_opt)); + p->index = index++; + list_add_tail(&(p->list), &(op_table->list)); + } + + if (!(p = (struct op_info *)kzalloc(sizeof(struct op_info), + GFP_KERNEL))) + return -ENOMEM; + if (!(p->op = (struct pxa910_md_opt *)kzalloc(sizeof(struct pxa910_md_opt), + GFP_KERNEL))) + return -ENOMEM; + md = (struct pxa910_md_opt *)p->op; + if (capture_op_info(info, md)) { + printk(KERN_WARNING "Failed to get current op setting\n"); + } else { + def_op = 0x5a5a; /* magic number */ + list_for_each_entry(q, &(op_table->list), list) { + smd = (struct pxa910_md_opt *)q->op; + if (md->pclk == smd->pclk && md->pdclk == smd->pdclk && + md->baclk == smd->baclk && md->xpclk == smd->xpclk && + md->dclk == smd->dclk && md->aclk == smd->aclk) { + def_op = q->index; + break; + } + } + } + md->power_mode = POWER_MODE_ACTIVE; + md->lpj = loops_per_jiffy; + sprintf(md->name, "BOOT OP"); + + if (!(q = (struct op_info *)kzalloc(sizeof(struct op_info), + GFP_KERNEL))) + return -ENOMEM; + if (!(q->op = (struct pxa910_md_opt *)kzalloc(sizeof(struct pxa910_md_opt), + GFP_KERNEL))) + return -ENOMEM; + smd = (struct pxa910_md_opt *)q->op; + memcpy(smd, md, sizeof(struct pxa910_md_opt)); + sprintf(smd->name, "CUSTOM OP"); + + /* Add CUSTOM OP into op list */ + q->index = index++; + list_add_tail(&q->list, &op_table->list); + /* Add BOOT OP into op list */ + p->index = index++; + preferred_op = p->index; + list_add_tail(&p->list, &op_table->list); + /* BOOT op */ + if (def_op == 0x5a5a) { + cur_op = p->index; + def_op = p->index; + } else + cur_op = def_op; + pr_debug("%s, def_op:%d, cur_op:%d\n", __FUNCTION__, def_op, cur_op); + + op_nums = proc->nr_op + 2; /* set the operating point number */ + + printk("Current Operating Point is %d\n", cur_op); + dump_op_list(info, op_table); + + return 0; +} + +#ifdef CONFIG_PM +static int pxa910_freq_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int pxa910_freq_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define pxa910_freq_suspend NULL +#define pxa910_freq_resume NULL +#endif + +static int pxa910_freq_probe(struct platform_device *pdev) +{ + struct resource *res; + struct pxa910_dvfm_info *info; + + if (!(info = kzalloc(sizeof(struct pxa910_dvfm_info), GFP_KERNEL))) + goto err; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmum_regs"); + if (!res) goto err; + info->pmum_base = ioremap(res->start, res->end - res->start + 1); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pmua_regs"); + if (!res) goto err; + info->pmua_base = ioremap(res->start, res->end - res->start + 1); + + pxa910_driver.priv = info; + + if (op_init(info, &pxa910_dvfm_op_list)) + goto err; + + if (dvfm_register("PXA910-DVFM", &dvfm_dev_id)) + goto err; + return dvfm_register_driver(&pxa910_driver, &pxa910_dvfm_op_list); +err: + printk("pxa910_dvfm init failed\n"); + return -EIO; +} + +static int pxa910_freq_remove(struct platform_device *pdev) +{ + kfree(pxa910_driver.priv); + dvfm_unregister("PXA910-DVFM", &dvfm_dev_id); + return dvfm_unregister_driver(&pxa910_driver); +} + +static struct platform_driver pxa910_freq_driver = { + .driver = { + .name = "pxa168-freq", + }, + .probe = pxa910_freq_probe, + .remove = pxa910_freq_remove, +#ifdef CONFIG_PM + .suspend = pxa910_freq_suspend, + .resume = pxa910_freq_resume, +#endif +}; + +static int __init pxa910_freq_init(void) +{ + if (!cpu_is_pxa910()) + return -EIO; + + return platform_driver_register(&pxa910_freq_driver); +} + +static void __exit pxa910_freq_exit(void) +{ + platform_driver_unregister(&pxa910_freq_driver); +} + +module_init(pxa910_freq_init); +module_exit(pxa910_freq_exit); diff --git a/arch/arm/mach-mmp/pxa910_mspm_idle.c b/arch/arm/mach-mmp/pxa910_mspm_idle.c new file mode 100644 index 00000000000000..6c710dfe0e049c --- /dev/null +++ b/arch/arm/mach-mmp/pxa910_mspm_idle.c @@ -0,0 +1,339 @@ +/* + * PXA910 MSPM IDLE + * + * Copyright (c) 2003 Intel Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2008 Marvell International Ltd. + * All Rights Reserved + */ + +/* +#undef DEBUG +#define DEBUG +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define DEBUG_IDLE_STATUS + +static int core_intidle_opidx = -1, core_extidle_opidx = -1,\ + apps_idle_opidx = -1, apps_sleep_opidx = -1, sys_sleep_opidx = -1; + +#define LOWPOWER_IDLE_INTERVAL 1 +#define IDLE_STATS_NUM 1000 + +struct idle_stats { + unsigned int index; + unsigned int ticks; +}; + +struct mspm_idle_stats { + struct idle_stats stats[IDLE_STATS_NUM]; + unsigned int stats_index; + spinlock_t lock; +}; + +static struct mspm_idle_stats mspm_stats = { + .stats_index = 0, + .lock = SPIN_LOCK_UNLOCKED, +}; + +static void (*orig_idle)(void); + +static void mspm_cpu_idle(void) +{ + struct op_info *info = NULL; + int op; + uint32_t irq_mask; + + op = dvfm_get_op(&info); + /* set the global interrupt mask in the ICU register to mask + * the SYNC IRQ to the core */ + irq_mask = __raw_readl(ICU_AP_GBL_IRQ_MSK); + irq_mask |= ICU_AP_GBL_IRQ_MSK_IRQ_MSK | \ + ICU_AP_GBL_IRQ_MSK_FIQ_MSK; + __raw_writel(irq_mask, ICU_AP_GBL_IRQ_MSK); + mspm_add_event(op, CPU_STATE_RUN); + cpu_do_idle(); + mspm_add_event(op, CPU_STATE_IDLE); +} + +#ifdef DEBUG_IDLE_STATUS +/* Collect statistic information before entering idle */ +static void record_idle_stats(void) +{ + int i; + + spin_lock(&mspm_stats.lock); + if (++mspm_stats.stats_index == IDLE_STATS_NUM) + mspm_stats.stats_index = 0; + i = mspm_stats.stats_index; + memset(&mspm_stats.stats[i], 0, sizeof(struct idle_stats)); + mspm_stats.stats[i].index = i; + mspm_stats.stats[i].ticks = read_timer(); + spin_unlock(&mspm_stats.lock); +} + +/* Collect statistic information after exiting idle. + */ +static void update_idle_stats(void) +{ + int i; + + spin_lock(&mspm_stats.lock); + i = mspm_stats.stats_index; + mspm_stats.stats[i].ticks = read_timer() + - mspm_stats.stats[i].ticks; + spin_unlock(&mspm_stats.lock); +} +#endif + +void set_idle_op(int idx, int mode) +{ + switch (mode) { + case POWER_MODE_CORE_INTIDLE: + core_intidle_opidx = idx; + break; + case POWER_MODE_CORE_EXTIDLE: + core_extidle_opidx = idx; + break; + case POWER_MODE_APPS_IDLE: + apps_idle_opidx = idx; + break; + case POWER_MODE_APPS_SLEEP: + apps_sleep_opidx = idx; + break; + case POWER_MODE_SYS_SLEEP: + sys_sleep_opidx = idx; + break; + } +} + +static int lpidle_is_valid(int enable, unsigned int msec, + struct dvfm_freqs *freqs, int lp_idle) +{ + struct op_info *info = NULL; + struct pxa910_md_opt *op; + int prev_op; + int ret; + + if ((freqs == NULL) + || (lp_idle == IDLE_CORE_EXTIDLE && core_extidle_opidx == -1) + || (lp_idle == IDLE_APPS_IDLE && apps_idle_opidx == -1) + || (lp_idle == IDLE_APPS_SLEEP && apps_sleep_opidx == -1) + || (lp_idle == IDLE_SYS_SLEEP && sys_sleep_opidx == -1)) + return 0; + if (enable & lp_idle) { + /* Check dynamic tick flag && idle interval */ + if (msec < LOWPOWER_IDLE_INTERVAL) + return 0; + /* Check whether the specified low power mode is valid */ + ret = dvfm_get_op(&info); + if (info == NULL) + return 0; + op = (struct pxa910_md_opt *)info->op; + if (op == NULL) + return 0; + if ((ret >= 0) && (op->power_mode == POWER_MODE_ACTIVE)) { + prev_op = ret; + freqs->old = ret; + freqs->new = + lp_idle == IDLE_CORE_EXTIDLE ? core_extidle_opidx : + lp_idle == IDLE_APPS_IDLE ? apps_idle_opidx : + lp_idle == IDLE_APPS_SLEEP ? apps_sleep_opidx : + lp_idle == IDLE_SYS_SLEEP ? sys_sleep_opidx : + -1; + if (freqs->new < 0) + return 0; + ret = dvfm_get_opinfo(freqs->new, &info); + if ((ret >= 0) && find_first_bit(info->device, + DVFM_MAX_CLIENT) == DVFM_MAX_CLIENT) { + return 1; + } + } + } + return 0; +} + + +/* + * IDLE Thread + */ +unsigned int get_remain_slice(void) +{ + uint32_t val1 = 0; + uint32_t val2 = 0; + + val1 = __raw_readl(TIMERS1_VIRT_BASE + TMR_TN_MM(0, 0)); + val2 = read_timer(); + return (val1-val2)/3250; +} + +static void mspm_do_idle(void) +{ + struct dvfm_freqs freqs; + int ret = -EINVAL; + unsigned int msec; + + local_irq_disable(); + if (!need_resched()) { +#ifdef DEBUG_IDLE_STATUS + record_idle_stats(); +#endif + + msec = get_remain_slice(); + + if (lpidle_is_valid(enable_deepidle, msec, &freqs, + IDLE_SYS_SLEEP)) { + ret = dvfm_set_op(&freqs, freqs.new, + RELATION_STICK); + if (ret == 0) + goto out; + } + + if (lpidle_is_valid(enable_deepidle, msec, &freqs, + IDLE_APPS_SLEEP)) { + ret = dvfm_set_op(&freqs, freqs.new, + RELATION_STICK); + if (ret == 0) + goto out; + } + + if (lpidle_is_valid(enable_deepidle, msec, &freqs, + IDLE_CORE_EXTIDLE)) { + ret = dvfm_set_op(&freqs, freqs.new, + RELATION_STICK); + if (ret == 0) + goto out; + } +out: + mspm_cpu_idle(); +#ifdef DEBUG_IDLE_STATUS + update_idle_stats(); +#endif + } + local_irq_enable(); +} + +static struct proc_dir_entry *entry_dir; +static struct proc_dir_entry *entry_stats; + +static int stats_show(struct seq_file *s, void *v) +{ + struct idle_stats *p = NULL; + int i, ui; + unsigned long flags; + + spin_lock_irqsave(&mspm_stats.lock, flags); + ui = mspm_stats.stats_index; + for (i = 0; i < IDLE_STATS_NUM; i++) { + p = &mspm_stats.stats[ui++]; + seq_printf(s, "index:%u idle_ticks:%u\n", + p->index, p->ticks); + if (ui == IDLE_STATS_NUM) + ui = 0; + } + spin_unlock_irqrestore(&mspm_stats.lock, flags); + return 0; +} + +static int stats_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, &stats_show, NULL); +} + +static struct file_operations stats_seq_ops = { + .owner = THIS_MODULE, + .open = stats_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int mspm_proc_init(void) +{ + entry_dir = proc_mkdir("driver/mspm", NULL); + if (entry_dir == NULL) + return -ENOMEM; + + entry_stats = create_proc_entry("stats", 0, entry_dir); + if (entry_stats) + entry_stats->proc_fops = &stats_seq_ops; + return 0; +} + +static void mspm_proc_cleanup(void) +{ + remove_proc_entry("stats", entry_dir); + remove_proc_entry("driver/mspm", NULL); +} + +void mspm_idle_load(void) +{ + orig_idle = pm_idle; + pm_idle = mspm_do_idle; + /* external idle as default idle */ + pxa910_pm_enter_lowpower_mode(POWER_MODE_CORE_EXTIDLE); +} + +void mspm_idle_clean(void) +{ + pm_idle = orig_idle; + pxa910_pm_enter_lowpower_mode(POWER_MODE_CORE_INTIDLE); +} + +static int __init mspm_init(void) +{ + if (!cpu_is_pxa910()) + return -EFAULT; + + /* Create file in procfs */ + if (mspm_proc_init()) + return -EFAULT; + + mspm_prof_init(); + + pr_info("Initialize PXA910 MSPM\n"); + + return 0; +} + +static void __exit mspm_exit(void) +{ + /* Remove procfs */ + mspm_proc_cleanup(); + + mspm_prof_exit(); + + pr_info("Quit PXA910 MSPM\n"); +} + +module_init(mspm_init); +module_exit(mspm_exit); + +MODULE_DESCRIPTION("PXA910_MSPM"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-mmp/pxa910_mspm_prof.c b/arch/arm/mach-mmp/pxa910_mspm_prof.c new file mode 100644 index 00000000000000..ee9f8799fa3263 --- /dev/null +++ b/arch/arm/mach-mmp/pxa910_mspm_prof.c @@ -0,0 +1,415 @@ +/* + * PXA910 MSPM Profiler + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2008 Marvell International Ltd. + * All Rights Reserved + */ + +/* + * Behavior of profiler + * + * When sample window is finished, profiler calculates the mips in sample + * window. System will be adjusted to suitable OP. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct mspm_op_stats { + int op; + int idle; + unsigned int timestamp; + unsigned int jiffies; +}; + +struct mspm_mips { + int mips; + int h_thres; /* high threshold */ + int l_thres; /* low threshold */ +}; + +enum { + DISABLE = 0, + ENABLE, +}; + + +static int mspm_ctrl = DISABLE, mspm_prof = DISABLE; + +/* Store OP's MIPS in op_mips[]. The lowest frequency OP is the first entry */ +static struct mspm_mips op_mips[MAX_OP_NUM]; + +/* Store duration time of all OP in op_duration[] */ +static int op_duration[MAX_OP_NUM]; + +/* Store costed time in run_op_time[] & idle_op_time[] */ +static int run_op_time[MAX_OP_NUM], idle_op_time[MAX_OP_NUM]; + +/* + * Store the first timestamp of sample window in first_stats + * Store the current timestamp of sample window in cur_stats + */ +static struct mspm_op_stats first_stats, cur_stats; + +/* OP numbers used in IPM IDLE Profiler */ +static int mspm_op_num; + +static struct timer_list idle_prof_timer; +static int mspm_window = DEF_SAMPLE_WINDOW; +static int window_jif; + +/* DVFM notifier and index */ +static int mspm_prof_notifier_freq(struct notifier_block *nb, + unsigned long val, void *data); +static struct notifier_block notifier_freq_block = { + .notifier_call = mspm_prof_notifier_freq, +}; + +static int dvfm_dev_idx; + +/* + * Adjust to the most appropriate OP according to MIPS result of + * sample window + */ +static int mspm_request_tune(int mips) +{ + int i; + + for (i = mspm_op_num - 1; i >= 0; i--) { + if (mips >= (op_mips[i].l_thres * + op_mips[i].mips / 100)) + break; + } + dvfm_request_op(i); + return 0; +} + +/* + * Calculate the MIPS in sample window + */ +static int mspm_calc_mips(unsigned int first_time) +{ + int i, mips, curop; + unsigned int time, sum_time = 0, sum = 0; + struct op_info *info = NULL; + + curop = dvfm_get_op(&info); + + /* Store the last slot as RUN state */ + time = read_timer(); + run_op_time[cur_stats.op] += time - cur_stats.timestamp; + cur_stats.timestamp = time; + cur_stats.jiffies = jiffies; + cur_stats.op = curop; + cur_stats.idle = CPU_STATE_RUN; + /* Calculate total time costed in sample window */ + for (i = 0; i < mspm_op_num; i++) { + sum_time += run_op_time[i] + idle_op_time[i]; + sum += run_op_time[i] * op_mips[i].mips; + op_duration[i] = run_op_time[i] + idle_op_time[i]; + } + if (sum_time == 0) { + /* CPU usage is 100% in current operating point */ + sum_time = time - first_time; + sum = sum_time * op_mips[curop].mips; + op_duration[curop] = sum_time; + } + + /* + * Calculate MIPS in sample window + * Formula: run_op_time[i] / sum_time * op_mips[i].mips + */ + mips = sum / sum_time; + return mspm_request_tune(mips); +} + +/* + * Record the OP index and RUN/IDLE state. + */ +int mspm_add_event(int op, int cpu_idle) +{ + unsigned int time; + + if (mspm_prof == ENABLE) { + time = read_timer(); + /* sum the current sample window */ + if (cpu_idle == CPU_STATE_IDLE) + idle_op_time[cur_stats.op] += time - \ + cur_stats.timestamp; + else if (cpu_idle == CPU_STATE_RUN) + run_op_time[cur_stats.op] += time - cur_stats.timestamp; + /* update start point of current sample window */ + cur_stats.op = op; + cur_stats.idle = cpu_idle; + cur_stats.timestamp = time; + cur_stats.jiffies = jiffies; + } + return 0; +} +EXPORT_SYMBOL(mspm_add_event); + +/* + * Prepare to do a new sample. + * Clear the index in mspm_op_stats table. + */ +static int mspm_do_new_sample(void) +{ + /* clear previous sample window */ + memset(&run_op_time, 0, sizeof(int) * MAX_OP_NUM); + memset(&idle_op_time, 0, sizeof(int) * MAX_OP_NUM); + /* prepare for the new sample window */ + first_stats.op = cur_stats.op; + first_stats.idle = cur_stats.idle; + first_stats.timestamp = read_timer(); + first_stats.jiffies = jiffies; + return 0; +} + +/*************************************************************************** + * Idle Profiler + ***************************************************************************/ + +static int launch_prof(int enable) +{ + struct op_info *info = NULL; + + /* early return if mspm is disabled */ + if (mspm_ctrl == DISABLE) + return 0; + + if (enable) { + window_jif = msecs_to_jiffies(mspm_window); + /* start next sample window */ + cur_stats.op = dvfm_get_op(&info); + cur_stats.idle = CPU_STATE_RUN; + cur_stats.timestamp = read_timer(); + cur_stats.jiffies = jiffies; + mspm_do_new_sample(); + mod_timer(&idle_prof_timer, jiffies + window_jif); + } else { + del_timer(&idle_prof_timer); + } + mspm_prof = enable; + + return 0; +} + +/* + * Handler of IDLE PROFILER + */ +static void idle_prof_handler(unsigned long data) +{ + mspm_calc_mips(first_stats.timestamp); + /* start next sample window */ + mspm_do_new_sample(); + mod_timer(&idle_prof_timer, jiffies + window_jif); +} + +/* + * Pause idle profiler when system enter Low Power mode. + * Continue it when system exit from Low Power mode. + */ +static int mspm_prof_notifier_freq(struct notifier_block *nb, + unsigned long val, void *data) +{ + /* will implement this when low power mode is enabled + * for pxa910, right now just return 0 + */ + return 0; +} + +/* It's invoked by initialization code & sysfs interface */ +static int launch_mspm(int enable) +{ + if (enable) { + /* enable mspm control */ + mspm_idle_load(); + if (mspm_prof == ENABLE) + printk(KERN_ALERT "prof is enabled before mspm\n"); + /* disable unused OP in MSPM */ + dvfm_disable_op_name("CUSTOM OP", dvfm_dev_idx); + dvfm_disable_op_name("BOOT OP", dvfm_dev_idx); + dvfm_disable_op_name("core_intidle", dvfm_dev_idx); + } else { + /* disable mspm control */ + /* check if profiler is launched. If it is, disable it */ + if (mspm_prof == ENABLE) + launch_prof(DISABLE); + mspm_idle_clean(); + /* enable unused OP in MSPM */ + dvfm_enable_op_name("CUSTOM OP", dvfm_dev_idx); + dvfm_enable_op_name("BOOT OP", dvfm_dev_idx); + dvfm_enable_op_name("core_intidle", dvfm_dev_idx); + } + mspm_ctrl = enable; + return 0; +} + +/************************************************************************ + * sysfs interface * + ************************************************************************/ + +#define mspm_attr(_name) \ +static struct kobj_attribute _name##_attr = { \ + .attr = { \ + .name = __stringify(_name), \ + .mode = 0644, \ + }, \ + .show = _name##_show, \ + .store = _name##_store, \ +} + +/* + * Show whether MSPM is enabled + */ +static ssize_t mspm_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", mspm_ctrl); +} + +/* + * Configure MSPM + * When MSPM is enabled, mspm idle is loaded. + */ +static ssize_t mspm_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int data; + sscanf(buf, "%u", &data); + if (data == ENABLE || data == DISABLE) + if (data != mspm_ctrl) + launch_mspm(data); + return len; +} +mspm_attr(mspm); + +/* + * Show whether MSPM profiler in kernel space is enabled + */ +static ssize_t prof_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%u\n", mspm_prof); +} + +/* + * Configure the MSPM profiler in kernel space + * This interface can't control deepidle + */ +static ssize_t prof_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int data; + sscanf(buf, "%u", &data); + if (data == ENABLE || data == DISABLE) + if (data != mspm_prof) + launch_prof(data); + return len; +} +mspm_attr(prof); + +/* Show the length of sample window */ +static ssize_t window_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%ums\n", mspm_window); +} + +static ssize_t window_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + sscanf(buf, "%u", &mspm_window); + return len; +} +mspm_attr(window); + +static struct attribute *g[] = { + &mspm_attr.attr, + &prof_attr.attr, + &window_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .name = "mspm", + .attrs = g, +}; + +/* + * Init MIPS of all OP + * Return OP numbers + */ +int __init mspm_init_mips(void) +{ + struct op_info *info = NULL; + struct pxa910_md_opt *md_op = NULL; + int i, ret; + memset(&op_mips, 0, MAX_OP_NUM * sizeof(struct mspm_mips)); + mspm_op_num = dvfm_op_count(); + for (i = 0; i < mspm_op_num; i++) { + ret = dvfm_get_opinfo(i, &info); + if (ret) + continue; + md_op = (struct pxa910_md_opt *)info->op; + op_mips[i].mips = md_op->pclk; + if (op_mips[i].mips) { + op_mips[i].h_thres = DEF_HIGH_THRESHOLD; + } else { + mspm_op_num = i; + break; + } + } + for (i = 0; i < mspm_op_num - 1; i++) + op_mips[i + 1].l_thres = op_mips[i].h_thres * op_mips[i].mips + / op_mips[i + 1].mips; + return mspm_op_num; +} + +int __init mspm_prof_init(void) +{ + if (sysfs_create_group(power_kobj, &attr_group)) + return -EFAULT; + + /* It's used to trigger sample window. + * If system is idle, the timer could be deferred. + */ + init_timer_deferrable(&idle_prof_timer); + idle_prof_timer.function = idle_prof_handler; + idle_prof_timer.data = 0; + + mspm_op_num = mspm_init_mips(); + + dvfm_register_notifier(¬ifier_freq_block, + DVFM_FREQUENCY_NOTIFIER); + dvfm_register("MSPM PROF", &dvfm_dev_idx); + + /* turn on mspm and turn off prof by default */ + launch_mspm(ENABLE); + launch_prof(DISABLE); + + return 0; +} + +void __exit mspm_prof_exit(void) +{ + dvfm_unregister("MSPM PROF", &dvfm_dev_idx); + dvfm_unregister_notifier(¬ifier_freq_block, + DVFM_FREQUENCY_NOTIFIER); +} diff --git a/arch/arm/mach-mmp/pxa910_pm.c b/arch/arm/mach-mmp/pxa910_pm.c new file mode 100644 index 00000000000000..d6eb4464ced293 --- /dev/null +++ b/arch/arm/mach-mmp/pxa910_pm.c @@ -0,0 +1,426 @@ +/* + * PXA910 Power Management Routines + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * + * (C) Copyright 2009 Marvell International Ltd. + * All Rights Reserved + */ + +#undef DEBUG +#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 +#include +#include +#include +#include +#include +#include +#include + +int enable_deepidle = IDLE_CORE_INTIDLE | IDLE_CORE_EXTIDLE | + IDLE_APPS_IDLE | IDLE_APPS_SLEEP | IDLE_SYS_SLEEP; +static unsigned long pm_state; +static int cur_lpmode = -1; +static unsigned int pm_irqs[] = { + IRQ_PXA168_KEYPAD, + IRQ_PXA168_PMIC_INT, + IRQ_PXA168_RTC_ALARM, + IRQ_PXA910_IPC_AP_DATAACK, + IRQ_PXA910_IPC_AP_SET_CMD, + IRQ_PXA910_IPC_AP_SET_MSG, +}; + +static uint32_t setup_wakeup_sources(void) +{ + uint32_t apcr, awucrm; + + awucrm = 0x0; + //awucrm |= PMUM_GSM_WAKEUPWMX; + //awucrm |= PMUM_WCDMA_WAKEUPX; + //awucrm |= PMUM_GSM_WAKEUPWM; + //awucrm |= PMUM_WCDMA_WAKEUPWM; + awucrm |= PMUM_AP_ASYNC_INT; + awucrm |= PMUM_AP_FULL_IDLE; + //awucrm |= PMUM_SDH1; + //awucrm |= PMUM_SDH2; + awucrm |= PMUM_KEYPRESS; + //awucrm |= PMUM_TRACKBALL; + //awucrm |= PMUM_NEWROTARY; + //awucrm |= PMUM_WDT; + awucrm |= PMUM_RTC_ALARM; + //awucrm |= PMUM_CP_TIMER_3; + //awucrm |= PMUM_CP_TIMER_2; + //awucrm |= PMUM_CP_TIMER_1; + //awucrm |= PMUM_AP2_TIMER_3; + //awucrm |= PMUM_AP2_TIMER_2; + //awucrm |= PMUM_AP2_TIMER_1; + //awucrm |= PMUM_AP1_TIMER_3; + //awucrm |= PMUM_AP1_TIMER_2; + awucrm |= PMUM_AP1_TIMER_1; + awucrm |= PMUM_WAKEUP7; + //awucrm |= PMUM_WAKEUP6; + //awucrm |= PMUM_WAKEUP5; + awucrm |= PMUM_WAKEUP4; + awucrm |= PMUM_WAKEUP3; + //awucrm |= PMUM_WAKEUP2; + //awucrm |= PMUM_WAKEUP1; + //awucrm |= PMUM_WAKEUP0; + + __raw_writel(awucrm, MPMU_AWUCRM); + + apcr = 0xff087fff; /* enable all wake-up ports */ + + return apcr; +} + +void pxa910_pm_enter_lowpower_mode(int state) +{ + uint32_t idle_cfg, apcr; + + if (state == cur_lpmode) + return; + + cur_lpmode = state; + idle_cfg = __raw_readl(APMU_IDLE_CFG); + apcr = __raw_readl(MPMU_APCR); + + apcr &= ~PMUM_SLPEN & ~PMUM_DDRCORSD & ~PMUM_APBSD & ~PMUM_AXISD; + apcr &= ~PMUM_VCTCXOSD; + idle_cfg &= ~PMUA_MOH_IDLE; + + switch (state) { + case POWER_MODE_SYS_SLEEP: + apcr |= PMUM_SLPEN; /* set the SLPEN bit */ + apcr |= PMUM_VCTCXOSD; /* set VCTCXOSD */ + apcr &= setup_wakeup_sources(); /* set up wakeup sources */ + /* fall through */ + case POWER_MODE_APPS_SLEEP: + apcr |= PMUM_DDRCORSD | PMUM_APBSD; /* set DDRCORSD and APBSD */ + /* fall through */ + case POWER_MODE_APPS_IDLE: + apcr |= PMUM_AXISD; /* set AXISDD bit */ + /* fall through */ + case POWER_MODE_CORE_EXTIDLE: + idle_cfg |= PMUA_MOH_IDLE; /* set the IDLE bit */ + //idle_cfg |= PMUA_MOH_PWRDWN; /* set the PWRDWN bit */ + idle_cfg |= 0x000f0000; /* number of power switch to 4 */ + /* fall through */ + case POWER_MODE_CORE_INTIDLE: + break; + } + + /* program the memory controller hardware sleep type and auto wakeup */ + idle_cfg |= PMUA_MOH_DIS_MC_SW_REQ; + idle_cfg |= PMUA_MOH_MC_WAKE_EN; + __raw_writel(0x0, APMU_MC_HW_SLP_TYPE); /* auto refresh */ + + /* set DSPSD, DTCMSD, BBSD, MSASLPEN */ + apcr |= PMUM_DSPSD | PMUM_DTCMSD | PMUM_BBSD | PMUM_MSASLPEN; + + /* finally write the registers back */ + __raw_writel(idle_cfg, APMU_IDLE_CFG); + __raw_writel(apcr, MPMU_APCR); + + //__raw_writel(0x20807, APMU_PWR_STBL_TIMER); + //__raw_writel(0x1, APMU_SRAM_PWR_DWN); + + //__raw_writel(0x0, APMU_MC_SW_SLP_TYPE); + //__raw_writel(0x1, APMU_MC_SLP_REQ); + + //__raw_writel(0x0, MPMU_VRCR); +} + +#define MAXTOKENS 80 + +static int tokenizer(char **tbuf, const char *userbuf, ssize_t n, + char **tokptrs, int maxtoks) +{ + char *cp, *tok; + char *whitespace = " \t\r\n"; + int ntoks = 0; + cp = kmalloc(n + 1, GFP_KERNEL); + if (!cp) + return -ENOMEM; + + *tbuf = cp; + memcpy(cp, userbuf, n); + cp[n] = '\0'; + + do { + cp = cp + strspn(cp, whitespace); + tok = strsep(&cp, whitespace); + if ((*tok == '\0') || (ntoks == maxtoks)) + break; + tokptrs[ntoks++] = tok; + } while (cp); + + return ntoks; +} + +static ssize_t deepidle_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int len = 0; + + if (enable_deepidle & IDLE_CORE_INTIDLE) + len += sprintf(buf + len, "core_intidle "); + if (enable_deepidle & IDLE_CORE_EXTIDLE) + len += sprintf(buf + len, "core_extidle "); + if (enable_deepidle & IDLE_APPS_IDLE) + len += sprintf(buf + len, "apps_idle "); + if (enable_deepidle & IDLE_APPS_SLEEP) + len += sprintf(buf + len, "apps_sleep "); + if (enable_deepidle & IDLE_SYS_SLEEP) + len += sprintf(buf + len, "sys_sleep"); + len += sprintf(buf + len, "\n"); + len += sprintf(buf + len, "Usage: echo [set|unset] " + "[core_intidle|core_extidle|apps_idle|apps_sleep|sys_sleep] " + "> deepidle\n"); + return len; +} + +static ssize_t deepidle_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + int error = 0; + char *tbuf = NULL; + char *token[MAXTOKENS]; + int ntoks = tokenizer(&tbuf, buf, len, (char **)&token, MAXTOKENS); + + if (ntoks <= 0) { + error = ntoks; + goto out; + } + + if (strcmp(token[0], "set") == 0) { + if (strcmp(token[1], "core_intidle") == 0) + enable_deepidle |= IDLE_CORE_INTIDLE; + else if (strcmp(token[1], "core_extidle") == 0) + enable_deepidle |= IDLE_CORE_EXTIDLE; + else if (strcmp(token[1], "apps_idle") == 0) + enable_deepidle |= IDLE_APPS_IDLE; + else if (strcmp(token[1], "apps_sleep") == 0) + enable_deepidle |= IDLE_APPS_SLEEP; + else if (strcmp(token[1], "sys_sleep") == 0) + enable_deepidle |= IDLE_SYS_SLEEP; + else + error = -EINVAL; + } else if (strcmp(token[0], "unset") == 0) { + if (strcmp(token[1], "core_intidle") == 0) + enable_deepidle &= ~IDLE_CORE_INTIDLE; + else if (strcmp(token[1], "core_extidle") == 0) + enable_deepidle &= ~IDLE_CORE_EXTIDLE; + else if (strcmp(token[1], "apps_idle") == 0) + enable_deepidle &= ~IDLE_APPS_IDLE; + else if (strcmp(token[1], "apps_sleep") == 0) + enable_deepidle &= ~IDLE_APPS_SLEEP; + else if (strcmp(token[1], "sys_sleep") == 0) + enable_deepidle &= ~IDLE_SYS_SLEEP; + else + error = -EINVAL; + } else { + if (strcmp(token[0], "0") == 0) + enable_deepidle = IDLE_ACTIVE; + else + error = -EINVAL; + } +out: + kfree(tbuf); + return error ? error : len; +} + +static struct kobj_attribute deepidle_attr = { + .attr = { + .name = __stringify(deepidle), + .mode = 0644, + }, + .show = deepidle_show, + .store = deepidle_store, +}; + +static ssize_t regdump_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + int len = 0; + unsigned int addr; + uint32_t ccr = __raw_readl(TIMERS1_VIRT_BASE + TMR_CCR); + uint32_t cr0 = __raw_readl(TIMERS1_VIRT_BASE + TMR_CR(0)); + + /*len += sprintf(buf + len, "apcr 0x%x, cpcr: 0x%x, " + "apidle 0x%x, cpidle 0x%x, " + "apwake 0x%x, cpwake 0x%x\n", + __raw_readl(MPMU_APCR), __raw_readl(MPMU_CPCR), + __raw_readl(APMU_IDLE_CFG), __raw_readl(APMU_CP_IDLE_CFG), + __raw_readl(MPMU_AWUCRM), __raw_readl(MPMU_CWUCRM));*/ + for (addr = MPMU_CPCR; addr <= (MPMU_CPCR+0x4c); addr += 4) + len += sprintf(buf + len, "0x%x: 0x%x\n", addr, __raw_readl(addr)); + for (addr = MPMU_APCR; addr <= (MPMU_APCR+0x4c); addr += 4) + len += sprintf(buf + len, "0x%x: 0x%x\n", addr, __raw_readl(addr)); + for (addr = (APMU_CCR-4); addr <= APMU_SMC_CLK_RES_CTRL; addr += 4) + len += sprintf(buf + len, "0x%x: 0x%x\n", addr, __raw_readl(addr)); + + len += sprintf(buf + len, "ccr 0x%x, cr0: 0x%x\n", ccr, cr0); + return len; +} + +static ssize_t regdump_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t len) +{ + unsigned int addr, value; + int error = 0; + char *tbuf = NULL; + char *token[MAXTOKENS]; + int ntoks = tokenizer(&tbuf, buf, len, (char **)&token, MAXTOKENS); + + if (ntoks <= 0) { + error = ntoks; + goto out; + } + + addr = simple_strtoul(token[0], NULL, 0); + if (addr < 0xd4000000 || addr > 0xd4400000) { + error = -EINVAL; + goto out; + } + value = simple_strtoul(token[1], NULL, 0); + + __raw_writel(value, addr+0x2a000000); +out: + kfree(tbuf); + return error ? error : len; +} + +static struct kobj_attribute regdump_attr = { + .attr = { + .name = __stringify(regdump), + .mode = 0644, + }, + .show = regdump_show, + .store = regdump_store, +}; + +static struct attribute * g[] = { + &deepidle_attr.attr, + ®dump_attr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = g, +}; + +static int pxa910_pm_enter(suspend_state_t state) +{ + pxa910_pm_enter_lowpower_mode(POWER_MODE_SYS_SLEEP); + __raw_writel(0x0, MPMU_VRCR); + cpu_do_idle(); + return 0; +} + +static void pxa910_pm_irq(unsigned int enable) +{ + struct irq_desc *desc = irq_to_desc(pm_irqs[0]); + unsigned int i, j, flag=0; + int reg; + + for (i = 0; i < 64; i++) { + flag = 0; + for (j = 0; j < ARRAY_SIZE(pm_irqs); j++) + if (i == pm_irqs[j]) { + flag = 1; + break; + } + if(flag) + continue; + reg = __raw_readl(ICU_INT_CONF(i)); + if(!(reg & ICU_INT_CONF_CP_INT)) { + if(enable) + desc->chip->unmask(i); + else + desc->chip->mask(i); + } + } +} + +/* + * Called after processes are frozen, but before we shut down devices. + */ +static int pxa910_pm_prepare(void) +{ + pxa910_pm_irq(0); + return 0; +} + +/* + * Called after devices are re-setup, but before processes are thawed. + */ +static void pxa910_pm_finish(void) +{ + pm_state = PM_SUSPEND_ON; + pxa910_pm_irq(1); + __raw_writel(0x1, MPMU_VRCR); +} + +static int pxa910_pm_valid(suspend_state_t state) +{ + int ret = 1; + + if (state == PM_SUSPEND_STANDBY) { + pm_state = PM_SUSPEND_STANDBY; + } else if (state == PM_SUSPEND_MEM) { + pm_state = PM_SUSPEND_MEM; + } else { + ret = 0; + } + return ret; +} + +/* + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. + */ +static struct platform_suspend_ops pxa910_pm_ops = { + .valid = pxa910_pm_valid, + .prepare = pxa910_pm_prepare, + .enter = pxa910_pm_enter, + .finish = pxa910_pm_finish, +}; + +static int __init pxa910_pm_init(void) +{ + if (!cpu_is_pxa910()) + return -EIO; + if (sysfs_create_group(power_kobj, &attr_group)) + return -1; + suspend_set_ops(&pxa910_pm_ops); + return 0; +} + +late_initcall(pxa910_pm_init); diff --git a/arch/arm/mach-mmp/pxa910_pm_ll.S b/arch/arm/mach-mmp/pxa910_pm_ll.S new file mode 100644 index 00000000000000..a2dbaec4e9e4f0 --- /dev/null +++ b/arch/arm/mach-mmp/pxa910_pm_ll.S @@ -0,0 +1,13 @@ +/* + * Low-level PXA910 hibernate mode support + * + * Copyright (C) 2009, Marvell Corporation. + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + */ + +#include +#include +#include + diff --git a/arch/arm/mach-mmp/resource/Makefile b/arch/arm/mach-mmp/resource/Makefile new file mode 100644 index 00000000000000..34eb7ffad02143 --- /dev/null +++ b/arch/arm/mach-mmp/resource/Makefile @@ -0,0 +1,5 @@ +# +# + +obj-y += misc.o + diff --git a/arch/arm/mach-mmp/resource/misc.c b/arch/arm/mach-mmp/resource/misc.c new file mode 100644 index 00000000000000..a6daff1d59ad16 --- /dev/null +++ b/arch/arm/mach-mmp/resource/misc.c @@ -0,0 +1,46 @@ +#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" + +void res_add_sanremo_vibrator(void) +{ +#if defined(CONFIG_SANREMO) + static struct vibrator_data vibrator_device_data = { + .max_v = 5, + .min_v = 0, + .set_vibrator = sanremo_set_vibrator, + }; + static struct platform_device vibrator_device = { + .name = "vibrator", + .id = 0, + .dev = { + .platform_data = &vibrator_device_data, + }, + }; + + platform_device_register(&vibrator_device); +#endif +} + diff --git a/arch/arm/mach-mmp/tavorevb.c b/arch/arm/mach-mmp/tavorevb.c index 0e0c9220eaba36..95f3ae9181e03f 100644 --- a/arch/arm/mach-mmp/tavorevb.c +++ b/arch/arm/mach-mmp/tavorevb.c @@ -12,16 +12,62 @@ #include #include #include +<<<<<<< HEAD:arch/arm/mach-mmp/tavorevb.c +#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" +/* +* tavorevb_keypad_type +* 0 - use tavorevb builtin keypad +* 1 - use zylonite keypad +* NOTE: default to 0 at the moment +*/ +static int tavorevb_keypad_type = 1; + +static int __init keypad_setup(char *type) +{ + tavorevb_keypad_type = simple_strtol(type, NULL, 0); + return 1; +} +__setup("tavorevb_keypad_type", keypad_setup); + +======= + +#include +#include +#include +#include +#include +#include + +#include "common.h" + +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/tavorevb.c static unsigned long tavorevb_pin_config[] __initdata = { /* UART2 */ GPIO47_UART2_RXD, @@ -35,6 +81,25 @@ static unsigned long tavorevb_pin_config[] __initdata = { SM_BE0_SM_BE0, SM_BE1_SM_BE1, +<<<<<<< HEAD:arch/arm/mach-mmp/tavorevb.c + /* IRDA */ + GPIO51_IRDA_SHDN, + + /* I2C */ + GPIO53_CI2C_SCL, + GPIO54_CI2C_SDA, + + /* SSP1 (I2S) */ + GPIO24_SSP1_SDATA_IN, + GPIO21_SSP1_BITCLK, + GPIO20_SSP1_SYSCLK, + GPIO22_SSP1_SYNC, + GPIO23_SSP1_DATA_OUT, + GPIO124_MN_CLK_OUT, + GPIO123_CLK_REQ, + +======= +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/tavorevb.c /* DFI */ DF_IO0_ND_IO0, DF_IO1_ND_IO1, @@ -58,8 +123,159 @@ static unsigned long tavorevb_pin_config[] __initdata = { DF_WEn_DF_WEn, DF_REn_DF_REn, DF_RDY0_DF_RDY0, +<<<<<<< HEAD:arch/arm/mach-mmp/tavorevb.c + + /*keypad*/ + GPIO00_KP_MKIN0, + GPIO01_KP_MKOUT0, + GPIO02_KP_MKIN1, + GPIO03_KP_MKOUT1, + GPIO04_KP_MKIN2, + GPIO05_KP_MKOUT2, + GPIO06_KP_MKIN3, + GPIO07_KP_MKOUT3, + GPIO08_KP_MKIN4, + GPIO09_KP_MKOUT4, + GPIO10_KP_MKIN5, + GPIO11_KP_MKOUT5, + GPIO12_KP_MKIN6, + GPIO13_KP_MKOUT6, + GPIO14_KP_MKIN7, + GPIO15_KP_MKOUT7, + GPIO16_KP_DKIN0, + GPIO17_KP_DKIN1, + GPIO18_KP_DKIN2, + GPIO19_KP_DKIN3, + + /* LCD */ + GPIO81_LCD_FCLK, + GPIO82_LCD_LCLK, + GPIO83_LCD_PCLK, + GPIO84_LCD_DENA, + GPIO85_LCD_DD0, + GPIO86_LCD_DD1, + GPIO87_LCD_DD2, + GPIO88_LCD_DD3, + GPIO89_LCD_DD4, + GPIO90_LCD_DD5, + GPIO91_LCD_DD6, + GPIO92_LCD_DD7, + GPIO93_LCD_DD8, + GPIO94_LCD_DD9, + GPIO95_LCD_DD10, + GPIO96_LCD_DD11, + GPIO97_LCD_DD12, + GPIO98_LCD_DD13, + GPIO100_LCD_DD14, + GPIO101_LCD_DD15, + GPIO102_LCD_DD16, + GPIO103_LCD_DD17, + GPIO104_LCD_DD18, + GPIO105_LCD_DD19, + GPIO106_LCD_DD20, + GPIO107_LCD_DD21, + GPIO108_LCD_DD22, + GPIO109_LCD_DD23, + + /*1wire*/ + GPIO106_1WIRE, + + /*CCIC/CAM*/ + GPIO67_CCIC_IN7, + GPIO68_CCIC_IN6, + GPIO69_CCIC_IN5, + GPIO70_CCIC_IN4, + GPIO71_CCIC_IN3, + GPIO72_CCIC_IN2, + GPIO73_CCIC_IN1, + GPIO74_CCIC_IN0, + GPIO75_CAM_HSYNC, + GPIO76_CAM_VSYNC, + GPIO77_CAM_MCLK, + GPIO78_CAM_PCLK, + +#if defined(CONFIG_MMC_PXA_SDH) + /*MMC sdh*/ + MMC1_DAT7_MMC1_DAT7, + MMC1_DAT6_MMC1_DAT6, + MMC1_DAT5_MMC1_DAT5, + MMC1_DAT4_MMC1_DAT4, + MMC1_DAT3_MMC1_DAT3, + MMC1_DAT2_MMC1_DAT2, + MMC1_DAT1_MMC1_DAT1, + MMC1_DAT0_MMC1_DAT0, + MMC1_CMD_MMC1_CMD, + MMC1_CLK_MMC1_CLK, + MMC1_CD_MMC1_CD, + MMC1_WP_MMC1_WP, +#endif +}; + +#if defined(CONFIG_PXA168_CAMERA) +/* sensor init */ +static int sensor_power_onoff(int on, int sensor) +{ + /* + * sensor, 0, low resolution + * sensor, 1, high resolution + */ + + unsigned int cam_hi_pwdn; + unsigned int cam_lo_pwdn; + + if (cpu_is_pxa910()) { + cam_hi_pwdn = mfp_to_gpio(MFP_PIN_GPIO80); + cam_lo_pwdn = mfp_to_gpio(MFP_PIN_GPIO79); + } + + if (gpio_request(cam_hi_pwdn, "CAM_EANBLE_HI_SENSOR")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", cam_hi_pwdn); + return -EIO; + } + + if (gpio_request(cam_lo_pwdn, "CAM_EANBLE_LO_SENSOR")){ + gpio_free(cam_hi_pwdn); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", cam_lo_pwdn); + return -EIO; + } + + if (on) { + if(sensor){ + gpio_direction_output(cam_hi_pwdn, 0); + gpio_direction_output(cam_lo_pwdn, 1); + }else{ + gpio_direction_output(cam_lo_pwdn, 0); + gpio_direction_output(cam_hi_pwdn, 1); + } + } else { + if (sensor) + gpio_direction_output(cam_hi_pwdn, 1); + else + gpio_direction_output(cam_lo_pwdn, 1); + + } + gpio_free(cam_hi_pwdn); + gpio_free(cam_lo_pwdn); + + return 0; +} + +static struct sensor_platform_data ov7660_sensor_data = { + .id = SENSOR_LOW, + .power_on = sensor_power_onoff, }; +static struct sensor_platform_data ov3640_sensor_data = { + .id = SENSOR_HIGH, + .power_on = sensor_power_onoff, +}; +#endif +======= +}; + +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/tavorevb.c static struct smc91x_platdata tavorevb_smc91x_info = { .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, }; @@ -87,12 +303,492 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; +<<<<<<< HEAD:arch/arm/mach-mmp/tavorevb.c +#if defined(CONFIG_MICCO) || defined(CONFIG_MICCO_MODULE) +static int micco_init_irq(void) +{ + return 0; +} + +static int micco_ack_irq(void) +{ + return 0; +} + +static void micco_init(void) +{ + u8 value; + + /* Mask interrupts that are not needed */ + micco_write(MICCO_IRQ_MASK_A, 0xFE); + micco_write(MICCO_IRQ_MASK_B, 0xFF); + micco_write(MICCO_IRQ_MASK_C, 0xFF); + micco_write(MICCO_IRQ_MASK_D, 0xFF); + + /* avoid SRAM power off during sleep*/ + micco_write(0x10, 0x07); + micco_write(0x11, 0xff); + //micco_write(0x12, 0xbf);/*never enable LDO4: CP controls it via COMM_OVER3 reg (0x22)*/ + + /* Enable the ONKEY power down functionality */ + micco_write(MICCO_SYSCTRL_B, 0x20); + micco_write(MICCO_SYSCTRL_A, 0x60); + + /* IRQ is masked during the power-up sequence and will not be released + * until they have been read for the first time */ + micco_read(MICCO_EVENT_A, &value); + micco_read(MICCO_EVENT_B, &value); + micco_read(MICCO_EVENT_C, &value); + micco_read(MICCO_EVENT_D, &value); +} + +/* micco_power_module[] should be consistent with enum + * in include/asm-arm/arch-pxa/pxa3xx_pmic.h */ +static struct power_supply_module miccoB0_power_modules[] = { + /* {command, power_module}, */ + {VCC_CORE, BUCK1}, + {VCC_SRAM, LDO2}, + {VCC_MVT, LDO1}, + {VCC_3V_APPS, LDO3}, + {VCC_SDIO, LDO14}, + {VCC_CAMERA_ANA, LDO13}, + {VCC_USB, LDO5}, + {VCC_LCD, LDO3}, + {VCC_TSI, 0}, + {VCC_CAMERA_IO, LDO3}, + {VCC_1P8V, LDO4}, + {VCC_MEM, BUCK3}, + {HDMI_TX, 0}, + {TECH_3V, 0}, + {TECH_1P8V, 0}, +}; + +/* micco_power_module[] should be consistent with enum + * in include/asm-arm/arch-pxa/pxa3xx_pmic.h */ +static struct power_supply_module miccoEA_power_modules[] = { + /* {command, power_module}, */ + {VCC_CORE, BUCK1}, + {VCC_SRAM, BUCK2}, + {VCC_MVT, LDO1}, + {VCC_3V_APPS, LDO3}, + {VCC_SDIO, LDO13}, + {VCC_CAMERA_ANA, LDO3}, + {VCC_USB, LDO5}, + {VCC_LCD, LDO3}, + {VCC_TSI, 0}, + {VCC_CAMERA_IO, LDO2}, + {VCC_1P8V, LDO4}, + {VCC_MEM, LDO2}, + {HDMI_TX, 0}, + {TECH_3V, 0}, + {TECH_1P8V, 0}, +}; + +static struct power_chip micco_chips[] = { + {0x10, "miccoB0", miccoB0_power_modules}, + {0x30, "miccoEA", miccoEA_power_modules}, + {0x31, "miccoEB", miccoEA_power_modules}, + {0, NULL, NULL}, +}; + +static struct micco_platform_data micco_data = { + .init_irq = micco_init_irq, + .ack_irq = micco_ack_irq, + .platform_init = micco_init, + .power_chips = micco_chips, +}; + +#endif /* CONFIG_MICCO || CONFIG_MICCO_MODULE*/ + +static unsigned int zylonite_matrix_key_map[] = { + /* KEY(row, col, key_code) */ + KEY(0, 0, KEY_A), KEY(0, 1, KEY_B), KEY(0, 2, KEY_C), + KEY(0, 5, KEY_D), KEY(1, 0, KEY_E), KEY(1, 1, KEY_F), + KEY(1, 2, KEY_G), KEY(1, 5, KEY_H), KEY(2, 0, KEY_I), + KEY(2, 1, KEY_J), KEY(2, 2, KEY_K), KEY(2, 5, KEY_L), + KEY(3, 0, KEY_M), KEY(3, 1, KEY_N), KEY(3, 2, KEY_O), + KEY(3, 5, KEY_P), KEY(5, 0, KEY_Q), KEY(5, 1, KEY_R), + KEY(5, 2, KEY_S), KEY(5, 5, KEY_T), KEY(6, 0, KEY_U), + KEY(6, 1, KEY_V), KEY(6, 2, KEY_W), KEY(6, 5, KEY_X), + KEY(7, 1, KEY_Y), KEY(7, 2, KEY_Z), + + KEY(4, 4, KEY_0), KEY(1, 3, KEY_1), KEY(4, 1, KEY_2), + KEY(1, 4, KEY_3), KEY(2, 3, KEY_4), KEY(4, 2, KEY_5), + KEY(2, 4, KEY_6), KEY(3, 3, KEY_7), KEY(4, 3, KEY_8), + KEY(3, 4, KEY_9), + + KEY(4, 5, KEY_SPACE), + KEY(5, 3, KEY_KPASTERISK), /* * */ + KEY(5, 4, KEY_KPDOT), /* #" */ + + KEY(0, 7, KEY_UP), + KEY(1, 7, KEY_DOWN), + KEY(2, 7, KEY_LEFT), + KEY(3, 7, KEY_RIGHT), + KEY(2, 6, KEY_HOME), + KEY(3, 6, KEY_END), + KEY(6, 4, KEY_DELETE), + KEY(6, 6, KEY_BACK), + KEY(6, 3, KEY_CAPSLOCK),/* KEY_LEFTSHIFT), */ + + KEY(4, 6, KEY_ENTER), /* scroll push */ + KEY(5, 7, KEY_ENTER), /* keypad action */ + + KEY(0, 4, KEY_EMAIL), + KEY(5, 6, KEY_SEND), + KEY(4, 0, KEY_CALENDAR), + KEY(7, 6, KEY_RECORD), + KEY(6, 7, KEY_VOLUMEUP), + KEY(7, 7, KEY_VOLUMEDOWN), + + KEY(0, 6, KEY_F22), /* soft1 */ + KEY(1, 6, KEY_F23), /* soft2 */ + + KEY(0, 3, KEY_AUX), /* contact */ +}; + +static unsigned int tavorevb_matrix_key_map[] = { + /* KEY(row, col, key_code) */ + KEY(0, 4, KEY_A), KEY(0, 5, KEY_B), KEY(0, 6, KEY_C), + KEY(0, 7, KEY_D), KEY(1, 4, KEY_E), KEY(1, 5, KEY_F), + KEY(1, 6, KEY_G), KEY(1, 7, KEY_H), KEY(2, 4, KEY_I), + KEY(2, 5, KEY_J), KEY(2, 6, KEY_K), KEY(2, 7, KEY_L), + KEY(3, 4, KEY_M), KEY(3, 5, KEY_N), KEY(3, 6, KEY_O), + KEY(3, 7, KEY_P), KEY(7, 3, KEY_Q), KEY(4, 5, KEY_R), + KEY(4, 6, KEY_S), KEY(4, 7, KEY_T), KEY(5, 4, KEY_U), + KEY(5, 5, KEY_V), KEY(5, 6, KEY_W), KEY(5, 7, KEY_X), + KEY(6, 4, KEY_Y), KEY(6, 5, KEY_Z), + + KEY(0, 3, KEY_0), KEY(2, 0, KEY_1), KEY(2, 1, KEY_2), + KEY(2, 2, KEY_3), KEY(2, 3, KEY_4), KEY(1, 0, KEY_5), + KEY(1, 1, KEY_6), KEY(1, 2, KEY_7), + KEY(1, 3, KEY_8), KEY(0, 2, KEY_9), + + KEY(6, 6, KEY_SPACE), + KEY(0, 0, KEY_KPASTERISK), /* * */ + KEY(0, 1, KEY_KPDOT), /* #" */ + + KEY(4, 1, KEY_UP), + KEY(4, 3, KEY_DOWN), + KEY(4, 0, KEY_LEFT), + KEY(4, 2, KEY_RIGHT), + KEY(6, 0, KEY_HOME), + KEY(3, 2, KEY_END), + KEY(6, 1, KEY_DELETE), + KEY(5, 2, KEY_BACK), + KEY(6, 3, KEY_CAPSLOCK),/* KEY_LEFTSHIFT), */ + + KEY(6, 2, KEY_ENTER), /* keypad action */ + + KEY(7, 2, KEY_EMAIL), + KEY(3, 1, KEY_SEND), + KEY(7, 1, KEY_CALENDAR), + KEY(5, 3, KEY_RECORD), + KEY(5, 0, KEY_VOLUMEUP), + KEY(5, 1, KEY_VOLUMEDOWN), + + KEY(3, 0, KEY_F22), /* soft1 */ + KEY(3, 3, KEY_F23), /* soft2 */ + + KEY(7, 0, KEY_AUX), /* contact */ +}; + +static struct pxa27x_keypad_platform_data tavorevb_keypad_info = { + .matrix_key_rows = 8, + .matrix_key_cols = 8, + .matrix_key_map = tavorevb_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(tavorevb_matrix_key_map), + .debounce_interval = 30, +}; +static struct pxa27x_keypad_platform_data zylonite_keypad_info = { + .matrix_key_rows = 8, + .matrix_key_cols = 8, + .matrix_key_map = zylonite_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(zylonite_matrix_key_map), + .debounce_interval = 30, +}; + + +static struct fb_videomode video_modes[] = { + /* sharp_ls037 QVGA mode info */ + [0] = { + .pixclock = 158000, + .refresh = 60, + .xres = 240, + .yres = 320, + .hsync_len = 4, + .left_margin = 39, + .right_margin = 39, + .vsync_len = 1, + .upper_margin = 1, + .lower_margin = 2, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, + }, + /* sharp_ls037 VGA mode info */ + [1] = { + .pixclock = 39700, + .refresh = 60, + .xres = 480, + .yres = 640, + .hsync_len = 8, + .left_margin = 81, + .right_margin = 81, + .vsync_len = 1, + .upper_margin = 2, + .lower_margin = 7, + .sync = 0, + }, +}; + +#define LCD_SCLK (312000000UL) +static struct pxa168fb_mach_info pxa168_tavorevb_lcd_info = { + .id = "GFX Layer", + .sclk_clock = LCD_SCLK, + .num_modes = ARRAY_SIZE(video_modes), + .modes = video_modes, + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_16_GPIO, + .dumb_mode = DUMB_MODE_RGB565, + .panel_rgb_reverse_lanes= 0, + .gpio_output_data = 1, + .gpio_output_mask = 0xff, + .invert_composite_blank = 0, + .invert_pix_val_ena = 0, + .invert_pixclock = 0, + .invert_vsync = 0, + .invert_hsync = 0, + .panel_rbswap = 1, + .active = 1, + .enable_lcd = 1, + .spi_gpio_cs = -1, + .spi_gpio_reset = -1, + .max_fb_size = 1280 * 720 * 4, +}; + +static struct pxa168fb_mach_info pxa168_tavorevb_lcd_ovly_info = { + .id = "Video Layer", + .sclk_clock = LCD_SCLK, + .num_modes = ARRAY_SIZE(video_modes), + .modes = video_modes, + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_16_GPIO, + .dumb_mode = DUMB_MODE_RGB565, + .panel_rgb_reverse_lanes= 0, + .gpio_output_data = 1, + .gpio_output_mask = 0xff, + .invert_composite_blank = 0, + .invert_pix_val_ena = 0, + .invert_pixclock = 0, + .invert_vsync = 0, + .invert_hsync = 0, + .panel_rbswap = 1, + .active = 1, + .enable_lcd = 1, + .spi_gpio_cs = -1, + .spi_gpio_reset = -1, + .max_fb_size = 1280 * 720 * 4, +}; + +#if defined(CONFIG_SANREMO) || defined(CONFIG_SANREMO_MODULE) +static int sanremo_init_irq(void) +{ + return 0; +} + +static int sanremo_ack_irq(void) +{ + return 0; +} + +static void sanremo_init(void) +{ + sanremo_write(SANREMO_GPADC_MISC1, 0x0b); +} + +/* sanremo_power_module[] should be consistent with enum + * in include/asm-arm/arch-pxa/pxa3xx_pmic.h */ +static struct power_supply_module sanremo_power_modules[] = { + /* {command, power_module}, */ + {VCC_CORE, SAN_BUCK1}, + {VCC_USB, SAN_LDO5}, + {VCC_CAMERA_ANA, SAN_LDO12}, + {VCC_SDIO, SAN_LDO14}, +}; + + +static struct power_chip sanremo_chips[] = { + {0x40, "sanremo", sanremo_power_modules}, + {0x41, "sanremo", sanremo_power_modules}, + {0, NULL, NULL}, +}; + +static struct sanremo_platform_data sanremo_data = { + .init_irq = sanremo_init_irq, + .ack_irq = sanremo_ack_irq, + .platform_init = sanremo_init, + .power_chips = sanremo_chips, +}; +#endif /* CONFIG_PXA3xx_SANREMO || CONFIG_PXA3xx_SANREMO_MODULE*/ + +static struct i2c_pxa_platform_data i2c_info __initdata = { + .use_pio = 1, +}; + +static struct i2c_board_info i2c_board_info[] = +{ +#if defined(CONFIG_MICCO) || defined(CONFIG_MICCO_MODULE) + { + .type = "micco", + .addr = 0x34, + .platform_data = &micco_data, + .irq = IRQ_PXA168_PMIC_INT, + }, +#endif +#if defined(CONFIG_SANREMO) || defined(CONFIG_SANREMO_MODULE) + { + .type = "sanremo", + .addr = 0x30, + .platform_data = &sanremo_data , + .irq = IRQ_PXA168_PMIC_INT, + }, +#endif +#if defined(CONFIG_PORTOFINO) || defined(CONFIG_PORTOFINO_MODULE) + { + .type = "portofino", + .addr = 0x10, + }, +#endif +#if defined(CONFIG_PXA168_CAMERA) + { + .type = "ov7660", + .addr = 0x21, + .platform_data = &ov7660_sensor_data, + }, + { + .type = "ov3640", + .addr = 0x3C, + .platform_data = &ov3640_sensor_data, + }, +#endif +}; + +DECLARE_ANDROID_128M_V75_PARTITIONS(android_128m_v75_partitions); +DECLARE_128M_V75_PARTITIONS(generic_128m_v75_partitions); +static struct pxa3xx_nand_platform_data tavorevb_nand_info; +static void __init tavorevb_add_nand(void) +{ + if (is_android()) { + tavorevb_nand_info.parts[0] = android_128m_v75_partitions; + tavorevb_nand_info.nr_parts[0] = ARRAY_SIZE(android_128m_v75_partitions); + } else { + tavorevb_nand_info.parts[0] = generic_128m_v75_partitions; + tavorevb_nand_info.nr_parts[0] = ARRAY_SIZE(generic_128m_v75_partitions); + } + + tavorevb_nand_info.use_dma = 0; + tavorevb_nand_info.enable_arbiter = 1; + pxa168_add_nand(&tavorevb_nand_info); +} + +static struct flash_platform_data tavorevb_onenand_info; +static void __init tavorevb_add_onenand(void) +{ + if (is_android()) { + tavorevb_onenand_info.parts = android_128m_v75_partitions; + tavorevb_onenand_info.nr_parts = ARRAY_SIZE(android_128m_v75_partitions); + } else { + tavorevb_onenand_info.parts = generic_128m_v75_partitions; + tavorevb_onenand_info.nr_parts = ARRAY_SIZE(generic_128m_v75_partitions); + } + pxa168_add_onenand(&tavorevb_onenand_info); +} + +#if defined(CONFIG_MMC_PXA_SDH) +static struct pxasdh_platform_data tavorevb_sdh_platform_data = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, +}; +#endif + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static int tavorevb_vbus_status(unsigned base) +{ + int status = VBUS_LOW; + + if (pxa3xx_pmic_is_vbus_assert()) { + status = VBUS_HIGH; + } + return status; +} + +static int tavorevb_vbus_detect(void *func, int enable) +{ + if (enable) { + pmic_callback_register(PMIC_EVENT_USB, func); + pxa3xx_pmic_set_pump(1); + } else { + pxa3xx_pmic_set_pump(0); + pmic_callback_unregister(PMIC_EVENT_USB, func); + } + + return 0; +} + +static struct pxa_usb_plat_info tavorevb_u2o_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_status = tavorevb_vbus_status, + .vbus_detect = tavorevb_vbus_detect, + .is_otg = 1, +}; +#endif + static void __init tavorevb_init(void) { + +======= +static void __init tavorevb_init(void) +{ +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/tavorevb.c mfp_config(ARRAY_AND_SIZE(tavorevb_pin_config)); /* on-chip devices */ pxa910_add_uart(1); +<<<<<<< HEAD:arch/arm/mach-mmp/tavorevb.c + pxa168_add_twsi(0, &i2c_info, ARRAY_AND_SIZE(i2c_board_info)); + tavorevb_add_nand(); + tavorevb_add_onenand(); + pxa910_add_ire(); + pxa910_add_acipc(); + if (tavorevb_keypad_type == 1) + pxa168_add_keypad(&zylonite_keypad_info); + else + pxa168_add_keypad(&tavorevb_keypad_info); +#if defined(CONFIG_MMC_PXA_SDH) + pxa168_add_sdh(0, &tavorevb_sdh_platform_data); +#endif + + if(cpu_is_pxa910_Ax()){ + pxa910_add_fb(&pxa168_tavorevb_lcd_info); + pxa910_add_fb_ovly(&pxa168_tavorevb_lcd_ovly_info); + }else{ + pxa168_add_fb(&pxa168_tavorevb_lcd_info); + pxa168_add_fb_ovly(&pxa168_tavorevb_lcd_ovly_info); + } +#if defined(CONFIG_PXA168_CAMERA) + pxa168_add_cam(); +#endif +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&tavorevb_u2o_info); +#endif +#ifdef CONFIG_USB_OTG + pxa168_add_u2ootg(&tavorevb_u2o_info); + pxa168_add_u2oehci(&tavorevb_u2o_info); +#endif + pxa910_add_ssp(1); + pxa910_add_imm(); + pxa168_add_freq(); +======= +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/tavorevb.c /* off-chip devices */ platform_device_register(&smc91x_device); @@ -103,7 +799,12 @@ MACHINE_START(TAVOREVB, "PXA910 Evaluation Board (aka TavorEVB)") .boot_params = 0x00000100, .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, .map_io = pxa_map_io, +<<<<<<< HEAD:arch/arm/mach-mmp/tavorevb.c + .init_irq = pxa168_init_irq, + .timer = &pxa168_timer, +======= .init_irq = pxa910_init_irq, .timer = &pxa910_timer, +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/tavorevb.c .init_machine = tavorevb_init, MACHINE_END diff --git a/arch/arm/mach-mmp/teton_bga.c b/arch/arm/mach-mmp/teton_bga.c new file mode 100644 index 00000000000000..51008dbec7a493 --- /dev/null +++ b/arch/arm/mach-mmp/teton_bga.c @@ -0,0 +1,704 @@ +/* + * linux/arch/arm/mach-mmp/teton_bga.c + * + * Support for the Marvell PXA168-based Teton BGA Platform. + * + * 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 +#include +#include + +#include "common.h" +#include +#include + +static unsigned long teton_bga_pin_config[] __initdata = { + + /* Data Flash Interface Nana */ + GPIO0_DFI_D15, + GPIO1_DFI_D14, + GPIO2_DFI_D13, + GPIO3_DFI_D12, + GPIO4_DFI_D11, + GPIO5_DFI_D10, + GPIO6_DFI_D9, + GPIO7_DFI_D8, + GPIO8_DFI_D7, + GPIO9_DFI_D6, + GPIO10_DFI_D5, + GPIO11_DFI_D4, + GPIO12_DFI_D3, + GPIO13_DFI_D2, + GPIO14_DFI_D1, + GPIO15_DFI_D0, + +#if defined(CONFIG_PXA168_CF) + /* Compact Flash Controller */ + GPIO19_CF_nCE1, + GPIO20_CF_nCE2, + GPIO22_ND_CLE, + GPIO23_CF_nALE, + GPIO25_CF_nRESET, + GPIO26_ND_RnB1, + GPIO27_ND_RnB2, + GPIO28_CF_RDY, + GPIO29_CF_STSCH, + GPIO30_CF_nREG, + GPIO31_CF_nIOIS16, +#if defined(CONFIG_PXA168_CF_USE_GPIO_CARDDETECT) + GPIO32_GPIO, /* CF_nCD1 IRQ */ + GPIO33_GPIO, /* CF_nCD2 IRQ */ +#else + GPIO32_CF_nCD1, + GPIO33_CF_nCD2, +#endif + GPIO34_SMC_nCS1, + GPIO35_CF_INPACK, + GPIO36_CF_nWAIT, +#endif + /* LCD */ + GPIO56_LCD_FCLK_RD, + GPIO57_LCD_LCLK_A0, + GPIO58_LCD_PCLK_WR, + GPIO59_LCD_DENA_BIAS, + GPIO60_LCD_DD0, + GPIO61_LCD_DD1, + GPIO62_LCD_DD2, + GPIO63_LCD_DD3, + GPIO64_LCD_DD4, + GPIO65_LCD_DD5, + GPIO66_LCD_DD6, + GPIO67_LCD_DD7, + GPIO68_LCD_DD8, + GPIO69_LCD_DD9, + GPIO70_LCD_DD10, + GPIO71_LCD_DD11, + GPIO72_LCD_DD12, + GPIO73_LCD_DD13, + GPIO74_LCD_DD14, + GPIO75_LCD_DD15, + GPIO76_LCD_DD16, + GPIO77_LCD_DD17, + + /* i2c bus */ + GPIO105_CI2C_SDA, + GPIO106_CI2C_SCL, + + /* UART1 */ + GPIO107_UART1_TXD, + /* CIR and UART1 on TetonBGA are multiplexed */ +#if 0 /* disabled CIR due to UART_RX conflict */ + GPIO108_GPIO, +#else + GPIO108_UART1_RXD, +#endif + + /* Keypad */ + GPIO110_KP_MKIN0, + GPIO109_KP_MKIN1, + GPIO111_KP_MKOUT7, + GPIO112_KP_MKOUT6, + + /* SSP0 */ + GPIO113_I2S_MCLK, + GPIO114_I2S_FRM, + GPIO115_I2S_BCLK, + GPIO116_I2S_TXD, + + /* USB OTG HPENA */ + GPIO85_GPIO, + + /* MFU */ + GPIO86_TX_CLK, + GPIO87_TX_EN, + GPIO88_TX_DQ3, + GPIO89_TX_DQ2, + GPIO90_TX_DQ1, + GPIO91_TX_DQ0, + GPIO92_MII_CRS, + GPIO93_MII_COL, + GPIO94_RX_CLK, + GPIO95_RX_ER, + GPIO96_RX_DQ3, + GPIO97_RX_DQ2, + GPIO98_RX_DQ1, + GPIO99_RX_DQ0, + GPIO100_MII_MDC, + GPIO101_MII_MDIO, + GPIO103_RX_DV, + + /* mspro detect */ + GPIO47_MSP_INS, + + /* MMC */ + GPIO27_MMC1_EN, +}; + +#define ENET_RESET_N (104) +#define ENET_COMA_N (102) + +static int pxa168_eth_init(void) +{ + if (gpio_request(ENET_RESET_N, "ENET_RESET_N")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", ENET_RESET_N); + return -EIO; + } + + if (gpio_request(ENET_COMA_N, "ENET_COMA_N")) { + gpio_free(ENET_RESET_N); + printk(KERN_ERR "Request GPIO failed," + "gpio: %d\n", ENET_COMA_N); + return -EIO; + } + + /* reset Ethernet Phy */ + gpio_direction_output(ENET_RESET_N, 0); + gpio_direction_output(ENET_COMA_N, 1); + gpio_direction_output(ENET_RESET_N, 1); + + gpio_free(ENET_RESET_N); + gpio_free(ENET_COMA_N); + + return 0; +} + +static struct pxa168_eth_platform_data pxa168_eth_data = { + .phy_addr = 0, /* phy addr depends on boards */ + .force_phy_addr = 1, + .init = pxa168_eth_init, +}; + +#if defined(CONFIG_PXA168_CF) + +#define CF_PWEN (82) +static int __init teton_bga_init_CF(void) +{ + printk("Enabling CF power..\r\n"); + if (gpio_request(CF_PWEN, "CF PWEN")) { + printk(KERN_ERR "Failed to enable CF Power\n"); + return -1; + } + + /* set CF power enable (not) pin to low */ + gpio_direction_output(CF_PWEN, 0); + gpio_free(CF_PWEN); + + return 0; + +} + +static struct resource pxa168_cf_resources[] = { + [0] = { + .start = 0xD4285000, + .end = 0xD4285800, + .flags = IORESOURCE_MEM, + }, + + [1] = { + .start = IRQ_PXA168_CF, + .end = IRQ_PXA168_CF, + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = IRQ_GPIO(32), + .end = IRQ_GPIO(32), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device pxa168_cf_device = { + .name = "pxa168-cf", + .id = -1, + .resource = pxa168_cf_resources, + .num_resources = ARRAY_SIZE(pxa168_cf_resources), +}; + +static void __init pxa168_cf_init(void) +{ + platform_device_register(&pxa168_cf_device); +} +#endif + +#if defined(CONFIG_PXA168_MSP) +/* msp platform data */ +static mfp_cfg_t mfp_cfg_msp[] = { + GPIO42_MSP_BS, + GPIO44_MSP_DAT1, + GPIO45_MSP_DAT0, + GPIO46_MSP_DAT2, + GPIO48_MSP_DAT3, + GPIO50_MSP_SCLK, +}; + +static void mspro_mfp_config(void) +{ + mfp_config(ARRAY_AND_SIZE(mfp_cfg_msp)); +} + +static struct card_platform_data msp_ops = { + /* GPIO47 used as mspro detect pin */ + .pin_detect = MFP_PIN_GPIO47, + .mfp_config = mspro_mfp_config, +}; +#endif + +static struct i2c_pxa_platform_data pwri2c_info __initdata = { + .use_pio = 1, +}; + +#define LCD_VDD_EN (79) +#define LCD_BKL_EN (80) +static int __init teton_bga_init_BKL(void) +{ + printk(KERN_NOTICE "Enabling LCD Vdd & Backlight..\r\n"); + if (gpio_request(LCD_VDD_EN, "LCD VDD")) { + printk(KERN_ERR "Failed to enable LCD Power\n"); + return -1; + } + if (gpio_request(LCD_BKL_EN, "LCD BKL")) { + printk(KERN_ERR "Failed to enable backlight\n"); + gpio_free(LCD_VDD_EN); + return -1; + } + + /* set VDD and BKL to output and high */ + gpio_direction_output(LCD_VDD_EN, 1); + msleep(1); + gpio_direction_output(LCD_BKL_EN, 1); + gpio_free(LCD_VDD_EN); + gpio_free(LCD_BKL_EN); + + return 0; + +} + +static struct fb_videomode video_modes_aspen[] = { + /* 10.1" WSVGA mode info */ + [0] = { + .pixclock = 18422, + .refresh = 60, + .xres = 1024, + .yres = 600, + .hsync_len = 381, + .left_margin = 0, + .right_margin = 0, /* H_total=1405 */ + .vsync_len = 50, + .upper_margin = 0, + .lower_margin = 0, /* V_total=650 */ + .sync = 2, + }, +}; + +struct pxa168fb_mach_info teton_bga_lcd_info __initdata = { + .id = "Base-aspen", + .modes = video_modes_aspen, + .num_modes = ARRAY_SIZE(video_modes_aspen), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_GPIO, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .spi_ctrl = -1, + .spi_gpio_cs = -1, + .spi_gpio_reset = -1, + .gpio_output_data = 0x10, + .gpio_output_mask = 0xff, + .invert_pixclock = 0, + .invert_vsync = 0, + .invert_hsync = 0, + .panel_rbswap = 0, + .max_fb_size = 1024 * 600 * 4 * 2, +}; + +struct pxa168fb_mach_info teton_bga_lcd_ovly_info __initdata = { + .id = "Ovly-aspen", + .modes = video_modes_aspen, + .num_modes = ARRAY_SIZE(video_modes_aspen), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_GPIO, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .spi_ctrl = -1, + .spi_gpio_cs = -1, + .spi_gpio_reset = -1, + .gpio_output_data = 0x10, + .gpio_output_mask = 0xff, + .invert_pixclock = 0, + .invert_vsync = 0, + .invert_hsync = 0, + .panel_rbswap = 0, + .max_fb_size = 1024 * 600 * 4 * 2, +}; + +static struct i2c_board_info teton_bga_i2c_board_info[] = { +#if defined(CONFIG_TSC2007) + { + .type = "tsc2007", + .addr = 0x48, /* 0x90/0x91 */ + .irq = IRQ_GPIO(86), /* EXT_WAKEUP pin for interrupt */ + }, +#endif + { + .type = "ds1337", + .addr = 0x68, + }, + +}; + +static unsigned int teton_bga_default_matrix_key_map[] = { + KEY(0, 6, KEY_UP), /* S4 */ + KEY(1, 6, KEY_LEFT), /* S5 */ + KEY(0, 7, KEY_DOWN), /* S7 */ + KEY(1, 7, KEY_RIGHT), /* S8 */ +}; + +static unsigned int teton_bga_android_matrix_key_map[] = { + KEY(0, 6, KEY_BACK), /* S4 */ + KEY(1, 6, KEY_MENU), /* S5 */ + KEY(0, 7, KEY_ENTER), /* S7 */ +}; + +static struct pxa27x_keypad_platform_data teton_bga_default_keypad_info + __initdata = { + .matrix_key_rows = 8, + .matrix_key_cols = 8, + .matrix_key_map = teton_bga_default_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(teton_bga_default_matrix_key_map), + .debounce_interval = 30, +}; + +static struct pxa27x_keypad_platform_data teton_bga_android_keypad_info + __initdata = { + .matrix_key_rows = 8, + .matrix_key_cols = 8, + .matrix_key_map = teton_bga_android_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(teton_bga_android_matrix_key_map), + .debounce_interval = 30, +}; + +DECLARE_ANDROID_512M_V75_PARTITIONS(android_512m_v75_partitions); +DECLARE_512M_V75_PARTITIONS(generic_512m_v75_partitions); +static struct pxa3xx_nand_platform_data teton_bga_nand_info; +static void __init teton_bga_add_nand(void) +{ + if (is_android()) { + teton_bga_nand_info.parts[0] = android_512m_v75_partitions; + teton_bga_nand_info.nr_parts[0] = + ARRAY_SIZE(android_512m_v75_partitions); + } else { + teton_bga_nand_info.parts[0] = generic_512m_v75_partitions; + teton_bga_nand_info.nr_parts[0] = + ARRAY_SIZE(generic_512m_v75_partitions); + } + + teton_bga_nand_info.use_dma = 1; + teton_bga_nand_info.enable_arbiter = 1; + pxa168_add_nand((struct flash_platform_data *)&teton_bga_nand_info); +} + +#if defined(CONFIG_MMC_PXA_SDH) +static struct pfn_cfg mmc1_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, GPIO37_MMC1_DAT7, GPIO37_GPIO), + PFN_CFG(PIN_MMC_DAT6, GPIO38_MMC1_DAT6, GPIO38_GPIO), + PFN_CFG(PIN_MMC_DAT5, GPIO54_MMC1_DAT5, GPIO54_GPIO), + PFN_CFG(PIN_MMC_DAT4, GPIO48_MMC1_DAT4, GPIO48_GPIO), + PFN_CFG(PIN_MMC_DAT3, GPIO51_MMC1_DAT3, GPIO51_GPIO), + PFN_CFG(PIN_MMC_DAT2, GPIO52_MMC1_DAT2, GPIO52_GPIO), + PFN_CFG(PIN_MMC_DAT1, GPIO40_MMC1_DAT1, GPIO40_GPIO), + PFN_CFG(PIN_MMC_DAT0, GPIO41_MMC1_DAT0, GPIO41_GPIO), + PFN_CFG(PIN_MMC_CMD, GPIO49_MMC1_CMD, GPIO49_GPIO), + PFN_CFG(PIN_MMC_CLK, GPIO43_MMC1_CLK, GPIO43_GPIO), + PFN_CFG(PIN_MMC_CD, GPIO53_MMC1_CD, GPIO53_GPIO), + PFN_CFG(PIN_MMC_WP, GPIO46_MMC1_WP, GPIO46_GPIO), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; + +static int sdh_mfp_config_mmc1(void) +{ + int ret = 0; + if (!ret) + pfn_config(mmc1_pfn_cfg, PFN_FN); + return ret; +} + +static struct pxasdh_platform_data teton_bga_sdh_platform_data_MMC1 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_29_30 | MMC_VDD_30_31, + .mfp_config = sdh_mfp_config_mmc1, + .pfn_table = mmc1_pfn_cfg, +}; + +#define MMC_PWEN (27) +static int __init teton_bga_init_mmc(void) +{ + printk(KERN_NOTICE "Enabling MMC Power..\r\n"); + if (gpio_request(MMC_PWEN, "MMC PWEN")) { + printk(KERN_ERR "Failed to enable MMC Power\n"); + return -1; + } + + /* set MMC power enable (not) pin to low */ + gpio_direction_output(MMC_PWEN, 0); + gpio_free(MMC_PWEN); + + return 0; +} + +#if defined(CONFIG_WLAN_8688_SDIO) +static struct pfn_cfg mmc2_pfn_cfg[] = { + PFN_CFG(PIN_MMC_DAT7, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT6, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT5, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT4, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_DAT3, GPIO122_MMC2_DAT3, GPIO122_GPIO), + PFN_CFG(PIN_MMC_DAT2, GPIO121_MMC2_DAT2, GPIO121_GPIO), + PFN_CFG(PIN_MMC_DAT1, GPIO120_MMC2_DAT1, GPIO120_GPIO), + PFN_CFG(PIN_MMC_DAT0, GPIO119_MMC2_DAT0, GPIO119_GPIO), + PFN_CFG(PIN_MMC_CMD, GPIO117_MMC2_CMD, GPIO117_GPIO), + PFN_CFG(PIN_MMC_CLK, GPIO118_MMC2_CLK, GPIO118_GPIO), + PFN_CFG(PIN_MMC_CD, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_WP, PFN_UNDEF, PFN_UNDEF), + PFN_CFG(PIN_MMC_END, PFN_TERM, PFN_TERM), +}; + +#define RST_WIFI (81) +static int __init teton_bga_reset_wifi(void) +{ + printk(KERN_NOTICE "Resetting WIFI..\r\n"); + if (gpio_request(RST_WIFI, "RST WIFI")) { + printk(KERN_ERR "Failed to Reset WiFi Module\n"); + gpio_free(RST_WIFI); + return -1; + } + + gpio_direction_output(RST_WIFI, 0); + mdelay(500); + gpio_direction_output(RST_WIFI, 1); + gpio_free(RST_WIFI); + + return 0; +} + +static struct pxasdh_platform_data teton_bga_sdh_platform_data_MMC2 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_29_30 | MMC_VDD_30_31, + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION, + .pfn_table = mmc2_pfn_cfg, +}; + +#endif +#endif + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static int teton_bga_u2o_vbus_status(unsigned base) +{ + int status = VBUS_HIGH; + +#ifdef CONFIG_USB_OTG + /* FIXME on teton_bga R0 boards otg stat1/stat2 could not + * reflect VBUS status yet, check with U2O itself instead + */ + if (u2o_get(base, U2xOTGSC) & U2xOTGSC_BSV) + status = VBUS_HIGH; + else + status = VBUS_LOW; +#endif + return status; +} + +#define USB_HPENA (85) +static int teton_bga_u2o_vbus_set(int vbus_type) +{ + unsigned long flags; + + local_irq_save(flags); + + if (gpio_request(USB_HPENA, "USB OTG Host Power Enable")) { + printk(KERN_ERR "%s USB_HPENA GPIO Request" + " Failed\n", __func__); + return -1; + } + + switch (vbus_type) { + case VBUS_SRP: + gpio_direction_output(USB_HPENA, 1); + udelay(10); + gpio_direction_output(USB_HPENA, 0); + break; + case VBUS_HIGH: + gpio_direction_output(USB_HPENA, 1); + break; + case VBUS_LOW: + gpio_direction_output(USB_HPENA, 0); + break; + default: + break; + } + gpio_free(USB_HPENA); + + local_irq_restore(flags); + + return 0; +} + +static int teton_bga_otg_init(void) +{ + return 0; +} + +static int teton_bga_u2o_vbus_set_ic(int function) +{ + printk(KERN_DEBUG "%s %d not implemented yet\n", __func__, function); + return 0; +} + +static struct otg_pmic_ops teton_bga_otg_ops = { + .otg_vbus_init = teton_bga_otg_init, + .otg_set_vbus = teton_bga_u2o_vbus_set, + .otg_set_vbus_ic = teton_bga_u2o_vbus_set_ic, + .otg_get_vbus_state = teton_bga_u2o_vbus_status, +}; + +struct otg_pmic_ops *init_teton_bga_otg_ops(void) +{ + return &teton_bga_otg_ops; +} + +static struct pxa_usb_plat_info teton_bga_u2o_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_set = teton_bga_u2o_vbus_set, + .vbus_status = teton_bga_u2o_vbus_status, + .init_pmic_ops = init_teton_bga_otg_ops, + .is_otg = 1, +}; +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H +/* USB 2.0 Host Controller */ +static int teton_bga_u2h_vbus_set(int enable) +{ + return 0; +} + +static struct pxa_usb_plat_info teton_bga_u2h_info = { + .phy_init = pxa168_usb_phy_init, + .vbus_set = teton_bga_u2h_vbus_set, +}; +#endif + +static void __init teton_bga_init(void) +{ + pxa168_mfp_set_fastio_drive(MFP_DS02X); + pxa168_set_vdd_iox(VDD_IO0, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO1, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO2, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO3, VDD_IO_3P3V); + pxa168_set_vdd_iox(VDD_IO4, VDD_IO_3P3V); + mfp_config(ARRAY_AND_SIZE(teton_bga_pin_config)); + + /* on-chip devices */ + + pxa168_add_uart(1); + teton_bga_add_nand(); + pxa168_add_ssp(0); + + pxa168_add_twsi(0, &pwri2c_info, + ARRAY_AND_SIZE(teton_bga_i2c_board_info)); + + if (is_android()) + pxa168_add_keypad(&teton_bga_android_keypad_info); + else + pxa168_add_keypad(&teton_bga_default_keypad_info); + +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&teton_bga_u2o_info); +#endif + +#ifdef CONFIG_USB_OTG + pxa168_add_u2ootg(&teton_bga_u2o_info); + pxa168_add_u2oehci(&teton_bga_u2o_info); +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2H + pxa168_add_u2h(&teton_bga_u2h_info); +#endif + pxa168_add_mfu(&pxa168_eth_data); + +#if defined(CONFIG_MMC_PXA_SDH) + if (teton_bga_init_mmc() == 0) + pxa168_add_sdh(0, &teton_bga_sdh_platform_data_MMC1); +#if defined(CONFIG_WLAN_8688_SDIO) + if (teton_bga_reset_wifi() == 0) + pxa168_add_sdh(1, &teton_bga_sdh_platform_data_MMC2); +#endif +#endif + +#if 0 /* disabled CIR due to UART_RX conflict */ + pxa168_cir_init(); /*init the gpio */ +#endif + +#if defined(CONFIG_PXA168_MSP) + pxa168_add_msp(&msp_ops); +#endif + +#if defined(CONFIG_PXA168_CF) + if (teton_bga_init_CF() == 0) + pxa168_cf_init(); +#endif + pxa168_add_freq(); + + if (teton_bga_init_BKL() == 0) { + pxa168_add_fb(&teton_bga_lcd_info); + pxa168_add_fb_ovly(&teton_bga_lcd_ovly_info); + } +#if defined(CONFIG_PXA_ICR) + pxa168_add_icr(); +#endif + +#if defined(CONFIG_SND_SOC_CS4344) + pxa168_add_cs4344(); +#endif +} + +MACHINE_START(TETON_BGA, "PXA168-based Teton BGA Platform") + .phys_io = APB_PHYS_BASE, + .boot_params = 0x00000100, + .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, + .map_io = pxa_map_io, + .init_irq = pxa168_init_irq, + .timer = &pxa168_timer, + .init_machine = teton_bga_init, +MACHINE_END diff --git a/arch/arm/mach-mmp/time.c b/arch/arm/mach-mmp/time.c index cf75694e9687d8..b972e5fbbb7d4f 100644 --- a/arch/arm/mach-mmp/time.c +++ b/arch/arm/mach-mmp/time.c @@ -28,14 +28,12 @@ #include #include +#include #include -#include #include +#include #include #include -#include - -#include "clock.h" #define TIMERS_VIRT_BASE TIMERS1_VIRT_BASE @@ -61,20 +59,52 @@ static void __init set_tcr2ns_scale(unsigned long tcr_rate) } /* - * FIXME: the timer needs some delay to stablize the counter capture + * Note: the timer needs some delay to stablize the counter capture */ static inline uint32_t timer_read(void) { - int delay = 100; + volatile int delay = 2; + unsigned long flags; + uint32_t val = 0; + + local_irq_save(flags); + +#ifdef CONFIG_PXA_32KTIMER + /* 32k timer needs more delay due to brain-damaged hardware :-( */ + //if (CLOCK_TICK_RATE == 32768) + //delay = 100;*/ + val = __raw_readl(TIMERS_VIRT_BASE + TMR_CR(0)); +#else __raw_writel(1, TIMERS_VIRT_BASE + TMR_CVWR(0)); - while (delay--) - cpu_relax(); + while (delay--) { + val = __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(0)); + } + + val = __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(0)); +#endif + local_irq_restore(flags); - return __raw_readl(TIMERS_VIRT_BASE + TMR_CVWR(0)); + return val; } +unsigned int read_timer(void) +{ + return (unsigned int)timer_read(); +} + +#if 0 /* FIX-ME: rzaman: figure out how to resolve this */ +unsigned long read_persistent_clock(void) +{ + unsigned int ticks; + unsigned long sec; + ticks = read_timer(); + sec = (unsigned long)ticks >> 15; + return sec; +} +#endif + unsigned long long sched_clock(void) { unsigned long long v = cnt32_to_63(timer_read()); @@ -133,8 +163,13 @@ static void timer_set_mode(enum clock_event_mode mode, static struct clock_event_device ckevt = { .name = "clockevent", .features = CLOCK_EVT_FEAT_ONESHOT, +#ifdef CONFIG_PXA_32KTIMER + .shift = 32, + .rating = 150, +#else .shift = 32, .rating = 200, +#endif .set_next_event = timer_set_next_event, .set_mode = timer_set_mode, }; @@ -146,8 +181,13 @@ static cycle_t clksrc_read(struct clocksource *cs) static struct clocksource cksrc = { .name = "clocksource", +#ifdef CONFIG_PXA_32KTIMER + .shift = 10, + .rating = 150, +#else .shift = 20, .rating = 200, +#endif .read = clksrc_read, .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, @@ -182,7 +222,7 @@ static struct irqaction timer_irq = { .dev_id = &ckevt, }; -void __init timer_init(int irq) +static void __init timer_init(int irq) { timer_config(); @@ -201,6 +241,157 @@ void __init timer_init(int irq) clockevents_register_device(&ckevt); } +static void __init pxa168_timer_init(void) +{ + uint32_t clk_rst; + + /* this is early, we have to initialize the CCU registers by + * ourselves instead of using clk_* API. Clock rate is defined + * by APBC_TIMERS_FNCLKSEL and enabled free-running + */ + __raw_writel(APBC_APBCLK | APBC_RST, APBC_PXA168_TIMERS); + +#ifdef CONFIG_PXA_32KTIMER + /* 32KHz, bus/functional clock enabled, release reset */ + clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(1); +#else + /* 3.25MHz, bus/functional clock enabled, release reset */ + clk_rst = APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3); +#endif + __raw_writel(clk_rst, APBC_PXA168_TIMERS); + + timer_init(IRQ_PXA168_TIMER1); +} + +#ifdef CONFIG_PM +struct tmr_regs { + unsigned int tmr_ccr; + unsigned int tmr_tn_mm[9]; + unsigned int tmr_crn[3]; + unsigned int tmr_srn[3]; + unsigned int tmr_iern[3]; + unsigned int tmr_plvrn[3]; + unsigned int tmr_plcrn[3]; + unsigned int tmr_wmer; + unsigned int tmr_wmr; + unsigned int tmr_wvr; + unsigned int tmr_wsr; + unsigned int tmr_icrn[3]; + unsigned int tmr_wicr; + unsigned int tmr_cer; + unsigned int tmr_cmr; + unsigned int tmr_ilrn[3]; + unsigned int tmr_wcr; + unsigned int tmr_wfar; + unsigned int tmr_wsar; + unsigned int tmr_cvwrn[3]; +}; + +static struct tmr_regs tmr_saved_regs; +/* static struct tmr_regs tmr2_saved_regs; */ + +static void pxa168_tmr_save(struct tmr_regs *context, unsigned int tmr_base) +{ + unsigned int i, j, temp; + + context->tmr_ccr = __raw_readl(tmr_base + TMR_CCR); + for (i = 0 ;i < 3;i++) { + /* double read to avoid metastability */ + do { + temp = __raw_readl(tmr_base + TMR_CR(i)); + context->tmr_crn[i] = __raw_readl(tmr_base + TMR_CR(i)); + } while (context->tmr_crn[i] != temp); + } + /* save time difference instead of match counter register itself */ + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + context->tmr_tn_mm[i*3+j] = __raw_readl(tmr_base + TMR_TN_MM(i,j)) + - context->tmr_crn[i]; + for (i = 0 ;i < 3;i++) + context->tmr_iern[i] = __raw_readl(tmr_base + TMR_IER(i)); + for (i = 0 ;i < 3;i++) + context->tmr_plvrn[i] = __raw_readl(tmr_base + TMR_PLVR(i)); + for (i = 0 ;i < 3;i++) + context->tmr_plcrn[i] = __raw_readl(tmr_base + TMR_PLCR(i)); + for (i = 0 ;i < 3;i++) + context->tmr_ilrn[i] = __raw_readl(tmr_base + TMR_ILR(i)); + context->tmr_cmr = __raw_readl(tmr_base + TMR_CMR); + context->tmr_cer = __raw_readl(tmr_base + TMR_CER); + + /* the following should be done in a watchdog driver... */ + context->tmr_wmer = __raw_readl(tmr_base + TMR_WMER); + do { + temp = __raw_readl(tmr_base + TMR_WVR); + context->tmr_wvr = __raw_readl(tmr_base + TMR_WVR); + } while (context->tmr_wvr != temp); + context->tmr_wmr = __raw_readl(tmr_base + TMR_WMR) - context->tmr_wvr; + context->tmr_wicr = __raw_readl(tmr_base + TMR_WICR); + context->tmr_wcr = __raw_readl(tmr_base + TMR_WCR); +} + +static void pxa168_tmr_restore(struct tmr_regs *context, unsigned int tmr_base) +{ + unsigned int i, j, temp; + + __raw_writel(context->tmr_ccr, tmr_base + TMR_CCR); + for (i = 0 ;i < 3;i++) { + /* double read to avoid metastability */ + do { + temp = __raw_readl(tmr_base + TMR_CR(i)); + context->tmr_crn[i] = __raw_readl(tmr_base + TMR_CR(i)); + } while (context->tmr_crn[i] != temp); + } + /* restore match counter register based on current counter and + * the time difference we saved before */ + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) { + context->tmr_tn_mm[3*i+j] += context->tmr_crn[i]; + __raw_writel(context->tmr_tn_mm[3*i+j], tmr_base + TMR_TN_MM(i,j)); + } + for (i = 0 ;i < 3;i++) + __raw_writel(context->tmr_iern[i], tmr_base + TMR_IER(i)); + for (i = 0 ;i < 3;i++) + __raw_writel(0x7, tmr_base + TMR_ICR(i)); + for (i = 0 ;i < 3;i++) + __raw_writel(context->tmr_plvrn[i], tmr_base + TMR_PLVR(i)); + for (i = 0 ;i < 3;i++) + __raw_writel(context->tmr_plcrn[i], tmr_base + TMR_PLCR(i)); + for (i = 0 ;i < 3;i++) + __raw_writel(context->tmr_ilrn[i], tmr_base + TMR_ILR(i)); + __raw_writel(context->tmr_cmr, tmr_base + TMR_CMR); + __raw_writel(context->tmr_cer, tmr_base + TMR_CER); + + /* the following should be done in a watchdog driver... */ + __raw_writel(0xbaba, tmr_base + TMR_WFAR); + __raw_writel(0xeb10, tmr_base + TMR_WSAR); + __raw_writel(context->tmr_wmr, tmr_base + TMR_WMR); + __raw_writel(context->tmr_wicr, tmr_base + TMR_WICR); + __raw_writel(context->tmr_wcr, tmr_base + TMR_WCR); + __raw_writel(context->tmr_wmer, tmr_base + TMR_WMER); +} + +static void pxa_timer_suspend(void) +{ + pxa168_tmr_save(&tmr_saved_regs, TIMERS1_VIRT_BASE); + /* pxa168_tmr_save(&tmr2_saved_regs, TIMERS2_VIRT_BASE); */ +} + +static void pxa_timer_resume(void) +{ + pxa168_tmr_restore(&tmr_saved_regs, TIMERS1_VIRT_BASE); + /* pxa168_tmr_restore(&tmr2_saved_regs, TIMERS2_VIRT_BASE); */ +} +#else +#define pxa_timer_suspend NULL +#define pxa_timer_resume NULL +#endif + +struct sys_timer pxa168_timer = { + .init = pxa168_timer_init, + .suspend = pxa_timer_suspend, + .resume = pxa_timer_resume, +}; + static void __init mmp2_timer_init(void) { unsigned long clk_rst; diff --git a/arch/arm/mach-mmp/timer_services.c b/arch/arm/mach-mmp/timer_services.c new file mode 100644 index 00000000000000..17ec38fdf5b6e4 --- /dev/null +++ b/arch/arm/mach-mmp/timer_services.c @@ -0,0 +1,503 @@ +/* + * linux/arch/arm/mach-ttc/timer_services.c + * + * Support for the MMP Development Platform + * + * Copyright (C) 2008 Marvell International Ltd. + * + * 2009-08-1: Ofer Zaarur + * + * 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. + * + * + * the driver provides timer services in parallel to the OS timer,currently it + * provices three timers and can be expend to six timer. each timer has its + * own resolution and can be enable or disable independently. + * the timer services driver can be set to different resolution that are not + * related to the OS timer. with very high resolution that can be changes + * accordingly to meet driver client requirements.in addition you can handle + * the resolution in your driver and just access directly the counter, in this + * case the driver will need to handle the translation for the required scale. + * + * for performance aspect you should make sure disable the timer when in not + * been used to minimize the interrupt. please be advicve that using periodic + * delay in your driver could introduce system delay. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + + +unsigned long resolution; +unsigned int jiff; +static int resolution_list[3] = { RESOLUTION_0, RESOLUTION_1, RESOLUTION_2}; +static struct timer_dev timers[3]; +static seqlock_t xtimer_lock[4]; /* 1-3 individual lock and 4 global lock*/ + +static irqreturn_t timer_services_interrupt(int irq, void *dev_id) +{ +struct timer_dev *dev = (struct timer_dev *) dev_id; +struct timer_device *timer = dev->timer; + +write_seqlock(&xtimer_lock[timer->counter_id]); +do { + /* Clear timer interrupt */ + writel((1 << timer->counter_id), timer->mmio_base + TMR_ICR1_OFFSET); + /* Calculate the next matching value */ + resolution = readl(timer->mmio_base + TMR_T1_M0_OFFSET + + (MATCH_REGISTER_OFFSET * timer->counter_id) + + (4 * timer->counter_id)); + /* Read current matching value */ + resolution += timer->resolution; + /* NOTE: We do not disable the timer before updating + TMR_T1_M0. Ignore what the specs says... */ + writel(resolution, timer->mmio_base + TMR_T1_M0_OFFSET + + (MATCH_REGISTER_OFFSET * timer->counter_id) + 4 * timer->counter_id); + ++timer->jiff; + +} while ((signed long)(resolution - readl(timer->mmio_base + + TMR_CR1_OFFSET)) <= 8); +write_sequnlock(&xtimer_lock[timer->counter_id]); +return IRQ_HANDLED; +} + +/** + * check_id - verify the ID number + */ + +int check_id(int id) +{ +if ((id) > 2 || ((id) < 0)) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return -EINVAL; +} else + return 0; +} + +/** + * timer_enable - enable the Timer counter + * + * Turn on the Timer ID. + * this function provides several modes to enable the timer: + * Continuasly running mode the counter will run in free mode and will generate + * interrupts accordingly. + * on demand mode the function provides period parameter that will be send with + * enable request,the timer will stop and will turn to disable mode after the + * requester period. + * + * period are in msec. + */ + +void timer_services_enable(int id, int period) +{ + unsigned int tmp; + +if (check_id(id) > 0) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return; +} + +if (period >= -1) { + write_seqlock(&xtimer_lock[id+1]); + write_seqlock(&xtimer_lock[id]); + tmp = readl(timers[id].timer->mmio_base + TMR_IER1_OFFSET); + writel(tmp | (1 << id), timers[id].timer->mmio_base + + TMR_IER1_OFFSET); + tmp = readl(timers[id].timer->mmio_base + TMR_CR1_OFFSET); + tmp += timers[id].timer->resolution; + writel(tmp, timers[id].timer->mmio_base + TMR_T1_M0_OFFSET + + (MATCH_REGISTER_OFFSET * timers[id].timer->counter_id) + + 4 * timers[id].timer->counter_id); + write_sequnlock(&xtimer_lock[id+1]); + write_sequnlock(&xtimer_lock[id]); + if (period != -1) { + mdelay(period); + write_seqlock(&xtimer_lock[id+1]); + write_seqlock(&xtimer_lock[id]); + tmp = readl(timers[id].timer->mmio_base + + TMR_IER1_OFFSET); + writel(tmp & ~(1 << id), timers[id].timer->mmio_base + + TMR_IER1_OFFSET); + write_sequnlock(&xtimer_lock[id+1]); + write_sequnlock(&xtimer_lock[id]); + } + } + +else { + printk(KERN_ERR "The period parameter for timer-%d \ + is out of range :( \n", id); + } +} +EXPORT_SYMBOL(timer_services_enable); + +/** + * timer_disable - shut down the Timer ID + * + * Turn off the Timer counter, optionally powering it down. + */ + +void timer_services_disable(int id) +{ + unsigned int tmp; + +if (check_id(id) > 0) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return; +} + +write_seqlock(&xtimer_lock[id+1]); +tmp = readl(timers[id].timer->mmio_base + TMR_IER1_OFFSET); +writel(tmp & ~(1 << id), timers[id].timer->mmio_base + TMR_IER1_OFFSET); +write_sequnlock(&xtimer_lock[id+1]); + +} +EXPORT_SYMBOL(timer_services_disable); + +/** + * timer_services_counter_read + * + * return the counter value. + */ + +unsigned int timer_services_counter_read(int id) +{ + unsigned int ret; + +if (check_id(id) > 0) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return -EINVAL; +} + +ret = readl(timers[id].timer->mmio_base + TMR_CR1_OFFSET); +return ret; +} +EXPORT_SYMBOL(timer_services_counter_read); + +/** + * timer_config - configure Timer counter settings + * @speed: counter speed + * + */ + +int timer_config(struct timer_dev *dev, u32 period) +{ +/* configuration option */ +return 0; +} + + +/** + * timer_init - setup the Timer counter + * + * initialise and claim resources for the Timer counter. + *The driver uses timer0 and counter1 within three matches registers + * Returns: + * %-ENODEV if the Timer counter is unavailable + * %-EBUSY if the resources are already in use + * %0 on success + */ + +int timer_init(struct timer_dev *dev, int id, u32 resolution) +{ + struct timer_device *timer; + unsigned long tmp; + + timer = timer_request(id, "TIMER"); + if (timer == NULL) + return -ENODEV; + + if (check_id(id) > 0) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return -EINVAL; +} +dev->timer = timer; +dev->counter = id; + + tmp = readl(timer->mmio_base + TMR_CMR_OFFSET); + + writel((tmp | 1<<1), timer->mmio_base + TMR_CMR_OFFSET); /* Setup + conter to free running mode - UNDOCUMENTED */ + + writel(0, timer->mmio_base + + TMR_PLVR1_OFFSET); /* Setup preload value */ + + + writel(0, timer->mmio_base + + TMR_PLCR1_OFFSET); /* Set to free running mode for preload + control */ + + writel(0x7, timer->mmio_base + + TMR_ICR1_OFFSET); /* Clear any pending match status {0-2} + for timer 0 */ + + writel(1, timer->mmio_base + + TMR_CVWR1_OFFSET); /* Cause the latch */ + + tmp = readl(timer->mmio_base + + TMR_CVWR1_OFFSET); + tmp += resolution; + + writel(tmp, timer->mmio_base + + TMR_T1_M0_OFFSET + (MATCH_REGISTER_OFFSET * timer->counter_id)); + /* Prime the match register */ + + /* + * Enable match irq for the timer services. + */ + + writel(3, timer->mmio_base + + TMR_CER_OFFSET); /* Enable timer */ + + return 0; + +} +EXPORT_SYMBOL(timer_init); + +/** + * timer_exit - undo the effects of timer_init + * + * release and free resources for the Timer counter. + */ +void timer_exit(struct timer_dev *dev) +{ +struct timer_device *timer = dev->timer; +timer_services_disable(timer->counter_id); +timer_free(timer); + +} +EXPORT_SYMBOL(timer_exit); + +static LIST_HEAD(timer_list); + +struct timer_device *timer_request(int id, const char *label) +{ + + struct timer_device *timer = NULL; + + if (check_id(id) > 0) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return NULL; + } + + write_seqlock(&xtimer_lock[id + 1]); + + list_for_each_entry(timer, &timer_list, node) { + if (timer->counter_id == id && timer->use_count == 0) { + timer->use_count++; + timer->label = label; + break; + } + } + + write_sequnlock(&xtimer_lock[id + 1]); + if (timer->counter_id != id) + return NULL; + return timer; +} +EXPORT_SYMBOL(timer_request); + +void timer_free(struct timer_device *timer) +{ +if (check_id(timer->counter_id) > 0) { + printk(KERN_ERR "Timer ID error, please check timer ID\n"); + return; +} + + write_seqlock(&xtimer_lock[timer->counter_id]); + if (timer->use_count) { + timer->use_count--; + timer->label = NULL; + } else + dev_err(&timer->pdev->dev, "device already free\n"); + write_sequnlock(&xtimer_lock[timer->counter_id]); +} +EXPORT_SYMBOL(timer_free); + + +static int __devinit timer_probe(struct platform_device *pdev, int type) +{ + + struct resource *res; + struct timer_device *timer; + int ret = 0; + + timer = kzalloc(sizeof(struct timer_device), GFP_KERNEL); + if (timer == NULL) { + dev_err(&pdev->dev, "failed to allocate memory"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + ret = -ENODEV; + } + + timer->mmio_base = ioremap_nocache(res->start, SZ_256); + + if (timer->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to ioremap() registers\n"); + ret = -ENODEV; + goto err_free_mem; + } + + timer->irq = platform_get_irq(pdev, 0); + if (timer->irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + ret = -ENODEV; + goto err_free_io; + } + + ret = request_irq(timer->irq, timer_services_interrupt, 0 , + "Timer Services", timer); + + if (ret) + goto err_free_io; + + if (check_id(pdev->id) > 0) { + dev_err(&pdev->dev, "Timer ID error, please check timer ID\n"); + ret = -EINVAL; + goto err_free_io; + } + + timer->counter_id = pdev->id ; + timers[timer->counter_id].timer = timer; + timer->jiff = 0; + timer->resolution = resolution_list[timer->counter_id]; + timer->use_count = 0; + timer->type = type; + + write_seqlock(&xtimer_lock[timer->counter_id]); + list_add(&timer->node, &timer_list); + write_sequnlock(&xtimer_lock[timer->counter_id]); + /* timer init*/ + timer_init((struct timer_dev *)timer, 0 , timer->resolution); + platform_set_drvdata(pdev, timer); + return 0; + +err_free_io: + iounmap(timer->mmio_base); +err_free_mem: + release_mem_region(res->start, res->end - res->start + 1); + + return ret; +} + +static int timer_remove(struct platform_device *pdev) +{ + struct resource *res; + struct timer_device *timer; + + timer = platform_get_drvdata(pdev); + if (timer == NULL) + return -ENODEV; + + iounmap(timer->mmio_base); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + if (check_id(pdev->id) > 0) { + dev_err(&pdev->dev, "Timer ID error, please check timer ID\n"); + return -EINVAL; + } + + write_seqlock(&xtimer_lock[timer->counter_id]); + list_del(&timer->node); + write_sequnlock(&xtimer_lock[timer->counter_id]); + + kfree(timer); + return 0; +} + +static int mmp_timer_probe(struct platform_device *pdev) +{ + return timer_probe(pdev, MMP_TIMER); +} + + +#ifdef CONFIG_PM +/* + * Basic power management. + */ + +static int timer_services_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct timer_device *timer = platform_get_drvdata(pdev); + + timer_free(timer); + + return 0; +} + +static int timer_services_resume(struct platform_device *pdev) +{ + struct timer_device *timer = platform_get_drvdata(pdev); + + timer_init((struct timer_dev *)timer, 0 , timer->resolution); + return 0; +} +#else +#define timer_services_suspend NULL +#define timer_services_resume NULL +#endif +static struct platform_driver mmp_timer_services = { + .driver = { + .name = "mmp-timer-services", + }, + .probe = mmp_timer_probe, + .remove = timer_remove, + .suspend = timer_services_suspend, + .resume = timer_services_resume, +}; + +static int __init timer_services_init(void) +{ + int ret ; + ret = platform_driver_register(&mmp_timer_services); + + if (ret) { + printk(KERN_ERR "failed to register mmp_timer_driver"); + return ret; + } + + return ret; +} + +static void __exit timer_services_exit(void) +{ + platform_driver_unregister(&mmp_timer_services); +} + +unsigned long get_jiff(int id) +{ +return timers[id].timer->jiff; +} +EXPORT_SYMBOL(get_jiff); + +module_init(timer_services_init); +module_exit(timer_services_exit); + +MODULE_DESCRIPTION("Timer driver services"); +MODULE_AUTHOR("Ofer Zaarur"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c index b22dec4abf78e3..d1ab179aadf02e 100644 --- a/arch/arm/mach-mmp/ttc_dkb.c +++ b/arch/arm/mach-mmp/ttc_dkb.c @@ -11,24 +11,127 @@ #include #include #include +<<<<<<< HEAD:arch/arm/mach-mmp/ttc_dkb.c +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +======= #include #include #include +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/ttc_dkb.c #include #include #include #include #include +<<<<<<< HEAD:arch/arm/mach-mmp/ttc_dkb.c +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "common.h" +#include + +#include +#include +#ifdef CONFIG_SD8XXX_RFKILL +#include +#endif + +extern int fake_suspend; + +static int is_td_dkb = 0; +static int __init td_dkb_setup(char *__unused) +{ + is_td_dkb = 1; + return 1; +} +__setup("td_dkb", td_dkb_setup); + + +#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x) + +#define GPIO_EXT0(x) (NR_BUILTIN_GPIO + (x)) +#define GPIO_EXT1(x) (NR_BUILTIN_GPIO + 16 + (x)) + +static unsigned long ttc_dkb_pin_config[] __initdata = { + /* GPS UART */ + GPIO43_UART1_RXD, + GPIO44_UART1_TXD, + + /* GPS GPIO */ + GPIO15_GPIO15, + GPIO16_GPIO16, + GPIO45_GPIO45, /*share with TPO reset*/ + +======= #include #include "common.h" static unsigned long ttc_dkb_pin_config[] __initdata = { +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/ttc_dkb.c /* UART2 */ GPIO47_UART2_RXD, GPIO48_UART2_TXD, +<<<<<<< HEAD:arch/arm/mach-mmp/ttc_dkb.c + /* UART3/BT_UART */ + GPIO29_UART3_CTS, + GPIO30_UART3_RTS, + GPIO31_UART3_TXD, + GPIO32_UART3_RXD, + + /* SMC */ + SM_nCS0_nCS0, + SM_ADV_SM_ADV, + SM_SCLK_SM_SCLK, + SM_SCLK_SM_SCLK, + SM_BE0_SM_BE0, + SM_BE1_SM_BE1, + + /* IRDA */ + GPIO51_IRDA_SHDN, + + /* USB_ID */ + /*GPIO44_USB_ID,*/ + + /* I2C */ + GPIO53_CI2C_SCL, + GPIO54_CI2C_SDA, + + + /* SSP1 (I2S) */ + GPIO24_SSP1_SDATA_IN, + GPIO21_SSP1_BITCLK, + GPIO20_SSP1_SYSCLK, + GPIO22_SSP1_SYNC, + GPIO23_SSP1_DATA_OUT, + GPIO124_MN_CLK_OUT, + GPIO123_CLK_REQ, + +======= +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/ttc_dkb.c /* DFI */ DF_IO0_ND_IO0, DF_IO1_ND_IO1, @@ -52,6 +155,1307 @@ static unsigned long ttc_dkb_pin_config[] __initdata = { DF_WEn_DF_WEn, DF_REn_DF_REn, DF_RDY0_DF_RDY0, +<<<<<<< HEAD:arch/arm/mach-mmp/ttc_dkb.c + + /*keypad*/ + GPIO00_KP_MKIN0, + GPIO01_KP_MKOUT0, + GPIO02_KP_MKIN1, + GPIO03_KP_MKOUT1, + GPIO04_KP_MKIN2, + GPIO05_KP_MKOUT2, + GPIO06_KP_MKIN3, + GPIO07_KP_MKOUT3, + GPIO08_KP_MKIN4, + GPIO09_KP_MKOUT4, + GPIO10_KP_MKIN5, + GPIO11_KP_MKOUT5, + GPIO12_KP_MKIN6, + + /* LCD */ + GPIO81_LCD_FCLK, + GPIO82_LCD_LCLK, + GPIO83_LCD_PCLK, + GPIO84_LCD_DENA, + GPIO85_LCD_DD0, + GPIO86_LCD_DD1, + GPIO87_LCD_DD2, + GPIO88_LCD_DD3, + GPIO89_LCD_DD4, + GPIO90_LCD_DD5, + GPIO91_LCD_DD6, + GPIO92_LCD_DD7, + GPIO93_LCD_DD8, + GPIO94_LCD_DD9, + GPIO95_LCD_DD10, + GPIO96_LCD_DD11, + GPIO97_LCD_DD12, + GPIO98_LCD_DD13, + GPIO100_LCD_DD14, + GPIO101_LCD_DD15, + GPIO102_LCD_DD16, + GPIO103_LCD_DD17, + GPIO104_LCD_SPIDOUT, + GPIO105_LCD_SPIDIN, + GPIO107_LCD_CS1, + GPIO108_LCD_DCLK, + GPIO106_LCD_RESET, + + /*1wire*/ +// GPIO106_1WIRE, + + /*CCIC/CAM*/ + GPIO67_CCIC_IN7, + GPIO68_CCIC_IN6, + GPIO69_CCIC_IN5, + GPIO70_CCIC_IN4, + GPIO71_CCIC_IN3, + GPIO72_CCIC_IN2, + GPIO73_CCIC_IN1, + GPIO74_CCIC_IN0, + GPIO75_CAM_HSYNC, + GPIO76_CAM_VSYNC, + GPIO77_CAM_MCLK, + GPIO78_CAM_PCLK, + GPIO16_CAM_PWR_SUB, + GPIO18_CAM_RESET_SUB, + GPIO15_CAM_PWR_MAIN, + GPIO17_CAM_RESET_MAIN, + GPIO49_CAM_AFEN, + + /*ethernet irq*/ + GPIO13_GPIO13, + +#if defined(CONFIG_RADIO_SI4703) || defined(CONFIG_RADIO_SI4703_MODULE) + GPIO19_FM_RDS_IRQ, + //GPIO20_FM_RESET, +#endif + +#if defined(CONFIG_MMC_PXA_SDH) + /*sdh MMC1*/ + MMC1_DAT7_MMC1_DAT7, + MMC1_DAT6_MMC1_DAT6, + MMC1_DAT5_MMC1_DAT5, + MMC1_DAT4_MMC1_DAT4, + MMC1_DAT3_MMC1_DAT3, + MMC1_DAT2_MMC1_DAT2, + MMC1_DAT1_MMC1_DAT1, + MMC1_DAT0_MMC1_DAT0, + MMC1_CMD_MMC1_CMD, + MMC1_CLK_MMC1_CLK, + MMC1_CD_MMC1_CD, + MMC1_WP_MMC1_WP, + + /*sdh MMC2*/ + MMC2_DAT3_GPIO_37, + MMC2_DAT2_GPIO_38, + MMC2_DAT1_GPIO_39, + MMC2_DAT0_GPIO_40, + MMC2_CMD_GPIO_41, + MMC2_CLK_GPIO_42, + + /*wlan_bt*/ + WLAN_PD_GPIO_14, + WLAN_RESET_GPIO_20, + WLAN_BT_RESET_GPIO_34, + WLAN_MAC_WAKEUP_GPIO_35, + WLAN_LHC_GPIO_36, +#endif +}; + +static struct smc91x_platdata ttc_dkb_smc91x_info = { + .flags = SMC91X_USE_16BIT | SMC91X_NOWAIT, +}; + +static struct resource smc91x_resources[] = { + [0] = { + .start = SMC_CS1_PHYS_BASE + 0x300, + .end = SMC_CS1_PHYS_BASE + 0xfffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = gpio_to_irq(13), + .end = gpio_to_irq(13), + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, + } +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .dev = { + .platform_data = &ttc_dkb_smc91x_info, + }, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +#if defined(CONFIG_PXA168_CAMERA) +/* sensor init */ +static int sensor_power_onoff(int on, int sensor) +{ + unsigned int cam_pwr; + unsigned int cam_reset; + unsigned int cam_afen; + + /* actually, no small power down pin needed */ + cam_pwr = sensor ? GPIO_EXT0(6):mfp_to_gpio(MFP_PIN_GPIO16); + cam_reset = sensor ? GPIO_EXT0(4):GPIO_EXT0(14); + cam_afen = mfp_to_gpio(MFP_PIN_GPIO49); + + if (gpio_request(cam_pwr, "CAM_PWR")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", cam_pwr); + return -EIO; + } + if (gpio_request(cam_reset, "CAM_RESET")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", cam_reset); + return -EIO; + } + if (gpio_request(cam_afen, "CAM_RESET")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", cam_afen); + return -EIO; + } + + if(on){ + gpio_direction_output(cam_afen, 1); + msleep(1); + gpio_direction_output(cam_pwr, 0); + msleep(1); + gpio_direction_output(cam_reset, 0); + msleep(1); + gpio_direction_output(cam_reset, 1); + msleep(1); + /* set MIPI_AVDD1P2V for MIPI IO */ + sanremo_write(SANREMO_LDO12, 0x8); + msleep(1); + }else{ + gpio_direction_output(cam_pwr, 1); + } + + gpio_free(cam_pwr); + gpio_free(cam_reset); + gpio_free(cam_afen); + return 0; +} +static struct sensor_platform_data ov7670_sensor_data = { + .id = SENSOR_LOW, + .power_on = sensor_power_onoff, +}; + +static struct sensor_platform_data ov3640_sensor_data = { + .id = SENSOR_HIGH, + .power_on = sensor_power_onoff, +}; + +/* sensor init over */ +#endif + +#if defined(CONFIG_SANREMO) || defined(CONFIG_SANREMO_MODULE) +static int sanremo_init_irq(void) +{ + return 0; +} + +static int sanremo_ack_irq(void) +{ + return 0; +} + +static void sanremo_init(void) +{ + u8 val; + /* Mask interrupts that are not needed */ + sanremo_write(SANREMO_INTERRUPT_ENABLE1, 0x00); + sanremo_write(SANREMO_INTERRUPT_ENABLE2, 0x00); + sanremo_write(SANREMO_INTERRUPT_ENABLE3, 0x00); + + /* disable LDO5 turn on/off by LDO3_EN */ + sanremo_read(SANREMO_MISC2, &val); + sanremo_write(SANREMO_MISC2, val | 0x80); + + /* enable LDO5 for AVDD_USB */ + sanremo_read(SANREMO_SUPPLIES_EN11, &val); + sanremo_write(SANREMO_SUPPLIES_EN11, val | 0x80); + + /* Set AVDD_USB voltage as 3.3V */ + sanremo_write(SANREMO_LDO5, 0x0F); + + /* avoid SRAM power off during sleep*/ + sanremo_read(SANREMO_SUPPLIES_EN11, &val); + printk("SANREMO_SUPPLIES_EN11 %x\n", val); + sanremo_write(SANREMO_SUPPLIES_EN11, val & 0xBF); /* never enable LDO4: CP controls it */ + sanremo_write(SANREMO_SUPPLIES_EN12, 0xFF); + + /* Enable the ONKEY power down functionality, power hold & watchdog disable */ + sanremo_write(SANREMO_WAKEUP, 0xA6); + + /* IRQ is masked during the power-up sequence and will not be released + * until they have been read for the first time */ + sanremo_write(SANREMO_INTERRUPT_STATUS1, 0x1F); + sanremo_write(SANREMO_INTERRUPT_STATUS2, 0xFF); + sanremo_write(SANREMO_INTERRUPT_STATUS3, 0xFF); + + sanremo_write(SANREMO_GPADC_MISC1, 0x0b); /*enable GADC for CP and touch*/ + sanremo_write(SANREMO_TSI_PREBIAS_TIME, 0x06); + sanremo_write(SANREMO_PD_PREBIAS_TIME, 0x50); + sanremo_write(SANREMO_RTC1, 0x40); /*Set RTC to use the external 32K frequency */ + sanremo_write(SANREMO_RTC_MISC2, 0x2); /* enable to set RTC to use external 32K frequency */ + sanremo_write(SANREMO_AUDIO_Side_Tone_1, 0x24); + sanremo_read(SANREMO_AUDIO_DAC_LO1_CTRL, &val); + sanremo_write(SANREMO_AUDIO_DAC_LO1_CTRL, val|0x60); + + /*sleep mode setting*/ + sanremo_write(SANREMO_SLEEP_MODE1, 0x85); + //sanremo_write(SANREMO_SLEEP_MODE2, 0xaa); + //sanremo_write(SANREMO_SLEEP_MODE3, 0xa2); + //sanremo_write(SANREMO_SLEEP_MODE4, 0x2a); + sanremo_write(SANREMO_VBUCK1_SET_SLP, 0x24); //vbuck1 0.9v in sleep mode + sanremo_write(SANREMO_VBUCK2_SET_SLP, 0x24); + + sanremo_write(SANREMO_LDO6, 0x1b); //set LDO6 to 2.65V for RF +} + +/* sanremo_power_module[] should be consistent with enum + * in include/asm-arm/arch-pxa/pxa3xx_pmic.h */ +static struct power_supply_module sanremo_power_modules[] = { + /* {command, power_module}, */ + {VCC_CORE, SAN_BUCK1}, + {VCC_USB, SAN_LDO5}, + {VCC_CAMERA_ANA, SAN_LDO12}, + {VCC_SDIO, SAN_LDO14}, +}; + + +static struct power_chip sanremo_chips[] = { + {0x40, "sanremoA0", sanremo_power_modules}, + {0x41, "sanremoA1", sanremo_power_modules}, + {0x48, "sanremoB0", sanremo_power_modules}, + {0, NULL, NULL}, +}; + +static struct sanremo_touch_platform_data sanremo_touch_data = { + .ts = {0, 3600, 0, 3800}, + .ts_revert = {-1, 0}, +}; + +static struct sanremo_platform_data sanremo_data = { + .init_irq = sanremo_init_irq, + .ack_irq = sanremo_ack_irq, + .platform_init = sanremo_init, + .power_chips = sanremo_chips, + .tsi = &sanremo_touch_data, +}; +#endif /* CONFIG_PXA3xx_SANREMO || CONFIG_PXA3xx_SANREMO_MODULE*/ + +static struct i2c_pxa_platform_data i2c_info __initdata = { + .use_pio = 1, + .get_ripc = get_RIPC, + .release_ripc = release_RIPC, +}; + +static struct pca953x_platform_data max7312_data[] = { + [0] = { + .gpio_base = GPIO_EXT0(0), + }, +}; + +#if defined(CONFIG_GPIO_PCA9575) + +/* GPIO expander PCA9575 */ +static struct pca9575_platform_data pca9575_data[] = { + [0] = { + .gpio_base = GPIO_EXT1(0), + }, +}; +#endif + +#if defined(CONFIG_RADIO_SI4703) || defined(CONFIG_RADIO_SI4703_MODULE) +int si4703_setup(struct i2c_client *client, void *context) +{ + int reset_pin = mfp_to_gpio(MFP_PIN_GPIO20); + int irq_pin = mfp_to_gpio(MFP_PIN_GPIO19); + + gpio_request(reset_pin, "si4703 FM radio reset"); + gpio_request(irq_pin, "si4703 FM radio interrupt"); + + /* clear GPIO96 edge detect */ + // pxa3xx_mfp_set_edge(MFP_CFG_PIN(GPIO19_FM_RDS_IRQ), MFP_EDGE_NONE); + + /* configure interrupt pin as input */ + gpio_direction_input(reset_pin); + + /* assert reset for 100 ms*/ + gpio_direction_output(reset_pin, 0); + mdelay(100); + + /* deassert reset */ + gpio_set_value(reset_pin, 1); + + gpio_free(reset_pin); + gpio_free(irq_pin); + + return 0; +} + +static struct si4703_platform_data si4703_data = { + .setup = si4703_setup, +}; +#endif + +#if defined(CONFIG_TOUCHSCREEN_TPO) || defined(CONFIG_TOUCHSCREEN_TOPCOM_MODULE) +static void __init top_touch_reset(void) +{ + int tpo_reset = mfp_to_gpio(MFP_PIN_GPIO46); + + if (gpio_request(tpo_reset, "tpo reset")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", tpo_reset); + return -EIO; + } + gpio_direction_output(tpo_reset, 1); + msleep(10); + gpio_set_value(tpo_reset, 0); + gpio_free(tpo_reset); +} +#endif + +static struct i2c_board_info i2c_board_info[] = +{ +#if defined(CONFIG_PORTOFINO) || defined(CONFIG_PORTOFINO_MODULE) + { + .type = "portofino", + .addr = 0x11, + }, +#endif + +#if defined(CONFIG_SANREMO) || defined(CONFIG_SANREMO_MODULE) + { + .type = "sanremo", + .addr = 0x30, + .platform_data = &sanremo_data , + .irq = IRQ_PXA168_PMIC_INT, + }, + { + .type = "sanremo", + .addr = 0x34, + .platform_data = &sanremo_data , + .irq = IRQ_PXA168_PMIC_INT, + }, +#endif +#if defined(CONFIG_PXA168_CAMERA) + { + .type = "ov7670", + .addr = 0x21, + .platform_data = &ov7670_sensor_data, + }, + { + .type = "ov3640", + .addr = 0x3C, + .platform_data = &ov3640_sensor_data, + }, +#endif +#if defined(CONFIG_TOUCHSCREEN_TPO) || defined(CONFIG_TOUCHSCREEN_TOPCOM_MODULE) + { + .type = "tpo_touch", + .addr = 0x18, + .irq = gpio_to_irq(45), + }, +#endif +#if defined(CONFIG_RADIO_SI4703) || defined(CONFIG_RADIO_SI4703_MODULE) + { + .type = "si4703", + .addr = 0x10, + .platform_data = &si4703_data, + .irq = IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO19)), + }, +#endif + +}; + +static struct i2c_board_info ttc_dkb_i2c_board_info[] = +{ + { + .type = "max7312", + .addr = 0x20, + .irq = IRQ_GPIO(80), + .platform_data = &max7312_data, + }, +}; + +static struct i2c_board_info td_dkb_i2c_board_info[] = +{ +#if defined(CONFIG_GPIO_PCA9575) + { + .type = "pca9575", + .addr = 0x20, + .irq = IRQ_GPIO(19), + .platform_data = &pca9575_data, + }, +#endif +}; + +static void (*spi_send)(struct pxa168fb_info *, void *, int , unsigned int ); + +#ifdef CONFIG_FB_PXA168 +static u16 tpo_spi_cmdon[] = { + 0x0801, + 0x0800, + 0x0200, + 0x0304, + 0x040e, + 0x0903, + 0x0b18, + 0x0c53, + 0x0d01, + 0x0ee0, + 0x0f01, + 0x1058, + 0x201e, + 0x210a, + 0x220a, + 0x231e, + 0x2400, + 0x2532, + 0x2600, + 0x27ac, + 0x2904, + 0x2aa2, + 0x2b45, + 0x2c45, + 0x2d15, + 0x2e5a, + 0x2fff, + 0x306b, + 0x310d, + 0x3248, + 0x3382, + 0x34bd, + 0x35e7, + 0x3618, + 0x3794, + 0x3801, + 0x395d, + 0x3aae, + 0x3bff, + 0x07c9, //auto power on +}; + +static u16 tpo_spi_cmdoff[] = { + 0x07d9, //auto power off +}; + +static void tpo_lcd_power(struct pxa168fb_info *fbi, unsigned int spi_gpio_cs, unsigned int spi_gpio_reset, int on) +{ + int err = 0; + /* turn on backlight on ttc dkb */ + if (machine_is_ttc_dkb()) { + portofino_write(0x10,0x0e); + portofino_write(0x15,0x02); + portofino_write(0x16,0x10); + portofino_write(0x00,0x0f); + portofino_write(0x01,0x00); + portofino_write(0x02,0x0f); + portofino_write(0x03,0x29); + } + if (on) { + if (spi_gpio_reset != -1) { + err = gpio_request(spi_gpio_reset, "TPO_LCD_SPI_RESET"); + if (err) { + printk("failed to request GPIO for TPO LCD RESET\n"); + return; + } + gpio_direction_output(spi_gpio_reset, 0); + msleep(100); + gpio_set_value(spi_gpio_reset, 1); + msleep(100); + gpio_free(spi_gpio_reset); + } + pxa168fb_spi_send(fbi, tpo_spi_cmdon, ARRAY_SIZE(tpo_spi_cmdon), spi_gpio_cs); + spi_send(fbi, tpo_spi_cmdon, ARRAY_SIZE(tpo_spi_cmdon), spi_gpio_cs); + } else + spi_send(fbi, tpo_spi_cmdoff, ARRAY_SIZE(tpo_spi_cmdoff), spi_gpio_cs); +} + +static struct fb_videomode video_modes[] = { + /* lpj032l001b HVGA mode info */ + [0] = { + .pixclock = 100000, + .refresh = 60, + .xres = 320, + .yres = 480, + .hsync_len = 10, + .left_margin = 15, + .right_margin = 10, + .vsync_len = 2, + .upper_margin = 4, + .lower_margin = 2, + .sync = 0, + }, +}; + +/* SPI Control Register. */ +#define CFG_SCLKCNT(div) (div<<24) /* 0xFF~0x2 */ +#define CFG_RXBITS(rx) ((rx - 1)<<16) /* 0x1F~0x1 */ +#define CFG_TXBITS(tx) ((tx - 1)<<8) /* 0x1F~0x1, 0x1: 2bits ... 0x1F: 32bits */ +#define CFG_SPI_ENA(spi) (spi<<3) +#define CFG_SPI_SEL(spi) (spi<<2) /* 1: port1; 0: port0 */ +#define CFG_SPI_3W4WB(wire) (wire<<1) /* 1: 3-wire; 0: 4-wire */ + +static struct pxa168fb_mach_info ttc_dkb_lcd_info __initdata = { + .id = "Base", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_SPI, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .spi_ctrl = CFG_SCLKCNT(16) | CFG_TXBITS(16) | CFG_SPI_SEL(1) | CFG_SPI_3W4WB(1) | CFG_SPI_ENA(1), + .spi_gpio_cs = -1, + .spi_gpio_reset = mfp_to_gpio(MFP_PIN_GPIO106), + .panel_rbswap = 1, + .pxa168fb_lcd_power = tpo_lcd_power, + .invert_pixclock = 1, + .max_fb_size = 1280 * 720 * 4, +}; + +static struct pxa168fb_mach_info ttc_dkb_lcd_ovly_info __initdata = { + .id = "Ovly", + .modes = video_modes, + .num_modes = ARRAY_SIZE(video_modes), + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_SPI, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .spi_ctrl = CFG_SCLKCNT(16) | CFG_TXBITS(16) | CFG_SPI_SEL(1) | CFG_SPI_3W4WB(1) | CFG_SPI_ENA(1), + .spi_gpio_cs = -1, + .spi_gpio_reset = mfp_to_gpio(MFP_PIN_GPIO106), + .panel_rbswap = 1, + .pxa168fb_lcd_power = tpo_lcd_power, + .invert_pixclock = 1, + .max_fb_size = 1280 * 720 * 4, +}; + +/* for oled panel */ +static int oled_lcd = 0; +static int __init oled_lcd_setup(char *__unused) +{ + oled_lcd = 1; + return 1; +} +__setup("oled_lcd", oled_lcd_setup); + +inline int is_oled_lcd(void) +{ + return oled_lcd; +} + +#define CMD1(x, x1) (((x) << 8) | (x1)) + +static uint32_t oscillation[] = { + CMD1(0x1E,0x00), +}; + +static uint32_t stand_by[] = { + CMD1(0x1E,0x03), +}; + +static uint32_t power_on[] = { + CMD1(0x12,0x11), + CMD1(0x13,0x08), + CMD1(0x14,0x25), + CMD1(0x15,0x6A), + CMD1(0x16,0x53), + CMD1(0x17,0x06), + CMD1(0x18,0x42), + CMD1(0x19,0x51), + CMD1(0x1B,0x21), /* swap R&B for line connection for OLED panel */ + // CMD1(0x1B,0x20), +}; + +static uint32_t boost_power_on[] = { + CMD1(0x1A,0x01), +}; + +static uint32_t power_off[] = { + CMD1(0x1A,0x00), +}; + +static uint32_t initialize[] = { + CMD1(0x20,0x08), + CMD1(0x21,0x16), + CMD1(0x22,0x00), + CMD1(0x23,0x28), + CMD1(0x26,0x06), + CMD1(0x24,0x00), + CMD1(0x27,0x01), + CMD1(0x25,0x01), + CMD1(0x01,0x63), + CMD1(0x02,0x46), + CMD1(0x03,0x0F), + CMD1(0x04,0x2C), + CMD1(0x05,0x00), + CMD1(0x06,0x00), + CMD1(0x07,0x00), + CMD1(0x08,0x00), + CMD1(0x09,0x00), + CMD1(0x0A,0x00), + CMD1(0x0B,0x04), + CMD1(0x0C,0x04), + CMD1(0x0D,0x1E), + CMD1(0x0E,0x1E), + CMD1(0x0F,0x1E), +}; +static uint32_t gamma_setting[] = { + CMD1(0x2D,0x01), + CMD1(0x30,0x19), + CMD1(0x31,0x1A), + CMD1(0x32,0x1F), + CMD1(0x33,0x5E), + CMD1(0x34,0x74), + CMD1(0x35,0x70), + CMD1(0x36,0x20), + CMD1(0x37,0x1E), + CMD1(0x38,0x1D), + CMD1(0x39,0x13), + CMD1(0x3A,0x11), + CMD1(0x3B,0x0F), + CMD1(0x3C,0x42), + CMD1(0x3D,0x3C), + CMD1(0x3E,0x36), + CMD1(0x3F,0x49), + CMD1(0x40,0x3F), + CMD1(0x41,0x3A), + CMD1(0x2D,0x00), +}; +static uint32_t disp_on_ampon[]={ + CMD1(0x1C,0x01), +}; +static uint32_t disp_on_elon[]={ + CMD1(0x1D,0x01), +}; +static uint32_t disp_on_glson[]={ + CMD1(0x1D,0x03), +}; +static uint32_t disp_on_elson[]={ + CMD1(0x1D,0x07), +}; +static uint32_t disp_on_dispon[]={ + CMD1(0x1D,0x0F), +}; + +static uint32_t disp_off_dispon[]={ + CMD1(0x1D,0x07), +}; +static uint32_t disp_off_elon[]={ + CMD1(0x1D,0x06), +}; +static uint32_t disp_off_glson[]={ + CMD1(0x1D,0x00), +}; +static uint32_t disp_off_ampon[]={ + CMD1(0x1C,0x00), +}; + +static void lcd_display_on(struct pxa168fb_info *fbi, unsigned int spi_gpio_cs) +{ + spi_send(fbi, disp_on_ampon, ARRAY_SIZE(disp_on_ampon), spi_gpio_cs); + mdelay(10); + spi_send(fbi, disp_on_elon, ARRAY_SIZE(disp_on_elon), spi_gpio_cs); + mdelay(17); + spi_send(fbi, disp_on_glson, ARRAY_SIZE(disp_on_glson), spi_gpio_cs); + mdelay(17); + spi_send(fbi, disp_on_elson, ARRAY_SIZE(disp_on_elson), spi_gpio_cs); + mdelay(17); + spi_send(fbi, disp_on_dispon, ARRAY_SIZE(disp_on_dispon), spi_gpio_cs); + mdelay(17); +} +static void lcd_display_off(struct pxa168fb_info *fbi, unsigned int spi_gpio_cs) +{ + spi_send(fbi, disp_off_dispon, ARRAY_SIZE(disp_off_dispon), spi_gpio_cs); + mdelay(17); + spi_send(fbi, disp_off_elon, ARRAY_SIZE(disp_off_elon), spi_gpio_cs); + mdelay(34); + spi_send(fbi, disp_off_glson, ARRAY_SIZE(disp_off_glson), spi_gpio_cs); + mdelay(17); + spi_send(fbi, disp_off_ampon, ARRAY_SIZE(disp_off_ampon), spi_gpio_cs); + mdelay(10); +} +static void benzina_lcd_power(struct pxa168fb_info *fbi, unsigned int spi_gpio_cs, unsigned int spi_gpio_reset, int on) +{ + int err = 0; + if (on) { + if (spi_gpio_reset != -1) { + err = gpio_request(spi_gpio_reset, "TPO_LCD_SPI_RESET"); + if (err) { + printk("failed to request GPIO for TPO LCD RESET\n"); + return; + } + msleep(10); + printk("lcd power off ...\n"); + + lcd_display_off(fbi, spi_gpio_cs); + spi_send(fbi, power_off, ARRAY_SIZE(power_off), spi_gpio_cs); + mdelay(20); + spi_send(fbi, stand_by, ARRAY_SIZE(stand_by), spi_gpio_cs); + mdelay(1000); + + printk("oled lcd power on ...\n"); + gpio_direction_output(spi_gpio_reset, 0); + mdelay(100); + gpio_direction_output(spi_gpio_reset, 1); + mdelay(100); + gpio_free(spi_gpio_reset); + } + + spi_send(fbi, oscillation, ARRAY_SIZE(oscillation), spi_gpio_cs); + spi_send(fbi, power_on, ARRAY_SIZE(power_on), spi_gpio_cs); + spi_send(fbi, boost_power_on, ARRAY_SIZE(boost_power_on), spi_gpio_cs); + mdelay(140); + spi_send(fbi, initialize, ARRAY_SIZE(initialize), spi_gpio_cs); + spi_send(fbi, gamma_setting, ARRAY_SIZE(gamma_setting), spi_gpio_cs); + lcd_display_on(fbi, spi_gpio_cs); + + } else { + printk("lcd power off ...\n"); + lcd_display_off(fbi, spi_gpio_cs); + spi_send(fbi, power_off, ARRAY_SIZE(power_off), spi_gpio_cs); + mdelay(10); + spi_send(fbi, stand_by, ARRAY_SIZE(stand_by), spi_gpio_cs); + } +} + +static struct fb_videomode novatek_l1N304_modes[] = { + [0] = { + .pixclock = 67833, + .refresh = 60, + .xres = 360, + .yres = 640, + .hsync_len = 2, + .left_margin = 5, + .right_margin = 10, + .vsync_len = 2, + .upper_margin = 4, + .lower_margin = 3, + .sync = 0, + }, +}; + +static struct pxa168fb_mach_info benzina_lcd_info __initdata = { + .id = "Base", + .modes = novatek_l1N304_modes, + .num_modes = 1, + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_SPI, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, + .invert_pix_val_ena = 1, /* DE pin - panel is active low while controll is active high */ + .spi_ctrl = CFG_SCLKCNT(2) | CFG_TXBITS(17) | CFG_SPI_SEL(1) | CFG_SPI_3W4WB(1) | CFG_SPI_ENA(1), + .spi_gpio_cs = mfp_to_gpio(MFP_PIN_GPIO107), + .spi_gpio_reset = mfp_to_gpio(MFP_PIN_GPIO106), /* GPIO106 is conflicted with 1WIRE */ + .pxa168fb_lcd_power = benzina_lcd_power, + .invert_pixclock = 1, +}; + +static struct pxa168fb_mach_info benzina_lcd_ovly_info __initdata = { + .id = "Ovly", + .modes = novatek_l1N304_modes, + .num_modes = 1, + .pix_fmt = PIX_FMT_RGB565, + .io_pin_allocation_mode = PIN_MODE_DUMB_18_SPI, + .dumb_mode = DUMB_MODE_RGB666, + .active = 1, +}; + +#endif + +static struct platform_device portofino_bl_device = { + .name = "portofino-bl", + .id = 0, +}; + +DECLARE_ANDROID_256M_V75_PARTITIONS(android_256m_v75_partitions); +DECLARE_256M_V75_PARTITIONS(generic_256m_v75_partitions); +static struct flash_platform_data ttc_dkb_onenand_info; +static struct pxa3xx_nand_platform_data ttc_dkb_nand_info; +static void __init ttc_dkb_add_flash(void) +{ + if (is_android()) { + ttc_dkb_onenand_info.parts = android_256m_v75_partitions; + ttc_dkb_onenand_info.nr_parts = ARRAY_SIZE(android_256m_v75_partitions); + ttc_dkb_nand_info.parts[0] = android_256m_v75_partitions; + ttc_dkb_nand_info.nr_parts[0] = ARRAY_SIZE(android_256m_v75_partitions); + } else { + ttc_dkb_onenand_info.parts = generic_256m_v75_partitions; + ttc_dkb_onenand_info.nr_parts = ARRAY_SIZE(generic_256m_v75_partitions); + ttc_dkb_nand_info.parts[0] = generic_256m_v75_partitions; + ttc_dkb_nand_info.nr_parts[0] = ARRAY_SIZE(generic_256m_v75_partitions); + } + pxa168_add_onenand(&ttc_dkb_onenand_info); + ttc_dkb_nand_info.use_dma = 0; + ttc_dkb_nand_info.enable_arbiter = 1; + pxa168_add_nand(&ttc_dkb_nand_info); +} + +static unsigned int ttc_dkb_matrix_key_map[] = { + KEY(0, 0, KEY_BACKSPACE), + KEY(0, 1, KEY_END), + KEY(0, 2, KEY_RIGHTCTRL), + KEY(0, 3, KEY_0), + KEY(0, 4, KEY_1), + + KEY(1, 0, KEY_MENU), + KEY(1, 1, KEY_HOME), + KEY(1, 2, KEY_SEND), + KEY(1, 3, KEY_8), + KEY(1, 4, KEY_9), + + KEY(2, 0, KEY_OK), + KEY(2, 1, KEY_2), + KEY(2, 2, KEY_3), + KEY(2, 3, KEY_4), + KEY(2, 4, KEY_5), + + KEY(3, 0, KEY_6), + KEY(3, 1, KEY_VOLUMEUP), + KEY(3, 2, KEY_7), + KEY(3, 3, KEY_VOLUMEDOWN), + KEY(3, 4, KEY_RECORD), + + KEY(4, 0, KEY_KPASTERISK), + KEY(4, 1, KEY_KPDOT), + KEY(4, 2, KEY_F2), + KEY(4, 3, KEY_CAMERA), + KEY(4, 4, KEY_CAMERA), + + KEY(6, 0, KEY_F1), + KEY(6, 1, KEY_UP), + KEY(6, 2, KEY_DOWN), + KEY(6, 3, KEY_LEFT), + KEY(6, 4, KEY_RIGHT), +}; + +static struct pxa27x_keypad_platform_data ttc_dkb_keypad_info __initdata = { + .matrix_key_rows = 7, + .matrix_key_cols = 5, + .matrix_key_map = ttc_dkb_matrix_key_map, + .matrix_key_map_size = ARRAY_SIZE(ttc_dkb_matrix_key_map), + .debounce_interval = 30, +}; + + +#if defined(CONFIG_MMC_PXA_SDH) +static struct pxasdh_platform_data ttc_dkb_sdh_platform_data_0 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_27_28|MMC_VDD_28_29, + .max_speed = 12000000, + //.sd_clock = 1, + //.sdclk_sel = 0, +}; + +static struct pxasdh_platform_data ttc_dkb_sdh_platform_data_1 = { + .detect_delay = 20, + .ocr_mask = MMC_VDD_165_195, +}; + +static void __init ttc_dkb_init_mmc(void) +{ +#ifdef CONFIG_SD8XXX_RFKILL + int gpio_reset; + int gpio_buck_en = mfp_to_gpio(WLAN_BT_RESET_GPIO_34); + int gpio_lhc = mfp_to_gpio(WLAN_LHC_GPIO_36); + + if(cpu_is_pxa910_Ax()) { + if (gpio_request(gpio_buck_en, "PG8211_BUCK2_EN2")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_buck_en); + return -1; + } + + if (gpio_request(gpio_lhc, "WLAN_LHC")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_lhc); + return -1; + } + gpio_direction_output(gpio_buck_en, 1); + gpio_direction_output(gpio_lhc, 1); + gpio_free(gpio_buck_en); + gpio_free(gpio_lhc); + + gpio_reset = mfp_to_gpio(WLAN_RESET_GPIO_20); + } else { + gpio_reset = mfp_to_gpio(WLAN_BT_RESET_GPIO_34); + } + + add_sd8x_rfkill_device(mfp_to_gpio(WLAN_PD_GPIO_14), gpio_reset, + &ttc_dkb_sdh_platform_data_1.pmmc); +#endif + pxa168_add_sdh(0, &ttc_dkb_sdh_platform_data_0); + pxa168_add_sdh(1, &ttc_dkb_sdh_platform_data_1); +} +#endif + +#ifdef CONFIG_USB_GADGET_PXA_U2O +static int ttc_dkb_vbus_status(unsigned base) +{ + int status = VBUS_LOW; + + if (pxa3xx_pmic_is_vbus_assert()) { + status = VBUS_HIGH; + } + return status; +} + +static int ttc_dkb_vbus_detect(void *func, int enable) +{ + if (enable) { + pmic_callback_register(PMIC_EVENT_USB, func); + pxa3xx_pmic_set_pump(1); + } else { + /*don't disable pump for charging*/ + /*pxa3xx_pmic_set_pump(0); */ + pmic_callback_unregister(PMIC_EVENT_USB, func); + } + + return 0; +} + +static int ttc_dkb_usbid_detect(struct otg_transceiver *otg); +irqreturn_t ttc_dkb_usbid_handler(int irq, struct otg_transceiver *otg) +{ + if (ttc_dkb_usbid_detect(otg)) { + /* USB_ID is HIGH, b-device */ + printk("%s id high\n", __func__); + } else { + /* USB_ID is LOW, a-device */ + printk("%s id low\n", __func__); + } + + otg_interrupt(otg); + return IRQ_HANDLED; +} + +static int ttc_dkb_usbid_detect(struct otg_transceiver *otg) +{ + int gpio_usbid = mfp_to_gpio(MFP_PIN_GPIO44), ret; + static int init_done; + + if (!init_done) { + init_done = 1; + gpio_direction_input(gpio_usbid); + request_irq(IRQ_GPIO(gpio_usbid), ttc_dkb_usbid_handler, + IRQF_DISABLED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, "usbid", otg); + } + + if (gpio_get_value(gpio_usbid)) + return 1; + else + return 0; +} + +static int ttc_dkb_usb_power(int enable) +{ + if (enable) + pxa3xx_pmic_set_voltage(VCC_USB, 2900); + else + pxa3xx_pmic_set_voltage(VCC_USB, 0); +} + +static struct pxa_usb_plat_info ttc_dkb_u2o_info = { + .phy_init = pxa168_usb_phy_init, + .phy_deinit = pxa168_usb_phy_deinit, + .vbus_status = ttc_dkb_vbus_status, + .vbus_detect = ttc_dkb_vbus_detect, + /* USB_ID connect with GPIO may cause chip damage + .usbid_detect = ttc_dkb_usbid_detect, */ + /* ECN001 on DKB rev2.0 fix the usb power issue */ + .rely_on_vbus = 1, + /* workaround for IN endpoint hang issue if multiple queue */ + .in_single = 1, + /* FIXME touch LDO5 would cause system power from USB, + * which would casue system hang if unplug the cable + .set_power = ttc_dkb_usb_power, */ + .is_otg = 1, +}; +#endif + +static struct platform_device sensor_input_device = { + .name = "sensor_input", + .id = -1, +}; + +static int ttc_dkb_is_ac_online(void) +{ + u8 tmp; + + return 0; +} + +static int ttc_dkb_is_usb_online(void) +{ + u8 tmp; + + return sanremo_usb_connect(); +} + +static char *ttc_dkb_supplicants[] = { + "battery", +}; + +static struct pda_power_pdata ttc_dkb_power_supply_info = { + .is_ac_online = ttc_dkb_is_ac_online, + .is_usb_online = ttc_dkb_is_usb_online, + .supplied_to = ttc_dkb_supplicants, + .num_supplicants = ARRAY_SIZE(ttc_dkb_supplicants), +}; + +static struct resource ttc_dkb_power_supply_resources[] = { + [0] = { + .name = "ac", + }, + [1] = { + .name = "usb", + }, +}; + +static struct platform_device ttc_dkb_power_supply = { + .name = "pda-power", + .id = -1, + .dev = { + .platform_data = &ttc_dkb_power_supply_info, + }, + .resource = ttc_dkb_power_supply_resources, + .num_resources = ARRAY_SIZE(ttc_dkb_power_supply_resources), +}; + +static struct platform_device sanremo_battery = { + .name = "sanremo_battery", + .id = -1, +}; + +static void __init ttc_dkb_init_power(void) +{ + platform_device_register(&ttc_dkb_power_supply); + platform_device_register(&sanremo_battery); +} + +static void (*headset_update_func)(int state); + +static void sanremo_headset_interrupt(unsigned long event) +{ + if (headset_update_func) + headset_update_func(sanremo_get_headset_state()); +} + +static int ttc_dkb_headset_detect(void *func, int enable) +{ + headset_update_func = func; + return sanremo_enable_headset_detect(sanremo_headset_interrupt, enable); +} + +static struct headset_switch_platform_data headset_switch_device_data = { + .name = "h2w", + .gpio = NULL, + .name_on = NULL, + .name_off = NULL, + .state_on = NULL, + .state_off = NULL, + .enable_detect = ttc_dkb_headset_detect, +}; + +static struct platform_device headset_switch_device = { + .name = "headset", + .id = 0, + .dev = { + .platform_data = &headset_switch_device_data, + }, +}; + +static void __init ttc_dkb_init_headset(void) +{ + platform_device_register(&headset_switch_device); +} + +static int gps_power_on(void) +{ + int gps_ldo, gps_rst_n, gps_on; + gps_ldo = GPIO_EXT1(8); + if (gpio_request(gps_ldo, "gpio_gps_ldo")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_ldo); + } + + gps_on = GPIO_EXT1(10); + if (gpio_request(gps_on, "gpio_gps_on")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_on); + } + + gps_rst_n = GPIO_EXT1(11); + if (gpio_request(gps_rst_n, "gpio_gps_rst")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_rst_n); + } + + gpio_direction_output(gps_ldo, 0); + gpio_direction_output(gps_rst_n, 0); + gpio_direction_output(gps_on, 0); + gpio_direction_output(gps_ldo, 1); + gpio_direction_output(gps_rst_n, 1); + mdelay(1); + gpio_direction_output(gps_on, 1); + + gpio_free(gps_ldo); + gpio_free(gps_on); + gpio_free(gps_rst_n); + printk(KERN_INFO "sirf gps chip (gsd3tw) powered on\n", __func__); +} + +static int gps_power_off(void) +{ + int gps_ldo, gps_rst_n, gps_on; + gps_ldo = GPIO_EXT1(8); + if (gpio_request(gps_ldo, "gpio_gps_ldo")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_ldo); + } + + gps_on = GPIO_EXT1(10); + if (gpio_request(gps_on, "gpio_gps_on")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_on); + } + + gps_rst_n = GPIO_EXT1(11); + if (gpio_request(gps_rst_n, "gpio_gps_rst")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_rst_n); + } + + gpio_direction_output(gps_ldo, 0); + gpio_direction_output(gps_rst_n, 0); + gpio_direction_output(gps_on, 0); + + gpio_free(gps_ldo); + gpio_free(gps_on); + gpio_free(gps_rst_n); + printk(KERN_INFO "sirf gps chip (gsd3tw) powered off\n", __func__); +} + +static int gps_reset(int flag) +{ + int gps_rst_n; + + gps_rst_n = GPIO_EXT1(11); + if (gpio_request(gps_rst_n, "gpio_gps_rst")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_rst_n); + } + gpio_direction_output(gps_rst_n, flag); + mdelay(1); + + gpio_free(gps_rst_n); + printk(KERN_INFO "sirf gps chip (gsd3tw) reset\n", __func__); +} + +static int gps_on_off(int flag) +{ + int gps_on; + + gps_on = GPIO_EXT1(10); + if (gpio_request(gps_on, "gpio_gps_on")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", gps_on); + } + gpio_direction_output(gps_on, flag); + mdelay(1); + + gpio_free(gps_on); + printk(KERN_INFO "sirf gps chip (gsd3tw) offon\n", __func__); +} + +#ifdef CONFIG_PROC_FS +#define PROC_PRINT(fmt, args...) do {len += sprintf(page + len, fmt, ##args); } while(0) + +static char sirf_status[4] = "off"; +static ssize_t sirf_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = strlen(sirf_status); + + sprintf(page, "%s\n", sirf_status); + return len + 1; +} + +static ssize_t sirf_write_proc(struct file *filp, + const char *buff, size_t len, loff_t *off) +{ + char messages[256]; + int flag, ret; + char buffer[7]; + + if (len > 256) + len = 256; + + if (copy_from_user(messages, buff, len)) + return -EFAULT; + + if (strncmp(messages, "off", 3) == 0) { + strcpy(sirf_status, "off"); + gps_power_off(); + } else if (strncmp(messages, "on", 2) == 0) { + strcpy(sirf_status, "on"); + gps_power_on(); + } else if (strncmp(messages, "reset", 5) == 0) { + strcpy(sirf_status, messages); + ret = sscanf(messages, "%s %d", buffer, &flag); + if (ret == 2) + gps_reset(flag); + } else if (strncmp(messages, "sirfon", 5) == 0) { + strcpy(sirf_status, messages); + ret = sscanf(messages, "%s %d", buffer, &flag); + if (ret == 2) + gps_on_off(flag); + } else { + printk("usage: echo {on/off} > /proc/driver/sirf\n"); + } + + return len; +} + +static void create_sirf_proc_file(void) +{ + struct proc_dir_entry *sirf_proc_file = + create_proc_entry("driver/sirf", 0644, NULL); + + if (sirf_proc_file) { + sirf_proc_file->read_proc = sirf_read_proc; + sirf_proc_file->write_proc = sirf_write_proc; + } else + printk(KERN_INFO "proc file create failed!\n"); +} +#endif + +#if defined(CONFIG_SENSORS_LIS3LV02D_I2C) || defined(CONFIG_SENSORS_LIS3LV02D_I2C_MODULE) +static int lis3lv02d_direction_idx = 1; +static struct platform_device g_sensor = { + .name = "lis3lv02d", + .id = -1, + .dev = { + .platform_data = &lis3lv02d_direction_idx, + }, +}; +#endif + +static void __init ttc_dkb_init(void) +{ + fake_suspend = 1; /*enable fake suspend*/ + + /*dummy driver init*/ + platform_device_register(&sensor_input_device); + +======= }; static struct mtd_partition ttc_dkb_onenand_partitions[] = { @@ -112,13 +1516,91 @@ static struct platform_device *ttc_dkb_devices[] = { static void __init ttc_dkb_init(void) { +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/ttc_dkb.c mfp_config(ARRAY_AND_SIZE(ttc_dkb_pin_config)); /* on-chip devices */ pxa910_add_uart(1); +<<<<<<< HEAD:arch/arm/mach-mmp/ttc_dkb.c + pxa910_add_uart(2); + pxa910_add_uart(3); + +#if defined(CONFIG_TOUCHSCREEN_TPO) || defined(CONFIG_TOUCHSCREEN_TOPCOM_MODULE) + top_touch_reset(); +#endif + if(is_td_dkb) + i2c_register_board_info(0, ARRAY_AND_SIZE(td_dkb_i2c_board_info)); + else + i2c_register_board_info(0, ARRAY_AND_SIZE(ttc_dkb_i2c_board_info)); + pxa910_add_twsi(0, &i2c_info, ARRAY_AND_SIZE(i2c_board_info)); + +#ifdef CONFIG_USB_GADGET_PXA_U2O + pxa168_add_u2o(&ttc_dkb_u2o_info); +#endif +#ifdef CONFIG_USB_OTG + pxa168_add_u2ootg(&ttc_dkb_u2o_info); + pxa168_add_u2oehci(&ttc_dkb_u2o_info); +#endif + pxa910_add_acipc(); + pxa910_add_ire(); + pxa168_add_keypad(&ttc_dkb_keypad_info); +#ifdef CONFIG_FB_PXA910 + if(!cpu_is_pxa910_Ax()) { + spi_send = pxa168fb_spi_send; + if (is_oled_lcd()) { + pxa168_add_fb(&benzina_lcd_info); + pxa168_add_fb_ovly(&benzina_lcd_ovly_info); + } else { + pxa168_add_fb(&ttc_dkb_lcd_info); + pxa168_add_fb_ovly(&ttc_dkb_lcd_ovly_info); + } + }else{ + spi_send = pxa910fb_spi_send; + if (is_oled_lcd()) { + pxa910_add_fb(&benzina_lcd_info); + pxa910_add_fb_ovly(&benzina_lcd_ovly_info); + } else { + pxa910_add_fb(&ttc_dkb_lcd_info); + pxa910_add_fb_ovly(&ttc_dkb_lcd_ovly_info); + } + } +#endif + platform_device_register(&portofino_bl_device); +#if defined(CONFIG_PXA168_CAMERA) + pxa168_add_cam(); +#endif + pxa910_add_ssp(1); + pxa910_add_imm(); + pxa168_add_freq(); + + ttc_dkb_add_flash(); +#if defined(CONFIG_MMC_PXA_SDH) + ttc_dkb_init_mmc(); +#endif + pxa910_add_rtc(); + /*power device*/ + ttc_dkb_init_power(); + + /*headset device*/ + ttc_dkb_init_headset(); + + /* off-chip devices */ + platform_device_register(&smc91x_device); + +#if defined(CONFIG_SENSORS_LIS3LV02D_I2C) || defined(CONFIG_SENSORS_LIS3LV02D_I2C_MODULE) + platform_device_register(&g_sensor); +#endif + /* create proc for sirf control */ + create_sirf_proc_file(); + + res_add_sanremo_vibrator(); + + pm_power_off = sanremo_turn_off_power; +======= /* off-chip devices */ platform_add_devices(ARRAY_AND_SIZE(ttc_dkb_devices)); +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/ttc_dkb.c } MACHINE_START(TTC_DKB, "PXA910-based TTC_DKB Development Platform") @@ -126,7 +1608,12 @@ MACHINE_START(TTC_DKB, "PXA910-based TTC_DKB Development Platform") .boot_params = 0x00000100, .io_pg_offst = (APB_VIRT_BASE >> 18) & 0xfffc, .map_io = pxa_map_io, +<<<<<<< HEAD:arch/arm/mach-mmp/ttc_dkb.c + .init_irq = pxa168_init_irq, + .timer = &pxa168_timer, +======= .init_irq = pxa910_init_irq, .timer = &pxa910_timer, +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-mmp/ttc_dkb.c .init_machine = ttc_dkb_init, MACHINE_END diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index da3156d8690bf1..3d663872148a8c 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-pxa/include/mach/pm.h b/arch/arm/mach-pxa/include/mach/pm.h index fd8360c6839d5a..c970f29019c100 100644 --- a/arch/arm/mach-pxa/include/mach/pm.h +++ b/arch/arm/mach-pxa/include/mach/pm.h @@ -27,8 +27,11 @@ extern void pxa27x_cpu_suspend(unsigned int); extern void pxa_cpu_resume(void); extern int pxa_pm_enter(suspend_state_t state); +<<<<<<< HEAD:arch/arm/mach-pxa/include/mach/pm.h +======= extern int pxa_pm_prepare(void); extern void pxa_pm_finish(void); +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-pxa/include/mach/pm.h /* NOTE: this is for PM debugging on Lubbock, it's really a big * ugly, but let's keep the crap minimum here, instead of direct diff --git a/arch/arm/mach-pxa/include/mach/pxa-regs.h b/arch/arm/mach-pxa/include/mach/pxa-regs.h new file mode 100644 index 00000000000000..a387eeacde09bf --- /dev/null +++ b/arch/arm/mach-pxa/include/mach/pxa-regs.h @@ -0,0 +1,191 @@ +/* + * arch/arm/mach-pxa/include/mach/pxa-regs.h + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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. + */ + +#ifndef __PXA_REGS_H +#define __PXA_REGS_H + +#include + +/* + * PXA Chip selects + */ + +#define PXA_CS0_PHYS 0x00000000 +#define PXA_CS1_PHYS 0x04000000 +#define PXA_CS2_PHYS 0x08000000 +#define PXA_CS3_PHYS 0x0C000000 +#define PXA_CS4_PHYS 0x10000000 +#define PXA_CS5_PHYS 0x14000000 + + +/* + * Personal Computer Memory Card International Association (PCMCIA) sockets + */ + +#define PCMCIAPrtSp 0x04000000 /* PCMCIA Partition Space [byte] */ +#define PCMCIASp (4*PCMCIAPrtSp) /* PCMCIA Space [byte] */ +#define PCMCIAIOSp PCMCIAPrtSp /* PCMCIA I/O Space [byte] */ +#define PCMCIAAttrSp PCMCIAPrtSp /* PCMCIA Attribute Space [byte] */ +#define PCMCIAMemSp PCMCIAPrtSp /* PCMCIA Memory Space [byte] */ + +#define PCMCIA0Sp PCMCIASp /* PCMCIA 0 Space [byte] */ +#define PCMCIA0IOSp PCMCIAIOSp /* PCMCIA 0 I/O Space [byte] */ +#define PCMCIA0AttrSp PCMCIAAttrSp /* PCMCIA 0 Attribute Space [byte] */ +#define PCMCIA0MemSp PCMCIAMemSp /* PCMCIA 0 Memory Space [byte] */ + +#define PCMCIA1Sp PCMCIASp /* PCMCIA 1 Space [byte] */ +#define PCMCIA1IOSp PCMCIAIOSp /* PCMCIA 1 I/O Space [byte] */ +#define PCMCIA1AttrSp PCMCIAAttrSp /* PCMCIA 1 Attribute Space [byte] */ +#define PCMCIA1MemSp PCMCIAMemSp /* PCMCIA 1 Memory Space [byte] */ + +#define _PCMCIA(Nb) /* PCMCIA [0..1] */ \ + (0x20000000 + (Nb)*PCMCIASp) +#define _PCMCIAIO(Nb) _PCMCIA (Nb) /* PCMCIA I/O [0..1] */ +#define _PCMCIAAttr(Nb) /* PCMCIA Attribute [0..1] */ \ + (_PCMCIA (Nb) + 2*PCMCIAPrtSp) +#define _PCMCIAMem(Nb) /* PCMCIA Memory [0..1] */ \ + (_PCMCIA (Nb) + 3*PCMCIAPrtSp) + +#define _PCMCIA0 _PCMCIA (0) /* PCMCIA 0 */ +#define _PCMCIA0IO _PCMCIAIO (0) /* PCMCIA 0 I/O */ +#define _PCMCIA0Attr _PCMCIAAttr (0) /* PCMCIA 0 Attribute */ +#define _PCMCIA0Mem _PCMCIAMem (0) /* PCMCIA 0 Memory */ + +#define _PCMCIA1 _PCMCIA (1) /* PCMCIA 1 */ +#define _PCMCIA1IO _PCMCIAIO (1) /* PCMCIA 1 I/O */ +#define _PCMCIA1Attr _PCMCIAAttr (1) /* PCMCIA 1 Attribute */ +#define _PCMCIA1Mem _PCMCIAMem (1) /* PCMCIA 1 Memory */ + + + +/* + * DMA Controller + */ +#define DCSR(x) __REG2(0x40000000, (x) << 2) + +#define DCSR_RUN (1 << 31) /* Run Bit (read / write) */ +#define DCSR_NODESC (1 << 30) /* No-Descriptor Fetch (read / write) */ +#define DCSR_STOPIRQEN (1 << 29) /* Stop Interrupt Enable (read / write) */ +#define DCSR_REQPEND (1 << 8) /* Request Pending (read-only) */ +#define DCSR_STOPSTATE (1 << 3) /* Stop State (read-only) */ +#define DCSR_ENDINTR (1 << 2) /* End Interrupt (read / write) */ +#define DCSR_STARTINTR (1 << 1) /* Start Interrupt (read / write) */ +#define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt (read / write) */ + +#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) +#define DCSR_EORIRQEN (1 << 28) /* End of Receive Interrupt Enable (R/W) */ +#define DCSR_EORJMPEN (1 << 27) /* Jump to next descriptor on EOR */ +#define DCSR_EORSTOPEN (1 << 26) /* STOP on an EOR */ +#define DCSR_SETCMPST (1 << 25) /* Set Descriptor Compare Status */ +#define DCSR_CLRCMPST (1 << 24) /* Clear Descriptor Compare Status */ +#define DCSR_CMPST (1 << 10) /* The Descriptor Compare Status */ +#define DCSR_EORINTR (1 << 9) /* The end of Receive */ +#endif + +#define DALGN __REG(0x400000a0) /* DMA Alignment Register */ +#define DINT __REG(0x400000f0) /* DMA Interrupt Register */ + +#define DRCMR(n) (*(((n) < 64) ? \ + &__REG2(0x40000100, ((n) & 0x3f) << 2) : \ + &__REG2(0x40001100, ((n) & 0x3f) << 2))) + +#define DRCMR_MAPVLD (1 << 7) /* Map Valid (read / write) */ +#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */ + +#define DDADR(x) __REG2(0x40000200, (x) << 4) +#define DSADR(x) __REG2(0x40000204, (x) << 4) +#define DTADR(x) __REG2(0x40000208, (x) << 4) +#define DCMD(x) __REG2(0x4000020c, (x) << 4) + +#define DDADR_DESCADDR 0xfffffff0 /* Address of next descriptor (mask) */ +#define DDADR_STOP (1 << 0) /* Stop (read / write) */ + +#define DCMD_INCSRCADDR (1 << 31) /* Source Address Increment Setting. */ +#define DCMD_INCTRGADDR (1 << 30) /* Target Address Increment Setting. */ +#define DCMD_FLOWSRC (1 << 29) /* Flow Control by the source. */ +#define DCMD_FLOWTRG (1 << 28) /* Flow Control by the target. */ +#define DCMD_STARTIRQEN (1 << 22) /* Start Interrupt Enable */ +#define DCMD_ENDIRQEN (1 << 21) /* End Interrupt Enable */ +#define DCMD_ENDIAN (1 << 18) /* Device Endian-ness. */ +#define DCMD_BURST8 (1 << 16) /* 8 byte burst */ +#define DCMD_BURST16 (2 << 16) /* 16 byte burst */ +#define DCMD_BURST32 (3 << 16) /* 32 byte burst */ +#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */ +#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */ +#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */ +#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ + +/* + * Real Time Clock + */ + +#define RCNR __REG(0x40900000) /* RTC Count Register */ +#define RTAR __REG(0x40900004) /* RTC Alarm Register */ +#define RTSR __REG(0x40900008) /* RTC Status Register */ +#define RTTR __REG(0x4090000C) /* RTC Timer Trim Register */ +#define PIAR __REG(0x40900038) /* Periodic Interrupt Alarm Register */ + +#define RTSR_PICE (1 << 15) /* Periodic interrupt count enable */ +#define RTSR_PIALE (1 << 14) /* Periodic interrupt Alarm enable */ +#define RTSR_HZE (1 << 3) /* HZ interrupt enable */ +#define RTSR_ALE (1 << 2) /* RTC alarm interrupt enable */ +#define RTSR_HZ (1 << 1) /* HZ rising-edge detected */ +#define RTSR_AL (1 << 0) /* RTC alarm detected */ + + +/* + * OS Timer & Match Registers + */ + +#define OSMR0 __REG(0x40A00000) /* */ +#define OSMR1 __REG(0x40A00004) /* */ +#define OSMR2 __REG(0x40A00008) /* */ +#define OSMR3 __REG(0x40A0000C) /* */ +#define OSMR4 __REG(0x40A00080) /* */ +#define OSCR __REG(0x40A00010) /* OS Timer Counter Register */ +#define OSCR4 __REG(0x40A00040) /* OS Timer Counter Register */ +#define OMCR4 __REG(0x40A000C0) /* */ +#define OSSR __REG(0x40A00014) /* OS Timer Status Register */ +#define OWER __REG(0x40A00018) /* OS Timer Watchdog Enable Register */ +#define OIER __REG(0x40A0001C) /* OS Timer Interrupt Enable Register */ + +#define OSSR_M3 (1 << 3) /* Match status channel 3 */ +#define OSSR_M2 (1 << 2) /* Match status channel 2 */ +#define OSSR_M1 (1 << 1) /* Match status channel 1 */ +#define OSSR_M0 (1 << 0) /* Match status channel 0 */ + +#define OWER_WME (1 << 0) /* Watchdog Match Enable */ + +#define OIER_E3 (1 << 3) /* Interrupt enable channel 3 */ +#define OIER_E2 (1 << 2) /* Interrupt enable channel 2 */ +#define OIER_E1 (1 << 1) /* Interrupt enable channel 1 */ +#define OIER_E0 (1 << 0) /* Interrupt enable channel 0 */ + + +/* + * Interrupt Controller + */ + +#define ICIP __REG(0x40D00000) /* Interrupt Controller IRQ Pending Register */ +#define ICMR __REG(0x40D00004) /* Interrupt Controller Mask Register */ +#define ICLR __REG(0x40D00008) /* Interrupt Controller Level Register */ +#define ICFP __REG(0x40D0000C) /* Interrupt Controller FIQ Pending Register */ +#define ICPR __REG(0x40D00010) /* Interrupt Controller Pending Register */ +#define ICCR __REG(0x40D00014) /* Interrupt Controller Control Register */ + +#define ICIP2 __REG(0x40D0009C) /* Interrupt Controller IRQ Pending Register 2 */ +#define ICMR2 __REG(0x40D000A0) /* Interrupt Controller Mask Register 2 */ +#define ICLR2 __REG(0x40D000A4) /* Interrupt Controller Level Register 2 */ +#define ICFP2 __REG(0x40D000A8) /* Interrupt Controller FIQ Pending Register 2 */ +#define ICPR2 __REG(0x40D000AC) /* Interrupt Controller Pending Register 2 */ + +#endif diff --git a/arch/arm/mach-pxa/include/mach/pxa3xx-regs.h b/arch/arm/mach-pxa/include/mach/pxa3xx-regs.h index e91d63cfe811f0..0ac46fab744632 100644 --- a/arch/arm/mach-pxa/include/mach/pxa3xx-regs.h +++ b/arch/arm/mach-pxa/include/mach/pxa3xx-regs.h @@ -24,6 +24,15 @@ #define PXA3xx_CS2_PHYS (0x10000000) #define PXA3xx_CS3_PHYS (0x14000000) +/* + * Static Chip Selects + */ + +#define PXA300_CS0_PHYS (0x00000000) /* PXA300/PXA310 _only_ */ +#define PXA300_CS1_PHYS (0x30000000) /* PXA300/PXA310 _only_ */ +#define PXA3xx_CS2_PHYS (0x10000000) +#define PXA3xx_CS3_PHYS (0x14000000) + /* * Oscillator Configuration Register (OSCC) */ diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c index 63d65a2a038779..c5e461b594ecc8 100644 --- a/arch/arm/mach-pxa/lubbock.c +++ b/arch/arm/mach-pxa/lubbock.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c index 5543c64da9efba..71b7e5dcefdfd7 100644 --- a/arch/arm/mach-pxa/mainstone.c +++ b/arch/arm/mach-pxa/mainstone.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include "generic.h" #include "devices.h" diff --git a/arch/arm/mach-pxa/palmtx.c b/arch/arm/mach-pxa/palmtx.c index 007b58c11f8dfd..369761d16c67e6 100644 --- a/arch/arm/mach-pxa/palmtx.c +++ b/arch/arm/mach-pxa/palmtx.c @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index 3a7925ca39440f..99abbe3eeeb84f 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c index 166c15f629162e..da993a9d345dd1 100644 --- a/arch/arm/mach-pxa/pm.c +++ b/arch/arm/mach-pxa/pm.c @@ -14,7 +14,10 @@ #include #include #include +<<<<<<< HEAD:arch/arm/mach-pxa/pm.c +======= #include +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:arch/arm/mach-pxa/pm.c #include diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c index d58a52415d75f4..cb3a3918bed623 100644 --- a/arch/arm/mach-pxa/poodle.c +++ b/arch/arm/mach-pxa/poodle.c @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 01bdd7500df442..48e2636be34451 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include "generic.h" diff --git a/arch/arm/mach-pxa/tavorevb.c b/arch/arm/mach-pxa/tavorevb.c index f02dcb5b4e97e5..0f440c9d7cbd5e 100644 --- a/arch/arm/mach-pxa/tavorevb.c +++ b/arch/arm/mach-pxa/tavorevb.c @@ -25,7 +25,7 @@ #include #include -#include +#include #include "devices.h" #include "generic.h" diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 5bd7c89a604515..d6cede8ec29bd5 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -362,6 +362,26 @@ config CPU_MOHAWK select CPU_TLB_V4WBI if MMU select CPU_COPY_V4WB if MMU +config CPU_MOHAWK_OLD_ID + bool "Accept Mohawk ARM926 Compatible CPUID" + depends on CPU_MOHAWK && !CPU_ARM926T + default y + +config CPU_L2_CACHE + bool "Enable Mohawk L2 Cache" + depends on CPU_MOHAWK && !CPU_ARM926T + default y + +config ENABLE_COREIDLE + bool "Enables core idle and low power mode, currently via WFI" + depends on CPU_MOHAWK && !MSPM_PXA168 + default n + help + Say Y here to allow the core to enter core idle via calls to do_idle. + Caution: enabling core idle will interfere with the debugger. + Note: When MSPM is enabled, this option is not necessary because + MSPM will determine when it is appropriate to enter low power modes. + # Feroceon config CPU_FEROCEON bool diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S index e46ecd84713831..307d59f0e98994 100644 --- a/arch/arm/mm/cache-v6.S +++ b/arch/arm/mm/cache-v6.S @@ -255,6 +255,11 @@ v6_dma_clean_range: * - end - virtual end address of region */ ENTRY(v6_dma_flush_range) +#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT + sub r2, r1, r0 + cmp r2, #CONFIG_CACHE_FLUSH_RANGE_LIMIT + bhi v6_dma_flush_dcache_all +#endif bic r0, r0, #D_CACHE_LINE_SIZE - 1 1: #ifdef CONFIG_SMP @@ -273,6 +278,18 @@ ENTRY(v6_dma_flush_range) mcr p15, 0, r0, c7, c10, 4 @ drain write buffer mov pc, lr +#ifdef CONFIG_CACHE_FLUSH_RANGE_LIMIT +v6_dma_flush_dcache_all: + mov r0, #0 +#ifdef HARVARD_CACHE + mcr p15, 0, r0, c7, c14, 0 @ D cache clean+invalidate +#else + mcr p15, 0, r0, c7, c15, 0 @ Cache clean+invalidate +#endif + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + mov pc, lr +#endif + /* * dma_map_area(start, size, dir) * - start - kernel virtual start address diff --git a/arch/arm/mm/proc-mohawk.S b/arch/arm/mm/proc-mohawk.S index caa31154e7dbf7..ca52fa11c02d49 100644 --- a/arch/arm/mm/proc-mohawk.S +++ b/arch/arm/mm/proc-mohawk.S @@ -1,9 +1,11 @@ /* - * linux/arch/arm/mm/proc-mohawk.S: MMU functions for Marvell PJ1 core + * linux/arch/arm/mm/proc-mohawk.S: MMU functions for ARM926EJ-S * - * PJ1 (codename Mohawk) is a hybrid of the xscale3 and Marvell's own core. - * - * Heavily based on proc-arm926.S and proc-xsc3.S + * Copyright (C) 1999-2001 ARM Limited + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * hacked for non-paged-MM by Hyok S. Choi, 2003. + * Copyright (C) 2008 Marvell International - L2 cache support by + * Jason Chagas * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +20,12 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * These are the low level assembler for performing cache and TLB + * functions on the mohawk. + * + * CONFIG_CPU_ARM926_CPU_IDLE -> nohlt */ #include @@ -31,16 +39,31 @@ #include "proc-macros.S" /* - * This is the maximum size of an area which will be flushed. If the - * area is larger than this, then we flush the whole cache. + * This is the maximum size of an area which will be invalidated + * using the single invalidate entry instructions. Anything larger + * than this, and we go for the whole cache. + * + * This value should be chosen such that we choose the cheapest + * alternative. */ -#define CACHE_DLIMIT 32768 +#define CACHE_DLIMIT 16384 /* - * The cache line size of the L1 D cache. + * the cache line size of the I and D cache */ #define CACHE_DLINESIZE 32 +#if !defined(CONFIG_CPU_PXA168_B0) +#define BAD_CACHE_LINE_OFFSET (PAGE_SZ - CACHE_DLINESIZE) +#endif + +/* + * Run with L2 enabled. + */ +#undef TTC_Z0_SUPPORT +/* #define L2_CACHE_PREFETCH_DISABLE */ + + .text /* * cpu_mohawk_proc_init() */ @@ -56,8 +79,8 @@ ENTRY(cpu_mohawk_proc_fin) msr cpsr_c, ip bl mohawk_flush_kern_cache_all mrc p15, 0, r0, c1, c0, 0 @ ctrl register - bic r0, r0, #0x1800 @ ...iz........... - bic r0, r0, #0x0006 @ .............ca. + bic r0, r0, #0x1000 @ ...i............ + bic r0, r0, #0x000e @ ............wca. mcr p15, 0, r0, c1, c0, 0 @ disable caches ldmfd sp!, {pc} @@ -69,19 +92,18 @@ ENTRY(cpu_mohawk_proc_fin) * to what would be the reset vector. * * loc: location to jump to for soft reset - * - * (same as arm926) */ .align 5 ENTRY(cpu_mohawk_reset) mov ip, #0 mcr p15, 0, ip, c7, c7, 0 @ invalidate I,D caches mcr p15, 0, ip, c7, c10, 4 @ drain WB +#ifdef CONFIG_MMU mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs - mrc p15, 0, ip, c1, c0, 0 @ ctrl register - bic ip, ip, #0x0007 @ .............cam - bic ip, ip, #0x1100 @ ...i...s........ +#endif + ldr ip, =0x5087a mcr p15, 0, ip, c1, c0, 0 @ ctrl register + mrc p15, 0, ip, c1, c0, 0 @ ctrl register mov pc, r0 /* @@ -89,11 +111,12 @@ ENTRY(cpu_mohawk_reset) * * Called with IRQs disabled */ - .align 5 + .align 10 ENTRY(cpu_mohawk_do_idle) mov r0, #0 - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer - mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt +#ifdef CONFIG_ENABLE_COREIDLE + mcr p15, 0, r0, c7, c0, 4 @ Wait for interrupt +#endif mov pc, lr /* @@ -114,10 +137,15 @@ ENTRY(mohawk_flush_kern_cache_all) mov r2, #VM_EXEC mov ip, #0 __flush_whole_cache: - mcr p15, 0, ip, c7, c14, 0 @ clean & invalidate all D cache +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache +#else +1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate + bne 1b +#endif tst r2, #VM_EXEC mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache - mcrne p15, 0, ip, c7, c10, 0 @ drain write buffer + mcrne p15, 0, ip, c7, c10, 4 @ drain WB mov pc, lr /* @@ -129,8 +157,6 @@ __flush_whole_cache: * - start - start address (inclusive) * - end - end address (exclusive) * - flags - vm_flags describing address space - * - * (same as arm926) */ ENTRY(mohawk_flush_user_cache_range) mov ip, #0 @@ -138,12 +164,21 @@ ENTRY(mohawk_flush_user_cache_range) cmp r3, #CACHE_DLIMIT bgt __flush_whole_cache 1: tst r2, #VM_EXEC +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry + mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry + add r0, r0, #CACHE_DLINESIZE + mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry + mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry + add r0, r0, #CACHE_DLINESIZE +#else mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry add r0, r0, #CACHE_DLINESIZE mcr p15, 0, r0, c7, c14, 1 @ clean and invalidate D entry mcrne p15, 0, r0, c7, c5, 1 @ invalidate I entry add r0, r0, #CACHE_DLINESIZE +#endif cmp r0, r1 blo 1b tst r2, #VM_EXEC @@ -172,17 +207,33 @@ ENTRY(mohawk_coherent_kern_range) * * - start - virtual start address * - end - virtual end address - * - * (same as arm926) */ ENTRY(mohawk_coherent_user_range) bic r0, r0, #CACHE_DLINESIZE - 1 +#if !defined(CONFIG_CPU_PXA168_B0) +#if defined(CONFIG_CPU_L2_CACHE) + mov r2, r0 +#endif +#endif 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry mcr p15, 0, r0, c7, c5, 1 @ invalidate I entry add r0, r0, #CACHE_DLINESIZE cmp r0, r1 blo 1b mcr p15, 0, r0, c7, c10, 4 @ drain WB +#if !defined(CONFIG_CPU_PXA168_B0) +#if defined(CONFIG_CPU_L2_CACHE) + mov r0, r2 + add r0, #BAD_CACHE_LINE_OFFSET +1: cmp r0, r1 + bgt 2f + mcr p15, 1, r0, c7, c11, 1 @ l2 clean single entry + add r0, #PAGE_SZ + b 1b +2: +#endif + mcr p15, 0, r0, c7, c10, 4 @ drain WB +#endif mov pc, lr /* @@ -205,6 +256,31 @@ ENTRY(mohawk_flush_kern_dcache_area) mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* + * flush_kern_dcache_page(void *page) + * + * Ensure no D cache aliasing occurs, either with itself or + * the I cache + * + * - addr - page aligned address + */ +ENTRY(mohawk_flush_kern_dcache_page) + add r1, r0, #PAGE_SZ +1: mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry + add r0, r0, #CACHE_DLINESIZE + cmp r0, r1 + blo 1b +#if !defined(CONFIG_CPU_PXA168_B0) +#if defined(CONFIG_CPU_L2_CACHE) + sub r1, r1, #CACHE_DLINESIZE + mcr p15, 1, r1, c7, c15, 1 @ L2C clean+invalidate entry +#endif +#endif + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache + mcr p15, 0, r0, c7, c10, 4 @ drain WB + mov pc, lr + /* * dma_inv_range(start, end) * @@ -218,16 +294,26 @@ ENTRY(mohawk_flush_kern_dcache_area) * * (same as v4wb) */ -mohawk_dma_inv_range: +ENTRY(mohawk_dma_inv_range) +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH tst r0, #CACHE_DLINESIZE - 1 mcrne p15, 0, r0, c7, c10, 1 @ clean D entry + mcrne p15, 1, r0, c7, c11, 1 @ clean L2 entry tst r1, #CACHE_DLINESIZE - 1 mcrne p15, 0, r1, c7, c10, 1 @ clean D entry + mcrne p15, 1, r1, c7, c11, 1 @ clean L2 entry +#endif bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c6, 1 @ invalidate D entry + mcr p15, 1, r0, c7, c7, 1 @ invalidate L2 entry add r0, r0, #CACHE_DLINESIZE cmp r0, r1 blo 1b +#if defined(CONFIG_CPU_L2_CACHE) && !defined(L2_CACHE_PREFETCH_DISABLE) + tst r0, #CACHE_DLINESIZE @ odd last cacheline? + mcrne p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry + mcrne p15, 1, r0, c7, c15, 1 @ L2C clean+invalidate entry +#endif mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr @@ -241,12 +327,20 @@ mohawk_dma_inv_range: * * (same as v4wb) */ -mohawk_dma_clean_range: +ENTRY(mohawk_dma_clean_range) +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH bic r0, r0, #CACHE_DLINESIZE - 1 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry + mcr p15, 1, r0, c7, c11, 1 @ clean L2 entry add r0, r0, #CACHE_DLINESIZE cmp r0, r1 blo 1b +#if defined(CONFIG_CPU_L2_CACHE) && !defined(L2_CACHE_PREFETCH_DISABLE) + tst r0, #CACHE_DLINESIZE @ odd last cacheline? + mcrne p15, 0, r0, c7, c10, 1 @ clean D entry + mcrne p15, 1, r0, c7, c11, 1 @ L2C clean entry +#endif +#endif mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr @@ -261,10 +355,21 @@ mohawk_dma_clean_range: ENTRY(mohawk_dma_flush_range) bic r0, r0, #CACHE_DLINESIZE - 1 1: +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH mcr p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry + mcr p15, 1, r0, c7, c15, 1 @ clean+invalidate L2 entry +#else + mcr p15, 0, r0, c7, c10, 1 @ clean D entry + mcr p15, 1, r0, c7, c11, 1 @ clean L2 entry +#endif add r0, r0, #CACHE_DLINESIZE cmp r0, r1 blo 1b +#if defined(CONFIG_CPU_L2_CACHE) && !defined(L2_CACHE_PREFETCH_DISABLE) + tst r0, #CACHE_DLINESIZE @ odd last cacheline? + mcrne p15, 0, r0, c7, c14, 1 @ clean+invalidate D entry + mcrne p15, 1, r0, c7, c15, 1 @ L2C clean+invalidate entry +#endif mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr @@ -304,13 +409,17 @@ ENTRY(mohawk_cache_fns) .long mohawk_dma_flush_range ENTRY(cpu_mohawk_dcache_clean_area) +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH 1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry add r0, r0, #CACHE_DLINESIZE subs r1, r1, #CACHE_DLINESIZE bhi 1b +#endif mcr p15, 0, r0, c7, c10, 4 @ drain WB mov pc, lr +/* =============================== PageTable ============================== */ + /* * cpu_mohawk_switch_mm(pgd) * @@ -320,13 +429,31 @@ ENTRY(cpu_mohawk_dcache_clean_area) */ .align 5 ENTRY(cpu_mohawk_switch_mm) +#ifdef CONFIG_MMU mov ip, #0 - mcr p15, 0, ip, c7, c14, 0 @ clean & invalidate all D cache +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache +#else +@ && 'Clean & Invalidate whole DCache' +1: mrc p15, 0, r15, c7, c14, 3 @ test,clean,invalidate + bne 1b +#endif mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache mcr p15, 0, ip, c7, c10, 4 @ drain WB - orr r0, r0, #0x18 @ cache the page table in L2 + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH +#if defined(CONFIG_CPU_L2_CACHE) + orr r0, r0, #0x10 @ L2 - Outer write-through +#endif +#else +#if defined(CONFIG_CPU_L2_CACHE) + orr r0, r0, #0x18 @ L2 - Outer write-back +#endif +#endif + mcr p15, 0, r0, c2, c0, 0 @ load page table pointer mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs +#endif mov pc, lr /* @@ -336,10 +463,36 @@ ENTRY(cpu_mohawk_switch_mm) */ .align 5 ENTRY(cpu_mohawk_set_pte_ext) - armv3_set_pte_ext +#ifdef CONFIG_MMU + str r1, [r0], #-2048 @ linux version + + eor r1, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY + + bic r2, r1, #PTE_SMALL_AP_MASK + bic r2, r2, #PTE_TYPE_MASK + orr r2, r2, #PTE_TYPE_SMALL + + tst r1, #L_PTE_USER @ User? + orrne r2, r2, #PTE_SMALL_AP_URO_SRW + + tst r1, #L_PTE_WRITE | L_PTE_DIRTY @ Write and Dirty? + orreq r2, r2, #PTE_SMALL_AP_UNO_SRW + + tst r1, #L_PTE_PRESENT | L_PTE_YOUNG @ Present and Young? + movne r2, #0 + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + eor r3, r2, #0x0a @ C & small page? + tst r3, #0x0b + biceq r2, r2, #4 +#endif + str r2, [r0] @ hardware version mov r0, r0 +#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH mcr p15, 0, r0, c7, c10, 1 @ clean D entry +#endif mcr p15, 0, r0, c7, c10, 4 @ drain WB +#endif mov pc, lr __INIT @@ -347,33 +500,72 @@ ENTRY(cpu_mohawk_set_pte_ext) .type __mohawk_setup, #function __mohawk_setup: mov r0, #0 - mcr p15, 0, r0, c7, c7 @ invalidate I,D caches - mcr p15, 0, r0, c7, c10, 4 @ drain write buffer - mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs - orr r4, r4, #0x18 @ cache the page table in L2 - mcr p15, 0, r4, c2, c0, 0 @ load page table pointer - - mov r0, #0 @ don't allow CP access - mcr p15, 0, r0, c15, c1, 0 @ write CP access register + mcr p15, 0, r0, c7, c7 @ invalidate I,D caches on v4 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 +#ifdef CONFIG_MMU + mcr p15, 0, r0, c8, c7 @ invalidate I,D TLBs on v4 +#if defined(CONFIG_CPU_L2_CACHE) + mcr p15, 1, r0, c7, c7, 0 @ invalidate L2 cache +#endif +#endif + + +#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH + mov r0, #4 @ disable write-back on caches explicitly + mcr p15, 7, r0, c15, c0, 0 +#if defined(CONFIG_CPU_L2_CACHE) + orr r4, r4, #0x10 @ L2 - Outer write through +#endif +#else +#if defined(CONFIG_CPU_L2_CACHE) + orr r4, r4, #0x18 @ L2 - Outer write back +#endif +#endif + +#if defined(CONFIG_CPU_L2_CACHE) && defined(L2_CACHE_PREFETCH_DISABLE) + mrc p15, 1, r0, c15, c1, 0 @ get extra features register + orr r0, r0, #(1 << 24) @ L2 prefetch disable + mcr p15, 1, r0, c15, c1, 0 @ set extra features register +#endif adr r5, mohawk_crval ldmia r5, {r5, r6} - mrc p15, 0, r0, c1, c0 @ get control register + mrc p15, 0, r0, c1, c0 @ get control register v4 bic r0, r0, r5 orr r0, r0, r6 - mov pc, lr + orr r0, r0, #0x800 @ BPU enable +#ifdef CONFIG_CPU_CACHE_ROUND_ROBIN + orr r0, r0, #0x4000 @ .1.. .... .... .... +#endif +#if defined(CONFIG_CPU_L2_CACHE) +#ifdef TTC_Z0_SUPPORT + ldr r6, =0xd4282c00 + ldr r5, [r6] + mov r5, r5, lsl #8 + mov r5, r5, lsr #28 + cmp r5, #0xa @ check TTC A0 stepping or not + orrne r0, r0, #(1 << 26) @ L2 enable - xscale/PJ41/PJ4 +#else + orr r0, r0, #(1 << 26) @ L2 enable - xscale/PJ41/PJ4 +#endif +#endif + mrc p15, 1, r5, c15, c1, 0 @ get extra features register + orr r5, r5, #(1<<29) @ streaming enable + orr r5, r5, #0x700 @ 8-wait cycles write coalesce + mcr p15, 1, r5, c15, c1, 0 @ set extra features register + mov pc, lr .size __mohawk_setup, . - __mohawk_setup /* * R * .RVI ZFRS BLDP WCAM - * .011 1001 ..00 0101 - * + * .011 0001 ..11 0101 + * */ .type mohawk_crval, #object mohawk_crval: - crval clear=0x00007f3f, mmuset=0x00003905, ucset=0x00001134 + crval clear=0x00007f3f, mmuset=0x00003135, ucset=0x00001134 __INITDATA @@ -384,7 +576,6 @@ mohawk_crval: .type mohawk_processor_functions, #object mohawk_processor_functions: .word v5t_early_abort - .word legacy_pabort .word cpu_mohawk_proc_init .word cpu_mohawk_proc_fin .word cpu_mohawk_reset @@ -398,7 +589,7 @@ mohawk_processor_functions: .type cpu_arch_name, #object cpu_arch_name: - .asciz "armv5te" + .asciz "armv5tej" .size cpu_arch_name, . - cpu_arch_name .type cpu_elf_name, #object @@ -408,16 +599,43 @@ cpu_elf_name: .type cpu_mohawk_name, #object cpu_mohawk_name: - .asciz "Marvell 88SV331x" + .asciz "Marvell Mohawk" .size cpu_mohawk_name, . - cpu_mohawk_name .align .section ".proc.info.init", #alloc, #execinstr - .type __88sv331x_proc_info,#object -__88sv331x_proc_info: - .long 0x56158000 @ Marvell 88SV331x (MOHAWK) +#ifdef CONFIG_CPU_MOHAWK_OLD_ID + .type __mohawk_old_id_proc_info,#object + __mohawk_old_id_proc_info: + .long 0x41159260 + .long 0xfffffff0 + .long PMD_TYPE_SECT | \ + PMD_SECT_BUFFERABLE | \ + PMD_SECT_CACHEABLE | \ + PMD_BIT4 | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ + .long PMD_TYPE_SECT | \ + PMD_BIT4 | \ + PMD_SECT_AP_WRITE | \ + PMD_SECT_AP_READ + b __mohawk_setup + .long cpu_arch_name + .long cpu_elf_name + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP + .long cpu_mohawk_name + .long mohawk_processor_functions + .long v4wbi_tlb_fns + .long v4wb_user_fns + .long mohawk_cache_fns + .size __mohawk_old_id_proc_info, . - __mohawk_old_id_proc_info +#endif + + .type __mohawk_proc_info,#object +__mohawk_proc_info: + .long 0x56158000 @ Marvell PXA168 .long 0xfffff000 .long PMD_TYPE_SECT | \ PMD_SECT_BUFFERABLE | \ @@ -432,10 +650,11 @@ __88sv331x_proc_info: b __mohawk_setup .long cpu_arch_name .long cpu_elf_name - .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP + .long HWCAP_SWP|HWCAP_HALF|HWCAP_THUMB|HWCAP_FAST_MULT|HWCAP_EDSP|HWCAP_JAVA .long cpu_mohawk_name .long mohawk_processor_functions .long v4wbi_tlb_fns .long v4wb_user_fns .long mohawk_cache_fns - .size __88sv331x_proc_info, . - __88sv331x_proc_info + .size __mohawk_proc_info, . - __mohawk_proc_info + diff --git a/arch/arm/plat-pxa/Kconfig b/arch/arm/plat-pxa/Kconfig index b158e98038edc2..4ddf4c76df8987 100644 --- a/arch/arm/plat-pxa/Kconfig +++ b/arch/arm/plat-pxa/Kconfig @@ -1,3 +1,5 @@ if PLAT_PXA +source "arch/arm/plat-pxa/imm/Kconfig" + endif diff --git a/arch/arm/plat-pxa/Makefile b/arch/arm/plat-pxa/Makefile index 0264bfb0ca4f24..10322f7d2e168f 100644 --- a/arch/arm/plat-pxa/Makefile +++ b/arch/arm/plat-pxa/Makefile @@ -2,10 +2,14 @@ # Makefile for code common across different PXA processor families # -obj-y := dma.o +obj-y := dma.o mfp.o ssp.o generic.o pxa3xx_pmic.o obj-$(CONFIG_GENERIC_GPIO) += gpio.o -obj-$(CONFIG_PXA3xx) += mfp.o -obj-$(CONFIG_ARCH_MMP) += mfp.o - obj-$(CONFIG_HAVE_PWM) += pwm.o + +obj-$(CONFIG_IMM) += imm/ + +ifeq ($(CONFIG_ANDROID_PMEM),y) +obj-m += pmem.o +endif + diff --git a/arch/arm/plat-pxa/dma.c b/arch/arm/plat-pxa/dma.c index 2d3c19d7c7b1cd..566c1eb63f842a 100644 --- a/arch/arm/plat-pxa/dma.c +++ b/arch/arm/plat-pxa/dma.c @@ -18,266 +18,22 @@ #include #include #include -#include #include #include -#include #include -#include - -#define DMA_DEBUG_NAME "pxa_dma" -#define DMA_MAX_REQUESTERS 64 +#include struct dma_channel { char *name; pxa_dma_prio prio; void (*irq_handler)(int, void *); void *data; - spinlock_t lock; }; static struct dma_channel *dma_channels; static int num_dma_channels; -/* - * Debug fs - */ -#ifdef CONFIG_DEBUG_FS -#include -#include -#include - -static struct dentry *dbgfs_root, *dbgfs_state, **dbgfs_chan; - -static int dbg_show_requester_chan(struct seq_file *s, void *p) -{ - int pos = 0; - int chan = (int)s->private; - int i; - u32 drcmr; - - pos += seq_printf(s, "DMA channel %d requesters list :\n", chan); - for (i = 0; i < DMA_MAX_REQUESTERS; i++) { - drcmr = DRCMR(i); - if ((drcmr & DRCMR_CHLNUM) == chan) - pos += seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i, - !!(drcmr & DRCMR_MAPVLD)); - } - return pos; -} - -static inline int dbg_burst_from_dcmd(u32 dcmd) -{ - int burst = (dcmd >> 16) & 0x3; - - return burst ? 4 << burst : 0; -} - -static int is_phys_valid(unsigned long addr) -{ - return pfn_valid(__phys_to_pfn(addr)); -} - -#define DCSR_STR(flag) (dcsr & DCSR_##flag ? #flag" " : "") -#define DCMD_STR(flag) (dcmd & DCMD_##flag ? #flag" " : "") - -static int dbg_show_descriptors(struct seq_file *s, void *p) -{ - int pos = 0; - int chan = (int)s->private; - int i, max_show = 20, burst, width; - u32 dcmd; - unsigned long phys_desc; - struct pxa_dma_desc *desc; - unsigned long flags; - - spin_lock_irqsave(&dma_channels[chan].lock, flags); - phys_desc = DDADR(chan); - - pos += seq_printf(s, "DMA channel %d descriptors :\n", chan); - pos += seq_printf(s, "[%03d] First descriptor unknown\n", 0); - for (i = 1; i < max_show && is_phys_valid(phys_desc); i++) { - desc = phys_to_virt(phys_desc); - dcmd = desc->dcmd; - burst = dbg_burst_from_dcmd(dcmd); - width = (1 << ((dcmd >> 14) & 0x3)) >> 1; - - pos += seq_printf(s, "[%03d] Desc at %08lx(virt %p)\n", - i, phys_desc, desc); - pos += seq_printf(s, "\tDDADR = %08x\n", desc->ddadr); - pos += seq_printf(s, "\tDSADR = %08x\n", desc->dsadr); - pos += seq_printf(s, "\tDTADR = %08x\n", desc->dtadr); - pos += seq_printf(s, "\tDCMD = %08x (%s%s%s%s%s%s%sburst=%d" - " width=%d len=%d)\n", - dcmd, - DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR), - DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG), - DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN), - DCMD_STR(ENDIAN), burst, width, - dcmd & DCMD_LENGTH); - phys_desc = desc->ddadr; - } - if (i == max_show) - pos += seq_printf(s, "[%03d] Desc at %08lx ... max display reached\n", - i, phys_desc); - else - pos += seq_printf(s, "[%03d] Desc at %08lx is %s\n", - i, phys_desc, phys_desc == DDADR_STOP ? - "DDADR_STOP" : "invalid"); - - spin_unlock_irqrestore(&dma_channels[chan].lock, flags); - return pos; -} - -static int dbg_show_chan_state(struct seq_file *s, void *p) -{ - int pos = 0; - int chan = (int)s->private; - u32 dcsr, dcmd; - int burst, width; - static char *str_prio[] = { "high", "normal", "low" }; - - dcsr = DCSR(chan); - dcmd = DCMD(chan); - burst = dbg_burst_from_dcmd(dcmd); - width = (1 << ((dcmd >> 14) & 0x3)) >> 1; - - pos += seq_printf(s, "DMA channel %d\n", chan); - pos += seq_printf(s, "\tPriority : %s\n", - str_prio[dma_channels[chan].prio]); - pos += seq_printf(s, "\tUnaligned transfer bit: %s\n", - DALGN & (1 << chan) ? "yes" : "no"); - pos += seq_printf(s, "\tDCSR = %08x (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n", - dcsr, DCSR_STR(RUN), DCSR_STR(NODESC), - DCSR_STR(STOPIRQEN), DCSR_STR(EORIRQEN), - DCSR_STR(EORJMPEN), DCSR_STR(EORSTOPEN), - DCSR_STR(SETCMPST), DCSR_STR(CLRCMPST), - DCSR_STR(CMPST), DCSR_STR(EORINTR), DCSR_STR(REQPEND), - DCSR_STR(STOPSTATE), DCSR_STR(ENDINTR), - DCSR_STR(STARTINTR), DCSR_STR(BUSERR)); - - pos += seq_printf(s, "\tDCMD = %08x (%s%s%s%s%s%s%sburst=%d width=%d" - " len=%d)\n", - dcmd, - DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR), - DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG), - DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN), - DCMD_STR(ENDIAN), burst, width, dcmd & DCMD_LENGTH); - pos += seq_printf(s, "\tDSADR = %08x\n", DSADR(chan)); - pos += seq_printf(s, "\tDTADR = %08x\n", DTADR(chan)); - pos += seq_printf(s, "\tDDADR = %08x\n", DDADR(chan)); - return pos; -} - -static int dbg_show_state(struct seq_file *s, void *p) -{ - int pos = 0; - - /* basic device status */ - pos += seq_printf(s, "DMA engine status\n"); - pos += seq_printf(s, "\tChannel number: %d\n", num_dma_channels); - - return pos; -} - -#define DBGFS_FUNC_DECL(name) \ -static int dbg_open_##name(struct inode *inode, struct file *file) \ -{ \ - return single_open(file, dbg_show_##name, inode->i_private); \ -} \ -static const struct file_operations dbg_fops_##name = { \ - .owner = THIS_MODULE, \ - .open = dbg_open_##name, \ - .llseek = seq_lseek, \ - .read = seq_read, \ - .release = single_release, \ -} - -DBGFS_FUNC_DECL(state); -DBGFS_FUNC_DECL(chan_state); -DBGFS_FUNC_DECL(descriptors); -DBGFS_FUNC_DECL(requester_chan); - -static struct dentry *pxa_dma_dbg_alloc_chan(int ch, struct dentry *chandir) -{ - char chan_name[11]; - struct dentry *chan, *chan_state = NULL, *chan_descr = NULL; - struct dentry *chan_reqs = NULL; - void *dt; - - scnprintf(chan_name, sizeof(chan_name), "%d", ch); - chan = debugfs_create_dir(chan_name, chandir); - dt = (void *)ch; - - if (chan) - chan_state = debugfs_create_file("state", 0400, chan, dt, - &dbg_fops_chan_state); - if (chan_state) - chan_descr = debugfs_create_file("descriptors", 0400, chan, dt, - &dbg_fops_descriptors); - if (chan_descr) - chan_reqs = debugfs_create_file("requesters", 0400, chan, dt, - &dbg_fops_requester_chan); - if (!chan_reqs) - goto err_state; - - return chan; - -err_state: - debugfs_remove_recursive(chan); - return NULL; -} - -static void pxa_dma_init_debugfs(void) -{ - int i; - struct dentry *chandir; - - dbgfs_root = debugfs_create_dir(DMA_DEBUG_NAME, NULL); - if (IS_ERR(dbgfs_root) || !dbgfs_root) - goto err_root; - - dbgfs_state = debugfs_create_file("state", 0400, dbgfs_root, NULL, - &dbg_fops_state); - if (!dbgfs_state) - goto err_state; - - dbgfs_chan = kmalloc(sizeof(*dbgfs_state) * num_dma_channels, - GFP_KERNEL); - if (!dbgfs_chan) - goto err_alloc; - - chandir = debugfs_create_dir("channels", dbgfs_root); - if (!chandir) - goto err_chandir; - - for (i = 0; i < num_dma_channels; i++) { - dbgfs_chan[i] = pxa_dma_dbg_alloc_chan(i, chandir); - if (!dbgfs_chan[i]) - goto err_chans; - } - - return; -err_chans: -err_chandir: - kfree(dbgfs_chan); -err_alloc: -err_state: - debugfs_remove_recursive(dbgfs_root); -err_root: - pr_err("pxa_dma: debugfs is not available\n"); -} - -static void __exit pxa_dma_cleanup_debugfs(void) -{ - debugfs_remove_recursive(dbgfs_root); -} -#else -static inline void pxa_dma_init_debugfs(void) {} -static inline void pxa_dma_cleanup_debugfs(void) {} -#endif - int pxa_request_dma (char *name, pxa_dma_prio prio, void (*irq_handler)(int, void *), void *data) @@ -316,7 +72,6 @@ int pxa_request_dma (char *name, pxa_dma_prio prio, local_irq_restore(flags); return i; } -EXPORT_SYMBOL(pxa_request_dma); void pxa_free_dma (int dma_ch) { @@ -334,26 +89,24 @@ void pxa_free_dma (int dma_ch) dma_channels[dma_ch].name = NULL; local_irq_restore(flags); } -EXPORT_SYMBOL(pxa_free_dma); static irqreturn_t dma_irq_handler(int irq, void *dev_id) { int i, dint = DINT; - struct dma_channel *channel; - while (dint) { - i = __ffs(dint); - dint &= (dint - 1); - channel = &dma_channels[i]; - if (channel->name && channel->irq_handler) { - channel->irq_handler(i, channel->data); - } else { - /* - * IRQ for an unregistered DMA channel: - * let's clear the interrupts and disable it. - */ - printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i); - DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + for (i = 0; i < num_dma_channels; i++) { + if (dint & (1 << i)) { + struct dma_channel *channel = &dma_channels[i]; + if (channel->name && channel->irq_handler) { + channel->irq_handler(i, channel->data); + } else { + /* + * IRQ for an unregistered DMA channel: + * let's clear the interrupts and disable it. + */ + printk (KERN_WARNING "spurious IRQ for DMA channel %d\n", i); + DCSR(i) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + } } } return IRQ_HANDLED; @@ -375,7 +128,6 @@ int __init pxa_init_dma(int irq, int num_ch) for (i = 0; i < num_ch; i++) { DCSR(i) = 0; dma_channels[i].prio = min((i & 0xf) >> 2, DMA_PRIO_LOW); - spin_lock_init(&dma_channels[i].lock); } ret = request_irq(irq, dma_irq_handler, IRQF_DISABLED, "DMA", NULL); @@ -384,9 +136,10 @@ int __init pxa_init_dma(int irq, int num_ch) kfree(dma_channels); return ret; } - num_dma_channels = num_ch; - - pxa_dma_init_debugfs(); + num_dma_channels = num_ch; return 0; } + +EXPORT_SYMBOL(pxa_request_dma); +EXPORT_SYMBOL(pxa_free_dma); diff --git a/arch/arm/plat-pxa/generic.c b/arch/arm/plat-pxa/generic.c new file mode 100644 index 00000000000000..5f878b32768e83 --- /dev/null +++ b/arch/arm/plat-pxa/generic.c @@ -0,0 +1,150 @@ +/* + * linux/arch/arm/plat-pxa/generic.c + * + * Code to PXA processor lines + * + * 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 + +char default_pxa_cmdline[COMMAND_LINE_SIZE] = "unknow"; +static int __init parse_tag_pxa(const struct tag *tag) +{ + strlcpy(default_pxa_cmdline, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); + printk(KERN_INFO "pxa cmdline:%s\n", default_pxa_cmdline); + return 0; +} +__tagtable('pxa', parse_tag_pxa); + +static ssize_t pxa_cmdline_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = strlen(default_pxa_cmdline); + + sprintf(page, "%s\n", default_pxa_cmdline); + return len + 1; +} +static void __init create_pxa_cmdline_proc_file(void) +{ + struct proc_dir_entry *pxa_cmdline_proc_file = + create_proc_entry("pxa_cmdline", 0644, NULL); + + if (pxa_cmdline_proc_file) + pxa_cmdline_proc_file->read_proc = pxa_cmdline_read_proc; + else + printk(KERN_INFO "pxa_cmdline proc file create failed!\n"); +} +module_init(create_pxa_cmdline_proc_file); + +static int android_project = 0; +static int __init android_setup(char *__unused) +{ + android_project = 1; + return 1; +} +__setup("android", android_setup); + +int is_android(void) +{ + return android_project; +} +EXPORT_SYMBOL(is_android); + +static int lab_kernel = 0; +static int __init lab_setup(char *__unused) +{ + lab_kernel = 1; + return 1; +} +__setup("lab", lab_setup); + +int is_lab(void) +{ + return lab_kernel; +} +EXPORT_SYMBOL(is_lab); + +static unsigned long long g_mlc_size = 1; /*FIXME: default is 4G ? */ +static int __init mlc_size_setup(char *str) +{ + char *endchar; + if (NULL == str) { + return 1; /* FIXME*/ + } + str++; /*skip = */ + + /* g_mlc_size = memparse(str, &endchar); */ + g_mlc_size = simple_strtoull(str, &endchar, 0); + + return 1; +} +__setup("pxastorage", mlc_size_setup); + +int get_mlc_size(void) +{ + return g_mlc_size; +} +EXPORT_SYMBOL(get_mlc_size); + +#ifdef CONFIG_ANDROID_PMEM +#include +#include +void android_add_pmem(char *name, size_t size, int no_allocator, int cached) +{ + struct platform_device *android_pmem_device; + struct android_pmem_platform_data *android_pmem_pdata; + struct page *page; + unsigned long addr, tmp; + static int id; + unsigned long paddr = 0; + + android_pmem_device = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if(android_pmem_device == NULL) + return ; + + android_pmem_pdata = kzalloc(sizeof(struct android_pmem_platform_data), GFP_KERNEL); + if(android_pmem_pdata == NULL) { + kfree(android_pmem_device); + return ; + } + + page = alloc_pages(GFP_KERNEL, get_order(size)); + if (page == NULL) + return ; + + addr = (unsigned long)page_address(page); + paddr = virt_to_phys((void *)addr); + tmp = size; + dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE); + while(tmp > 0) { + SetPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + tmp -= PAGE_SIZE; + } + android_pmem_pdata->name = name; + android_pmem_pdata->start = paddr; + android_pmem_pdata->size = size; + android_pmem_pdata->no_allocator = no_allocator ; + android_pmem_pdata->cached = cached; + + android_pmem_device->name = "android_pmem"; + android_pmem_device->id = id++; + android_pmem_device->dev.platform_data = android_pmem_pdata; + + platform_device_register(android_pmem_device); +} +EXPORT_SYMBOL(android_add_pmem); +#endif + diff --git a/arch/arm/plat-pxa/imm/Kconfig b/arch/arm/plat-pxa/imm/Kconfig new file mode 100644 index 00000000000000..fb3a6e27fdce45 --- /dev/null +++ b/arch/arm/plat-pxa/imm/Kconfig @@ -0,0 +1,44 @@ +# +# linux/arch/arm/mach-pxa/imm/Kconfig +# +# Intel Memory Management +# +# Module defines and configs +# +# Todd Brandt +# Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) +# +# 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. +# + +# (C) Copyright 2006 Marvell International Ltd. +# All Rights Reserved + +menu "Marvell(R) Wireless Memory Management Technology" +depends on PLAT_PXA + +config IMM + bool "Marvell(R) Wireless Memory Management Support" + help + If you say Y here, the Intel Memory Management device will be + built into the kernel. IMM supplies specialized optimizations + for Intel XScale based processors. + +config IMM_DEBUG + bool "Debug Info" + depends on IMM + help + If you say Y here, IMM will print lots and lots of irritating + debug messages. + +config IMM_API_SRAM + bool "SRAM Allocation API" + depends on IMM && PROFILING + help + If you say Y here, IMM will provide an API to applications + and drivers allowing them to allocate internal SRAM. + +endmenu + diff --git a/arch/arm/plat-pxa/imm/Makefile b/arch/arm/plat-pxa/imm/Makefile new file mode 100644 index 00000000000000..eaf66887d40871 --- /dev/null +++ b/arch/arm/plat-pxa/imm/Makefile @@ -0,0 +1,19 @@ +# +# linux/arch/arm/mach-pxa/imm/imm.h +# +# Intel Memory Management +# +# Makefile for the Intel Memory Management. +# +# Todd Brandt +# Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) +# +# 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. +# + +# policy.c is the heart of the IMM code +obj-$(CONFIG_IMM) += policy.o +obj-$(CONFIG_IMM_API_SRAM) += sram.o ioremap.o + diff --git a/arch/arm/plat-pxa/imm/imm.h b/arch/arm/plat-pxa/imm/imm.h new file mode 100644 index 00000000000000..054ea4ea072e06 --- /dev/null +++ b/arch/arm/plat-pxa/imm/imm.h @@ -0,0 +1,160 @@ +/* + * linux/arch/arm/mach-pxa/imm/imm.h + * + * Intel Memory Management + * + * Internal IMM Defines/Globals/Functions + * + * Todd Brandt + * Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) + * + * 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. + + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef _ARCH_PXA_IMM_H +#define _ARCH_PXA_IMM_H + +#include +#include + +/* -------- IMM global data --------------- */ + +extern struct semaphore imm_sem; + +/* error list */ +enum error_cond { + IMM_ERROR_NONE=0, + IMM_ERROR_KMALLOC, + IMM_ERROR_NOSPACE, + IMM_ERROR_SIZETOOLOW, + IMM_ERROR_ENTRY_NOTFOUND, + IMM_ERROR_PERMISSION_DENIED, + IMM_ERROR_UNSUPPORTED, + IMM_ERROR_RANGEOVERLAP, + IMM_ERROR_UNINIT, + IMM_ERROR_UNKNOWN +}; + +/* defines and macros */ +#define VMALLOC_VMADDR(x) ((unsigned long)(x)) +#define IMM_FIRST_KERNEL_IMMID 0xc0000000 +#define IMM_DEFAULT_DRIVER_NAME "unnamed" +#define IMMID_USER(x) ((u32)(x) < IMM_FIRST_KERNEL_IMMID) +/* #define IMMID_OVERLAP(id1, id2) \ + * ((!IMMID_USER(id1)&&!IMMID_USER(id2))?1:(id1 == id2)) */ +#define IMMID_OVERLAP(id1, id2) ((id1) == (id2)) +#define IMMID_VALUE(x) ((IMMID_USER(x))?(x): \ + ((x)-IMM_FIRST_KERNEL_IMMID)) +#define IMMID_TYPE(x) ((IMMID_USER(x))?"USER":"KERNEL") +#define IMMID_TYPEC(x) ((IMMID_USER(x))?'U':'K') + +/* -------- IMM global functions --------------- */ + +extern void register_error(u32 immid, u32 err); +extern void imm_dau_sram_init(struct proc_dir_entry *); +extern int imm_free_immid(u32 immid); +/* __ioremap subfunction from ioremap.c */ +extern int remap_area_pages(unsigned long, unsigned long, + unsigned long, unsigned long); +extern void unmap_area_pmd(pgd_t *dir, unsigned long address, + unsigned long size); + +/* -------- IMM proc interface functions --------------- */ + +/* entry data to be read out from read_procs */ +struct imm_proc_t { + char *buffer; + u32 len; + struct imm_proc_t *prev; + struct imm_proc_t *next; +}; + +/* pprintf_helper commands (passed through cmd var) */ +#define PCMD_EXPORT 0 +#define PCMD_STORE 1 +#define PCMD_CLEAN 2 + +/* pprintf helper function, handles multiple calls to this proc to + * export data greater than a page + */ +extern int pprintf_helper(struct imm_proc_t *proc_head, char *page, + char **start, int count, int *eof, int cmd, + const char *fmt, ...); + +/* prototype for a read proc function, needed to get ubquity on variable + * names + */ +#define READ_PROC_PROTO(name) static int name(char* page, char** start, \ + off_t off, int count, int* eof, void* data) + +/* internal variables that each read proc function must have (used only + * by these helper macros) + */ +#define READ_PROC_VARS static struct imm_proc_t proc_head = \ + {NULL, 0, &proc_head, &proc_head}; + +/* init call which is used to print additional pages in multipage outputs, + * off = 0 means that this is a new call + */ +#define READ_PROC_INIT if (off == 0) { \ + pprintf_helper(&proc_head, page, \ + start, count, eof, \ + PCMD_CLEAN, NULL); \ + } else { \ + return pprintf_helper(&proc_head, \ + page, start, count, \ + eof, PCMD_EXPORT, NULL);\ + } + +/* return call which stars exporting the data queued */ +#define READ_PROC_RETURN return pprintf_helper(&proc_head, page, start, count, eof, PCMD_EXPORT, NULL); +#define REG_READ_PROC(e, d, n, f) if((e = create_proc_entry(n, S_IRUSR | S_IRGRP | S_IROTH, d)) != NULL) e->read_proc = f; +#define pprintf(s, args...) pprintf_helper(&proc_head, page, start, count, eof, PCMD_STORE, s, ## args); + +#define WRITE_PROC_PROTO(name) static int name(struct file* file, const char* buffer, unsigned long count, void* data) +#define WRITE_PROC_VAR_NUM(s) char val[s]; +#define WRITE_PROC_GET_NUM(n, s) copy_from_user(val, buffer, s); n = simple_strtol(val, NULL, 0); +#define WRITE_PROC_RETURN return(count); +#define REG_RW_PROC(e, d, n, rf, wf) if((e = create_proc_entry(n, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, d)) != NULL) \ +{ e->read_proc = rf; e->write_proc = wf; } + +/* -------- IMM client data --------------- */ + +#ifdef CONFIG_IMM_API_SRAM +extern struct imm_virt_t malloc_list; + +/* entries for virtual memory sram malloc list */ +struct imm_virt_t { + u32 immid; + u32 start; /* pointer to physical start of high speed memory block */ + u32 end; /* pointer to first physical address after the high speed memory block */ + struct imm_virt_t *prev; + struct imm_virt_t *next; +}; +#endif + +/* entries for the global immid list */ +struct imm_info_t { + /* general info for the imm instance */ + u32 immid; /* imm ID of the caller */ + int last_error; /* last registered error */ + char name[IMM_CLIENT_NAME_SIZE]; /* name of the client process/driver */ + struct mm_struct *mm; /* memory struct used for remapping */ + +#ifdef CONFIG_IMM_API_SRAM + struct imm_virt_t *malloc_list; /* pointer to virtual allocation list for this space */ + u32 used_space; +#endif + /* list pointers */ + struct imm_info_t *prev; + struct imm_info_t *next; +}; + +struct imm_info_t *imm_info_getentry(u32); +#endif + diff --git a/arch/arm/plat-pxa/imm/ioremap.c b/arch/arm/plat-pxa/imm/ioremap.c new file mode 100644 index 00000000000000..0b348539e7b8a5 --- /dev/null +++ b/arch/arm/plat-pxa/imm/ioremap.c @@ -0,0 +1,104 @@ +/* + * linux/arch/arm/mm/ioremap.c + * + * Re-map IO memory to kernel address space so that we can access it. + * + * (C) Copyright 1995 1996 Linus Torvalds + * + * Hacked for ARM by Phil Blundell + * Hacked to allow all architectures to build, and various cleanups + * by Russell King + * + * This allows a driver to remap an arbitrary region of bus memory into + * virtual space. One should *only* use readl, writel, memcpy_toio and + * so on with such remapped areas. + * + * Because the ARM only has a 32-bit address space we can't address the + * whole of the (physical) PCI space at once. PCI huge-mode addressing + * allows us to circumvent this restriction by splitting PCI space into + * two 2GB chunks and mapping only one at a time into processor memory. + * We use MMU protection domains to trap any attempt to access the bank + * that is not currently mapped. (This isn't fully implemented yet.) + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int remap_area_pte(pmd_t *pmd, unsigned long addr, unsigned long end, + unsigned long phys_addr, pgprot_t prot) +{ + pte_t *pte; + + pte = pte_alloc_kernel(pmd, addr); + if (!pte) + return -ENOMEM; + + do { + if (!pte_none(*pte)) + goto bad; + + set_pte_ext(pte, pfn_pte(phys_addr >> PAGE_SHIFT, prot), 0); + phys_addr += PAGE_SIZE; + } while (pte++, addr += PAGE_SIZE, addr != end); + return 0; + + bad: + printk(KERN_CRIT "remap_area_pte: page already exists\n"); + BUG(); +} + +static inline int remap_area_pmd(pgd_t *pgd, unsigned long addr, + unsigned long end, unsigned long phys_addr, + pgprot_t prot) +{ + unsigned long next; + pmd_t *pmd; + int ret = 0; + + pmd = pmd_alloc(&init_mm, pgd, addr); + if (!pmd) + return -ENOMEM; + + do { + next = pmd_addr_end(addr, end); + ret = remap_area_pte(pmd, addr, next, phys_addr, prot); + if (ret) + return ret; + phys_addr += next - addr; + } while (pmd++, addr = next, addr != end); + return ret; +} + +int remap_area_pages(unsigned long start, unsigned long pfn, + unsigned long size, unsigned long flags) +{ + unsigned long addr = start; + unsigned long next, end = start + size; + unsigned long phys_addr = __pfn_to_phys(pfn); + pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | + L_PTE_DIRTY | L_PTE_WRITE | flags); + pgd_t *pgd; + int err = 0; + + BUG_ON(addr >= end); + pgd = pgd_offset_k(addr); + do { + next = pgd_addr_end(addr, end); + err = remap_area_pmd(pgd, addr, next, phys_addr, prot); + if (err) + break; + phys_addr += next - addr; + } while (pgd++, addr = next, addr != end); + + return err; +} + + diff --git a/arch/arm/plat-pxa/imm/policy.c b/arch/arm/plat-pxa/imm/policy.c new file mode 100644 index 00000000000000..b2d72541c98b30 --- /dev/null +++ b/arch/arm/plat-pxa/imm/policy.c @@ -0,0 +1,620 @@ +/* + * linux/arch/arm/mach-pxa/imm/policy.c + * + * Intel Memory Management + * + * Policy Manager - focal point for IMM functionality + * + * Todd Brandt + * Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) + * + * 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. + + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "imm.h" + +int phys_sram_start; +int phys_sram_size; +int imm_sram_start; +int imm_sram_size; + +extern int imm_malloc_map_size; +/* the imm semaphore */ +DECLARE_MUTEX(imm_sem); + +/* error list */ +const char *imm_errors[] = { + "Operation completed successfully", + "System memory is too low, kernel malloc failed", + "Insufficient space left", + "The size argument is too small, size must be > 0 bytes", + "The requested entry does not exist", + "The address range requested is off limits to this process", + "This functionality is currently unsupported", + "The requested address range overlaps with an existing entry's \ + address range", + "The requested IMM module failed to initialize due to a kernel error", + "Unknown Error!" +}; + +/* immid list */ +struct imm_info_t immid_list; +struct proc_dir_entry *proc_imm = NULL; +static u32 kernel_immid = IMM_FIRST_KERNEL_IMMID; +static int imm_initialized = 0; + +/*---------------------------------------------------------------------- + * + * High Level Call Tree: + * + * [Application or Driver Call] + * imm_register_kernel + * imm_info_newentry + * imm_register_user + * imm_info_newentry + * imm_get_physical + * imm_get_virtual + * imm_error + * + * [IMM Module call (SRAM, Power)] + * imm_info_getentry + * register_error + * pprintf_helper + * + * [Process Exit - Kernel Hook from System Profile Interface] + * imm_task_exit + * imm_free_immid (sram.c) + * imm_info_delentry + * + * [Module Initialization] + * imm_init + * imm_dau_sram_init (sram.c) + * imm_autopower_init (power.c) + * + *--------------------------------------------------------------------*/ + +/************************************************************************* + * + * Function: imm_get_physical + * Description: Retrieve the physical address for a given imm virtual + * address + * + * Arguments: + * virtual - address returned from imm_malloc + * immid - the id of the caller + * + * Return Value: + * the physical address, 0 on failure + *************************************************************************/ + +u32 +imm_get_physical(void *v, u32 immid) +{ + pmd_t *pmd; + pte_t *pte; + pgd_t *pgd; + u32 val = 0, virtual = (u32)v; + struct mm_struct* mm; + + if (IMMID_USER(immid)) + mm = current->mm; + else + mm = &init_mm; + + pgd = pgd_offset(mm, virtual); + if (!pgd_none(*pgd) && !pgd_bad(*pgd)) { + /* 1st level entry pointer */ + pmd = pmd_offset(pgd, virtual); + if (!pmd_none(*pmd) && !pmd_bad(*pmd)) { + /* 2nd level entry pointer */ + pte = pte_offset_kernel(pmd, virtual); + if (pte) { + val = (*(u32 *)((u32)pte-2048))&PAGE_MASK; + val += virtual%PAGE_SIZE; + } + } else if (!pmd_none(*pmd)) { + /* work around */ + val = (unsigned long)virt_to_phys((void*)virtual); + /* + * old method address conversion is not correct + val = (*(u32 *)pmd) & 0xFFF00000; + val += virtual%0x100000; + */ + } + } + + return val; +} + +/************************************************************************* + * + * Function: imm_get_virtual + * Description: Retrieve the virtual address for a given physical + * address + * + * Arguments: + * physical - address of some memory in the immid's address space + * immid - the id of the caller + * + * Return Value: + * the virtual address, 0 on failure + *************************************************************************/ + +u32 +imm_get_virtual(u32 p, struct task_struct *tsk) +{ + pmd_t *pmd; + pte_t *pte; + pgd_t *pgd; + u32 val, v; + struct mm_struct* mm; + struct vm_area_struct *vm_search; + + down(&imm_sem); + if ((tsk == NULL)||(tsk->mm == NULL)) return 0; + mm = tsk->mm; + + for (vm_search = mm->mmap; vm_search; vm_search = vm_search->vm_next) { + for (v = vm_search->vm_start; v < vm_search->vm_end; + v+=PAGE_SIZE) { + pgd = pgd_offset(mm, v); + if (!pgd_none(*pgd) && !pgd_bad(*pgd)) { + pmd = pmd_offset(pgd, v); + if (!pmd_none(*pmd) && !pmd_bad(*pmd)) { + pte = pte_offset_kernel(pmd, v); + if (pte) { + val = (*(u32 *)((u32)pte-2048))&PAGE_MASK; + val += p%PAGE_SIZE; + if (p == val) { + up(&imm_sem); + return(v+(p%PAGE_SIZE)); + } + } + } + } + } + } + up(&imm_sem); + return 0; +} + +/************************************************************************* + * imm_info list handlers + ************************************************************************/ + +/* retrieve an imm_info object from the list */ +struct imm_info_t * +imm_info_getentry(u32 immid) +{ + struct imm_info_t *head, *p; + + if (immid < 1) + return NULL; + head = &immid_list; + p = head->next; + + /* loop through the list til you hit the end or the immid */ + while ((p != head) && (p->immid != immid)) p = p->next; + if (p->immid == immid) { + return p; + } else { + return NULL; + } +} + +/* create a new imm_info entry for a new immid */ +static struct imm_info_t * +imm_info_newentry(u32 immid, char *name, u32 user_imm_space) +{ + struct imm_info_t *head, *p, *new; + + head = &immid_list; + p = head->next; + + /* loop through the list til you hit the end or the immid */ + while ((p != head) && (p->immid != immid)) p = p->next; + if (p->immid == immid) { + return p; + } else { + imm_debug("imm_info_newentry called, initializing a new \ + imm_info entry\n"); + new = kmalloc(sizeof(struct imm_info_t), GFP_ATOMIC); + if (new == NULL) { + imm_debug("imm_info_newentry failed, kmalloc \ + returned NULL: %s, line %d\n", + __FILE__, __LINE__); + return NULL; + } + + new->immid = immid; + new->last_error = 0; + memcpy(new->name, name, min(strlen(name)+1, + (size_t)(IMM_CLIENT_NAME_SIZE-1))); + new->name[IMM_CLIENT_NAME_SIZE-1] = '\0'; + if (IMMID_USER(immid)) { + new->mm = current->mm; + } else { + new->mm = &init_mm; + } + +#ifdef CONFIG_IMM_API_SRAM + if (IMMID_USER(immid)) { + new->malloc_list = kmalloc(sizeof(struct imm_virt_t), + GFP_ATOMIC); + if (new->malloc_list == NULL) { + imm_debug("imm_info_newentry failed, kmalloc \ + returned NULL: %s, line %d\n", + __FILE__, __LINE__); + kfree(new); + return NULL; + } + new->malloc_list->immid = immid; + new->malloc_list->start = user_imm_space + + imm_malloc_map_size; + new->malloc_list->end = user_imm_space; + new->malloc_list->prev = new->malloc_list; + new->malloc_list->next = new->malloc_list; + } else { + new->malloc_list = &malloc_list; + } + new->used_space = 0; +#endif + + new->next = p; + new->prev = p->prev; + new->next->prev = new; + new->prev->next = new; + + return new; + } +} + +#if defined(CONFIG_IMM_API_SRAM) +/* remove an immid from the list */ +static int +imm_info_delentry(u32 immid) +{ + struct imm_info_t *head, *p; + + head = &immid_list; + p = head->next; + + /* loop through the list til you hit the end or the immid */ + while ((p != head) && (p->immid != immid)) p = p->next; + if (p->immid == immid) { + if (IMMID_USER(immid)) { + struct imm_virt_t *headv = p->malloc_list; + struct imm_virt_t *pv = headv->next; + while (pv != headv) { + /* pv now points to the memory block + * to be removed + */ + pv->prev->next = pv->next; + pv->next->prev = pv->prev; + kfree(pv); + pv = headv->next; + } + kfree(p->malloc_list); + } + p->prev->next = p->next; + p->next->prev = p->prev; + kfree(p); + return 0; + } + return -1; +} + +static int +imm_task_exit(struct notifier_block * self, unsigned long val, void * data) { +struct task_struct * tsk = (struct task_struct *)data; + + down(&imm_sem); + if (tsk && imm_initialized) { + /* cache must be cleared first since some of it may be */ + /* locked over SRAM */ + imm_free_immid(tsk->pid); + imm_info_delentry(tsk->pid); + } + up(&imm_sem); + + return NOTIFY_OK; +} + +static struct notifier_block imm_task_exit_nb = { + .notifier_call = imm_task_exit, +}; +#endif + +/* associate an error with an immid */ +void +register_error(u32 immid, u32 err) +{ + struct imm_info_t *head, *p; + + head = &immid_list; + p = head->next; + + /* loop through the list til you hit the end or the immid */ + while ((p != head) && (p->immid != immid)) p = p->next; + if (p->immid == immid) + p->last_error = err; +} + +/************************************************************************* + * + * Function: imm_register(kernel and user) + * Description: Create a new immid instance for either the kernel or user + * space. + * + * Return Value: + * immid - a new unique id which will never conflict with user immids + * + *************************************************************************/ + +u32 +imm_register_kernel(char *name) +{ + struct imm_info_t *imm_info; + + down(&imm_sem); + if (name == NULL) name = IMM_DEFAULT_DRIVER_NAME; + imm_debug("imm_register_kernel called for %s, new ID = %08X\n", + name, kernel_immid); + if ((imm_info = imm_info_newentry(kernel_immid, name, 0)) == NULL) { + up(&imm_sem); + return 0; + } + up(&imm_sem); + return(kernel_immid++); +} + +u32 +imm_register_user(u32 vstart, u32 pid) +{ + struct imm_info_t *imm_info; + + down(&imm_sem); + imm_debug("imm_register_user called for %s, vstart = %08X, pid = %d\n", + current->comm, vstart, pid); + if (!vstart || !pid || + ((imm_info = imm_info_newentry(pid, current->comm, vstart)) + == NULL)) { + up(&imm_sem); + return 0; + } + up(&imm_sem); + return(pid); +} + +/************************************************************************* + * + * Function: imm_error + * Description: Return the last error for the given immid + * + * Return Value: + * a const char pointer of the error string + * + *************************************************************************/ + +const char * +imm_error(u32 immid) +{ + struct imm_info_t *head, *p; + + down(&imm_sem); + head = &immid_list; + p = head->next; + + /* loop through the list til you hit the end or the immid */ + while ((p != head) && (p->immid != immid)) p = p->next; + if (p->immid == immid) { + if ((p->last_error >= 0) + && (p->last_error < IMM_ERROR_UNKNOWN)) { + up(&imm_sem); + return imm_errors[p->last_error]; + } + } + up(&imm_sem); + return "The caller is not a client of IMM"; +} + +int +pprintf_helper(struct imm_proc_t *proc_head, char *page, char **start, + int count, int *eof, int cmd, const char *fmt, ...) +{ + char buffer[1000]; + struct imm_proc_t *new; + va_list args; + int len, temp; + + switch(cmd) { + case PCMD_CLEAN: /* init mode */ + for (new = proc_head->next; new != proc_head; + new = proc_head->next) { + new->prev->next = new->next; + new->next->prev = new->prev; + kfree(new); + } + break; + case PCMD_STORE: /* input mode */ + va_start(args, fmt); + len = vsprintf(buffer, fmt, args); + va_end(args); + + if ((new = kmalloc(sizeof(struct imm_proc_t), GFP_ATOMIC)) + != NULL) { + temp = strlen(buffer)+1; + new->buffer = kmalloc(temp, GFP_ATOMIC); + memcpy(new->buffer, buffer, temp); + new->len = len; + new->next = proc_head; + new->prev = proc_head->prev; + new->next->prev = new; + new->prev->next = new; + } + break; + case PCMD_EXPORT: /* output mode */ + len = 0; + for (new = proc_head->next; new != proc_head; + new = proc_head->next) { + /* check if we're about to exceed count */ + if ((len + new->len) > count) { + /* if we haven't reached count, write part + * of the current entry + */ + if (len < count) { + /* copy out part of the entry */ + memcpy(page+len, new->buffer, + count - len); + /* remove the outputted data */ + memmove(new->buffer, + new->buffer + (count - len), + len + new->len - count + 1); + new->len -= count - len; + } + /* we're not done yet, more printing is + * necessary + */ + *eof = 0; + *start = page; + return count; + } + + memcpy(page+len, new->buffer, new->len); + len += new->len; + new->prev->next = new->next; + new->next->prev = new->prev; + kfree(new); + } + /* done printing */ + *eof = 1; + *start = page; + return len; + } + return 0; +} + +static int __init __imm_init (void) +{ +#if defined(CONFIG_IMM_API_SRAM) +int exit_cb_err = 1; +#endif + + kernel_immid = IMM_FIRST_KERNEL_IMMID; + +#if defined(CONFIG_IMM_API_SRAM) + /* the exit callback is needed for SRAM and Cache garbage collection */ + exit_cb_err = profile_event_register(PROFILE_TASK_EXIT, + &imm_task_exit_nb); + if (exit_cb_err) + printk(KERN_ERR "Process exit callback failed to register, \ + all APIs have been disabled\n"); +#endif + + immid_list.immid = 0; + immid_list.last_error = 0; + immid_list.mm = NULL; + immid_list.prev = &immid_list; + immid_list.next = &immid_list; +#ifdef CONFIG_IMM_API_SRAM + immid_list.malloc_list = NULL; + immid_list.used_space = 0; +#endif + + if ((proc_imm = proc_mkdir("imm", NULL)) == NULL) { + printk(KERN_ERR "Failed to initialize the IMM proc \ + interface\n"); + } + +#ifdef CONFIG_IMM_API_SRAM + /* if the exit cb is available */ + if (!exit_cb_err) + imm_dau_sram_init(proc_imm); +#endif + pr_info("Intel(c) Memory Management is now Enabled\n"); + + imm_initialized = 1; + + return 0; +} + +static int __init imm_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + ret = -ENODEV; + return ret; + } + phys_sram_start = res->start; + phys_sram_size = res->end - res->start + 1; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + ret = -ENODEV; + return ret; + } + imm_sram_start = res->start; + imm_sram_size = res->end - res->start + 1; + + __imm_init(); + + return 0; +} + +static int imm_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver imm_driver = { + .probe = imm_probe, + .remove = imm_remove, + .driver = { + .name = "pxa3xx-imm", + }, +}; + +static int __init imm_init(void) +{ + return platform_driver_register(&imm_driver); +} + +static void __exit imm_exit(void) +{ + platform_driver_unregister(&imm_driver); +} + +module_init(imm_init); +module_exit(imm_exit); +MODULE_LICENSE("GPL"); + +#if defined(CONFIG_IMM_API_SRAM) +EXPORT_SYMBOL(imm_task_exit); +#endif + +/* app/driver calls */ +EXPORT_SYMBOL(imm_register_kernel); +EXPORT_SYMBOL(imm_register_user); +EXPORT_SYMBOL(imm_error); +EXPORT_SYMBOL(imm_get_physical); +EXPORT_SYMBOL(imm_get_virtual); + diff --git a/arch/arm/plat-pxa/imm/sram.c b/arch/arm/plat-pxa/imm/sram.c new file mode 100644 index 00000000000000..375a61c7de361f --- /dev/null +++ b/arch/arm/plat-pxa/imm/sram.c @@ -0,0 +1,935 @@ +/* + * linux/arch/arm/mach-pxa/imm/sram.c + * + * Intel Memory Management + * + * SRAM Allocation API + * + * Todd Brandt + * Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) + * + * 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. + + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "sram.h" + +int imm_sram_pages; +int imm_malloc_map_size; + +extern int phys_sram_start; +extern int phys_sram_size; +extern int imm_sram_start; +extern int imm_sram_size; + +#ifdef CONFIG_IMM_DEBUG +extern char *imm_errors[]; +#endif + +/* flag showing that this module was initialized */ +static int module_initialized = 0; + +u32 num_free_sram_pages; + +/* kernel level virtual memory list ordered only by allocation start address */ +struct imm_virt_t malloc_list = { 0, 0, 0, &malloc_list, &malloc_list }; + +/* physical memory page list */ +struct imm_page_t *page_list; + +int page_compatible(u32 start, u32 flags, u32 immid); + +/*---------------------------------------------------------------------- + * + * High Level Call Tree: + * + * [Application or Driver Call] + * imm_malloc + * page_compatible + * imm_alloc_pages + * imm_map_page + * imm_free + * release_entry + * imm_free_pages + * imm_unmap_page + * imm_get_freespace + * + * [Process Exit - from Policy Manager] + * imm_free_immid + * release_immid + * imm_free_pages_immid + * + * [Proc Filesystem] + * /proc/imm/sram_vmap (read-only) + * imm_sram_vmap_read_proc + * /proc/imm/sram_pages (read-only) + * imm_sram_pages_read_proc + * /proc/imm/sram_usagemap (read-only) + * imm_sram_usagemap_read_proc + * + * [Module Initialization - from Policy Manager] + * imm_dau_sram_init + * + *--------------------------------------------------------------------*/ + +#ifdef CONFIG_IMM_DEBUG +extern struct imm_info_t immid_list; +char *plist; + +void set_plist(int s, int d, int c) +{ + static char plist_ch = 'A'; + + switch(c) { + case 0: /* map */ + plist[s] = plist_ch; + break; + case 1: /* unmap */ + plist[s] = '.'; + break; + case 2: /* remap */ + plist[d] = plist[s]; + plist[s] = '.'; + break; + case 3: /* swap */ + plist[d] = plist[s]; + plist[s] = '.'; + break; + case 4: /* update char */ + plist_ch++; + if (plist_ch > 'Z') plist_ch = 'A'; + break; + } +} + +void print_page_list(char *s) +{ + int i, count, space=0; + struct imm_page_t *p; + + if (!strcmp(s, "malloc")) set_plist(-1, -1, 4); + + printk(KERN_DEBUG "Page list changed as a result of %s\n", s); + + space = num_free_sram_pages; + printk(KERN_DEBUG "%d pages total, %d free\n", + &page_list[imm_sram_pages]-&page_list[0], space); + for (count = 0, i = 0, p = &page_list[0]; p < &page_list[imm_sram_pages]; p++, i++) { + if (!PAGE_IS_MAPPED(p->flag)) { + count++; + } + printk(KERN_DEBUG "%c", plist[p->index]); + if ((i+1)%64 == 0) printk(KERN_DEBUG "\n"); + } + if (i%64) printk(KERN_DEBUG "\n"); + if (count != space) + printk(KERN_WARNING "Free Count Mismatch, actual free pages is %d\n", + count); +} + +void print_page_map(int num, struct imm_page_t **page_map, s16 num_pages) +{ + int i; + + printk(KERN_DEBUG "Scan %d reveals this page_map:\n", num); + for (i = 0; i < num_pages; i++) { + printk(KERN_DEBUG "%03d:%03d ", i, (page_map[i])?page_map[i]->index:-1); + if ((i+1)%9 == 0) printk(KERN_DEBUG "\n"); + } + if ((i+1)%9) printk(KERN_DEBUG "\n"); + +} +#endif + +/************************************************************************* + * + * IMM Single Page Functions + * + * imm_map_page: create a new virtual-physical page mapping + * imm_unmap_page: remove a virtual-physical page mapping + * + *************************************************************************/ +void imm_map_page(struct imm_page_t *page, u32 virtual, u32 flags, + struct imm_info_t *imm_info) +{ + struct mm_struct *mm = imm_info->mm; + struct vm_area_struct *vma; + pgd_t *dir; + u32 f; + +#ifdef CONFIG_IMM_DEBUG + set_plist(page->index, -1, 0); +#endif + + num_free_sram_pages--; + + page->flag |= IMM_INFO_USED | (flags & IMM_INFO_DMA); + page->flag &= ~IMM_INFO_RESERVED; + page->immid = imm_info->immid; + page->virt_addr = virtual; + + /* user space mappings are handled differently from kernel */ + if (IMMID_USER(imm_info->immid)) { + vma = find_vma(mm, page->virt_addr); + if (unlikely(vma == NULL)) { + imm_failure("find_vma failed"); + return; + } + + /* first destroy the existing mapping */ + zap_page_range(vma, page->virt_addr, PAGE_SIZE, NULL); + + /* the initial cacheability depends on the HARDWARE flag */ + if (PAGE_IS_DMA(flags)) + f = PAGE_SHARED & (~L_PTE_CACHEABLE); + else + f = PAGE_SHARED; + + /* now map in the underlying physical memory */ + if (io_remap_pfn_range(vma, page->virt_addr, (page->phys_addr) + >> PAGE_SHIFT, PAGE_SIZE, f)) + imm_failure("io_remap_pfn_range failed"); + } else { + dir = pgd_offset_k(page->virt_addr); + + /* the initial cacheability depends on the HARDWARE flag */ + if (PAGE_IS_DMA(flags)) + f = L_PTE_BUFFERABLE | L_PTE_EXEC; + else + f = L_PTE_BUFFERABLE | L_PTE_CACHEABLE; + + /* now map in the underlying physical memory */ + if (remap_area_pages(page->virt_addr, (page->phys_addr) + >> PAGE_SHIFT, PAGE_SIZE, f)) + imm_failure("remap_area_pages failed"); + } +} + +void imm_unmap_page_lite(struct imm_page_t *page, struct imm_info_t *imm_info) +{ + +#ifdef CONFIG_IMM_DEBUG + set_plist(page->index, -1, 1); +#endif + + num_free_sram_pages++; + + page->flag &= IMM_INFO_SRAM; + page->immid = 0; + page->virt_addr = 0; +} + +extern void unmap_kernel_range(unsigned long addr, unsigned long size); + +void imm_unmap_page(struct imm_page_t *page, struct imm_info_t *imm_info) +{ + struct vm_area_struct *vma; + + /* if the page isn't mapped, forget it */ + if (PAGE_IS_MAPPED(page->flag)) { + if (IMMID_USER(imm_info->immid)) { + vma = find_vma(imm_info->mm, page->virt_addr); + if (unlikely(vma == NULL)) { + imm_failure("find_vma failed\n"); + return; + } + zap_page_range(vma, page->virt_addr, PAGE_SIZE, NULL); + } else { + unmap_kernel_range(page->virt_addr, PAGE_SIZE); + } + } + imm_unmap_page_lite(page, imm_info); +} + +/************************************************************************* + * + * IMM Page Range Functions + * + * imm_alloc_pages: given a valid virtual address range, map available + * physical pages. + * imm_free_pages: frees a specific range of physical pages + * imm_free_pages_immid: frees all physical pages for a given immid + * + *************************************************************************/ + +int imm_alloc_pages(u32 virt, u32 size, u32 flags, struct imm_info_t *imm_info) +{ + int i; + u32 virt_start = virt&PAGE_MASK; + u32 virt_end = PAGE_ALIGN(virt+size); + u32 pstart, pend, immid = imm_info->immid; + u32 pages_needed, pages_found = 0; + struct imm_page_t **page_map, **page_map_ptr, *p; + + imm_debug("imm_alloc_pages: virt=%08X, size=%d, flags=%03X, \ + immid=%d\n", virt, size, flags, immid); + + if ((virt%PAGE_SIZE)&&page_compatible(virt, flags, immid)) + pages_needed = (virt_end-(virt_start+PAGE_SIZE))/PAGE_SIZE; + else + pages_needed = (virt_end-virt_start)/PAGE_SIZE; + + /* if there aren't enough pages left leave now */ + if (pages_needed > num_free_sram_pages) + return IMM_ERROR_NOSPACE; + + /* create a physical page mapping for the new allocation */ + if ((page_map_ptr = kmalloc((pages_needed+2) + *sizeof(struct imm_page_t *), GFP_ATOMIC)) == NULL) + return IMM_ERROR_KMALLOC; + + /* add two extra places in case we find border pages */ + for (i = 0; i < pages_needed+2; i++) { + page_map_ptr[i] = NULL; + } + page_map = &page_map_ptr[1]; + + if (!PAGE_IS_DMA(flags)) { + /* look for new and border pages */ + for (p = &page_list[0]; (p < &page_list[imm_sram_pages])&& + (pages_found < pages_needed); p++) + { + /* check all memory types for border pages */ + if (PAGE_IS_MAPPED(p->flag)&& + !PAGE_IS_DMA(p->flag)&& + (p->immid == immid)) + { + pstart = p->virt_addr; + pend = p->virt_addr + PAGE_SIZE; + if (virt_start == pstart) { + pages_found++; + page_map_ptr[0] = p; + } else if (virt_end == pend) { + pages_found++; + page_map_ptr[pages_needed+1] = p; + } + } + if (!PAGE_IS_MAPPED(p->flag)) { + page_map[pages_found++] = p; + p->flag |= IMM_INFO_RESERVED; + } + } + + /* we may have found up to two more than pages_needed */ + pages_found = min(pages_found, pages_needed); + + /* if we have found a starting border page, move the map forward */ + if (page_map_ptr[0]) page_map[0] = page_map_ptr[0]; + + /* if we have found an ending border page, overwrite the last page + * with it + */ + if (page_map_ptr[pages_needed+1]) + page_map[pages_needed-1] = page_map_ptr[pages_needed+1]; + } else { + /* next scan to pick up needed new pages */ + for (p = &page_list[0], pages_found = 0; + (p < &page_list[imm_sram_pages])&&(pages_found < pages_needed); + p++) + { + if (!PAGE_IS_MAPPED(p->flag)) + page_map[pages_found++] = p; + else + pages_found = 0; + } + } + + if (pages_found < pages_needed) + { + kfree(page_map_ptr); + return IMM_ERROR_NOSPACE; + } + +#ifdef CONFIG_IMM_DEBUG + for (i = 0; i < pages_needed; i++) { + printk(KERN_DEBUG "V(0x%08X) --> P(0x%08X), page index = %03d, %s\n", + (u32)(virt_start+(i*PAGE_SIZE)), page_map[i]->phys_addr, + page_map[i]->index, + (PAGE_IS_MAPPED(page_map[i]->flag))?"USED":"NEW"); + } +#endif + + for (i = 0; i < pages_needed; i++) + { + p = page_map[i]; + if (!PAGE_IS_MAPPED(p->flag)) + { + imm_map_page(p, virt_start+(i*PAGE_SIZE), + flags, imm_info); + } + } + + /* flush the cache and TLBs afterward to ensure the mapping took */ + flush_cache_all(); + flush_tlb_all(); + + kfree(page_map_ptr); + return IMM_ERROR_NONE; +} + +void +imm_free_pages(u32 virt, u32 size, struct imm_info_t *imm_info) +{ + struct imm_page_t *p; + u32 virt_start = virt&PAGE_MASK; + u32 virt_end = PAGE_ALIGN(virt+size); + s16 pages_freed, pages_found = 0; + + pages_freed = (virt_end - virt_start) / PAGE_SIZE; + if (unlikely(!pages_freed)) { + imm_failure("imm_free_pages called with 0 size"); + return; + } + + /* first scan to pick up needed pages that are already mapped */ + for (p = &page_list[0]; (p < &page_list[imm_sram_pages])&& + (pages_found < pages_freed); p++) + { + if (PAGE_IS_MAPPED(p->flag) + && (p->immid == imm_info->immid) + && ((p->virt_addr >= virt_start) + && (p->virt_addr < virt_end))) + { + imm_unmap_page(p, imm_info); + pages_found++; + } + } + + flush_cache_all(); + flush_tlb_all(); +} + +int +imm_free_pages_immid(struct imm_info_t *imm_info) +{ + struct imm_page_t *p; + u32 immid = imm_info->immid; + + /* first scan to pick up needed pages that are already mapped */ + for (p = &page_list[0]; p < &page_list[imm_sram_pages]; p++) + { + if (PAGE_IS_MAPPED(p->flag)&& + (p->immid == immid)) + { + imm_unmap_page_lite(p, imm_info); + } + } + + flush_cache_all(); + flush_tlb_all(); + + return 0; +} + +/************************************************************************* + * virtual allocation list handlers + * + * release_entry: frees a specific malloc, or cacheability entry + * release_immid: frees all instances of mallocs, hotswaps, or cacheability + * changes for a given immid. + * page_compatible: (kernel pages only) determines if a given page is + * compatible with a new allocation's flags, this is necessary if a new + * allocation shares a page with an existing allocation + *************************************************************************/ + +int release_entry(void *address, struct imm_info_t *imm_info) +{ + struct imm_virt_t *head = imm_info->malloc_list, + *p = imm_info->malloc_list->next; + u32 start=0, end=0, immid = imm_info->immid; + int found = 0; + + while (p != head) { + if ((p->immid == immid)&&(p->start == (u32)address)) { + /* calculate the page address range */ + start = p->start&PAGE_MASK; + end = PAGE_ALIGN(p->end); + + /* we need to see if other allocations overlap + in the beginning or end pages of the range */ + if ((p->prev->immid == immid)&& + (p->prev->end > start)) { + start += PAGE_SIZE; + } + if ((p->next->immid == immid)&& + (p->next->start < end)) { + end -= PAGE_SIZE; + } + found = 1; + break; + } + p = p->next; + } + + if (!found) return IMM_ERROR_ENTRY_NOTFOUND; + + imm_info->used_space -= p->end - p->start; + + /* p now points to the memory block to be removed */ + p->prev->next = p->next; + p->next->prev = p->prev; + kfree(p); + + if (start < end) { + imm_free_pages(start, end-start, imm_info); + } + return IMM_ERROR_NONE; +} + +int release_immid(struct imm_info_t *imm_info) +{ + struct imm_virt_t *head = imm_info->malloc_list, + *p = imm_info->malloc_list->next; + u32 immid = imm_info->immid; + + while (p != head) { + if (p->immid == immid) { + /* p now points to the memory block to be removed */ + p->prev->next = p->next; + p->next->prev = p->prev; + kfree(p); + } + p = p->next; + } + return 0; +} + +int page_compatible(u32 start, u32 flags, u32 immid) +{ + u32 vstart = start&PAGE_MASK; + struct imm_page_t *p; + + /* if the dma flag is set, the page is not compatible */ + /* dma pages can't be moved, non-dma pages can, so */ + /* allocations can't share both */ + if (PAGE_IS_DMA(flags)) { + return 0; + } + + /* find the page in question */ + for (p = &page_list[0]; p < &page_list[imm_sram_pages]; p++) { + if (IMMID_OVERLAP(p->immid, immid)&& + PAGE_IS_MAPPED(p->flag)&& + ((p->virt_addr&PAGE_MASK) == vstart)) + break; + } + + /* if the page isn't found, it's not compatible (shouldn't */ + /* occur, but just to be safe handle the error) */ + if (p >= &page_list[imm_sram_pages]) { + return 0; + } + + /* if the dma flag is set, the page is not compatible */ + /* dma pages can't be moved, non-dma pages can, so */ + /* allocations can't share both */ + if (PAGE_IS_DMA(p->flag)) { + return 0; + } + + return 1; +} + +/************************************************************************* + * Function: imm_malloc + * Description: Allocates a block and returns the kernel/user space + * address + * + * Arguments: + * size - the size in bytes of the requested allocation + * flags - malloc flags + * immid - the id to assign this malloc to + * + * Return Value: the new allocation address (NULL for failure) + *************************************************************************/ + +void * imm_malloc(size_t size, u32 flags, u32 immid) +{ + struct imm_virt_t *head, *p, *new; + struct imm_info_t *imm_info; + int res; + u32 address=0, end; + + down(&imm_sem); + imm_debug("imm_malloc: size=%d, flags=%02X, immid=%u\n", + size, flags, immid); + + /* if this client isn't registered, fail */ + if ((imm_info = imm_info_getentry(immid)) == NULL) { + up(&imm_sem); + return NULL; + } + + /* if this module isn't initialized (very rare), fail */ + if (!module_initialized) { + register_error(immid, IMM_ERROR_UNINIT); + up(&imm_sem); + return(NULL); + } + + /* if the size is invalid, fail */ + if (size < 1) { + register_error(immid, IMM_ERROR_SIZETOOLOW); + up(&imm_sem); + return(NULL); + } + + if ((!PAGE_IS_DMA(flags) && ((size+imm_info->used_space) + > phys_sram_size)) || (PAGE_IS_DMA(flags) + &&(size+imm_info->used_space > phys_sram_size))) { + + register_error(immid, IMM_ERROR_NOSPACE); + up(&imm_sem); + return(NULL); + } + + p = head = imm_info->malloc_list; + + /* request is valid, let's do it */ + while (!address) { + p = p->next; + if (p->start - p->prev->end >= size) { + address = p->prev->end; + /* if this is in the middle of a page, make sure */ + /* the existing page is compatible */ + if ((address%PAGE_SIZE)&& + !page_compatible(address, flags, immid)) { + /* if not, align the allocation to next page */ + address = PAGE_ALIGN(address); + /* we may now not have enough space */ + if (p->start - address < size) { + address = 0; + } + } + if (address) { + /* if we're still in business, check the end + * address + */ + end = address + size; + if ((end%PAGE_SIZE) && ((end&PAGE_MASK) + == (p->start&PAGE_MASK)) && + !page_compatible(end, flags, immid)) { + /* in the unlikely event that an + * allocation is perfectly sandwiched + * between two others, and the end + * page is incompatible, move on + */ + address = 0; + } + } + } + if (p == head) break; + } + + if (!address) { + register_error(immid, IMM_ERROR_NOSPACE); + up(&imm_sem); + return NULL; + } + + if ((res = imm_alloc_pages(address, size, + flags, imm_info)) != 0) { + + register_error(immid, res); + up(&imm_sem); + return NULL; + } + + /* p now points to the memory block just after the new block's dest */ + new = kmalloc(sizeof(struct imm_virt_t), GFP_ATOMIC); + if (new == NULL) { + register_error(immid, IMM_ERROR_KMALLOC); + up(&imm_sem); + return NULL; + } + + new->start = address; + new->end = address + size; + new->immid = immid; + + new->next = p; + new->prev = p->prev; + new->next->prev = new; + new->prev->next = new; + + imm_info->used_space += size; +#ifdef CONFIG_IMM_DEBUG + print_page_list("malloc"); +#endif + register_error(immid, IMM_ERROR_NONE); + up(&imm_sem); + return (void *)new->start; +} + +/************************************************************************* + * + * Function: imm_free + * Description: Frees a block of imm memory + * + * Arguments: + * ptr - the address of the block to free + * flags - release flags, can be 0 or IMM_NO_REMAP + * + * Return Value: + * the error condition, can be sent to imm_error to retrieve the + * error string + *************************************************************************/ + +int imm_free(void *address, u32 immid) +{ + int res; + struct imm_info_t *imm_info; + + down(&imm_sem); + imm_debug("imm_free: address=%08X, immid=%d, region=%s\n", + (u32)address, immid, (IMMID_USER(immid))?"USER":"KERNEL"); + + /* if this client isn't registered, forget it */ + if ((imm_info = imm_info_getentry(immid)) == NULL) { + up(&imm_sem); + return IMM_ERROR_UNKNOWN; + } + + /* if this module isn't initialized (very rare), forget it */ + if (!module_initialized) { + register_error(immid, IMM_ERROR_UNINIT); + up(&imm_sem); + return IMM_ERROR_UNINIT; + } + + if (address == NULL) { + register_error(immid, IMM_ERROR_ENTRY_NOTFOUND); + up(&imm_sem); + return IMM_ERROR_ENTRY_NOTFOUND; + } + + res = release_entry(address, imm_info); +#ifdef CONFIG_IMM_DEBUG + print_page_list("free"); +#endif + register_error(immid, res); + up(&imm_sem); + return res; +} + +/************************************************************************* + * + * Function: imm_free_immid + * Description: Frees all imm memory assigned to a given process, for + * applications the immid is the process id, for drivers the immid is the value + * retrieved from imm_immid(). + * + * Arguments: + * flags - release flags, can be 0 or IMM_NO_REMAP + * immid - the immid for whom all sram allocations should be freed + * + * Return Value: + * the error condition, can be sent to imm_error to retrieve the + * error string + *************************************************************************/ + +int imm_free_immid(u32 immid) +{ + int res; + struct imm_info_t *imm_info; + + /* if this client isn't registered, forget it */ + if ((!module_initialized) || ((imm_info = imm_info_getentry(immid)) + == NULL)) { + + return 0; + } + res = release_immid(imm_info); + res = imm_free_pages_immid(imm_info); + imm_info->used_space = 0; +#ifdef CONFIG_IMM_DEBUG + print_page_list("task exit"); +#endif + return res; +} + +/************************************************************************* + * + * Function: imm_get_freespace + * Description: Figure out how much SRAM space is left + * + * Arguments: + * flags - flags for the type of memory to check the free space for + * immid - the immid for the client who's asking + * + * Return Value: + * (unsigned int) the amount of SRAM remaining in bytes + *************************************************************************/ + +unsigned int imm_get_freespace(u32 flags, u32 immid) { + struct imm_page_t *p; + unsigned int bytes = phys_sram_size; + struct imm_info_t *imm_info; + + down(&imm_sem); + + /* if this client isn't registered, forget it */ + if ((!module_initialized) || ((imm_info = imm_info_getentry(immid)) + == NULL)) { + + up(&imm_sem); + return 0; + } + + for (p = &page_list[0]; p < &page_list[imm_sram_pages]; p++) { + if (PAGE_IS_MAPPED(p->flag)&& + (immid != p->immid)) + { + bytes -= PAGE_SIZE; + } + } + bytes -= imm_info->used_space; + if (unlikely(bytes < 0)) { + imm_failure("used_space failure"); + } + up(&imm_sem); + return min(bytes, phys_sram_size-imm_info->used_space); +} + +/************************************************************************* + * + * Functions: imm_sram_pages_read_proc, imm_sram_vmap_read_proc + * Description: Proc fs functions which display the mapped pages + * and virtual memory allocation lists + * + *************************************************************************/ + +READ_PROC_PROTO(imm_sram_usagemap_read_proc) +{ + READ_PROC_VARS + int i, j; + int imm_sram_first_bank; + + imm_sram_first_bank = (imm_sram_start - phys_sram_start)/PHYS_SRAM_BANK_SIZE; + + READ_PROC_INIT + for (i = 0; i < imm_sram_pages; i+=PHYS_SRAM_PAGES_PER_BANK) { + pprintf(" "); + for (j = 0; j < PHYS_SRAM_PAGES_PER_BANK; j++) + pprintf("%c", (i - imm_sram_first_bank)?'-':'_'); + pprintf(" \n|"); + for (j = 0; j < PHYS_SRAM_PAGES_PER_BANK; j++) { + pprintf("%c", PAGE_IS_MAPPED(page_list[i+j].flag)? + (IMMID_USER(page_list[i+j].immid)?'U':'K'):'.'); + } + pprintf("| SRAM Bank %d\n", i/PHYS_SRAM_PAGES_PER_BANK + imm_sram_first_bank); + } + pprintf(" "); + for (j = 0; j < PHYS_SRAM_PAGES_PER_BANK; j++) + pprintf("-"); + pprintf("\n"); + READ_PROC_RETURN +} + +READ_PROC_PROTO(imm_sram_pages_read_proc) { + READ_PROC_VARS + struct imm_page_t *p; + int i; + + READ_PROC_INIT + for (i = 0, p = &page_list[0]; p < &page_list[imm_sram_pages]; p++, i++) { + if (PAGE_IS_MAPPED(p->flag)) { + pprintf("I%03d V:%08X P:%08X L:%d ID:%u \ + FLAGS:%c%c%c\n", i, p->virt_addr, p->phys_addr, + 0, + IMMID_VALUE(p->immid), IMMID_TYPEC(p->immid), + 'S', + PAGE_IS_DMA(p->flag)?'D':'.'); + } + } + + READ_PROC_RETURN +} + +/************************************************************************* + * imm_init - initialize Intel Memory Management + *************************************************************************/ + +void imm_dau_sram_init (struct proc_dir_entry *imm_dir) +{ + struct proc_dir_entry *entry; + struct imm_page_t *p; + int i; + struct vm_struct *kernel_area; + struct clk *isc_clk; + + imm_sram_pages = imm_sram_size / PAGE_SIZE; + num_free_sram_pages = imm_sram_pages; + imm_malloc_map_size = phys_sram_size << 1; + + page_list = kzalloc(sizeof(struct imm_page_t) * imm_sram_pages, GFP_KERNEL); +#ifdef CONFIG_IMM_DEBUG + plist = kzalloc(sizeof(char) * imm_sram_pages, GFP_KERNEL); +#endif + + /* grab a vm_area struct (from mm/vmalloc.c) */ + kernel_area = get_vm_area(imm_malloc_map_size, VM_IOREMAP); + if ((!kernel_area)||((u32)kernel_area->addr == 0)) { + imm_failure("get_vm_area failed\n"); + return; + } + + malloc_list.start = (u32)kernel_area->addr + imm_malloc_map_size; + malloc_list.end = (u32)kernel_area->addr; + + for (i = 0, p = &page_list[0]; p < &page_list[imm_sram_pages]; p++, i++) { +#ifdef CONFIG_IMM_DEBUG + p->index = i; + plist[i] = '.'; +#endif + p->phys_addr = imm_sram_start + i * PAGE_SIZE; + p->flag = IMM_INFO_SRAM; + p->immid = 0; + p->virt_addr = 0; + } + if (imm_dir) { + REG_READ_PROC(entry, imm_dir, "sram_usagemap", + imm_sram_usagemap_read_proc) + REG_READ_PROC(entry, imm_dir, "sram_pages", + imm_sram_pages_read_proc) + } + + /* Enable clock */ + isc_clk = clk_get(NULL, "ISCCLK"); + clk_enable(isc_clk); + + pr_info("Intel(c) Memory Management - SRAM Allocation\n"); + module_initialized = 1; +} + +EXPORT_SYMBOL(imm_malloc); +EXPORT_SYMBOL(imm_free); +EXPORT_SYMBOL(imm_get_freespace); +EXPORT_SYMBOL(imm_free_immid); + + + diff --git a/arch/arm/plat-pxa/imm/sram.h b/arch/arm/plat-pxa/imm/sram.h new file mode 100644 index 00000000000000..a41a997bbbee91 --- /dev/null +++ b/arch/arm/plat-pxa/imm/sram.h @@ -0,0 +1,70 @@ +/* + * linux/arch/arm/mach-pxa/imm/sram.h + * + * Intel Memory Management + * + * SRAM API Defines/Globals/Functions + * + * Todd Brandt + * Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) + * + * 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. + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef _ARCH_PXA_SRAM_H +#define _ARCH_PXA_SRAM_H + +#include "imm.h" + +/* imm internal flags */ +#define IMM_INFO_USED 0x10 +#define IMM_INFO_RESERVED 0x20 +#define IMM_INFO_DMA IMM_MALLOC_HARDWARE +#define IMM_INFO_SRAM IMM_MALLOC_SRAM + +/* defines and macros */ +#define PAGE_IS_MAPPED(x) ((x) & IMM_INFO_USED) +#define PAGE_IS_DMA(x) ((x) & IMM_INFO_DMA) + +/* entries for the global physical memory list */ +struct imm_page_t { + u8 flag; /* flags */ + u32 immid; /* each page is associated with a process id */ + u32 virt_addr; /* virtual address for this page (into the immid's space) */ + u32 phys_addr; /* fixed physical address associated with this page */ +#ifdef CONFIG_IMM_DEBUG + s16 index; +#endif +}; + +/* global data */ +extern u32 num_free_sram_pages; + +#ifdef CONFIG_IMM_DEBUG +extern void print_page_map(int, struct imm_page_t **, s16); +extern void set_plist(int, int, int); +#endif + +/* physical memory page list */ +extern struct imm_page_t *page_list; +extern struct imm_info_t immid_list; + +static inline void +page_map_reverse(struct imm_page_t **page_map, int n) +{ +int i, j; +struct imm_page_t *p; + + for(i = 0, j = n - 1; i < j; i++, j--) + { + p = page_map[i]; + page_map[i] = page_map[j]; + page_map[j] = p; + } +} +#endif + diff --git a/arch/arm/plat-pxa/include/plat/dma.h b/arch/arm/plat-pxa/include/plat/dma.h index a7b91dc06852bc..075a2d18a95407 100644 --- a/arch/arm/plat-pxa/include/plat/dma.h +++ b/arch/arm/plat-pxa/include/plat/dma.h @@ -1,6 +1,8 @@ #ifndef __PLAT_DMA_H #define __PLAT_DMA_H +#include + #define DMAC_REG(x) (*((volatile u32 *)(DMAC_REGS_VIRT + (x)))) #define DCSR(n) DMAC_REG((n) << 2) diff --git a/arch/arm/plat-pxa/include/plat/generic.h b/arch/arm/plat-pxa/include/plat/generic.h new file mode 100644 index 00000000000000..e0ce64971dc7ad --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/generic.h @@ -0,0 +1,10 @@ +#ifndef __PLAT_PXA_GENERIC_H_ +#define __PLAT_PXA_GENERIC_H_ + +int is_android(void); +int get_mlc_size(void); +int is_lab(void); + +#endif +void android_add_pmem(char *name, size_t size, int no_allocator, int cached); + diff --git a/arch/arm/plat-pxa/include/plat/i2c.h b/arch/arm/plat-pxa/include/plat/i2c.h index 1a9f65e6ec0f04..6d40b88b3df8f1 100644 --- a/arch/arm/plat-pxa/include/plat/i2c.h +++ b/arch/arm/plat-pxa/include/plat/i2c.h @@ -67,6 +67,8 @@ struct i2c_pxa_platform_data { unsigned int class; unsigned int use_pio :1; unsigned int fast_mode :1; + void (*get_ripc) (void); + void (*release_ripc) (void); }; extern void pxa_set_i2c_info(struct i2c_pxa_platform_data *info); diff --git a/arch/arm/plat-pxa/include/plat/imm.h b/arch/arm/plat-pxa/include/plat/imm.h new file mode 100644 index 00000000000000..6ab45fefc901de --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/imm.h @@ -0,0 +1,225 @@ +/* + * linux/include/asm/arch/imm.h + * + * Intel Memory Management + * + * User/Driver level IMM Defines/Globals/Functions + * + * Todd Brandt + * Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) + * + * 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. + + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef _ASM_ARCH_IMM_H +#define _ASM_ARCH_IMM_H +#include + +/* -------- Hardware specific capabilities --------------- */ +#ifdef CONFIG_ARCH_MMP +#define PHYS_SRAM_START (0xd1000000) +#define PHYS_SRAM_BANK_SIZE (128 * 1024) +#define PHYS_SRAM_PAGES_PER_BANK (PHYS_SRAM_BANK_SIZE / PAGE_SIZE) + +#define PHYS_DDR_START (0) +#define PHYS_DDR_SIZE (128 * 1024 * 1024) +#define PHYS_PASR_LEVEL 2 +#define IMM_DRAM_ORDER 9 +#endif + +#ifdef CONFIG_PXA3xx /* Intel Monahans Processor on Zylonite*/ +/* Hardware properties of SRAM */ +#define PHYS_SRAM_START 0x5c000000 /* physical start of SRAM */ +#define PHYS_SRAM_BANK_SIZE (128*1024) /* physical size of an SRAM bank */ +#define PHYS_SRAM_PAGES_PER_BANK (PHYS_SRAM_BANK_SIZE / PAGE_SIZE) + +/* Hardware properties of DRAM */ + /* Zylonite uses Infineon HYB18M512160AF-7.5 512-Mbit Mobile-RAM chips */ +#define PHYS_DDR_START (PHYS_OFFSET) +#define PHYS_DDR_SIZE (128*1024*1024) + /* JEDEC Standard for PASR says chips must support up to 3/4 powerdown */ + /* 0 = upper 3/4 of each ddr chip max may be shut down if unused (JEDEC standard) */ + /* 1 = upper 7/8 of each ddr chip max may be shut down if unused (Extension 1) */ + /* 2 = upper 15/16 of each ddr chip max may be shut down if unused (Extension 2) */ +#define PHYS_PASR_LEVEL 2 /* Infineon supports 1/16 */ +/* Software Configurable properties of IMM controlled DRAM */ +#define IMM_DRAM_ORDER 9 /* space to use as swap for SRAM */ +#endif + +#ifdef CONFIG_PXA27x /* Intel Bulverde Processor on Mainstone III */ +/* Hardware properties of SRAM */ +#define PHYS_SRAM_BANK_SIZE (64*1024) /* physical size of an SRAM bank */ +#define PHYS_SRAM_BANK_COUNT 4 /* Total SRAM banks */ +#define PHYS_SRAM_START 0x5c000000 /* physical start of SRAM */ +#define PHYS_SRAM_SIZE (PHYS_SRAM_BANK_COUNT*PHYS_SRAM_BANK_SIZE) +#define PHYS_SRAM_PAGES_PER_BANK (PHYS_SRAM_BANK_SIZE / PAGE_SIZE) +/* Software Configurable properties of IMM controlled SRAM */ +#define IMM_SRAM_FIRST_BANK 0 /* first IMM SRAM bank */ +#define IMM_SRAM_START (PHYS_SRAM_START) + +/* Hardware properties of DRAM */ + /* Mainstone III uses Infineon HYB25L256160AC-7.5 256-Mbit Mobile-RAM chips */ +#define PHYS_DDR_START (PHYS_OFFSET) +#define PHYS_DDR_SIZE (64*1024*1024) + /* JEDEC Standard for PASR says chips must support up to 3/4 powerdown */ + /* 0 = upper 3/4 of each ddr chip max may be shut down if unused (JEDEC standard) */ + /* 1 = upper 7/8 of each ddr chip max may be shut down if unused (Extension 1) */ + /* 2 = upper 15/16 of each ddr chip max may be shut down if unused (Extension 2) */ +#define PHYS_PASR_LEVEL 2 /* Infineon supports 1/16 */ +#endif + +/********************************* + * Driver Registration API * + ********************************* + * + *-- Function name: imm_register_kernel + * Description: Register with IMM in order to use the API functions, this must be + * done by any driver wishing to use IMM + * Output: + * [u32] A unique identifier which must be passed to all subsequent IMM calls + * + * Example: + * + * u32 my_immid = imm_register_kernel(); + * + */ + +extern u32 imm_register_kernel(char *); + + +/********************************* + * SRAM Allocation API * + ********************************* + * + *-- Function name: imm_malloc + * Description: Allocate a block of internal memory + * Inputs: + * (int)size - the byte size of the requested memory block + * (int)flags - the type and organization of the memory block + * IMM_MALLOC_SRAM: forces SRAM + * IMM_MALLOC_HARDWARE: force contiguous, immovable physical pages + * this option is typically used for DMA memory, without it + * the pages allocated may be fragmented and may also + * be remapped for performance purposes by IMM internally + * (u32)immid - the immid of the caller, obtained from imm_register_kernel + * Output: + * [void *] the address of the allocated memory, or NULL on failure + * + *-- Function name: imm_free + * Description: Free a block of internal memory returned from imm_malloc + * Inputs: + * (void *)addr - the address of the block to free as returned from + * a previous call to imm_malloc + * (u32)immid - the immid of the caller, obtained from imm_register_kernel + * + *-- Function name: imm_get_physical + * Description: Retrieve the physical address from a virtual (PTE lookup) + * Inputs: + * (void *)address - the virtual address of some allocateed memory + * (u32)immid - the immid of the caller, obtained from imm_register_kernel + * Output: + * [u32] the physical address mapping, 0 if not found 0-mapped + * + * Examples: + * + * u32 my_immid = imm_register_kernel(); + * + * -- Allocate a 64K block of DMA SRAM -- + * int *vaddress, paddress; + * + * vaddress = (int *)imm_malloc(64*1024, + * IMM_MALLOC_SRAM | IMM_MALLOC_HARDWARE, my_immid); + * if(vaddress == NULL) printk(KERN_DEBUG "SRAM Allocation Failure: %s\n", + * imm_error(my_immid)); + * paddress = imm_get_physical(vaddress, my_immid); + * + * imm_free(vaddress, my_immid); + * + * + * -- Allocate a 1K block of SRAM -- + * int *array = (int *)imm_malloc(256*sizeof(int), IMM_MALLOC_SRAM, + * my_immid); + * if(array == NULL) printk(KERN_DEBUG "SRAM Allocation Failure: %s\n", + * imm_error(my_immid)); + * + * imm_free(array, my_immid); + * + */ + +#define IMM_MALLOC_L2CACHE 0x08 /* target L2 Cache */ +#define IMM_MALLOC_SRAM 0x01 /* target SRAM */ +#define IMM_MALLOC_DRAM 0x02 /* target DRAM */ +#define IMM_MALLOC_HARDWARE 0x04 /* force contiguous, immovable physical pages */ + +extern void * imm_malloc(u32 size, u32 flags, u32 immid); +extern int imm_free(void *address, u32 immid); +extern u32 imm_get_physical(void *address, u32 immid); +extern u32 imm_get_virtual(u32 address, struct task_struct *); +extern u32 imm_get_freespace(u32 flags, u32 immid); + + +/********************************* + * Error Information * + ********************************* + * + *-- Function name: imm_error + * Description: Retrieve the error string from the last imm function call + * Inputs: + * (u32)immid - the immid of the caller, obtained from imm_register_kernel + * + * Example: + * + * u32 my_immid = imm_register_kernel(); + * + * printk(KERN_DEBUG "Last IMM Error: %s\n", imm_error(my_immid)); + * + */ + +extern const char * imm_error(u32 immid); + +#define IMM_CLIENT_NAME_SIZE 16 + +/* user level ioctl commands for accessing APIs */ +#define IMM_MALLOC 0 +#define IMM_FREE 1 +#define IMM_CACHEFETCHLOCK 2 +#define IMM_CACHEUNLOCK 3 +#define IMM_SETPAGEATTR 4 +#define IMM_ERROR 5 +#define IMM_GET_PHYSICAL 6 +#define IMM_GETPAGEATTR 7 +#define IMM_CACHEINVAL 8 +#define IMM_GET_VIRTUAL 9 +#define IMM_CACHEALLOCLOCK 10 +#define IMM_GET_PHYS_SRAM_SIZE 11 +#define IMM_GET_VIRT_SRAM_SIZE 12 +#define IMM_GET_FREE_SPACE 13 + +struct imm_memory_block { + unsigned long *start; /* the starting address of the block of memory */ + unsigned long size; /* the size in bytes of the block of memory */ + unsigned long flag; /* flags associated with this action */ +}; + +/* ---- For IMM Application level usage, cut below this line ---- */ + +#ifdef CONFIG_IMM_DEBUG +#define imm_debug(s, args...) printk(KERN_DEBUG s, ## args) +#else +#define imm_debug(s, args...) +#endif +#define imm_failure(s) printk(KERN_ERR "%s: file %s, line %d\n", s, __FILE__, __LINE__) + +/* register a user level client */ +extern u32 imm_register_user(u32 vstart, u32 pid); + +extern int phys_sram_size; +extern int imm_malloc_map_size; +#endif + + diff --git a/arch/arm/plat-pxa/include/plat/mfp.h b/arch/arm/plat-pxa/include/plat/mfp.h index 857a6839071cd5..c56f44d0beb0b6 100644 --- a/arch/arm/plat-pxa/include/plat/mfp.h +++ b/arch/arm/plat-pxa/include/plat/mfp.h @@ -16,7 +16,7 @@ #ifndef __ASM_PLAT_MFP_H #define __ASM_PLAT_MFP_H -#define mfp_to_gpio(m) ((m) % 256) +#define mfp_to_gpio(m) ((m) % 128) /* list of all the configurable MFP pins */ enum { @@ -150,74 +150,6 @@ enum { MFP_PIN_GPIO125, MFP_PIN_GPIO126, MFP_PIN_GPIO127, - - MFP_PIN_GPIO128, - MFP_PIN_GPIO129, - MFP_PIN_GPIO130, - MFP_PIN_GPIO131, - MFP_PIN_GPIO132, - MFP_PIN_GPIO133, - MFP_PIN_GPIO134, - MFP_PIN_GPIO135, - MFP_PIN_GPIO136, - MFP_PIN_GPIO137, - MFP_PIN_GPIO138, - MFP_PIN_GPIO139, - MFP_PIN_GPIO140, - MFP_PIN_GPIO141, - MFP_PIN_GPIO142, - MFP_PIN_GPIO143, - MFP_PIN_GPIO144, - MFP_PIN_GPIO145, - MFP_PIN_GPIO146, - MFP_PIN_GPIO147, - MFP_PIN_GPIO148, - MFP_PIN_GPIO149, - MFP_PIN_GPIO150, - MFP_PIN_GPIO151, - MFP_PIN_GPIO152, - MFP_PIN_GPIO153, - MFP_PIN_GPIO154, - MFP_PIN_GPIO155, - MFP_PIN_GPIO156, - MFP_PIN_GPIO157, - MFP_PIN_GPIO158, - MFP_PIN_GPIO159, - MFP_PIN_GPIO160, - MFP_PIN_GPIO161, - MFP_PIN_GPIO162, - MFP_PIN_GPIO163, - MFP_PIN_GPIO164, - MFP_PIN_GPIO165, - MFP_PIN_GPIO166, - MFP_PIN_GPIO167, - MFP_PIN_GPIO168, - MFP_PIN_GPIO169, - MFP_PIN_GPIO170, - MFP_PIN_GPIO171, - MFP_PIN_GPIO172, - MFP_PIN_GPIO173, - MFP_PIN_GPIO174, - MFP_PIN_GPIO175, - MFP_PIN_GPIO176, - MFP_PIN_GPIO177, - MFP_PIN_GPIO178, - MFP_PIN_GPIO179, - MFP_PIN_GPIO180, - MFP_PIN_GPIO181, - MFP_PIN_GPIO182, - MFP_PIN_GPIO183, - MFP_PIN_GPIO184, - MFP_PIN_GPIO185, - MFP_PIN_GPIO186, - MFP_PIN_GPIO187, - MFP_PIN_GPIO188, - MFP_PIN_GPIO189, - MFP_PIN_GPIO190, - MFP_PIN_GPIO191, - - MFP_PIN_GPIO255 = 255, - MFP_PIN_GPIO0_2, MFP_PIN_GPIO1_2, MFP_PIN_GPIO2_2, @@ -393,9 +325,9 @@ typedef unsigned long mfp_cfg_t; #define MFP_PULL_LOW (0x1 << 21) #define MFP_PULL_HIGH (0x2 << 21) #define MFP_PULL_BOTH (0x3 << 21) +#define MFP_PULL_MASK (0x3 << 21) +#define MFP_PULL(x) (((x) >> 21) & 0x3) #define MFP_PULL_FLOAT (0x4 << 21) -#define MFP_PULL_MASK (0x7 << 21) -#define MFP_PULL(x) (((x) >> 21) & 0x7) #define MFP_CFG_DEFAULT (MFP_AF0 | MFP_DS03X | MFP_LPM_DEFAULT |\ MFP_LPM_EDGE_NONE | MFP_PULL_NONE) @@ -460,6 +392,8 @@ void __init mfp_init_addr(struct mfp_addr_map *map); */ unsigned long mfp_read(int mfp); void mfp_write(int mfp, unsigned long mfpr_val); +void mfp_set(int mfp, unsigned long mask); +void mfp_clr(int mfp, unsigned long mask); void mfp_config(unsigned long *mfp_cfgs, int num); void mfp_config_run(void); void mfp_config_lpm(void); diff --git a/arch/arm/plat-pxa/include/plat/part_table.h b/arch/arm/plat-pxa/include/plat/part_table.h new file mode 100644 index 00000000000000..d86c148226a9f3 --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/part_table.h @@ -0,0 +1,738 @@ +#ifndef __PLAT_PART_TABLE_H +#define __PLAT_PART_TABLE_H + +#define DECLARE_LAB_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "lab", \ + .offset = 0, \ + .size = MTDPART_SIZ_FULL, /* massstorage*/ \ + }, \ + } + +#define DECLARE_ANDROID_128M_V75_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0, \ + .size = 0x100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "NVM", \ + .offset = 0x100000, \ + .size = 0x020000, \ + }, \ + [2] = { \ + .name = "Arbel and Greyback Image", \ + .offset = 0x120000, \ + .size = 0x800000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x920000, \ + .size = 0x300000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "system", \ + .offset = 0x0c20000, \ + .size = 0x4000000, /* mount 64M fs */ \ + }, \ + [5] = { \ + .name = "userdata", \ + .offset = 0x4c20000, \ + .size = 0x20E0000, /* mount 32.875M */ \ + }, \ + [6] = { \ + .name = "filesystem", \ + .offset = 0x6d00000, \ + .size = 0x1000000, /* mount 16M fs */ \ + }, \ + [7] = { \ + .name = "BBT", \ + .offset = 0x7d00000, \ + .size = 0x0080000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [8] = { \ + .name = "reserved_for_bbm", \ + .offset = 0x7d80000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* NOTES: We reserve some blocks for PXA3xx BBM at the end of NAND.*/ \ + /* And the max relocation blocks is not same on different platform. */\ + /* Please take care it when define the partition table.*/ \ + } + +#define DECLARE_128M_V75_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0, \ + .size = 0x100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "NVM", \ + .offset = 0x100000, \ + .size = 0x020000, \ + }, \ + [2] = { \ + .name = "Arbel and Greyback Image", \ + .offset = 0x120000, \ + .size = 0x800000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x920000, \ + .size = 0x300000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "Filesystem", \ + .offset = 0x0c20000, \ + .size = 0x3000000, /* only mount 48M fs */ \ + }, \ + [5] = { \ + .name = "MassStorage", \ + .offset = 0x3c20000, \ + .size = 0x40e0000, /* 64.875M */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x7d00000, \ + .size = 0x0080000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [7] = { \ + .name = "reserved_for_bbm", \ + .offset = 0x7d80000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* NOTES: We reserve some blocks for PXA3xx BBM at the end of NAND.*/ \ + /* And the max relocation blocks is not same on different platform. */ \ + /* Please take care it when define the partition table.*/ \ + } + + +#define DECLARE_64M_V75_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0, \ + .size = 0x4000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Spare", \ + .offset = 0x4000, \ + .size = 0x4000, \ + }, \ + [2] = { \ + .name = "Spare", \ + .offset = 0x8000, \ + .size = 0x400000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x100000, \ + .size = 0x400000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "Filesystem", \ + .offset = 0x0500000, \ + .size = 0x3000000, /* only mount 48M fs */ \ + }, \ + [5] = { \ + .name = "MassStorage", \ + .offset = 0x3500000, \ + .size = 0x9c0000, /* 9 Mb */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x3ec0000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* NOTES: We reserve some blocks for BBM at the end of NAND.*/ \ + /* And the max relocation blocks changes per platform. */ \ + /* Please take care it when define the partition table.*/ \ + } + + +#define DECLARE_512M_V75_PARTITIONS(partitions) \ +static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0x00040000, \ + .size = 0x000b0000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Reserve", \ + .offset = 0x00100000, \ + .size = 0x00020000, \ + }, \ + [2] = { \ + .name = "Reserve", \ + .offset = 0x00120000, \ + .size = 0x00800000, \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x00920000, \ + .size = 0x00300000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "Filesystem", \ + .offset = 0x00c20000, \ + .size = 0x10000000, /* only mount 256M fs */ \ + }, \ + [5] = { \ + .name = "MassStorage", \ + .offset = 0x10c20000, \ + .size = 0x0e920000, /* 233M */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x1f540000, \ + .size = 0x00080000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [7] = { \ + .name = "reserved_for_bbm", \ + .offset = 0x1f5c0000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* NOTES: We reserve some blocks for PXA3xx */ \ + /* BBM at the end of NAND.*/ \ + /* And the max relocation blocks is not */ \ + /* same on different platform. */ \ + /* Please take care it when define the partition table.*/ \ + } + + + +#define DECLARE_ANDROID_512M_V75_PARTITIONS(partitions) \ +static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0x00040000, \ + .size = 0x000b0000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Reserve", \ + .offset = 0x00100000, \ + .size = 0x00020000, \ + }, \ + [2] = { \ + .name = "Reserve", \ + .offset = 0x00120000, \ + .size = 0x00800000, \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x00920000, \ + .size = 0x00300000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "system", \ + .offset = 0x00c20000, \ + .size = 0x10000000, /* only mount 256M fs */ \ + }, \ + [5] = { \ + .name = "userdata", \ + .offset = 0x10c20000, \ + .size = 0x0e920000, /* 233.125M */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x1f540000, /* 81 Blocks reserved*/ \ + .size = 0x00080000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [7] = { \ + .name = "reserved_for_bbm", \ + .offset = 0x1f5c0000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + } + +#define DECLARE_ANDROID_256M_V75_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "init", \ + .offset = 0xc0000, \ + .size = 0x40000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "NVM", \ + .offset = 0x100000, \ + .size = 0x020000, \ + }, \ + [2] = { \ + .name = "Arbel and Greyback Image", \ + .offset = 0x120000, \ + .size = 0x800000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x920000, \ + .size = 0x300000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "system", \ + .offset = 0x0c20000, \ + .size = 0x7000000, /* mount 112M fs */ \ + }, \ + [5] = { \ + .name = "userdata", \ + .offset = 0x7c20000, \ + .size = 0x7000000, /* mount 112M */ \ + }, \ + [6] = { \ + .name = "filesystem", \ + .offset = 0xec20000, \ + .size = 0xEE0000, /* mount 14.875M fs */ \ + }, \ + [7] = { \ + .name = "reserved_for_bbm", \ + .offset = 0xfb00000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + } + +#define DECLARE_256M_V75_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0, \ + .size = 0x100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "NVM", \ + .offset = 0x100000, \ + .size = 0x020000, \ + }, \ + [2] = { \ + .name = "Arbel and Greyback Image", \ + .offset = 0x120000, \ + .size = 0x800000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x920000, \ + .size = 0x300000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "Filesystem", \ + .offset = 0x0c20000, \ + .size = 0x3000000, /* only mount 48M fs */ \ + }, \ + [5] = { \ + .name = "MassStorage", \ + .offset = 0x3c20000, \ + .size = 0x40e0000, /* 64.875M */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x7d00000, \ + .size = 0x0080000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [7] = { \ + .name = "SWAP", \ + .offset = 0x8000000, \ + .size = 0x6000000, \ + }, \ + [8] = { \ + .name = "reserved_for_bbm", \ + .offset = 0xe000000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + } + + +/* 32G partition table is for 32Gbit parts */ + +#define DECLARE_32G_V75_PARTITIONS(partitions) \ +static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0x00100000, \ + .size = 0x00100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Reserve", \ + .offset = 0x00200000, \ + .size = 0x00100000, \ + }, \ + [2] = { \ + .name = "Reserve", \ + .offset = 0x00300000, \ + .size = 0x00700000, \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x00a00000, \ + .size = 0x00400000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "Filesystem", \ + .offset = 0x00e00000, \ + .size = 0x10000000, /* only mount 256M fs */ \ + }, \ + [5] = { \ + .name = "MassStorage", \ + .offset = 0x10e00000, \ + .size = 0xe9200000, /* 3.911G */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0xfa000000, /* 80 blocks */ \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* NOTES: We reserve some blocks for \ + * PXA3xx BBM at the end of NAND. \ + * And the max relocation blocks is not same \ + * on different platform. \ + * Please take care it when define the partition table.*/\ + } + +#define DECLARE_ANDROID_32G_V75_PARTITIONS(partitions) \ +static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0x00100000, \ + .size = 0x00100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Reserve", \ + .offset = 0x00100000, \ + .size = 0x00100000, \ + }, \ + [2] = { \ + .name = "Reserve", \ + .offset = 0x00200000, \ + .size = 0x00700000, \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x00a00000, \ + .size = 0x00400000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "system", \ + .offset = 0x00e00000, \ + .size = 0x10000000, /* only mount 256M fs */ \ + }, \ + [5] = { \ + .name = "userdata", \ + .offset = 0x10e00000, \ + .size = 0xe9200000, /* 3.911G */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0xf1000000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* Please take care it when define the partition table.*/ \ +} + + + + +/* 8G partition table is for 8Gbit parts */ + +#define DECLARE_8G_V75_PARTITIONS(partitions) \ +static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0x00100000, \ + .size = 0x00100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Reserve", \ + .offset = 0x00200000, \ + .size = 0x00100000, \ + }, \ + [2] = { \ + .name = "Reserve", \ + .offset = 0x00300000, \ + .size = 0x00700000, \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x00a00000, \ + .size = 0x00400000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "Filesystem", \ + .offset = 0x00e00000, \ + .size = 0x10000000, /* only mount 256M fs */ \ + }, \ + [5] = { \ + .name = "MassStorage", \ + .offset = 0x10e00000, \ + .size = 0x29200000, /* 658M */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x3a000000, /* 80 blocks */ \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* NOTES: We reserve some blocks for \ + * * PXA3xx BBM at the end of NAND. \ + * * And the max relocation blocks is not same \ + * * on different platform. \ + * * Please take care it when define the partition table.*/\ +} + +#define DECLARE_ANDROID_8G_V75_PARTITIONS(partitions) \ +static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0x00100000, \ + .size = 0x00100000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Reserve", \ + .offset = 0x00100000, \ + .size = 0x00100000, \ + }, \ + [2] = { \ + .name = "Reserve", \ + .offset = 0x00200000, \ + .size = 0x00700000, \ + }, \ + [3] = { \ + .name = "Kernel", \ + .offset = 0x00a00000, \ + .size = 0x00400000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [4] = { \ + .name = "system", \ + .offset = 0x00e00000, \ + .size = 0x10000000, /* only mount 256M fs */ \ + }, \ + [5] = { \ + .name = "userdata", \ + .offset = 0x10e00000, \ + .size = 0x29200000, /* 3.911G */ \ + }, \ + [6] = { \ + .name = "BBT", \ + .offset = 0x3a000000, \ + .size = MTDPART_SIZ_FULL, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + /* Please take care it when define the partition table.*/ \ +} + + + + +#define DECLARE_AVENGERSLITE_SLC_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0, \ + .size = 0x100000, \ + }, \ + [1] = { \ + .name = "MassStorage0", \ + .offset = 0x100000, \ + .size = MTDPART_SIZ_FULL, /*use rest of flash*/ \ + }, \ + } + +#define DECLARE_AVENGERSLITE_MLC_4G_ANDROID_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "resereved", \ + .offset = 0, \ + .size = 0xA00000, /* res for relo table */ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Kernel", \ + .offset = 0xA00000, \ + .size = 0x380000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [2] = { \ + .name = "Kernel_recovery", \ + .offset = 0xD80000, \ + .size = 0x400000, /* massstorage*/ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "System", \ + .offset = 0x1180000, \ + .size = 0xEE80000, /* massstorage*/ \ + }, \ + [4] = { \ + .name = "Userdata", \ + .offset = 0x10000000, \ + .size = 0x70000000, /* massstorage*/ \ + }, \ + [5] = { \ + .name = "MassStorage1", \ + .offset = 0x80000000, \ + .size = MTDPART_SIZ_FULL, /*use rest of flash*/ \ + }, \ + } + +#define DECLARE_AVENGERSLITE_MLC_1G_ANDROID_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "resereved", \ + .offset = 0, \ + .size = 0xA00000, /* res for relo table */ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Kernel", \ + .offset = 0xA00000, \ + .size = 0x380000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [2] = { \ + .name = "Kernel_recovery", \ + .offset = 0xD80000, \ + .size = 0x400000, /* massstorage*/ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "System", \ + .offset = 0x1180000, \ + .size = 0x7680000, /* massstorage*/ \ + }, \ + [4] = { \ + .name = "Userdata", \ + .offset = 0x8800000, \ + .size = 0x13800000, /* massstorage*/ \ + }, \ + [5] = { \ + .name = "MassStorage1", \ + .offset = 0x1c000000, \ + .size = MTDPART_SIZ_FULL, /*use rest of flash*/ \ + }, \ + } + +#define DECLARE_AVENGERSLITE_MLC_4G_MAEMO_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "resereved", \ + .offset = 0, \ + .size = 0xA00000, /* res for relo table */ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Kernel", \ + .offset = 0xA00000, \ + .size = 0x380000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [2] = { \ + .name = "Kernel_recovery", \ + .offset = 0xD80000, \ + .size = 0x400000, /* massstorage*/ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Filesystem", \ + .offset = 0x1180000, \ + .size = 0x3EE80000, /* massstorage*/ \ + }, \ + [4] = { \ + .name = "Userdata", \ + .offset = 0x40000000, \ + .size = 0x40000000, /* massstorage*/ \ + }, \ + [5] = { \ + .name = "MassStorage1", \ + .offset = 0x80000000, \ + .size = MTDPART_SIZ_FULL, \ + }, \ + } + +#define DECLARE_AVENGERSLITE_MLC_1G_MAEMO_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "resereved", \ + .offset = 0, \ + .size = 0xA00000, /* res for relo table */ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [1] = { \ + .name = "Kernel", \ + .offset = 0xA00000, \ + .size = 0x380000, \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [2] = { \ + .name = "Kernel_recovery", \ + .offset = 0xD80000, \ + .size = 0x400000, /* massstorage*/ \ + .mask_flags = MTD_WRITEABLE, /* force read-only */ \ + }, \ + [3] = { \ + .name = "Filesystem", \ + .offset = 0x1180000, \ + .size = 0x1AE80000, /* massstorage*/ \ + }, \ + [4] = { \ + .name = "Userdata", \ + .offset = 0x1c000000, \ + .size = MTDPART_SIZ_FULL, /* massstorage*/ \ + },\ + } + +#define DECLARE_SPI_PARTITIONS(partitions) \ + static struct mtd_partition partitions[] = { \ + [0] = { \ + .name = "Bootloader", \ + .offset = 0, \ + .size = 0x100000, \ + }, \ + [1] = { \ + .name = "MassStorage0", \ + .offset = 0x100000, \ + .size = MTDPART_SIZ_FULL, /* rest of flash*/ \ + }, \ + } + +#endif /*__PLAT_PART_TABLE_H*/ + diff --git a/arch/arm/plat-pxa/include/plat/pfn_cfg.h b/arch/arm/plat-pxa/include/plat/pfn_cfg.h new file mode 100644 index 00000000000000..ac4ff1be0c6598 --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/pfn_cfg.h @@ -0,0 +1,71 @@ +/* + * arch/arm/plat-pxa/include/plat/pfn_cfg.h + * + * Pin Configuration Abstraction + * + * Copyright (C) 2010 Marvell International Ltd. + * + * 2010-06-03: Mark F. Brown + * + * 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. + */ + +#ifndef __ASM_ARCH_PFN_CFG_H +#define __ASM_ARCH_PFN_CFG_H + +#include + +/** + * This describes an entry in the pin table + * entry can be a function (fn) or gpio + */ + +struct pfn_cfg { + mfp_cfg_t fn; + mfp_cfg_t gpio; +}; + +/* Table entry helper macro */ +#define PFN_CFG(index, fn, gpio) \ + [index] = { fn, gpio } + +/* Describes sentinel values for table searches */ +#define PFN_TERM 0xffffffff + +/* This describes an undefined table entry */ +#define PFN_UNDEF 0x0 + +enum pfn_type { + PFN_FN, + PFN_GPIO, +}; + +/** + * pfn_lookup - lookup pin setting from table by index number + * @table: table of functions corresponding gpio settings + * @type: function or gpio + * @index: index to the table + */ +static inline mfp_cfg_t *pfn_lookup(struct pfn_cfg *table, enum pfn_type type, + int index) +{ + return (type == PFN_FN) ? &table[index].fn : &table[index].gpio; +} + +/** + * pfn_config - set all pin settings in a table to function or gpio + * @table: table of functions corresponding gpio settings + * @type: function or gpio + */ +static inline void pfn_config(struct pfn_cfg *table, enum pfn_type type) +{ + struct pfn_cfg *cur = table; + for (; cur->fn != PFN_TERM && cur->gpio != PFN_TERM; cur++) { + if (cur->fn != PFN_UNDEF && cur->gpio != PFN_UNDEF) + mfp_config((type == PFN_FN) ? &cur->fn : &cur->gpio, 1); + } +} + +#endif /* _ASM_ARCH_PFN_CFG_H */ diff --git a/arch/arm/mach-pxa/include/mach/pxa27x_keypad.h b/arch/arm/plat-pxa/include/plat/pxa27x_keypad.h similarity index 100% rename from arch/arm/mach-pxa/include/mach/pxa27x_keypad.h rename to arch/arm/plat-pxa/include/plat/pxa27x_keypad.h diff --git a/arch/arm/mach-pxa/include/mach/pxa2xx_spi.h b/arch/arm/plat-pxa/include/plat/pxa2xx_spi.h similarity index 100% rename from arch/arm/mach-pxa/include/mach/pxa2xx_spi.h rename to arch/arm/plat-pxa/include/plat/pxa2xx_spi.h diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_otg.h b/arch/arm/plat-pxa/include/plat/pxa3xx_otg.h new file mode 100644 index 00000000000000..c7487dd05ed613 --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_otg.h @@ -0,0 +1,172 @@ +#ifndef __PXA3XX_USB_OTG_CONTROLLER__ +#define __PXA3XX_USB_OTG_CONTROLLER__ + +#include + +enum otg_function { + OTG_B_DEVICE = 0, + OTG_A_DEVICE +}; + +/* OTG working mode */ +#define USB_OTG_OFF 0 +#define USB_NON_OTG_CLIENT_SEP 1 +#define USB_NON_OTG_HOST_SEP 2 +#define USB_EXT_OTG_CLIENT_SEP 3 +#define USB_EXT_OTG_HOST_SEP 4 +#define USB_NON_OTG_HOST_SEP_CLIENT_DP 5 +#define USB_NON_OTG_CLIENT_SEP_HOST_DP 6 +#define USB_INT_OTG_HOST_DP 7 +#define USB_INT_OTG_CLIENT_DP 8 +#define USB_NON_OTG_CLIENT_DP 9 +#define USB_NON_OTG_HOST_DP 10 +#define USB_OTG_LP 11 +#define USB_OTG_CARKIT 12 +#define USB_OTG_PRE_SYNCH 13 + +/* UDC Handle Structure */ +struct pxa_otgc { + /* internal variables*/ + u8 a_set_b_hnp_en; /* A-Device set b_hnp_en */ + + /* OTG inputs*/ + u8 a_bus_drop; + u8 a_bus_req; + u8 a_bus_resume; + u8 a_bus_suspend; + u8 a_conn; + u8 a_sess_vld; + u8 a_srp_det; + u8 a_vbus_vld; + u8 a_clr_err; + u8 b_bus_req; /* B-Device Require Bus */ + u8 b_bus_resume; + u8 b_bus_suspend; + u8 b_conn; + u8 b_se0_srp; + u8 b_sess_end; + u8 b_sess_vld; + u8 a_suspend_req; + + /*Timer event*/ + u8 a_aidl_bdis_timeout; + u8 b_ase0_brst_timeout; + u8 a_bidl_adis_timeout; + u8 a_wait_bcon_timeout; + + /* Timer identifiers */ + int a_wait_vrise_tmr; /* Identifier of timer */ + int a_wait_bcon_tmr; + int a_aidl_bdis_tmr; + int b_ase0_brst_tmr; + int b_srp_fail_tmr; + int b_se0_srp_tmr; + int a_srp_rspns_tmr; + int a_bidl_adis_tmr; + int a_drv_rsm_tmr; +}; + +struct otg_pmic_ops { + int (*otg_vbus_init)(void); + int (*otg_set_vbus)(int vbus_type); + int (*otg_set_vbus_ic)(int function); + int (*otg_get_vbus_state)(unsigned base); +}; +struct pxa_otg; + +struct otg_ctrl_ops { + int (*otgc_init)(void*); + void (*otgc_interrupt_init)(void); + int (*otgc_interrupt_handle)(struct pxa_otg *pOtgHandle); + void (*otgc_init_gadget)(void); + void (*otgc_deinit)(void); +}; + +struct otg_xceiv_ops { + int (*otgx_get_mode)(void); + int (*otgx_set_mode)(int mode); + void (*otgx_init)(void); + enum otg_function (*otgx_detect_default_func)(void); + int (*otgx_dp_session)(void); + int (*otgx_vbus_session)(struct pxa_otg *pOtgHandle); + int (*otgx_check_b_hnp)(void); + int (*otgx_check_vbus)(void); + int (*otgx_start_autoresume)(void); + int (*otgx_drive_resume)(void); + void (*reset_xcvr_init)(void); + void (*ulpi_dat3_work)(void); + +}; + +struct pxa_otg_plat_info { + struct otg_pmic_ops *pmic_ops; + struct otg_ctrl_ops *ctrl_ops; + struct otg_xceiv_ops *xceiv_ops; +}; + +struct pxa_otg { + struct otg_transceiver otg; + struct usb_device *udev; + + unsigned working : 1; + + struct work_struct work; + unsigned long todo; +#define WORK_UPDATE_OTG 0 /* update OTG state machine */ +#define WORK_HOST_RESUME 1 /* resume host */ +#define WORK_TIMER 2 /* timer fired */ +#define WORK_STOP 3 /* don't resubmit */ + struct pxa_otgc *otg_ctrl; + struct otg_pmic_ops *pmic_ops; + struct otg_ctrl_ops *ctrl_ops; + struct otg_xceiv_ops *xceiv_ops; + struct pxa_otg_plat_info *info; +}; + +/* OTG software error code */ +/* #define OTG_SUCCESS 0 */ +#define OTG_INVALID_PARAMETER -1 +#define OTG_UDC_DISABLED -2 +#define OTG_WRONG_STATE -3 +#define OTG_WRONG_RESISTER -4 +#define OTG_I2C_ERROR -5 +#define OTG_MFP_FAILED -6 +#define OTG_TIMER_FAILED -7 + +/* Timer's interval, unit 10ms */ +#define T_A_WAIT_VRISE 100 +#define T_A_WAIT_BCON 2000 +#define T_A_AIDL_BDIS 100 +#define T_A_BIDL_ADIS 20 +/* In order to debug, we use a longer timer */ +/* #define T_B_ASE0_BRST 400 */ +#define T_B_ASE0_BRST 400 +#define T_B_SE0_SRP 300 +#define T_B_SRP_FAIL 2000 +#define T_B_DATA_PLS 10 +#define T_B_SRP_INIT 100 +#define T_A_SRP_RSPNS 10 +#define T_A_DRV_RSM 5 + +/* USBOTG EVENTS for require bus */ +#define USBOTG_VBUS_VALID (1 << 0) +#define USBOTG_SRP_DETECT (1 << 1) + +#define USBOTG_STATUS_REQUIRED (USBOTG_VBUS_VALID | USBOTG_SRP_DETECT) + +/* OTG interrupt type*/ +#define OTG_INT_INIT 0 +#define OTG_INT_IDF (1 << 0) +#define OTG_INT_IDR (1 << 1) +#define OTG_INT_RSV (1 << 2) +#define OTG_INT_RVV (1 << 3) +#define OTG_INT_FVV (1 << 4) +#define OTG_INT_LS (1 << 5) +#define OTG_INT_LP_DIS (1 << 6) + +#define PXA3xx_OTG_VBUS_PMIC + +extern void pxa3xx_otg_require_bus(int require); + +#endif + diff --git a/arch/arm/plat-pxa/include/plat/pxa3xx_pmic.h b/arch/arm/plat-pxa/include/plat/pxa3xx_pmic.h new file mode 100644 index 00000000000000..7a3cabb4034b81 --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/pxa3xx_pmic.h @@ -0,0 +1,161 @@ +#ifndef __PLAT_PXA3XX_H +#define __PLAT_PXA3XX_H + +#include +#include + +struct power_supply_module { + int command; + int power_module; +}; + +struct power_chip { + int chip_id; + char *chip_name; + struct power_supply_module *power_supply_modules; +}; + +/* this enum should be consistent with micco_power_module[] + * in arch/arm/mach-pxa/xxx(platform).c */ +enum { + /* Set command > 0xFFFF0000 to avoid wrong + * parameter is used for pmic_set_voltage. + */ + VCC_CORE = 0xFFFF0000, + VCC_SRAM, + VCC_MVT, + VCC_3V_APPS, + VCC_SDIO, + VCC_CAMERA_ANA, + VCC_USB, + VCC_LCD, + VCC_TSI, + VCC_CAMERA_IO, + VCC_1P8V, + VCC_MEM, + HDMI_TX, + TECH_3V, + TECH_1P8V, + HDMI_1P2V, + VCC_MISC1, + VCC_MISC2, + + /* add your command here */ + + /* max command */ + MAX_CMD, +}; + +#define PMIC_EVENT_EXTON (1 << 0) +#define PMIC_EVENT_VBUS (1 << 1) +#define PMIC_EVENT_USB (PMIC_EVENT_EXTON | PMIC_EVENT_VBUS) + +#define PMIC_EVENT_TOUCH (1 << 2) + +#define PMIC_EVENT_OTGCP_IOVER (1 << 3) + +#define PMIC_EVENT_TBAT (1 << 4) +#define PMIC_EVENT_REV_IOVER (1 << 5) +#define PMIC_EVENT_IOVER (1 << 6) +#define PMIC_EVENT_CHDET (1 << 7) +#define PMIC_EVENT_VBATMON (1 << 8) +#define PMIC_EVENT_HSDETECT (1 << 9) +#define PMIC_EVENT_HOOKSWITCH (1 << 10) +#define PMIC_EVENT_ON_KEY (1 << 11) +#define PMIC_EVENT_MICDETECT (1 << 12) + +#define PMIC_EVENT_CHARGER (PMIC_EVENT_TBAT | \ + PMIC_EVENT_REV_IOVER | \ + PMIC_EVENT_IOVER | \ + PMIC_EVENT_CHDET | \ + PMIC_EVENT_VBATMON) + +struct pmic_ops { + int (*get_voltage)(unsigned int cmd, int *pmv); + int (*set_voltage)(unsigned int cmd, unsigned int mv); + + int (*is_vbus_assert)(void); + int (*is_avbusvld)(void); + int (*is_asessvld)(void); + int (*is_bsessvld)(void); + int (*is_srp_ready)(void); + + int (*set_pump)(int enable); + int (*set_vbus_supply)(int enable, int srp); + int (*set_usbotg_a_mask)(void); + int (*set_usbotg_b_mask)(void); + int (*is_usbpump_chg)(void); + + int (*init)(struct device *dev); + int (*deinit)(struct device *dev); + + struct list_head list; /* callback list */ + spinlock_t cb_lock; /* spinlock for callback list */ +}; + +struct pmic_callback { + unsigned long event; /* the event which callback care about */ + void (*func)(unsigned long event); /*callback function */ + struct list_head list; +}; + +struct pxa3xx_pmic_regs { + unsigned int data:8; + unsigned int hit:1; + unsigned int mask:1; +}; + +extern void start_calc_time(void); +extern void end_calc_time(void); + +extern int pxa3xx_pmic_write(u8 reg, u8 val); +extern int pxa3xx_pmic_read(u8 reg, u8 *pval); + +extern void pmic_set_ops(struct pmic_ops *ops); + +extern int pmic_callback_register(unsigned long event, + void (*func)(unsigned long event)); +extern int pmic_callback_unregister(unsigned long event, + void (*func)(unsigned long event)); + +extern int pmic_event_handle(unsigned long event); + +extern int pxa3xx_pmic_get_voltage(int cmd, int *pval); +extern int pxa3xx_pmic_set_voltage(int cmd, int val); +/* Check whether USB VBUS is asserted */ +extern int pxa3xx_pmic_is_vbus_assert(void); +/* Check whether USB VBUS has gone above A-device VBUS valid threshold + * Min: 4.4V Max: N/A + */ +extern int pxa3xx_pmic_is_avbusvld(void); +/* Check whether VBUS has gone above A-device Session Valid threshold + * Min: 0.8V Max: 2.0V + */ +extern int pxa3xx_pmic_is_asessvld(void); +/* Check whether VBUS has gone above B-device Session Valid threshold + * Min: 0.8V Max: 4.0V + */ +extern int pxa3xx_pmic_is_bsessvld(void); +/* Check whether VBUS has gone above B-device Session End threshold + * Min: 0.2V Max: 0.8V + */ +extern int pxa3xx_pmic_is_srp_ready(void); +/* Initialize the USB PUMP */ +extern int pxa3xx_pmic_set_pump(int enable); +/* Check the events change of PMIC */ +extern int pxa3xx_pmic_event_change(void); +/* enable/disable VBUS supply */ +extern int pxa3xx_pmic_set_vbus_supply(int enable, int srp); +/* Set events mask for USB A-device + * A-device Sessino Valid event + * A-device VBUS Valid event + */ +extern int pxa3xx_pmic_set_usbotg_a_mask(void); +/* Set events mask for USB B-device + * B-device Session Valid event + * B-device Session end event + */ +extern int pxa3xx_pmic_set_usbotg_b_mask(void); +#endif + + diff --git a/arch/arm/plat-pxa/include/plat/pxa_u2o.h b/arch/arm/plat-pxa/include/plat/pxa_u2o.h new file mode 100644 index 00000000000000..d790aae0650272 --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/pxa_u2o.h @@ -0,0 +1,197 @@ +/* + * linux/include/asm-arm/arch-pxa/pxa_u2o.h + * + * This supports machine-specific differences in how the PXA + * USB 2.0 Device Controller (U2O) is wired. + * + * (C) Copyright 2008 Marvell International Ltd. + * All Rights Reserved + */ + +#ifndef __ASM_ARCH_PXA_U2O_H +#define __ASM_ARCH_PXA_U2O_H + +#include + +/* usb otg controller register base */ +#define PXA168_U2O_REGBASE (0xd4208000) +#define PXA168_U2O_PHYBASE (0xd4207000) + +#define PXA935_U2O_REGBASE (0x55502000) +#define PXA935_U2O_PHYBASE (0x5550a000) + +#define PXA168_U2H_REGBASE (0xd4209000) +#define PXA168_U2H_PHYBASE (0xd4206000) + +#define USB_REG_RANGE (0x1ff) +#define USB_PHY_RANGE (0xff) + +/* registers */ +#define U2x_CAPREGS_OFFSET 0x100 + +#define U2xUSBCMD (0x140) /* U2O Command */ +#define U2xUSBCMD_RST (1<<1) /* Reset */ +#define U2xUSBCMD_RS (1<<0) /* Run/Stop */ + +#define U2xUSBSTS (0x144) /* U2O Status */ +#define U2xUSBINTR (0x148) /* U2O Interrupt Enable */ + +#define U2xPORTSC (0x184) /* U2O Port Status */ +#define U2xPORTSC_PP (1<<12) /* Port Power */ +#define U2xPORTSC_PTS_MASK (3<<30) /* Parallel Transceiver Select */ + +#define U2xUSBINTR (0x148) /* U2O Interrupt Enable */ +#define U2xUSBMODE (0x1A8) /* U2O Device Mode */ +#define U2xUSBMODE_CM_MASK (3<<0) /* U2O Controller Mode */ + +#define U2xOTGSC (0x1A4) /* U2O On-The-Go Status and Control */ + +/* OTG interrupt enable bit masks */ +#define U2xOTGSC_DPIE (0x40000000) /* Data-line pulsing IE */ +#define U2xOTGSC_1MSIE (0x20000000) /* 1 Millisecond timer IE */ +#define U2xOTGSC_BSEIE (0x10000000) /* B-session end IE */ +#define U2xOTGSC_BSVIE (0x08000000) /* B-session valid IE */ +#define U2xOTGSC_ASVIE (0x04000000) /* A-session valid IE */ +#define U2xOTGSC_AVVIE (0x02000000) /* A-V-bus valid IE */ +#define U2xOTGSC_IDIE (0x01000000) /* OTG ID IE */ +#define U2xOTGSC_IE_MASK (0x7F000000) + +/* OTG interrupt status bit masks */ +#define U2xOTGSC_IS_MASK (0x007F0000) +#define U2xOTGSC_DPIS (0x00400000) /* Data-line pulsing IS */ +#define U2xOTGSC_1MSIS (0x00200000) /* 1 Millisecond timer IS */ +#define U2xOTGSC_BSEIS (0x00100000) /* B-session end IS */ +#define U2xOTGSC_BSVIS (0x00080000) /* B-session valid IS */ +#define U2xOTGSC_ASVIS (0x00040000) /* A-session valid IS */ +#define U2xOTGSC_AVVIS (0x00020000) /* A-Vbus valid IS */ +#define U2xOTGSC_IDIS (0x00010000) /* OTG ID IS */ + +/* OTG status bit masks */ +#define U2xOTGSC_DPS (0x00004000) /* Data-line pulsing */ +#define U2xOTGSC_1MST (0x00002000) /* 1 Milliseconf timer toggle */ +#define U2xOTGSC_BSE (0x00001000) /* B-session end */ +#define U2xOTGSC_BSV (0x00000800) /* B-session valid */ +#define U2xOTGSC_ASV (0x00000400) /* A-session valid */ +#define U2xOTGSC_AVV (0x00000200) /* A-Vbus Valid */ +#define U2xOTGSC_ID (0x00000100) /* OTG ID */ + +/* OTG control bit masks */ +#define U2xOTGSC_CTL_BITS (0x2F) +#define U2xOTGSC_HABA (0x00000080) /* hardware assisted B-Dis to A-Con */ +#define U2xOTGSC_HADP (0x00000040) /* hardware assisted data pulse bits*/ +#define U2xOTGSC_IDPU (0x00000020) /* ID pull-up enable */ +#define U2xOTGSC_DP (0x00000010) /* Data-pulsing */ +#define U2xOTGSC_OT (0x00000008) /* OTG termination */ +#define U2xOTGSC_HAAR (0x00000004) /* Auto reset bit */ +#define U2xOTGSC_VC (0x00000002) /* Vbus charge */ +#define U2xOTGSC_VD (0x00000001) /* Vbus discharge */ + +#define UTMI_REVISION 0x0 +#define UTMI_CTRL 0x4 +#define UTMI_PLL 0x8 +#define UTMI_TX 0xc +#define UTMI_RX 0x10 +#define UTMI_IVREF 0x14 +#define UTMI_T0 0x18 +#define UTMI_T1 0x1c +#define UTMI_T2 0x20 +#define UTMI_T3 0x24 +#define UTMI_T4 0x28 +#define UTMI_T5 0x2c +#define UTMI_RESERVE 0x30 +#define UTMI_USB_INT 0x34 +#define UTMI_DBG_CTL 0x38 +#define UTMI_OTG_ADDON 0x3c + +/* For UTMICTRL Register */ +#define UTMI_CTRL_USB_CLK_EN (1<<31) +/* pxa168 */ +#define UTMI_CTRL_SUSPEND_SET1 (1<<30) +#define UTMI_CTRL_SUSPEND_SET2 (1<<29) +#define UTMI_CTRL_RXBUF_PDWN (1<<24) +#define UTMI_CTRL_TXBUF_PDWN (1<<11) + +#define UTMI_CTRL_INPKT_DELAY_SHIFT 30 +#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28 +#define UTMI_CTRL_PU_REF_SHIFT 20 +#define UTMI_CTRL_ARC_PULLDN_SHIFT 12 +#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1 +#define UTMI_CTRL_PWR_UP_SHIFT 0 +/* For UTMI_PLL Register */ +#define UTMI_PLL_CLK_BLK_EN_SHIFT 24 +#define UTMI_PLL_FBDIV_SHIFT 4 +#define UTMI_PLL_REFDIV_SHIFT 0 +#define UTMI_PLL_FBDIV_MASK 0x00000FF0 +#define UTMI_PLL_REFDIV_MASK 0x0000000F +#define UTMI_PLL_ICP_MASK 0x00007000 +#define UTMI_PLL_KVCO_MASK 0x00031000 +#define UTMI_PLL_PLLCALI12_SHIFT 29 +#define UTMI_PLL_PLLCALI12_MASK (0x3<<29) +#define UTMI_PLL_PLLVDD18_SHIFT 27 +#define UTMI_PLL_PLLVDD18_MASK (0x3<<27) +#define UTMI_PLL_PLLVDD12_SHIFT 25 +#define UTMI_PLL_PLLVDD12_MASK (0x3<<25) +#define UTMI_PLL_KVCO_SHIFT 15 +#define UTMI_PLL_ICP_SHIFT 12 +/* For UTMI_TX Register */ +#define UTMI_TX_LOW_VDD_EN_SHIFT 11 +#define UTMI_TX_IMPCAL_VTH_SHIFT 14 +#define UTMI_TX_IMPCAL_VTH_MASK (0x7<<14) +#define UTMI_TX_CK60_PHSEL_SHIFT 17 +#define UTMI_TX_CK60_PHSEL_MASK (0xf<<17) +#define UTMI_TX_TXVDD12_SHIFT 22 +#define UTMI_TX_TXVDD12_MASK (0x3<<22) +/* For UTMI_RX Register */ +#define UTMI_RX_SQ_THRESH_SHIFT 4 +#define UTMI_RX_SQ_THRESH_MASK (0xf<<4) +#define UTMI_REG_SQ_LENGTH_SHIFT 15 +#define UTMI_REG_SQ_LENGTH_MASK (0x3<<15) + +#define REG_RCAL_START 0x00001000 +#define VCOCAL_START 0x00200000 +#define KVCO_EXT 0x00400000 +#define PLL_READY 0x00800000 +#define CLK_BLK_EN 0x01000000 + +#define UTMI_OTG_ADDON_OTG_ON (1<<0) + +#define res_size(res) ((res)->end - (res)->start + 1) + +struct device; +struct otg_transceiver; + +struct pxa_usb_plat_info { + int (*phy_init) (unsigned base); + int (*phy_deinit) (unsigned base); + int (*plat_init) (struct device *dev); + int (*vbus_set) (int); + int (*vbus_status) (unsigned base); /* do we see host? */ + int (*vbus_detect) (void *func, int enable); + int (*usbid_detect) (struct otg_transceiver *otg); + int (*set_power) (int); + int clk_gating; + int rely_on_vbus; + int in_single; + int out_single; + int is_otg; + unsigned regbase; + unsigned phybase; + void * (*init_pmic_ops) (void); +}; + +extern struct otg_xceiv_ops *init_pxa9xx_otg_xceiv_ops(void); +extern struct otg_ctrl_ops *init_pxa9xx_otg_ctrl_ops(void); + +extern int otg_is_client(void); +extern int otg_is_host(void); + + +extern unsigned u2o_get(unsigned base, unsigned offset); +extern void u2o_set(unsigned base, unsigned offset, unsigned value); +extern void u2o_clear(unsigned base, unsigned offset, unsigned value); +extern void u2o_write(unsigned base, unsigned offset, unsigned value); + +extern int rely_on_vbus; +extern int connected; +#endif /* __ASM_ARCH_PXA_U2O_H */ + diff --git a/arch/arm/plat-pxa/include/plat/pxausb_common.h b/arch/arm/plat-pxa/include/plat/pxausb_common.h new file mode 100644 index 00000000000000..ee7100fc269dfa --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/pxausb_common.h @@ -0,0 +1,20 @@ +#ifndef __LINUX_USB_GADGET_PXA_COMMON_H +#define __LINUX_USB_GADGET_PXA_COMMON_H + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_STALL, + EP0_IN_FAKE, + EP0_NO_ACTION +}; + +#define DMSG(stuff...) pr_debug("udc: " stuff) + +#define VBUS_LOW (0) +#define VBUS_HIGH (1<<0) +#define VBUS_CHARGE (1<<1) +#define VBUS_SRP (1<<2) + +#endif /* __LINUX_USB_GADGET_PXA_COMMON_H */ diff --git a/arch/arm/plat-pxa/include/plat/pxausb_comp.h b/arch/arm/plat-pxa/include/plat/pxausb_comp.h new file mode 100644 index 00000000000000..49fdf8bfe2af1b --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/pxausb_comp.h @@ -0,0 +1,114 @@ + +#ifndef __LINUX_USB_GADGET_PXA_COMP_H +#define __LINUX_USB_GADGET_PXA_COMP_H + +#include + +#ifdef DEBUG +#define comp_print(args...) printk(args) +#else +#define comp_print(args...) +#endif + +#define MULTI_P3 /* add iad descriptor */ +#define UDC_DEFAULT_CONFIG 1 /* FIXME for rndis */ +#define UDC_VENDOR_NUM 0x1286 +#define UDC_PRODUCT_NUM 0x8002 + +#define MAX_CONFIG_LENGTH 256 + +struct pxa3xx_comp_ep { + struct pxa3xx_comp_ep *next; + unsigned config; + unsigned interface; +//#ifdef CONFIG_USB_COMPOSITE xj del + int log_ep_num; + unsigned assigned_interface; /* actual interface number report to the host */ + struct gadget_driver_info *driver_info;/* pointer to gadget_driver_info */ +//#endif +}; + +#ifdef CONFIG_USB_COMPOSITE +struct gadget_driver_info { + struct gadget_driver_info *next;/* point to next gadget_driver_info */ + unsigned config; /* configuration number used by the driver */ + unsigned assigned_intf_start; /* the first assigned interface number */ + unsigned num_intfs; /* total interface number for the driver */ + /*struct usb_descriptor_header *desc; // point to configuration from gadget */ + /*int desc_length; // length of the configuration above */ +#ifdef MULTI_P3 + unsigned char device_desc[18];/* device desc for the driver */ +#endif + unsigned char config_desc[MAX_CONFIG_LENGTH]; /* configuration desc for th driver */ + unsigned char config_desc_hs[MAX_CONFIG_LENGTH]; + /*unsigned config_length; */ + + struct usb_gadget_driver *driver; /* store the driver pointer of gadget */ + void *driver_data; /* pointer to the driver private data */ + unsigned stopped; /* driver disconnected or not */ +}; +#endif + +struct pxa_comp_request { + struct usb_request req; + struct list_head queue; +}; + +struct pxa3xx_comp { + int driver_count; + struct pxa3xx_comp_ep *first_ep; +#ifdef CONFIG_USB_COMPOSITE + struct gadget_driver_info *first_gadget; /* head of a gadget_driver_info queue */ + struct gadget_driver_info *active_gadget; /* currently active gadget */ + int interface_count; + /* FIXME, should be usb_request */ + struct pxa_comp_request ep0_req; /* include the usb_req to respond to the get_desc request, etc */ + struct t_str_id *str_id; + int rm_flag; +#endif + enum ep0_state ep0state; + unsigned req_config; + unsigned char configs[MAX_CONFIG_LENGTH]; + unsigned config_length; + unsigned configuration, + interface, + alternate; +#ifdef CONFIG_USB_OTG + struct otg_transceiver *transceiver; +#endif +}; + + +/* for controller driver */ +extern int get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr); +extern void get_fake_config(struct pxa3xx_comp *dev, struct usb_request *req, int spd); +extern void comp_val_init(struct pxa3xx_comp *dev); +extern void comp_set_ep(struct pxa3xx_comp *dev, int ep_num, int config, int interface); +extern struct pxa3xx_comp_ep *find_ep_num(struct pxa3xx_comp_ep *head, int num); +extern int comp_calc_config(struct pxa3xx_comp *dev, int ep_num); +extern int comp_calc_interface(struct pxa3xx_comp *dev, int ep_num); +extern int stop_cur_gadget(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +extern int comp_check_driver(struct pxa3xx_comp *dev, + struct usb_gadget_driver *slf_drver, + struct usb_gadget_driver *driver); +extern void comp_register_driver(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_gadget_driver *driver); +extern void comp_unregister_driver(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver **slf_drver, struct usb_gadget_driver *driver); +extern void comp_driver_suspend(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_gadget_driver *driver); +extern void comp_driver_resume(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_gadget_driver *driver); +extern int comp_change_config(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_gadget_driver *driver, struct usb_ctrlrequest *req); +extern struct usb_gadget_driver *comp_change_interface(struct pxa3xx_comp *dev, int active_interface, struct usb_ctrlrequest *req, \ + struct usb_gadget *gadget, struct usb_gadget_driver *driver, int *ret); +extern void stop_gadget(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_gadget_driver *driver); +extern void udc_stop(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_gadget_driver *driver, int state); +extern int comp_ep0_req(struct pxa3xx_comp *dev, struct usb_gadget *gadget, struct usb_ep *ep0, struct usb_ctrlrequest *usb_req); +extern int comp_is_dev_busy(struct pxa3xx_comp *dev, struct usb_gadget_driver *driver); + +extern int stop_udc(struct usb_gadget_driver *driver); +extern int set_eps(__u8 num_eps, struct usb_endpoint_descriptor *p_ep_desc, int len, + int config, int interface, int alt); +extern void udc_reinit(void); + + +#endif /* __LINUX_USB_GADGET_PXA_COMP_H */ diff --git a/arch/arm/plat-pxa/include/plat/regs-ssp.h b/arch/arm/plat-pxa/include/plat/regs-ssp.h new file mode 100644 index 00000000000000..0e59f669da7dfb --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/regs-ssp.h @@ -0,0 +1,129 @@ +#ifndef __ASM_ARCH_REGS_SSP_H +#define __ASM_ARCH_REGS_SSP_H + +/* + * SSP Serial Port Registers + * PXA250, PXA255, PXA26x and PXA27x SSP controllers are all slightly different. + * PXA255, PXA26x and PXA27x have extra ports, registers and bits. + */ + +#define SSCR0 (0x00) /* SSP Control Register 0 */ +#define SSCR1 (0x04) /* SSP Control Register 1 */ +#define SSSR (0x08) /* SSP Status Register */ +#define SSITR (0x0C) /* SSP Interrupt Test Register */ +#define SSDR (0x10) /* SSP Data Write/Data Read Register */ + +#define SSTO (0x28) /* SSP Time Out Register */ +#define SSPSP (0x2C) /* SSP Programmable Serial Protocol */ +#define SSTSA (0x30) /* SSP Tx Timeslot Active */ +#define SSRSA (0x34) /* SSP Rx Timeslot Active */ +#define SSTSS (0x38) /* SSP Timeslot Status */ +#define SSACD (0x3C) /* SSP Audio Clock Divider */ + +#if defined(CONFIG_PXA3xx) || defined(CONFIG_CPU_PXA168) +#define SSACDD (0x40) /* SSP Audio Clock Dither Divider */ +#endif + +/* Common PXA2xx bits first */ +#define SSCR0_DSS (0x0000000f) /* Data Size Select (mask) */ +#define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */ +#define SSCR0_FRF (0x00000030) /* FRame Format (mask) */ +#define SSCR0_Motorola (0x0 << 4) /* Motorola's Serial Peripheral Interface (SPI) */ +#define SSCR0_TI (0x1 << 4) /* Texas Instruments' Synchronous Serial Protocol (SSP) */ +#define SSCR0_National (0x2 << 4) /* National Microwire */ +#define SSCR0_ECS (1 << 6) /* External clock select */ +#define SSCR0_SSE (1 << 7) /* Synchronous Serial Port Enable */ + +#if defined(CONFIG_PXA25x) +#define SSCR0_SCR (0x0000ff00) /* Serial Clock Rate (mask) */ +#define SSCR0_SerClkDiv(x) ((((x) - 2)/2) << 8) /* Divisor [2..512] */ +#elif defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) || defined(CONFIG_CPU_PXA168) +#define SSCR0_SCR (0x000fff00) /* Serial Clock Rate (mask) */ +#define SSCR0_SerClkDiv(x) (((x) - 1) << 8) /* Divisor [1..4096] */ +#endif + +#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) || defined(CONFIG_CPU_PXA168) +#define SSCR0_EDSS (1 << 20) /* Extended data size select */ +#define SSCR0_NCS (1 << 21) /* Network clock select */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO overrrun interrupt mask */ +#define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun interrupt mask */ +#define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */ +#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame [1..8] */ +#define SSCR0_ADC (1 << 30) /* Audio clock select */ +#define SSCR0_MOD (1 << 31) /* Mode (normal or network) */ +#endif + +#if defined(CONFIG_PXA3xx) || defined(CONFIG_CPU_PXA168) +#define SSCR0_FPCKE (1 << 29) /* FIFO packing enable */ +#endif + +#define SSCR1_RIE (1 << 0) /* Receive FIFO Interrupt Enable */ +#define SSCR1_TIE (1 << 1) /* Transmit FIFO Interrupt Enable */ +#define SSCR1_LBM (1 << 2) /* Loop-Back Mode */ +#define SSCR1_SPO (1 << 3) /* Motorola SPI SSPSCLK polarity setting */ +#define SSCR1_SPH (1 << 4) /* Motorola SPI SSPSCLK phase setting */ +#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */ +#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */ +#define SSCR1_TxTresh(x) (((x) - 1) << 6) /* level [1..16] */ +#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */ +#define SSCR1_RxTresh(x) (((x) - 1) << 10) /* level [1..16] */ + +#define SSSR_TNF (1 << 2) /* Transmit FIFO Not Full */ +#define SSSR_RNE (1 << 3) /* Receive FIFO Not Empty */ +#define SSSR_BSY (1 << 4) /* SSP Busy */ +#define SSSR_TFS (1 << 5) /* Transmit FIFO Service Request */ +#define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ +#define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ + +#define SSCR0_TIM (1 << 23) /* Transmit FIFO Under Run Interrupt Mask */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO Over Run interrupt Mask */ +#define SSCR0_NCS (1 << 21) /* Network Clock Select */ +#define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */ + +/* extra bits in PXA255, PXA26x and PXA27x SSP ports */ +#define SSCR0_TISSP (1 << 4) /* TI Sync Serial Protocol */ +#define SSCR0_PSP (3 << 4) /* PSP - Programmable Serial Protocol */ +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */ +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */ +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */ +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */ +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */ +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */ +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */ +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */ +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */ +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */ +#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interupt Enable */ +#define SSCR1_IFS (1 << 16) /* Invert Frame Signal */ +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */ +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */ + +#define SSSR_BCE (1 << 23) /* Bit Count Error */ +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */ +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */ +#define SSSR_EOC (1 << 20) /* End Of Chain */ +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */ +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */ + +#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */ +#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */ +#define SSPSP_SFRMWDTH(x) ((x) << 16) /* Serial Frame Width */ +#define SSPSP_SFRMDLY(x) ((x) << 9) /* Serial Frame Delay */ +#define SSPSP_DMYSTRT(x) ((x) << 7) /* Dummy Start */ +#define SSPSP_STRTDLY(x) ((x) << 4) /* Start Delay */ +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */ +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_SCMODE(x) ((x) << 0) /* Serial Bit Rate Clock Mode */ + +#define SSACD_SCDB (1 << 3) /* SSPSYSCLK Divider Bypass */ +#define SSACD_ACPS(x) ((x) << 4) /* Audio clock PLL select */ +#define SSACD_ACDS(x) ((x) << 0) /* Audio clock divider select */ +#if defined(CONFIG_PXA3xx) || defined(CONFIG_CPU_PXA168) +#define SSACD_SCDX8 (1 << 7) /* SYSCLK division ratio select */ +#endif + + +#endif /* __ASM_ARCH_REGS_SSP_H */ diff --git a/arch/arm/plat-pxa/include/plat/ssp.h b/arch/arm/plat-pxa/include/plat/ssp.h new file mode 100644 index 00000000000000..7300da4893d75b --- /dev/null +++ b/arch/arm/plat-pxa/include/plat/ssp.h @@ -0,0 +1,109 @@ +/* + * ssp.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * 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. + * + * This driver supports the following PXA CPU/SSP ports:- + * + * PXA250 SSP + * PXA255 SSP, NSSP + * PXA26x SSP, NSSP, ASSP + * PXA27x SSP1, SSP2, SSP3 + * PXA3xx SSP1, SSP2, SSP3, SSP4 + * PXA168 SSP1, SSP2, SSP3, SSP4, SSP5 + */ + +#ifndef __ASM_ARCH_SSP_H +#define __ASM_ARCH_SSP_H + +#include +#include + +enum pxa_ssp_type { + SSP_UNDEFINED = 0, + PXA25x_SSP, /* pxa 210, 250, 255, 26x */ + PXA25x_NSSP, /* pxa 255, 26x (including ASSP) */ + PXA27x_SSP, + PXA168_SSP, +}; + +struct ssp_device { + struct platform_device *pdev; + struct list_head node; + + struct clk *clk; + void __iomem *mmio_base; + unsigned long phys_base; + + const char *label; + int port_id; + int type; + int use_count; + int irq; + int drcmr_rx; + int drcmr_tx; +}; + +/* + * SSP initialisation flags + */ +#define SSP_NO_IRQ 0x1 /* don't register an irq handler in SSP driver */ + +struct ssp_state { + u32 cr0; + u32 cr1; + u32 to; + u32 psp; +}; + +struct ssp_dev { + struct ssp_device *ssp; + u32 port; + u32 mode; + u32 flags; + u32 psp_flags; + u32 speed; + int irq; +}; + +int ssp_write_word(struct ssp_dev *dev, u32 data); +int ssp_read_word(struct ssp_dev *dev, u32 *data); +int ssp_flush(struct ssp_dev *dev); +void ssp_enable(struct ssp_dev *dev); +void ssp_disable(struct ssp_dev *dev); +void ssp_save_state(struct ssp_dev *dev, struct ssp_state *ssp); +void ssp_restore_state(struct ssp_dev *dev, struct ssp_state *ssp); +int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags); +int ssp_config(struct ssp_dev *dev, u32 mode, u32 flags, u32 psp_flags, u32 speed); +void ssp_exit(struct ssp_dev *dev); + +/** + * ssp_write_reg - Write to a SSP register + * + * @dev: SSP device to access + * @reg: Register to write to + * @val: Value to be written. + */ +static inline void ssp_write_reg(struct ssp_device *dev, u32 reg, u32 val) +{ + __raw_writel(val, dev->mmio_base + reg); +} + +/** + * ssp_read_reg - Read from a SSP register + * + * @dev: SSP device to access + * @reg: Register to read from + */ +static inline u32 ssp_read_reg(struct ssp_device *dev, u32 reg) +{ + return __raw_readl(dev->mmio_base + reg); +} + +struct ssp_device *ssp_request(int port, const char *label); +void ssp_free(struct ssp_device *); +#endif /* __ASM_ARCH_SSP_H */ diff --git a/arch/arm/plat-pxa/mfp.c b/arch/arm/plat-pxa/mfp.c index be58f9fe65b0db..27e6d526f54334 100644 --- a/arch/arm/plat-pxa/mfp.c +++ b/arch/arm/plat-pxa/mfp.c @@ -20,6 +20,7 @@ #include #include +#include #define MFPR_SIZE (PAGE_SIZE) @@ -141,7 +142,7 @@ static const unsigned long mfpr_edge[] = { * perform a read-back of any MFPR register to make sure the * previous writings are finished */ -#define mfpr_sync() (void)__raw_readl(mfpr_mmio_base + 0) +#define mfpr_sync() (void)__raw_readl(mfpr_mmio_base + 0x0204) static inline void __mfp_config_run(struct mfp_pin *p) { @@ -196,12 +197,25 @@ void mfp_config(unsigned long *mfp_cfgs, int num) p->mfpr_run = tmp | mfpr_pull[pull]; } + /* + * pxa168 fast IO pins drive strength handled by + * pxa168_mfp_fastio_drive(), skip settings here + */ + if (cpu_is_pxa168()) { + if ((pin >= 56) && (pin <= 85)) { + p->mfpr_run = (mfpr_readl(mfp_table[pin].mfpr_off) & (3<<10)) | + (p->mfpr_run & ~(3 << 10)); + } + /* bit7 set always */ + p->mfpr_run |= (1<<7); + } p->config = c; __mfp_config_run(p); } mfpr_sync(); spin_unlock_irqrestore(&mfp_spin_lock, flags); } +EXPORT_SYMBOL(mfp_config); unsigned long mfp_read(int mfp) { @@ -228,6 +242,22 @@ void mfp_write(int mfp, unsigned long val) spin_unlock_irqrestore(&mfp_spin_lock, flags); } +void mfp_set(int mfp, unsigned long mask) +{ + unsigned long tmp = mfp_read(mfp); + + tmp |= mask; + mfp_write(mfp, tmp); +} + +void mfp_clr(int mfp, unsigned long mask) +{ + unsigned long tmp = mfp_read(mfp); + + tmp &= ~mask; + mfp_write(mfp, tmp); +} + void __init mfp_init_base(unsigned long mfpr_base) { int i; diff --git a/arch/arm/plat-pxa/pmem.c b/arch/arm/plat-pxa/pmem.c new file mode 100644 index 00000000000000..53d06bc2c15601 --- /dev/null +++ b/arch/arm/plat-pxa/pmem.c @@ -0,0 +1,65 @@ +/* + * pmem.c + * + * Buffer Management Module + * + * 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. + + *(C) Copyright 2009 Marvell International Ltd. + * All Rights Reserved + */ + +//#define DEBUG +#include +#include +#include +#include + +#define PMEM_NUM_MAX 8 +static char *pmem_name[PMEM_NUM_MAX]; +static unsigned int pmem_size[PMEM_NUM_MAX]; +static int pmem_cache[PMEM_NUM_MAX], pmem_no_allocator[PMEM_NUM_MAX]; +static int pmem_num; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bin Yang "); +MODULE_DESCRIPTION("PMEM Module"); +MODULE_PARM_DESC(pmem_name, "pmem device name"); +MODULE_PARM_DESC(pmem_size, "pmem memory size"); +MODULE_PARM_DESC(pmem_cache, "pmem memory cacheable attribute"); +MODULE_PARM_DESC(pmem_no_allocator, "pmem does not allocate"); +module_param_array(pmem_name, charp, &pmem_num, 0); +module_param_array(pmem_size, uint, NULL, 0); +module_param_array(pmem_cache, int, NULL, 0); +module_param_array(pmem_no_allocator, int, NULL, 0); +/*usage sample: + * insmod pmem.ko pmem_name="pmem","pmem_dsp" pmem_size=0x800000,0x500000 pmem_ + * no_allocator=1,0 pmem_cache=0,1 + * +*/ + +void android_add_pmem(char *name, size_t size, int no_allocator, int cached); + +static int __init pmem_init(void) +{ + int i; + pr_info("PMEM: create %d pmem devices\n", pmem_num); + + for(i=0; i, size: 0x%x, no_allocator: %d, cacheable: %d \n", + pmem_name[i], pmem_size[i], pmem_no_allocator[i], pmem_cache[i]); + android_add_pmem(pmem_name[i], pmem_size[i], pmem_no_allocator[i], pmem_cache[i]); + } + return 0; +} + +static void __exit pmem_exit(void) +{ + pr_info("PMEM remove is not supported\n"); +} + +module_init(pmem_init); +module_exit(pmem_exit); + diff --git a/arch/arm/plat-pxa/pwm.c b/arch/arm/plat-pxa/pwm.c index 0732c6c8d51197..f0b75236c5b6eb 100644 --- a/arch/arm/plat-pxa/pwm.c +++ b/arch/arm/plat-pxa/pwm.c @@ -14,26 +14,13 @@ #include #include #include -#include #include #include #include #include #include - -#define HAS_SECONDARY_PWM 0x10 -#define PWM_ID_BASE(d) ((d) & 0xf) - -static const struct platform_device_id pwm_id_table[] = { - /* PWM has_secondary_pwm? */ - { "pxa25x-pwm", 0 }, - { "pxa27x-pwm", 0 | HAS_SECONDARY_PWM }, - { "pxa168-pwm", 1 }, - { "pxa910-pwm", 1 }, - { }, -}; -MODULE_DEVICE_TABLE(platform, pwm_id_table); +#include /* PWM registers and bits definitions */ #define PWMCR (0x00) @@ -45,8 +32,7 @@ MODULE_DEVICE_TABLE(platform, pwm_id_table); struct pwm_device { struct list_head node; - struct pwm_device *secondary; - struct platform_device *pdev; + struct platform_device *pdev; const char *label; struct clk *clk; @@ -90,11 +76,12 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) /* NOTE: the clock to PWM has to be enabled first * before writing to the registers */ - clk_enable(pwm->clk); - __raw_writel(prescale, pwm->mmio_base + PWMCR); - __raw_writel(dc, pwm->mmio_base + PWMDCR); + pwm_enable(pwm); + __raw_writel(prescale|0x040, pwm->mmio_base + PWMCR); __raw_writel(pv, pwm->mmio_base + PWMPCR); - clk_disable(pwm->clk); + __raw_writel(dc, pwm->mmio_base + PWMDCR); + + pwm_disable(pwm); return 0; } @@ -174,20 +161,20 @@ static inline void __add_pwm(struct pwm_device *pwm) mutex_unlock(&pwm_lock); } -static int __devinit pwm_probe(struct platform_device *pdev) +static struct pwm_device *pwm_probe(struct platform_device *pdev, + unsigned int pwm_id, struct pwm_device *parent_pwm) { - struct platform_device_id *id = platform_get_device_id(pdev); - struct pwm_device *pwm, *secondary = NULL; + struct pwm_device *pwm; struct resource *r; int ret = 0; pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); if (pwm == NULL) { dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } - pwm->clk = clk_get(&pdev->dev, NULL); + pwm->clk = clk_get(&pdev->dev, "PWMCLK"); if (IS_ERR(pwm->clk)) { ret = PTR_ERR(pwm->clk); goto err_free; @@ -195,9 +182,16 @@ static int __devinit pwm_probe(struct platform_device *pdev) pwm->clk_enabled = 0; pwm->use_count = 0; - pwm->pwm_id = PWM_ID_BASE(id->driver_data) + pdev->id; + pwm->pwm_id = pwm_id; pwm->pdev = pdev; + if (parent_pwm != NULL) { + /* registers for the second PWM has offset of 0x10 */ + pwm->mmio_base = parent_pwm->mmio_base + 0x10; + __add_pwm(pwm); + return pwm; + } + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&pdev->dev, "no memory resource defined\n"); @@ -205,49 +199,66 @@ static int __devinit pwm_probe(struct platform_device *pdev) goto err_free_clk; } - r = request_mem_region(r->start, resource_size(r), pdev->name); + r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); if (r == NULL) { dev_err(&pdev->dev, "failed to request memory resource\n"); ret = -EBUSY; goto err_free_clk; } - pwm->mmio_base = ioremap(r->start, resource_size(r)); + pwm->mmio_base = ioremap(r->start, r->end - r->start + 1); if (pwm->mmio_base == NULL) { dev_err(&pdev->dev, "failed to ioremap() registers\n"); ret = -ENODEV; goto err_free_mem; } - if (id->driver_data & HAS_SECONDARY_PWM) { - secondary = kzalloc(sizeof(struct pwm_device), GFP_KERNEL); - if (secondary == NULL) { - ret = -ENOMEM; - goto err_free_mem; - } - - *secondary = *pwm; - pwm->secondary = secondary; - - /* registers for the second PWM has offset of 0x10 */ - secondary->mmio_base = pwm->mmio_base + 0x10; - secondary->pwm_id = pdev->id + 2; - } - __add_pwm(pwm); - if (secondary) - __add_pwm(secondary); - platform_set_drvdata(pdev, pwm); - return 0; + return pwm; err_free_mem: - release_mem_region(r->start, resource_size(r)); + release_mem_region(r->start, r->end - r->start + 1); err_free_clk: clk_put(pwm->clk); err_free: kfree(pwm); - return ret; + return ERR_PTR(ret); +} + +static int __devinit pxa25x_pwm_probe(struct platform_device *pdev) +{ + struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL); + + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + return 0; +} + +static int __devinit pxa27x_pwm_probe(struct platform_device *pdev) +{ + struct pwm_device *pwm; + + pwm = pwm_probe(pdev, pdev->id, NULL); + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + pwm = pwm_probe(pdev, pdev->id + 2, pwm); + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + return 0; +} + +static int __devinit pxa168_pwm_probe(struct platform_device *pdev) +{ + struct pwm_device *pwm = pwm_probe(pdev, pdev->id, NULL); + + if (IS_ERR(pwm)) + return PTR_ERR(pwm); + + return 0; } static int __devexit pwm_remove(struct platform_device *pdev) @@ -260,44 +271,74 @@ static int __devexit pwm_remove(struct platform_device *pdev) return -ENODEV; mutex_lock(&pwm_lock); - - if (pwm->secondary) { - list_del(&pwm->secondary->node); - kfree(pwm->secondary); - } - list_del(&pwm->node); mutex_unlock(&pwm_lock); iounmap(pwm->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); + release_mem_region(r->start, r->end - r->start + 1); clk_put(pwm->clk); kfree(pwm); return 0; } -static struct platform_driver pwm_driver = { +static struct platform_driver pxa25x_pwm_driver = { .driver = { .name = "pxa25x-pwm", - .owner = THIS_MODULE, }, - .probe = pwm_probe, + .probe = pxa25x_pwm_probe, + .remove = __devexit_p(pwm_remove), +}; + +static struct platform_driver pxa27x_pwm_driver = { + .driver = { + .name = "pxa27x-pwm", + }, + .probe = pxa27x_pwm_probe, + .remove = __devexit_p(pwm_remove), +}; + +static struct platform_driver pxa168_pwm_driver = { + .driver = { + .name = "pxa168-pwm", + }, + .probe = pxa168_pwm_probe, .remove = __devexit_p(pwm_remove), - .id_table = pwm_id_table, }; static int __init pwm_init(void) { - return platform_driver_register(&pwm_driver); + int ret = 0; + + ret = platform_driver_register(&pxa25x_pwm_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa25x_pwm_driver\n"); + return ret; + } + + ret = platform_driver_register(&pxa27x_pwm_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa27x_pwm_driver\n"); + return ret; + } + + ret = platform_driver_register(&pxa168_pwm_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa168_pwm_driver\n"); + return ret; + } + + return ret; } arch_initcall(pwm_init); static void __exit pwm_exit(void) { - platform_driver_unregister(&pwm_driver); + platform_driver_unregister(&pxa25x_pwm_driver); + platform_driver_unregister(&pxa27x_pwm_driver); + platform_driver_unregister(&pxa168_pwm_driver); } module_exit(pwm_exit); diff --git a/arch/arm/plat-pxa/pxa3xx_pmic.c b/arch/arm/plat-pxa/pxa3xx_pmic.c new file mode 100644 index 00000000000000..937c6aac4420dd --- /dev/null +++ b/arch/arm/plat-pxa/pxa3xx_pmic.c @@ -0,0 +1,298 @@ +/* + * Monahans PMIC abstrction layer + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ + +#include + +#include + +static struct pmic_ops *pxa3xx_pmic_ops; + +#ifdef DEBUG +/* calculate the elapsed time on operating PMIC */ +static unsigned int start_time, end_time; +void start_calc_time(void) +{ + start_time = OSCR; +} + +void end_calc_time(void) +{ + unsigned int time; + end_time = OSCR + time = (end_time - start_time) * 100 / 325; + + pr_debug("\n%s:\t:%dus\n", __func__, time); +} +#else +void start_calc_time(void) {} +void end_calc_time(void) {} +#endif + +void pmic_set_ops(struct pmic_ops *ops) +{ + if (pxa3xx_pmic_ops != NULL) { + printk(KERN_ERR "set pmic_ops when pmic_ops is not NULL\n"); + return; + } + pxa3xx_pmic_ops = ops; + INIT_LIST_HEAD(&pxa3xx_pmic_ops->list); + spin_lock_init(&pxa3xx_pmic_ops->cb_lock); +} + +/***************************************************************************** + * Operation of PMIC * + *****************************************************************************/ +int check_pmic_ops(void) +{ + if (!pxa3xx_pmic_ops) { + printk(KERN_WARNING "No pmic_ops registered!\n"); + return -EINVAL; + } else + return 0; +} + +int pxa3xx_pmic_get_voltage(int cmd, int *pval) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + if (pxa3xx_pmic_ops->get_voltage) + return pxa3xx_pmic_ops->get_voltage(cmd, pval); + else + return -EINVAL; +} +EXPORT_SYMBOL(pxa3xx_pmic_get_voltage); + +int pxa3xx_pmic_set_voltage(int cmd, int val) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + if (pxa3xx_pmic_ops->set_voltage) + return pxa3xx_pmic_ops->set_voltage(cmd, val); + else + return -EINVAL; +} +EXPORT_SYMBOL(pxa3xx_pmic_set_voltage); + +int pxa3xx_pmic_is_vbus_assert(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) /* If illegal pmic_ops, always no vbus activity */ + return 0; + + if (pxa3xx_pmic_ops->is_vbus_assert) + return pxa3xx_pmic_ops->is_vbus_assert(); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_is_vbus_assert); + +int pxa3xx_pmic_is_avbusvld(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) /* If illegal pmic_ops, always no A vbus valid */ + return 0; + + if (pxa3xx_pmic_ops->is_avbusvld) + return pxa3xx_pmic_ops->is_avbusvld(); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_is_avbusvld); + +int pxa3xx_pmic_is_asessvld(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) /* If illegal pmic_ops, always no A assert valid */ + return 0; + + if (pxa3xx_pmic_ops->is_asessvld) + return pxa3xx_pmic_ops->is_asessvld(); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_is_asessvld); + +int pxa3xx_pmic_is_bsessvld(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) /* If illegal pmic_ops, always no B assert valid */ + return 0; + + if (pxa3xx_pmic_ops->is_bsessvld) + return pxa3xx_pmic_ops->is_bsessvld(); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_is_bsessvld); + +int pxa3xx_pmic_is_srp_ready(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) /* If illegal pmic_ops, always no SRP detect */ + return 0; + + if (pxa3xx_pmic_ops->is_srp_ready) + return pxa3xx_pmic_ops->is_srp_ready(); + + return 0; + +} +EXPORT_SYMBOL(pxa3xx_pmic_is_srp_ready); + +int pxa3xx_pmic_set_pump(int enable) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + if (pxa3xx_pmic_ops->set_pump) + return pxa3xx_pmic_ops->set_pump(enable); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_set_pump); + +int pxa3xx_pmic_set_vbus_supply(int enable, int srp) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + if (pxa3xx_pmic_ops->set_vbus_supply) + return pxa3xx_pmic_ops->set_vbus_supply(enable, srp); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_set_vbus_supply); + +int pxa3xx_pmic_set_usbotg_a_mask(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + if (pxa3xx_pmic_ops->set_usbotg_a_mask) + return pxa3xx_pmic_ops->set_usbotg_a_mask(); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_set_usbotg_a_mask); + +int pxa3xx_pmic_set_usbotg_b_mask(void) +{ + int ret; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + if (pxa3xx_pmic_ops->set_usbotg_b_mask) + return pxa3xx_pmic_ops->set_usbotg_b_mask(); + + return 0; +} +EXPORT_SYMBOL(pxa3xx_pmic_set_usbotg_b_mask); + +/* Register pmic callback */ +int pmic_callback_register(unsigned long event, + void (*func)(unsigned long event)) +{ + int ret; + unsigned long flags; + struct pmic_callback *pmic_cb; + + might_sleep(); + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + pmic_cb = kzalloc(sizeof(*pmic_cb), GFP_KERNEL); + if (!pmic_cb) + return -ENOMEM; + + INIT_LIST_HEAD(&pmic_cb->list); + pmic_cb->event = event; + pmic_cb->func = func; + + spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags); + list_add(&pmic_cb->list, &pxa3xx_pmic_ops->list); + spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags); + + return 0; +} +EXPORT_SYMBOL(pmic_callback_register); + +/* Unregister pmic callback */ +int pmic_callback_unregister(unsigned long event, + void (*func)(unsigned long event)) +{ + unsigned long flags; + struct pmic_callback *pmic_cb, *next; + + spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags); + list_for_each_entry_safe(pmic_cb, next, &pxa3xx_pmic_ops->list, list) { + if ((pmic_cb->event == event) && (pmic_cb->func == func)) { + list_del_init(&pmic_cb->list); + kfree(pmic_cb); + } + } + spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags); + return 0; +} +EXPORT_SYMBOL(pmic_callback_unregister); + +int pmic_event_handle(unsigned long event) +{ + int ret; + unsigned long flags; + struct pmic_callback *pmic_cb; + + ret = check_pmic_ops(); + if (ret < 0) + return ret; + + spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags); + list_for_each_entry(pmic_cb, &pxa3xx_pmic_ops->list, list) { + spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags); + /* event is bit-wise parameter, need bit AND here as filter */ + if ((pmic_cb->event & event) && (pmic_cb->func)) + pmic_cb->func(event); + spin_lock_irqsave(&pxa3xx_pmic_ops->cb_lock, flags); + } + spin_unlock_irqrestore(&pxa3xx_pmic_ops->cb_lock, flags); + return 0; +} +EXPORT_SYMBOL(pmic_event_handle); + diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/plat-pxa/ssp.c similarity index 82% rename from arch/arm/mach-pxa/ssp.c rename to arch/arm/plat-pxa/ssp.c index a81d6dbf662db5..c0fea4ecab088e 100644 --- a/arch/arm/mach-pxa/ssp.c +++ b/arch/arm/plat-pxa/ssp.c @@ -32,8 +32,8 @@ #include #include -#include -#include +#include +#include #ifdef CONFIG_PXA_SSP_LEGACY @@ -79,8 +79,8 @@ int ssp_write_word(struct ssp_dev *dev, u32 data) int timeout = TIMEOUT; while (!(__raw_readl(ssp->mmio_base + SSSR) & SSSR_TNF)) { - if (!--timeout) - return -ETIMEDOUT; + if (!--timeout) + return -ETIMEDOUT; cpu_relax(); } @@ -110,8 +110,8 @@ int ssp_read_word(struct ssp_dev *dev, u32 *data) int timeout = TIMEOUT; while (!(__raw_readl(ssp->mmio_base + SSSR) & SSSR_RNE)) { - if (!--timeout) - return -ETIMEDOUT; + if (!--timeout) + return -ETIMEDOUT; cpu_relax(); } @@ -144,12 +144,12 @@ int ssp_flush(struct ssp_dev *dev) do { while (__raw_readl(ssp->mmio_base + SSSR) & SSSR_RNE) { - if (!--timeout) - return -ETIMEDOUT; + if (!--timeout) + return -ETIMEDOUT; (void)__raw_readl(ssp->mmio_base + SSDR); } - if (!--timeout) - return -ETIMEDOUT; + if (!--timeout) + return -ETIMEDOUT; } while (__raw_readl(ssp->mmio_base + SSSR) & SSSR_BSY); return 0; @@ -275,7 +275,7 @@ int ssp_init(struct ssp_dev *dev, u32 port, u32 init_flags) if (!(init_flags & SSP_NO_IRQ)) { ret = request_irq(ssp->irq, ssp_interrupt, 0, "SSP", dev); - if (ret) + if (ret) goto out_region; dev->irq = ssp->irq; } else @@ -345,9 +345,8 @@ void ssp_free(struct ssp_device *ssp) } EXPORT_SYMBOL(ssp_free); -static int __devinit ssp_probe(struct platform_device *pdev) +static int __devinit ssp_probe(struct platform_device *pdev, int type) { - const struct platform_device_id *id = platform_get_device_id(pdev); struct resource *res; struct ssp_device *ssp; int ret = 0; @@ -359,7 +358,7 @@ static int __devinit ssp_probe(struct platform_device *pdev) } ssp->pdev = pdev; - ssp->clk = clk_get(&pdev->dev, NULL); + ssp->clk = clk_get(&pdev->dev, "SSPCLK"); if (IS_ERR(ssp->clk)) { ret = PTR_ERR(ssp->clk); goto err_free; @@ -417,7 +416,7 @@ static int __devinit ssp_probe(struct platform_device *pdev) */ ssp->port_id = pdev->id + 1; ssp->use_count = 0; - ssp->type = (int)id->driver_data; + ssp->type = type; mutex_lock(&ssp_lock); list_add(&ssp->node, &ssp_list); @@ -461,31 +460,96 @@ static int __devexit ssp_remove(struct platform_device *pdev) return 0; } -static const struct platform_device_id ssp_id_table[] = { - { "pxa25x-ssp", PXA25x_SSP }, - { "pxa25x-nssp", PXA25x_NSSP }, - { "pxa27x-ssp", PXA27x_SSP }, - { }, -}; +static int __devinit pxa25x_ssp_probe(struct platform_device *pdev) +{ + return ssp_probe(pdev, PXA25x_SSP); +} + +static int __devinit pxa25x_nssp_probe(struct platform_device *pdev) +{ + return ssp_probe(pdev, PXA25x_NSSP); +} + +static int __devinit pxa27x_ssp_probe(struct platform_device *pdev) +{ + return ssp_probe(pdev, PXA27x_SSP); +} + +static int __devinit pxa168_ssp_probe(struct platform_device *pdev) +{ + return ssp_probe(pdev, PXA168_SSP); +} -static struct platform_driver ssp_driver = { - .probe = ssp_probe, +static struct platform_driver pxa25x_ssp_driver = { + .driver = { + .name = "pxa25x-ssp", + }, + .probe = pxa25x_ssp_probe, .remove = __devexit_p(ssp_remove), +}; + +static struct platform_driver pxa25x_nssp_driver = { + .driver = { + .name = "pxa25x-nssp", + }, + .probe = pxa25x_nssp_probe, + .remove = __devexit_p(ssp_remove), +}; + +static struct platform_driver pxa27x_ssp_driver = { + .driver = { + .name = "pxa27x-ssp", + }, + .probe = pxa27x_ssp_probe, + .remove = __devexit_p(ssp_remove), +}; + +static struct platform_driver pxa168_ssp_driver = { .driver = { - .owner = THIS_MODULE, - .name = "pxa2xx-ssp", + .name = "pxa168-ssp", }, - .id_table = ssp_id_table, + .probe = pxa168_ssp_probe, + .remove = __devexit_p(ssp_remove), }; static int __init pxa_ssp_init(void) { - return platform_driver_register(&ssp_driver); + int ret = 0; + + ret = platform_driver_register(&pxa25x_ssp_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa25x_ssp_driver"); + return ret; + } + + ret = platform_driver_register(&pxa25x_nssp_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa25x_nssp_driver"); + return ret; + } + + ret = platform_driver_register(&pxa27x_ssp_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa27x_ssp_driver"); + return ret; + } + + printk(KERN_ERR "platform_driver_register\n"); + ret = platform_driver_register(&pxa168_ssp_driver); + if (ret) { + printk(KERN_ERR "failed to register pxa168_ssp_driver"); + return ret; + } + + return ret; } static void __exit pxa_ssp_exit(void) { - platform_driver_unregister(&ssp_driver); + platform_driver_unregister(&pxa25x_ssp_driver); + platform_driver_unregister(&pxa25x_nssp_driver); + platform_driver_unregister(&pxa27x_ssp_driver); + platform_driver_unregister(&pxa168_ssp_driver); } arch_initcall(pxa_ssp_init); diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index 8f10d24ae62540..d024d32af9ea53 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -1319,7 +1319,7 @@ mistral MACH_MISTRAL MISTRAL 1315 msm MACH_MSM MSM 1316 ct5910 MACH_CT5910 CT5910 1317 ct5912 MACH_CT5912 CT5912 1318 -hynet_ine MACH_HYNET_INE HYNET_INE 1319 +argonst_foundation MACH_HYNET_INE HYNET_INE 1319 hynet_app MACH_HYNET_APP HYNET_APP 1320 msm7200 MACH_MSM7200 MSM7200 1321 msm7600 MACH_MSM7600 MSM7600 1322 @@ -2077,7 +2077,8 @@ drp255 MACH_DRP255 DRP255 2085 thoth MACH_THOTH THOTH 2086 firestone MACH_FIRESTONE FIRESTONE 2087 asusp750 MACH_ASUSP750 ASUSP750 2088 -ctera_dl MACH_CTERA_DL CTERA_DL 2089 +#ctera_dl MACH_CTERA_DL CTERA_DL 2089 +dkb_generic MACH_DKB_GENERIC DKB_GENERIC 2089 socr MACH_SOCR SOCR 2090 htcoxygen MACH_HTCOXYGEN HTCOXYGEN 2091 heroc MACH_HEROC HEROC 2092 @@ -2125,7 +2126,9 @@ mx27wallace MACH_MX27WALLACE MX27WALLACE 2133 fmzwebmodul MACH_FMZWEBMODUL FMZWEBMODUL 2134 rd78x00_masa MACH_RD78X00_MASA RD78X00_MASA 2135 smallogger MACH_SMALLOGGER SMALLOGGER 2136 -ccw9p9215 MACH_CCW9P9215 CCW9P9215 2137 +# Temporary fix to support IPCAM +# ccw9p9215 MACH_CCW9P9215 CCW9P9215 2137 +ipcam MACH_IPCAM IPCAM 2137 dm355_leopard MACH_DM355_LEOPARD DM355_LEOPARD 2138 ts219 MACH_TS219 TS219 2139 tny_a9263 MACH_TNY_A9263 TNY_A9263 2140 @@ -2539,7 +2542,9 @@ esyx MACH_ESYX ESYX 2551 dove_db2 MACH_DOVE_DB2 DOVE_DB2 2552 bulldog MACH_BULLDOG BULLDOG 2553 derell_me2000 MACH_DERELL_ME2000 DERELL_ME2000 2554 -bcmring_base MACH_BCMRING_BASE BCMRING_BASE 2555 +# Temporary change to support edge board!!!. +# bcmring_base MACH_BCMRING_BASE BCMRING_BASE 2555 +edge MACH_EDGE EDGE 2555 bcmring_evm MACH_BCMRING_EVM BCMRING_EVM 2556 bcmring_evm_jazz MACH_BCMRING_EVM_JAZZ BCMRING_EVM_JAZZ 2557 bcmring_sp MACH_BCMRING_SP BCMRING_SP 2558 diff --git a/block/blk-core.c b/block/blk-core.c index 9fe174dc74d1df..311dcd83deb96d 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1566,11 +1566,12 @@ void submit_bio(int rw, struct bio *bio) if (unlikely(block_dump)) { char b[BDEVNAME_SIZE]; - printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n", + printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)\n", current->comm, task_pid_nr(current), (rw & WRITE) ? "WRITE" : "READ", (unsigned long long)bio->bi_sector, - bdevname(bio->bi_bdev, b)); + bdevname(bio->bi_bdev, b), + count); } } diff --git a/drivers/Kconfig b/drivers/Kconfig index a2b902f4d43706..6781b709cac595 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -88,6 +88,8 @@ source "drivers/memstick/Kconfig" source "drivers/leds/Kconfig" +source "drivers/switch/Kconfig" + source "drivers/accessibility/Kconfig" source "drivers/infiniband/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index f42a03029b7c01..e5213b685cc3ea 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -95,6 +95,7 @@ obj-y += idle/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-$(CONFIG_NEW_LEDS) += leds/ +obj-$(CONFIG_SWITCH) += switch/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 49cffb6094a318..c441d1f8aecc64 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6295,7 +6295,9 @@ int ata_host_activate(struct ata_host *host, int irq, /* Special case for polling mode */ if (!irq) { +#ifndef CONFIG_PXA168_CF WARN_ON(irq_handler); +#endif return ata_host_register(host, sht); } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 823e630963627f..20ffc7d048b235 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -208,4 +208,29 @@ extern u8 ata_irq_on(struct ata_port *ap); extern void ata_pio_task(struct work_struct *work); #endif /* CONFIG_ATA_SFF */ +#ifdef CONFIG_PXA168_CF +extern void pxa168_cf_mem_writeb(u8 attr_mem_transfer, u8 val, u32 addr); +extern u8 pxa168_cf_mem_readb(u8 attr_mem_transfer, u32 addr); +extern void pxa168_cf_mem_writew(u8 attr_mem_transfer, u16 val, u32 addr); +extern u32 pxa168_cf_mem_readw(u8 attr_mem_transfer, u32 addr); +extern void pxa168_cf_mem_write(u8 attr_mem_transfer, u8* buf, u32 addr, u32 words); +extern void pxa168_cf_mem_read(u8 attr_mem_transfer, u8* buf, u32 addr, u32 words); + +/* XXX needs to be refactored!!! XXX */ + +#undef ioread8 +#undef iowrite8 +#undef ioread16 +#undef iowrite16 +#undef ioread16_rep +#undef iowrite16_rep + +#define ioread8(addr) pxa168_cf_mem_readb(0,((u32)addr)) +#define iowrite8(val,addr) pxa168_cf_mem_writeb(0,(val),((u32)addr)) +#define ioread16(addr) pxa168_cf_mem_readw(0,((u32)addr)) +#define iowrite16(buf,addr) pxa168_cf_mem_writew(0,(buf),((u32)addr)) +#define ioread16_rep(addr,buf,words) pxa168_cf_mem_read(0,(buf),((u32)addr),(words)) +#define iowrite16_rep(addr,buf,words) pxa168_cf_mem_write(0,(buf),((u32)addr),(words)) +#endif /* CONFIG_PXA168_CF */ + #endif /* __LIBATA_H__ */ diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index d94b8f0bd743b7..f730e06fac7058 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -45,6 +45,11 @@ #define DRV_NAME "pata_pcmcia" #define DRV_VERSION "0.3.5" +#ifdef CONFIG_PXA168_CF +extern void pxa168_cf_mem_writeb(u8 attr_mem_transfer, u8 val, u32 addr); +extern u8 pxa168_cf_mem_readb(u8 attr_mem_transfer, u32 addr); +#endif + /* * Private data structure to glue stuff together */ @@ -291,27 +296,53 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) if (pcmcia_loop_config(pdev, pcmcia_check_one_config, stk)) goto failed; /* No suitable config found */ } +#ifndef CONFIG_PXA168_CF io_base = pdev->io.BasePort1; ctl_base = stk->ctl_base; +#else + io_base = 0x0; + ctl_base = 0x0e; +#endif + +#ifndef CONFIG_PXA168_CF ret = pcmcia_request_irq(pdev, &pdev->irq); if (ret) goto failed; +#endif ret = pcmcia_request_configuration(pdev, &pdev->conf); if (ret) goto failed; +#ifdef CONFIG_PXA168_CF + pxa168_cf_mem_writeb(1, 0x0, 0x200); +#endif /* iomap */ ret = -ENOMEM; +#ifndef CONFIG_PXA168_CF io_addr = devm_ioport_map(&pdev->dev, io_base, 8); ctl_addr = devm_ioport_map(&pdev->dev, ctl_base, 1); if (!io_addr || !ctl_addr) goto failed; +#else + io_addr = (void *) io_base; + ctl_addr = (void *) ctl_base; +#endif /* Success. Disable the IRQ nIEN line, do quirks */ + +#ifndef CONFIG_PXA168_CF iowrite8(0x02, ctl_addr); +#else + pxa168_cf_mem_writeb(0, 0x02, (u32)ctl_addr); +#endif + if (is_kme) +#ifndef CONFIG_PXA168_CF iowrite8(0x81, ctl_addr + 0x01); +#else + pxa168_cf_mem_writeb(0, 0x81, (u32)(ctl_addr + 0x01)); +#endif /* FIXME: Could be more ports at base + 0x10 but we only deal with one right now */ @@ -335,6 +366,9 @@ static int pcmcia_init_one(struct pcmcia_device *pdev) ap->ops = ops; ap->pio_mask = ATA_PIO0; /* ISA so PIO 0 cycles */ ap->flags |= ATA_FLAG_SLAVE_POSS; +#ifdef CONFIG_PXA168_CF + ap->flags |= ATA_FLAG_PIO_POLLING; +#endif ap->ioaddr.cmd_addr = io_addr + 0x10 * p; ap->ioaddr.altstatus_addr = ctl_addr + 0x10 * p; ap->ioaddr.ctl_addr = ctl_addr + 0x10 * p; diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 77bfce52e9cabc..fec85abbe41890 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -488,4 +488,10 @@ config BLK_DEV_HD If unsure, say N. +config PXA168_MSP + bool "Marvell MSPRO MFP support" + default n + help + This provide mspro MFP configuration function + endif # BLK_DEV diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 3141dd3b6e530f..bf35630be8ed31 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -88,6 +88,19 @@ config VT_HW_CONSOLE_BINDING information. For framebuffer console users, please refer to . +config DEVMEM + bool "Memory device driver" + default y + help + The memory driver provides two character devices, mem and kmem, which + provide access to the system's memory. The mem device is a view of + physical memory, and each byte in the device corresponds to the + matching physical address. The kmem device is the same as mem, but + the addresses correspond to the kernel's virtual address space rather + than physical memory. These devices are standard parts of a Linux + system and most users should say Y here. You might say N if very + security conscience or memory is tight. + config DEVKMEM bool "/dev/kmem virtual device support" default y @@ -1111,7 +1124,31 @@ config DEVPORT depends on ISA || PCI default y +config DCC_TTY + tristate "DCC tty driver" + depends on ARM + source "drivers/s390/char/Kconfig" +config PXA930_ACIPC + tristate "PXA930 AC-IPC Driver" + depends on CPU_PXA930 || CPU_PXA910 + help + Please say Y here if want to support Application to Communication + Intel-Processor Communication on PXA930 chip + +config PXA910_IRE + tristate "image rotation engine" + depends on CPU_PXA910 + help + Please say Y here if want to support image rotation engine + +source "drivers/char/icr/Kconfig" + +config PXA_CNM + tristate "chip and media codaDx8 codec driver" + help + Please say Y here if want to support chip and media codec + endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f957edf7e45d20..1dfbabbe7065e2 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -106,11 +106,18 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o obj-$(CONFIG_TCG_TPM) += tpm/ +obj-$(CONFIG_DCC_TTY) += dcc_tty.o obj-$(CONFIG_PS3_FLASH) += ps3flash.o obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o +obj-$(CONFIG_IMM) += imm.o +obj-$(CONFIG_PXA930_ACIPC) += pxa930_acipc.o +obj-$(CONFIG_PXA910_IRE) += pxa910_ire.o +obj-$(CONFIG_PXA_ICR) += icr/ +obj-$(CONFIG_PXA_CNM) += pxa-cnm.o + # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.c diff --git a/drivers/char/dcc_tty.c b/drivers/char/dcc_tty.c new file mode 100644 index 00000000000000..a787accdcb14b2 --- /dev/null +++ b/drivers/char/dcc_tty.c @@ -0,0 +1,326 @@ +/* drivers/char/dcc_tty.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 +#include +#include + +MODULE_DESCRIPTION("DCC TTY Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static spinlock_t g_dcc_tty_lock = SPIN_LOCK_UNLOCKED; +static struct hrtimer g_dcc_timer; +static char g_dcc_buffer[16]; +static int g_dcc_buffer_head; +static int g_dcc_buffer_count; +static unsigned g_dcc_write_delay_usecs = 1; +static struct tty_driver *g_dcc_tty_driver; +static struct tty_struct *g_dcc_tty; +static int g_dcc_tty_open_count; + +static void dcc_poll_locked(void) +{ + char ch; + int rch; + int written; + + while (g_dcc_buffer_count) { + ch = g_dcc_buffer[g_dcc_buffer_head]; + asm( + "mrc 14, 0, r15, c0, c1, 0\n" + "mcrcc 14, 0, %1, c0, c5, 0\n" + "movcc %0, #1\n" + "movcs %0, #0\n" + : "=r" (written) + : "r" (ch) + ); + if (written) { + if (ch == '\n') + g_dcc_buffer[g_dcc_buffer_head] = '\r'; + else { + g_dcc_buffer_head = (g_dcc_buffer_head + 1) % ARRAY_SIZE(g_dcc_buffer); + g_dcc_buffer_count--; + if (g_dcc_tty) + tty_wakeup(g_dcc_tty); + } + g_dcc_write_delay_usecs = 1; + } else { + if (g_dcc_write_delay_usecs > 0x100) + break; + g_dcc_write_delay_usecs <<= 1; + udelay(g_dcc_write_delay_usecs); + } + } + + if (g_dcc_tty && !test_bit(TTY_THROTTLED, &g_dcc_tty->flags)) { + asm( + "mrc 14, 0, %0, c0, c1, 0\n" + "tst %0, #(1 << 30)\n" + "moveq %0, #-1\n" + "mrcne 14, 0, %0, c0, c5, 0\n" + : "=r" (rch) + ); + if (rch >= 0) { + ch = rch; + tty_insert_flip_string(g_dcc_tty, &ch, 1); + tty_flip_buffer_push(g_dcc_tty); + } + } + + + if (g_dcc_buffer_count) + hrtimer_start(&g_dcc_timer, ktime_set(0, g_dcc_write_delay_usecs * NSEC_PER_USEC), HRTIMER_MODE_REL); + else + hrtimer_start(&g_dcc_timer, ktime_set(0, 20 * NSEC_PER_MSEC), HRTIMER_MODE_REL); +} + +static int dcc_tty_open(struct tty_struct * tty, struct file * filp) +{ + int ret; + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + if (g_dcc_tty == NULL || g_dcc_tty == tty) { + g_dcc_tty = tty; + g_dcc_tty_open_count++; + ret = 0; + } else + ret = -EBUSY; + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + + printk("dcc_tty_open, tty %p, f_flags %x, returned %d\n", tty, filp->f_flags, ret); + + return ret; +} + +static void dcc_tty_close(struct tty_struct * tty, struct file * filp) +{ + printk("dcc_tty_close, tty %p, f_flags %x\n", tty, filp->f_flags); + if (g_dcc_tty == tty) { + if (--g_dcc_tty_open_count == 0) + g_dcc_tty = NULL; + } +} + +static int dcc_write(const unsigned char *buf_start, int count) +{ + const unsigned char *buf = buf_start; + unsigned long irq_flags; + int copy_len; + int space_left; + int tail; + + if (count < 1) + return 0; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + do { + tail = (g_dcc_buffer_head + g_dcc_buffer_count) % ARRAY_SIZE(g_dcc_buffer); + copy_len = ARRAY_SIZE(g_dcc_buffer) - tail; + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + if (copy_len > space_left) + copy_len = space_left; + if (copy_len > count) + copy_len = count; + memcpy(&g_dcc_buffer[tail], buf, copy_len); + g_dcc_buffer_count += copy_len; + buf += copy_len; + count -= copy_len; + if (copy_len < count && copy_len < space_left) { + space_left -= copy_len; + copy_len = count; + if (copy_len > space_left) { + copy_len = space_left; + } + memcpy(g_dcc_buffer, buf, copy_len); + buf += copy_len; + count -= copy_len; + g_dcc_buffer_count += copy_len; + } + dcc_poll_locked(); + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + } while(count && space_left); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return buf - buf_start; +} + +static int dcc_tty_write(struct tty_struct * tty, const unsigned char *buf, int count) +{ + int ret; + /* printk("dcc_tty_write %p, %d\n", buf, count); */ + ret = dcc_write(buf, count); + if (ret != count) + printk("dcc_tty_write %p, %d, returned %d\n", buf, count, ret); + return ret; +} + +static int dcc_tty_write_room(struct tty_struct *tty) +{ + int space_left; + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + space_left = ARRAY_SIZE(g_dcc_buffer) - g_dcc_buffer_count; + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return space_left; +} + +static int dcc_tty_chars_in_buffer(struct tty_struct *tty) +{ + int ret; + asm( + "mrc 14, 0, %0, c0, c1, 0\n" + "mov %0, %0, LSR #30\n" + "and %0, %0, #1\n" + : "=r" (ret) + ); + return ret; +} + +static void dcc_tty_unthrottle(struct tty_struct * tty) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + dcc_poll_locked(); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); +} + +static enum hrtimer_restart dcc_tty_timer_func(struct hrtimer *timer) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&g_dcc_tty_lock, irq_flags); + dcc_poll_locked(); + spin_unlock_irqrestore(&g_dcc_tty_lock, irq_flags); + return HRTIMER_NORESTART; +} + +void dcc_console_write(struct console *co, const char *b, unsigned count) +{ +#if 1 + dcc_write(b, count); +#else + /* blocking printk */ + while (count > 0) { + int written; + written = dcc_write(b, count); + if (written) { + b += written; + count -= written; + } + } +#endif +} + +static struct tty_driver *dcc_console_device(struct console *c, int *index) +{ + *index = 0; + return g_dcc_tty_driver; +} + +static int __init dcc_console_setup(struct console *co, char *options) +{ + if (co->index != 0) + return -ENODEV; + return 0; +} + + +static struct console dcc_console = +{ + .name = "ttyDCC", + .write = dcc_console_write, + .device = dcc_console_device, + .setup = dcc_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +static struct tty_operations dcc_tty_ops = { + .open = dcc_tty_open, + .close = dcc_tty_close, + .write = dcc_tty_write, + .write_room = dcc_tty_write_room, + .chars_in_buffer = dcc_tty_chars_in_buffer, + .unthrottle = dcc_tty_unthrottle, +}; + +static int __init dcc_tty_init(void) +{ + int ret; + + hrtimer_init(&g_dcc_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + g_dcc_timer.function = dcc_tty_timer_func; + + g_dcc_tty_driver = alloc_tty_driver(1); + if (!g_dcc_tty_driver) { + printk(KERN_ERR "dcc_tty_probe: alloc_tty_driver failed\n"); + ret = -ENOMEM; + goto err_alloc_tty_driver_failed; + } + g_dcc_tty_driver->owner = THIS_MODULE; + g_dcc_tty_driver->driver_name = "dcc"; + g_dcc_tty_driver->name = "ttyDCC"; + g_dcc_tty_driver->major = 0; // auto assign + g_dcc_tty_driver->minor_start = 0; + g_dcc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + g_dcc_tty_driver->subtype = SERIAL_TYPE_NORMAL; + g_dcc_tty_driver->init_termios = tty_std_termios; + g_dcc_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(g_dcc_tty_driver, &dcc_tty_ops); + ret = tty_register_driver(g_dcc_tty_driver); + if (ret) { + printk(KERN_ERR "dcc_tty_probe: tty_register_driver failed, %d\n", ret); + goto err_tty_register_driver_failed; + } + tty_register_device(g_dcc_tty_driver, 0, NULL); + + register_console(&dcc_console); + hrtimer_start(&g_dcc_timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + +err_tty_register_driver_failed: + put_tty_driver(g_dcc_tty_driver); + g_dcc_tty_driver = NULL; +err_alloc_tty_driver_failed: + return ret; +} + +static void __exit dcc_tty_exit(void) +{ + int ret; + + tty_unregister_device(g_dcc_tty_driver, 0); + ret = tty_unregister_driver(g_dcc_tty_driver); + if (ret < 0) { + printk(KERN_ERR "dcc_tty_remove: tty_unregister_driver failed, %d\n", ret); + } else { + put_tty_driver(g_dcc_tty_driver); + } + g_dcc_tty_driver = NULL; +} + +module_init(dcc_tty_init); +module_exit(dcc_tty_exit); + + diff --git a/drivers/char/icr/Kconfig b/drivers/char/icr/Kconfig new file mode 100644 index 00000000000000..f374079bd8e180 --- /dev/null +++ b/drivers/char/icr/Kconfig @@ -0,0 +1,10 @@ +menu "Color Management Unit" + +config PXA_ICR + tristate "PXA ICR driver" + default m + help + Add the color management driver for PXA + +endmenu + diff --git a/drivers/char/icr/Makefile b/drivers/char/icr/Makefile new file mode 100644 index 00000000000000..796cad5eebdbfc --- /dev/null +++ b/drivers/char/icr/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the linux kernel. +# + +obj-$(CONFIG_PXA_ICR) += pxa168_icr.o diff --git a/drivers/char/icr/pxa168_ICRioctlTable.h b/drivers/char/icr/pxa168_ICRioctlTable.h new file mode 100644 index 00000000000000..1de8f22a2bc3ec --- /dev/null +++ b/drivers/char/icr/pxa168_ICRioctlTable.h @@ -0,0 +1,368 @@ +/* + * linux/drivers/icr/pxa168_ICRioctlTable.h + * + * Copyright: (C) Copyright 2009 Marvell International Ltd. + * + * Author: Alan Guenther <> + * + * 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 + * publishhed by the Free Software Foundation. + * + */ + +#ifndef __PXA168_ICRIOCTLTABLE_H +#define __PXA168_ICRIOCTLTABLE_H + +/* table to convert userspace transform register request to + actual ICR registers address */ + +static u16 ICRtagTable[] = { + ICR_ICSC_M_C0_L, + ICR_ICSC_M_C0_H, + ICR_ICSC_M_C1_L, + ICR_ICSC_M_C1_H, + ICR_ICSC_M_C2_L, + ICR_ICSC_M_C2_H, + ICR_ICSC_M_C3_L, + ICR_ICSC_M_C3_H, + ICR_ICSC_M_C4_L, + ICR_ICSC_M_C4_H, + ICR_ICSC_M_C5_L, + ICR_ICSC_M_C5_H, + ICR_ICSC_M_C6_L, + ICR_ICSC_M_C6_H, + ICR_ICSC_M_C7_L, + ICR_ICSC_M_C7_H, + ICR_ICSC_M_C8_L, + ICR_ICSC_M_C8_H, + ICR_ICSC_M_O1_0, + ICR_ICSC_M_O1_1, + ICR_ICSC_M_O1_2, + ICR_ICSC_M_O2_0, + ICR_ICSC_M_O2_1, + ICR_ICSC_M_O2_2, + ICR_ICSC_M_O3_0, + ICR_ICSC_M_O3_1, + ICR_ICSC_M_O3_2, + ICR_ICSC_P_C0_L, + ICR_ICSC_P_C0_H, + ICR_ICSC_P_C1_L, + ICR_ICSC_P_C1_H, + ICR_ICSC_P_C2_L, + ICR_ICSC_P_C2_H, + ICR_ICSC_P_C3_L, + ICR_ICSC_P_C3_H, + ICR_ICSC_P_C4_L, + ICR_ICSC_P_C4_H, + ICR_ICSC_P_C5_L, + ICR_ICSC_P_C5_H, + ICR_ICSC_P_C6_L, + ICR_ICSC_P_C6_H, + ICR_ICSC_P_C7_L, + ICR_ICSC_P_C7_H, + ICR_ICSC_P_C8_L, + ICR_ICSC_P_C8_H, + ICR_ICSC_P_O1_0, + ICR_ICSC_P_O1_1, + ICR_ICSC_P_O1_2, + ICR_ICSC_P_O2_0, + ICR_ICSC_P_O2_1, + ICR_ICSC_P_O2_2, + ICR_ICSC_P_O3_0, + ICR_ICSC_P_O3_1, + ICR_ICSC_P_O3_2, + ICR_FTDC_M_EN, + ICR_FTDC_P_EN, + ICR_FTDC_INLOW_L, + ICR_FTDC_INLOW_H, + ICR_FTDC_INHIGH_L, + ICR_FTDC_INHIGH_H, + ICR_FTDC_OUTLOW_L, + ICR_FTDC_OUTLOW_H, + ICR_FTDC_OUTHIGH_L, + ICR_FTDC_OUTHIGH_H, + ICR_FTDC_YLOW, + ICR_FTDC_YHIGH, + ICR_FTDC_CH1, + ICR_FTDC_CH2_L, + ICR_FTDC_CH2_H, + ICR_FTDC_CH3_L, + ICR_FTDC_CH3_H, + ICR_FTDC_1_C00, + ICR_FTDC_2_C00, + ICR_FTDC_3_C00, + ICR_FTDC_4_C00, + ICR_FTDC_5_C00, + ICR_FTDC_6_C00, + ICR_FTDC_1_C01, + ICR_FTDC_2_C01, + ICR_FTDC_3_C01, + ICR_FTDC_4_C01, + ICR_FTDC_5_C01, + ICR_FTDC_6_C01, + ICR_FTDC_1_C11, + ICR_FTDC_2_C11, + ICR_FTDC_3_C11, + ICR_FTDC_4_C11, + ICR_FTDC_5_C11, + ICR_FTDC_6_C11, + ICR_FTDC_1_C10, + ICR_FTDC_2_C10, + ICR_FTDC_3_C10, + ICR_FTDC_4_C10, + ICR_FTDC_5_C10, + ICR_FTDC_6_C10, + ICR_FTDC_1_OFF00, + ICR_FTDC_2_OFF00, + ICR_FTDC_3_OFF00, + ICR_FTDC_4_OFF00, + ICR_FTDC_5_OFF00, + ICR_FTDC_6_OFF00, + ICR_FTDC_1_OFF10, + ICR_FTDC_2_OFF10, + ICR_FTDC_3_OFF10, + ICR_FTDC_4_OFF10, + ICR_FTDC_5_OFF10, + ICR_FTDC_6_OFF10, + ICR_HS_M_EN, + ICR_HS_M_AX1_L, + ICR_HS_M_AX1_H, + ICR_HS_M_AX2_L, + ICR_HS_M_AX2_H, + ICR_HS_M_AX3_L, + ICR_HS_M_AX3_H, + ICR_HS_M_AX4_L, + ICR_HS_M_AX4_H, + ICR_HS_M_AX5_L, + ICR_HS_M_AX5_H, + ICR_HS_M_AX6_L, + ICR_HS_M_AX6_H, + ICR_HS_M_AX7_L, + ICR_HS_M_AX7_H, + ICR_HS_M_AX8_L, + ICR_HS_M_AX8_H, + ICR_HS_M_AX9_L, + ICR_HS_M_AX9_H, + ICR_HS_M_AX10_L, + ICR_HS_M_AX10_H, + ICR_HS_M_AX11_L, + ICR_HS_M_AX11_H, + ICR_HS_M_AX12_L, + ICR_HS_M_AX12_H, + ICR_HS_M_AX13_L, + ICR_HS_M_AX13_H, + ICR_HS_M_AX14_L, + ICR_HS_M_AX14_H, + ICR_HS_M_H1, + ICR_HS_M_H2, + ICR_HS_M_H3, + ICR_HS_M_H4, + ICR_HS_M_H5, + ICR_HS_M_H6, + ICR_HS_M_H7, + ICR_HS_M_H8, + ICR_HS_M_H9, + ICR_HS_M_H10, + ICR_HS_M_H11, + ICR_HS_M_H12, + ICR_HS_M_H13, + ICR_HS_M_H14, + ICR_HS_M_S1, + ICR_HS_M_S2, + ICR_HS_M_S3, + ICR_HS_M_S4, + ICR_HS_M_S5, + ICR_HS_M_S6, + ICR_HS_M_S7, + ICR_HS_M_S8, + ICR_HS_M_S9, + ICR_HS_M_S10, + ICR_HS_M_S11, + ICR_HS_M_S12, + ICR_HS_M_S13, + ICR_HS_M_S14, + ICR_HS_M_GL, + ICR_HS_M_MAXSAT_RGB_Y_L, + ICR_HS_M_MAXSAT_RGB_Y_H, + ICR_HS_M_MAXSAT_RCR_L, + ICR_HS_M_MAXSAT_RCR_H, + ICR_HS_M_MAXSAT_RCB_L, + ICR_HS_M_MAXSAT_RCB_H, + ICR_HS_M_MAXSAT_GCR_L, + ICR_HS_M_MAXSAT_GCR_H, + ICR_HS_M_MAXSAT_GCB_L, + ICR_HS_M_MAXSAT_GCB_H, + ICR_HS_M_MAXSAT_BCR_L, + ICR_HS_M_MAXSAT_BCR_H, + ICR_HS_M_MAXSAT_BCB_L, + ICR_HS_M_MAXSAT_BCB_H, + ICR_HS_M_ROFF_L, + ICR_HS_M_ROFF_H, + ICR_HS_M_GOFF_L, + ICR_HS_M_GOFF_H, + ICR_HS_M_BOFF_L, + ICR_HS_M_BOFF_H, + ICR_HS_P_EN, + ICR_HS_P_AX1_L, + ICR_HS_P_AX1_H, + ICR_HS_P_AX2_L, + ICR_HS_P_AX2_H, + ICR_HS_P_AX3_L, + ICR_HS_P_AX3_H, + ICR_HS_P_AX4_L, + ICR_HS_P_AX4_H, + ICR_HS_P_AX5_L, + ICR_HS_P_AX5_H, + ICR_HS_P_AX6_L, + ICR_HS_P_AX6_H, + ICR_HS_P_AX7_L, + ICR_HS_P_AX7_H, + ICR_HS_P_AX8_L, + ICR_HS_P_AX8_H, + ICR_HS_P_AX9_L, + ICR_HS_P_AX9_H, + ICR_HS_P_AX10_L, + ICR_HS_P_AX10_H, + ICR_HS_P_AX11_L, + ICR_HS_P_AX11_H, + ICR_HS_P_AX12_L, + ICR_HS_P_AX12_H, + ICR_HS_P_AX13_L, + ICR_HS_P_AX13_H, + ICR_HS_P_AX14_L, + ICR_HS_P_AX14_H, + ICR_HS_P_H1, + ICR_HS_P_H2, + ICR_HS_P_H3, + ICR_HS_P_H4, + ICR_HS_P_H5, + ICR_HS_P_H6, + ICR_HS_P_H7, + ICR_HS_P_H8, + ICR_HS_P_H9, + ICR_HS_P_H10, + ICR_HS_P_H11, + ICR_HS_P_H12, + ICR_HS_P_H13, + ICR_HS_P_H14, + ICR_HS_P_S1, + ICR_HS_P_S2, + ICR_HS_P_S3, + ICR_HS_P_S4, + ICR_HS_P_S5, + ICR_HS_P_S6, + ICR_HS_P_S7, + ICR_HS_P_S8, + ICR_HS_P_S9, + ICR_HS_P_S10, + ICR_HS_P_S11, + ICR_HS_P_S12, + ICR_HS_P_S13, + ICR_HS_P_S14, + ICR_HS_P_GL, + ICR_HS_P_MAXSAT_RGB_Y_L, + ICR_HS_P_MAXSAT_RGB_Y_H, + ICR_HS_P_MAXSAT_RCR_L, + ICR_HS_P_MAXSAT_RCR_H, + ICR_HS_P_MAXSAT_RCB_L, + ICR_HS_P_MAXSAT_RCB_H, + ICR_HS_P_MAXSAT_GCR_L, + ICR_HS_P_MAXSAT_GCR_H, + ICR_HS_P_MAXSAT_GCB_L, + ICR_HS_P_MAXSAT_GCB_H, + ICR_HS_P_MAXSAT_BCR_L, + ICR_HS_P_MAXSAT_BCR_H, + ICR_HS_P_MAXSAT_BCB_L, + ICR_HS_P_MAXSAT_BCB_H, + ICR_HS_P_ROFF_L, + ICR_HS_P_ROFF_H, + ICR_HS_P_GOFF_L, + ICR_HS_P_GOFF_H, + ICR_HS_P_BOFF_L, + ICR_HS_P_BOFF_H, + ICR_GCSC_M_C0_L, + ICR_GCSC_M_C0_H, + ICR_GCSC_M_C1_L, + ICR_GCSC_M_C1_H, + ICR_GCSC_M_C2_L, + ICR_GCSC_M_C2_H, + ICR_GCSC_M_C3_L, + ICR_GCSC_M_C3_H, + ICR_GCSC_M_C4_L, + ICR_GCSC_M_C4_H, + ICR_GCSC_M_C5_L, + ICR_GCSC_M_C5_H, + ICR_GCSC_M_C6_L, + ICR_GCSC_M_C6_H, + ICR_GCSC_M_C7_L, + ICR_GCSC_M_C7_H, + ICR_GCSC_M_C8_L, + ICR_GCSC_M_C8_H, + ICR_GCSC_M_O1_0, + ICR_GCSC_M_O1_1, + ICR_GCSC_M_O1_2, + ICR_GCSC_M_O2_0, + ICR_GCSC_M_O2_1, + ICR_GCSC_M_O2_2, + ICR_GCSC_M_O3_0, + ICR_GCSC_M_O3_1, + ICR_GCSC_M_O3_2, + ICR_GCSC_P_C0_L, + ICR_GCSC_P_C0_H, + ICR_GCSC_P_C1_L, + ICR_GCSC_P_C1_H, + ICR_GCSC_P_C2_L, + ICR_GCSC_P_C2_H, + ICR_GCSC_P_C3_L, + ICR_GCSC_P_C3_H, + ICR_GCSC_P_C4_L, + ICR_GCSC_P_C4_H, + ICR_GCSC_P_C5_L, + ICR_GCSC_P_C5_H, + ICR_GCSC_P_C6_L, + ICR_GCSC_P_C6_H, + ICR_GCSC_P_C7_L, + ICR_GCSC_P_C7_H, + ICR_GCSC_P_C8_L, + ICR_GCSC_P_C8_H, + ICR_GCSC_P_O1_0, + ICR_GCSC_P_O1_1, + ICR_GCSC_P_O1_2, + ICR_GCSC_P_O2_0, + ICR_GCSC_P_O2_1, + ICR_GCSC_P_O2_2, + ICR_GCSC_P_O3_0, + ICR_GCSC_P_O3_1, + ICR_GCSC_P_O3_2, + ICR_CPCB_PIXVAL_M_EN, + ICR_CPCB_PIXVAL_P_EN, +}; + +static u16 ICRioctlTable[] = { + ICR_SRAM_WTC_RTC_REG, + ICR_DBL_BUF_TRG_REG, + ICR_DMA_GCR_REG, + ICR_SVF0_SIZ_REG, + ICR_SVF1_SIZ_REG, + ICR_DF0DISP_SIZE_REG, + ICR_DF1DISP_SIZE_REG, + ICR_DF0DISP_SPSCR_REG, + ICR_DF1DISP_SPSCR_REG, + ICR_DF0_VID_SIZ_REG, + ICR_DF1_VID_SIZ_REG, + ICR_SVF0RGB_STRT_ADDR_REG, + ICR_SVF1RGB_STRT_ADDR_REG, + ICR_DF0DISP_STRT_ADDR_REG, + ICR_DF1DISP_STRT_ADDR_REG, + ICR_RX_DMA_CTRL0_REG, + ICR_RX_DMA_CTRL1_REG, + ICR_TX_DMA_CTRL0_REG, + ICR_TX_DMA_CTRL1_REG, + ICR_INTR_MASK_REG, + ICR_INTR_CLR_SEL_REG, + ICR_INTR_STAT_MASK_REG, + ICR_INTR_STATUS_REG +}; + +#endif /* __PXA168_ICRIOCTLTABLE_H */ diff --git a/drivers/char/icr/pxa168_icr.c b/drivers/char/icr/pxa168_icr.c new file mode 100644 index 00000000000000..0f71efe32782d2 --- /dev/null +++ b/drivers/char/icr/pxa168_icr.c @@ -0,0 +1,909 @@ +/* + * linux/drivers/icr/pxa168_icr.c + * + * Copyright: (C) Copyright 2009 Marvell International Ltd. + * + * Author: Alan Guenther <> + * + * 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 + * publishhed 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 "pxa168_icr.h" +#include "pxa168_ICRioctlTable.h" + +#define ICR_TIMEOUT (20*HZ) +#define ICRaddr(offset) ((int)(icr->mmio_base + (offset))) + +#define WRITEREG(value,offset) __raw_writel((value),ICRaddr(offset)) + +#define READREG(offset) __raw_readl(ICRaddr(offset)) + +#define PRINTKREG(a, ZZZ) DEBUG("%s %s = 0x%x\n", a, #ZZZ, READREG((ZZZ))) + +#define ORREG(reg, value) { u32 temp = READREG((reg));\ + temp |= value;\ + WRITEREG(temp, (reg));\ + } + +/* ICR need to allocate a very large contiguous area */ +#define ENABLE_ALL_INTERUPTS 0xfff +#define CLEAR_ALL_INTERUPTS (~0xfff) + +#define ICR_NAME "icr" +#define PFX ICR_NAME ": " +#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) + +#if 0 +#define DEBUG(args...) {printk(args); } +#else +#define DEBUG(args...) +#endif + +static int icr_count = 1; +static dev_t icr_dev; + +DECLARE_WAIT_QUEUE_HEAD(dma_wait); + +static struct cdev *icr_cdev; + +struct dma_slots { + u32 inAddr; + u32 inSize; + u32 outAddr; + u32 outSize; + u16 inInUse; + u16 outInUse; + u32 waitToken; +}; + +#define SLOTMAX 2 +static struct dma_slots DMAslot[SLOTMAX]; + +static int DMAnextslotInR = 0; +static int DMAnextslotInW = 0; + +#define DMA_READ 0 +#define DMA_WRITE 1 +#define DMA_READWRITE 2 +#define DMAdirection int +#define DMANoSlot(slot) (slot < 0) + +struct pxa168_icr { + int initialized; /* non-zero if ICR in use */ + int interrupt_status; + int sem; + int lock; + void *dev_id; + int icr_dma_transfer_size; + char *mmio_base; + char *clockReg; + int waitToken; + int irq; + struct clk *clk; +}; + +static int icr_svf_siz_reg[SLOTMAX] = { + ICR_SVF0_SIZ_REG, + ICR_SVF1_SIZ_REG +}; + +static int icr_df_disp_size_reg[SLOTMAX] = { + ICR_DF0DISP_SIZE_REG, + ICR_DF1DISP_SIZE_REG +}; + +static int icr_rx_dma_ctrl_reg[SLOTMAX] = { + ICR_RX_DMA_CTRL0_REG, + ICR_RX_DMA_CTRL1_REG +}; + +static int icr_tx_dma_ctrl_reg[SLOTMAX] = { + ICR_TX_DMA_CTRL0_REG, + ICR_TX_DMA_CTRL1_REG +}; + +static int icr_svf_rgb_strt_addr_reg[SLOTMAX] = { + ICR_SVF0RGB_STRT_ADDR_REG, + ICR_SVF1RGB_STRT_ADDR_REG +}; + +static int icr_df_disp_strt_addr_reg[SLOTMAX] = { + ICR_DF0DISP_STRT_ADDR_REG, + ICR_DF1DISP_STRT_ADDR_REG +}; + +static int icr_df_disp_spscr_reg[SLOTMAX] = { + ICR_DF0DISP_SPSCR_REG, + ICR_DF1DISP_SPSCR_REG +}; + +static int icr_df_vid_siz_reg[SLOTMAX] = { + ICR_DF0_VID_SIZ_REG, + ICR_DF1_VID_SIZ_REG +}; + +static char *icr_dev_name = "icr"; + +static struct pxa168_icr *my_pxa168_icr; + +/* dma_slots access methods */ +static void setAvailable(int slot, int rw) +{ + switch(rw) { + case DMA_READ: + DMAslot[slot].inInUse = 0; + break; + case DMA_WRITE: + DMAslot[slot].outInUse = 0; + break; + case DMA_READWRITE: + DMAslot[slot].inInUse = 0; + DMAslot[slot].outInUse = 0; + break; + default: + printk("Fatal error in setAvailable\n"); + } +} + +static u32 getKaddr(struct mm_struct *mm, u32 Vaddr) +{ + u32 ret = 0UL; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + struct page *page; + + pgd = pgd_offset(mm, Vaddr); + if (!pgd_none(*pgd)) { + pud = pud_offset(pgd, Vaddr); + if (!pud_none(*pud)) { + pmd = pmd_offset(pud, Vaddr); + if (!pmd_none(*pmd)) { + pte = pte_offset_map(pmd, Vaddr); + if (!pte_none(*pte) && pte_present(*pte)) { + page = pte_page(*pte); + if(page) { + ret = page_to_phys(page); + ret |= (Vaddr & (PAGE_SIZE-1)); + } + } + } + } + } + return ret; +} + +static irqreturn_t pxa168_icr_irq_handler(int irq, void *dev_id) +{ + struct pxa168_icr *icr = dev_id; + irqreturn_t ret = IRQ_NONE; + u32 icr_interrupt_status; + u32 wake = 0; + u32 temp; + + icr_interrupt_status = READREG(ICR_INTR_STATUS_REG); + DEBUG("**** interupt: ICR_INTR_STATUS_REG = 0x%x\n", + icr_interrupt_status); + WRITEREG(~icr_interrupt_status, ICR_INTR_STATUS_REG); + icr->interrupt_status |= icr_interrupt_status; + if (ICR_INTR_STATUS_ERRORS(icr_interrupt_status)) + DEBUG("errors: ICR_INTR_STATUS_REG = 0x%x\n", + icr_interrupt_status); + if (ICR_INTR_STATUS_DMA0_DONE(icr->interrupt_status)) { + DEBUG("Process DMA0 interrupt\n"); + temp = READREG(ICR_RX_DMA_CTRL0_REG); + if (temp & 1) + DEBUG("RX_DMA0_REG %d bad\n", temp); + temp = READREG(ICR_TX_DMA_CTRL0_REG); + if (temp & 1) + DEBUG("TX_DMA0_REG %d bad\n", temp); + ICR_INTR_STATUS_DMA0_CLEAR(icr->interrupt_status); + dma_unmap_single(NULL, DMAslot[0].inAddr, + DMAslot[0].inSize, DMA_FROM_DEVICE); + dma_unmap_single(NULL, DMAslot[0].outAddr, + DMAslot[0].outSize, DMA_TO_DEVICE); + setAvailable(0, DMA_READWRITE); + wake = 1; + } + if (ICR_INTR_STATUS_DMA1_DONE(icr->interrupt_status)) { + DEBUG("Process DMA1 interrupt\n"); + temp = READREG(ICR_RX_DMA_CTRL1_REG); + if (temp & 1) + DEBUG("RX_DMA1_REG %d bad\n", temp); + temp = READREG(ICR_TX_DMA_CTRL1_REG); + if (temp & 1) + DEBUG("TX_DMA1_REG %d bad\n", temp); + ICR_INTR_STATUS_DMA1_CLEAR(icr->interrupt_status); + dma_unmap_single(NULL, DMAslot[1].inAddr, + DMAslot[1].inSize, DMA_FROM_DEVICE); + dma_unmap_single(NULL, DMAslot[1].outAddr, + DMAslot[1].outSize, DMA_TO_DEVICE); + setAvailable(1, DMA_READWRITE); + wake = 1; + } + if (wake) { + DEBUG("wake up\n"); + wake_up_interruptible(&dma_wait); + } + if (icr_interrupt_status != 0) { + DEBUG("Interrupt handled\n"); + ret = IRQ_HANDLED; + } + return ret; +} + +/* + * the ICR works with DMA. If a DMA slot is available, + * return it, otherwise return -1 + */ +static int icr_get_slot(DMAdirection dir) +{ + int slot = -1; + if ((dir == DMA_WRITE) && (DMAslot[DMAnextslotInW].outInUse == 0)) + slot = DMAnextslotInW; + if ((dir == DMA_READ) && (DMAslot[DMAnextslotInR].inInUse == 0)) + slot = DMAnextslotInR; + if ((dir == DMA_READWRITE) && (DMAslot[DMAnextslotInR].inInUse == 0) + && (DMAslot[DMAnextslotInW].outInUse == 0)) { + slot = DMAnextslotInW; + /* fatal error? */ + if (DMAnextslotInW != DMAnextslotInR) + slot = -1; + } + return slot; +} + +/* + * advance to the next slot + */ +static void icr_update_slot(int slot, DMAdirection dir) +{ + if ((dir == DMA_WRITE) && (DMAnextslotInW == slot)) { + DMAslot[slot].outInUse = 1; + DMAnextslotInW ^= 1; + DEBUG("DMAnextslotInW: %d\n", DMAnextslotInW); + } + if ((dir == DMA_READ) && (DMAnextslotInR == slot)) { + DMAslot[slot].inInUse = 1; + DMAnextslotInR ^= 1; + DEBUG("DMAnextslotInR: %d\n", DMAnextslotInR); + } + if ((dir == DMA_READWRITE) && (DMAnextslotInR == slot) + && (DMAnextslotInW == slot)) { + DMAslot[slot].inInUse = 1; + DMAslot[slot].outInUse = 1; + DMAnextslotInW ^= 1; + DMAnextslotInR ^= 1; + DEBUG("DMAnextslotInRW: %d\n", DMAnextslotInR); + } + DEBUG("update slot error: %d\n", DMAnextslotInR); +} + +static int pxa168_icr_ioctl(struct inode *inode, struct file *file, + u32 cmd, unsigned long arg1) +{ + struct pxa168_icr *icr = (struct pxa168_icr *)file->private_data; + int blocking = !(file->f_flags & O_NONBLOCK); + int slot; + int type = _IOC_TYPE(cmd); + int number = _IOC_NR(cmd); + + if (cmd == ICR_IOCTL_transform || cmd == ICR_IOCTL_transformDBwait) { + int psrc_buf; /* ICR_SRC_BUFFER * */ + int pdst_buf; /* ICR_DST_BUFFER * */ + int pwaitToken; /* int * */ + ICR_SRC_BUFFER src_buf; + ICR_DST_BUFFER dst_buf; + u32 srcPaddr; + u32 dstPaddr; + int temp; + int sSize; + int dSize; + struct mm_struct *mm = current->mm; + int new_format; + + /* get user space pointer to input and output addresses */ + if (get_user(psrc_buf, (int *)arg1)) + return -EFAULT; + + if (get_user(pdst_buf, (int *)(arg1 + 4))) + return -EFAULT; + + if (get_user(pwaitToken, (int *)(arg1 + 8))) + return -EFAULT; + + temp = copy_from_user((char *)&src_buf, __user (char *)psrc_buf, sizeof(src_buf)); + if (temp != 0) + return -EFAULT; + + temp = copy_from_user((char *)&dst_buf, __user (char *)pdst_buf, sizeof(dst_buf)); + if (temp != 0) + return -EFAULT; + + /* require FULL SCREEN mode */ + if (src_buf.buf.height != dst_buf.buf.height || + src_buf.buf.width != dst_buf.buf.width || + src_buf.buf.height != dst_buf.video_height || + src_buf.buf.width != dst_buf.video_width || + dst_buf.video_start_y != 0 || + dst_buf.video_start_x != 0 ) + return -EINVAL; + + sSize = src_buf.buf.height * src_buf.buf.width; + dSize = dst_buf.buf.height * dst_buf.buf.width; + if (sSize == 0 || dSize == 0) + return -EINVAL; + if (!access_ok(VERIFY_READ, (unsigned int)src_buf.buf.addr, sSize)) + return -EFAULT; + if (!access_ok(VERIFY_WRITE, (unsigned int)dst_buf.buf.addr, dSize)) + return -EFAULT; + + /* convert user space addresses to kernel physical addresses */ + spin_lock(&mm->page_table_lock); + srcPaddr = getKaddr(mm, (unsigned int)src_buf.buf.addr); + dstPaddr = getKaddr(mm, (unsigned int)dst_buf.buf.addr); + spin_unlock(&mm->page_table_lock); + + if (srcPaddr == -1 || dstPaddr == -1) + return -EFAULT; + + /* wait for a DMA slot to be available */ + do { + slot = icr_get_slot(DMA_READWRITE); + /* if no slot and blocking, wait for a slot */ + if (DMANoSlot(slot)) { + if (!blocking) + return -EAGAIN; + else { + int timeout = interruptible_sleep_on_timeout(&dma_wait,ICR_TIMEOUT); + if(timeout == 0) + return -EIO; + } + } + if (signal_pending(current)) + return -EINTR; + } while (DMANoSlot(slot)); + + DEBUG + ("setup and start transform: DMA %d source VA 0x%x(0x%x) dest 0x%x(0x%x)\n", + slot, (unsigned int)src_buf.buf.addr, srcPaddr, + (unsigned int)dst_buf.buf.addr, dstPaddr); + + if (pwaitToken != 0) { + DMAslot[slot].waitToken = icr->waitToken++; + put_user(DMAslot[slot].waitToken, (__u32 __user *) pwaitToken); + DEBUG("slot %d - waitToken 0x%x\n",slot, DMAslot[slot].waitToken ); + } + DMAslot[slot].inAddr = srcPaddr; + DMAslot[slot].outAddr = dstPaddr; + DMAslot[slot].inSize = sSize; + DMAslot[slot].outSize = dSize; + + WRITEREG(((src_buf.buf.height << 16) | src_buf.buf.width), + icr_svf_siz_reg[slot]); + WRITEREG((dst_buf.buf.height << 16 | dst_buf.buf.width), + icr_df_disp_size_reg[slot]); + WRITEREG(((dst_buf.video_start_y << 16) | dst_buf.video_start_x), + icr_df_disp_spscr_reg[slot]); + WRITEREG(((dst_buf.video_height << 16)| dst_buf.video_width), + icr_df_vid_siz_reg[slot]); + + DEBUG("ICR_DF%dDISP_SIZE_REG = 0x%x dSize = %d\n", + slot, temp, DMAslot[slot].outSize); + + WRITEREG(5, ICR_DBL_BUF_TRG_REG); + + new_format = (src_buf.buf.format == ICR_HWFORMAT_XRGB8888) + ? ICR_GCR_SRC_FORMAT : 0; + new_format |= (dst_buf.buf.format == ICR_HWFORMAT_XRGB8888) + ? ICR_GCR_DST_FORMAT : 0; + + temp = READREG(ICR_DMA_GCR_REG); + if ((temp & ICR_DMA_FORMAT_MASK) != new_format) { + int i; + /* if any DMA channel is active, + * do NOT change the format or garbage will result + */ + for (i = 0; i < SLOTMAX; i++) { + if (((DMAslot[i].inInUse == 1) + || (DMAslot[i].outInUse == 1))) + return -EAGAIN; + } + temp &= ~ICR_DMA_FORMAT_MASK; + temp |= new_format; + DEBUG("Changing DMA format -0x%x to 0x%x\n", (temp & ICR_DMA_FORMAT_MASK), new_format); + WRITEREG(temp, ICR_DMA_GCR_REG); + } + + icr_update_slot (slot,DMA_READWRITE); + + PRINTKREG("initial TX DMA channel reg", icr_tx_dma_ctrl_reg[slot]); + PRINTKREG("initial RX DMA channel reg", icr_rx_dma_ctrl_reg[slot]); + WRITEREG(srcPaddr, icr_svf_rgb_strt_addr_reg[slot]); + WRITEREG(dstPaddr, icr_df_disp_strt_addr_reg[slot]); + PRINTKREG("input addr:", icr_svf_rgb_strt_addr_reg[slot]); + PRINTKREG("output addr:", icr_df_disp_strt_addr_reg[slot]); + + ORREG(icr_tx_dma_ctrl_reg[slot], 1); + PRINTKREG("after setting", icr_tx_dma_ctrl_reg[slot]); + + ORREG(icr_rx_dma_ctrl_reg[slot], 1); + PRINTKREG("after setting", icr_rx_dma_ctrl_reg[slot]); + + + /* DMA is running on this channel now */ + DEBUG("DMA%d setup\n", slot); + + if (blocking) { + /* if this is a blocking IO, + *then wait for completion here + */ + int timeout = interruptible_sleep_on_timeout(&dma_wait,ICR_TIMEOUT); + if (timeout == 0) + return -EIO; + if (signal_pending(current)) + return -EINTR; + } + if (cmd != ICR_IOCTL_transformDBwait) + return 0; + } + if (cmd == ICR_IOCTL_wait || cmd == ICR_IOCTL_transformDBwait) { + int wait; + int waitToken; + int i; + int ret = 0; + if (cmd == ICR_IOCTL_wait) { + waitToken = arg1; + } else { + if (get_user(waitToken, (int*)(arg1 + 12))) + return -EFAULT; + } + + DEBUG("ICRwait - 0x%x\n", waitToken); + do { + DEBUG("wait loop\n"); + wait = -1; + for (i = 0; i < SLOTMAX; i++) { + if (((DMAslot[i].inInUse == 1) + || (DMAslot[i].outInUse == 1)) + && (DMAslot[i].waitToken == waitToken)) + wait = i; + } + + if (wait >= 0) { + DEBUG("Waiting for 0x%x\n", wait); + /* last chance check for sleep */ + if ((DMAslot[wait].inInUse == 1) + || (DMAslot[wait].outInUse == 1)) { + int timeout = interruptible_sleep_on_timeout(&dma_wait,ICR_TIMEOUT); + if(timeout == 0) { + ret = -EIO; + break; + } + } + } + if (signal_pending(current)) { + ret = -EINTR; + break; + } + } while (wait >= 0); + + return ret; + } + + if (cmd == ICR_IOCTL_tagged_set) { + int tags; + int data; + u32 datalen; + u32 copied_data; + u32 copied_tags; + u8 *local_data; + u16 *local_tags; + int i; + int retval = 0; + + /* get user space pointer to input and output addresses */ + if (get_user(tags, (int *)arg1)) + return -EFAULT; + + if (get_user(data, (int *)(arg1 + 4))) + return -EFAULT; + + if (get_user(datalen, (int *)(arg1 + 8))) + return -EFAULT; + + if ((datalen > sizeof(ICRtagTable)/2) || datalen == 0) + return -EINVAL; + + local_data = (u8 *)kmalloc(datalen*sizeof(u8), GFP_KERNEL); + if (local_data == NULL) + return -EFAULT; + copied_data = copy_from_user(local_data, __user (u8 *)data, datalen); + local_tags = (u16 *)kmalloc(datalen*sizeof(u16), GFP_KERNEL); + if (local_tags == NULL) + return -EFAULT; + copied_tags = copy_from_user(local_tags, __user (u16 *)tags, datalen*sizeof(u16)); + if(copied_data == 0 && copied_tags == 0) { + for(i=0;i 1024) || datalen == 0) + return -EINVAL; /*value out of range */ + + local_data = (u8 *)kmalloc(datalen*sizeof(u8), GFP_KERNEL); + local_tags = (u16 *)kmalloc(datalen*sizeof(u16), GFP_KERNEL); + copied_tags = copy_from_user(local_tags, __user (u16 *)tags, datalen*sizeof(u16)); + if(copied_tags == 0) { + for(i=0;i= SET_ICR_REG_FIRST) + && (number <= SET_ICR_REG_LAST)) { + int i = number - SET_ICR_REG_FIRST; + int reg = ICRioctlTable[i]; + + /* if this is a dma transfer size, remember it */ + if (ICRioctlTable[i] >= 0x30 && ICRioctlTable[i] <= 0x3c) + icr->icr_dma_transfer_size = arg1; + WRITEREG(arg1, reg); + return 0; + } + if (type == ICR_GET_CODE && (number >= GET_ICR_REG_FIRST) + && (number <= GET_ICR_REG_LAST)) { + int i = number - GET_ICR_REG_FIRST; + int reg = (int)ICRioctlTable[i]; + u32 val; + + val = READREG(reg); + + put_user(val, (__u32 __user *) arg1); + return 0; + } + + return -ENOIOCTLCMD; + +} + +static int pxa168_icr_open(struct inode *inode, struct file *file) +{ + struct pxa168_icr *icr = my_pxa168_icr; + + DEBUG("ICR open\n"); + + if (icr->initialized++) + return -EBUSY; + + WRITEREG(ICR_GCR_FULL_RESET, ICR_DMA_GCR_REG); + WRITEREG(ICR_GCR_FULL_ENABLE, ICR_DMA_GCR_REG); + + file->private_data = icr; + + return nonseekable_open(inode, file); +} + +static int pxa168_icr_release(struct inode *inode, struct file *file) +{ + struct pxa168_icr *icr = (struct pxa168_icr *)file->private_data; + + DEBUG("ICR Release"); + + if (icr) + icr->initialized = 0; + + return 0; +} + +#define res_size(res) ((res)->end - (res)->start + 1) + +static const struct file_operations icr_fops = { + .owner = THIS_MODULE, + .ioctl = pxa168_icr_ioctl, + .open = pxa168_icr_open, + .release = pxa168_icr_release, +}; + +static void pxa168_icr_initdev(struct pxa168_icr *icr) +{ + clk_enable(icr->clk); + + icr->icr_dma_transfer_size = ICR_DMA_TRANSFER_64; /*_32, _64, or _128*/ + WRITEREG(ICR_GCR_FULL_RESET, ICR_DMA_GCR_REG); + + WRITEREG(icr->icr_dma_transfer_size, ICR_RX_DMA_CTRL0_REG); + WRITEREG(icr->icr_dma_transfer_size, ICR_RX_DMA_CTRL1_REG); + WRITEREG(icr->icr_dma_transfer_size, ICR_TX_DMA_CTRL0_REG); + WRITEREG(icr->icr_dma_transfer_size, ICR_TX_DMA_CTRL1_REG); + + WRITEREG(ICR_GCR_FULL_ENABLE, ICR_DMA_GCR_REG); + PRINTKREG("", ICR_DMA_GCR_REG); + + WRITEREG(1, ICR_DBL_BUF_TRG_REG); + + /* set interrupts clear on write */ + WRITEREG(0, ICR_INTR_CLR_SEL_REG); + + /* then clear them by writing */ + WRITEREG(CLEAR_ALL_INTERUPTS, ICR_INTR_STATUS_REG); + + /* do interrupt in all cases */ + WRITEREG(ENABLE_ALL_INTERUPTS, ICR_INTR_MASK_REG); + + WRITEREG(ENABLE_ALL_INTERUPTS, ICR_INTR_STAT_MASK_REG); + + DMAnextslotInR = 0; + DMAnextslotInW = 0; + setAvailable(0, DMA_READWRITE); + setAvailable(1, DMA_READWRITE); +} + +static int __devinit pxa168_icr_probe(struct platform_device *pdev) +{ + struct pxa168_icr *icr; + struct resource *res; + int error; + + DEBUG("ICR probe"); + my_pxa168_icr = icr = kzalloc(sizeof(struct pxa168_icr), GFP_KERNEL); + if (icr == NULL) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + icr->irq = platform_get_irq(pdev, 0); + if (icr->irq < 0) { + dev_err(&pdev->dev, "failed to get icr irq\n"); + error = -ENXIO; + goto failed_free; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + error = -ENXIO; + goto failed_free; + } + + res = request_mem_region(res->start, res_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + error = -EBUSY; + goto failed_free; + } + + icr->mmio_base = ioremap(res->start, res_size(res)); + printk("mmio_base = 0x%x, res->start = 0x%x, res_sizer = 0x%x\n", + (unsigned int)icr->mmio_base, res->start, res_size(res) ); + if (icr->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + error = -ENXIO; + goto failed_free_mem; + } + + icr->clk = clk_get(&pdev->dev, "ICRCLK"); + if (IS_ERR(icr->clk)) { + dev_err(&pdev->dev, "failed to get icr clock\n"); + error = PTR_ERR(icr->clk); + goto failed_free_io; + } + + platform_set_drvdata(pdev, icr); + + DEBUG("request ICR irq - %d\n",icr->irq); + error = request_irq(icr->irq, pxa168_icr_irq_handler, IRQF_DISABLED, + pdev->name, icr); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto failed_free_dev; + } + + device_init_wakeup(&pdev->dev, 1); + + if (alloc_chrdev_region(&icr_dev, 0, icr_count, icr_dev_name)) { + DEBUG(KERN_INFO "Init ICR alloc failed\n"); + error = -ENODEV; + // goto error_exit; + } + + icr_cdev = cdev_alloc(); + + if (!icr_cdev) { + error = -ENOMEM; + DEBUG(KERN_INFO "Init ICR nomem2\n"); + goto error_dev_unregister; + } + + icr_cdev->ops = &icr_fops; + icr_cdev->owner = THIS_MODULE; + + if (cdev_add(icr_cdev, icr_dev, icr_dev)) { + DEBUG(KERN_INFO "Init ICR nodev2\n"); + error = -ENODEV; + goto error_free_cdev; + } + + pxa168_icr_initdev(icr); + + init_waitqueue_head(&dma_wait); + + DEBUG(KERN_INFO "*********Init ICR good\n"); + + return 0; + +error_free_cdev: + cdev_del(icr_cdev); + +error_dev_unregister: + unregister_chrdev_region(icr_dev, icr_count); + +failed_free_dev: + platform_set_drvdata(pdev, NULL); +failed_free_io: + iounmap(icr->mmio_base); + //failed_put_clk: + clk_put(icr->clk); +failed_free_mem: + release_mem_region(res->start, res_size(res)); +failed_free: + kfree(icr); + return error; +} + +static int __devexit pxa168_icr_remove(struct platform_device *pdev) +{ + struct pxa168_icr *icr = platform_get_drvdata(pdev); + + DEBUG("****** ICR remove ****"); + free_irq(icr->irq, pdev); + + clk_disable(icr->clk); + clk_put(icr->clk); + + iounmap(icr->mmio_base); + + // res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + // release_mem_region(res->start, res_size(res)); + + platform_set_drvdata(pdev, NULL); + kfree(icr); + + DEBUG(KERN_INFO "Exit ICR\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int pxa168_icr_suspend(struct platform_device *pdev, pm_message_t msg) +{ + + DEBUG(KERN_INFO "ICR suspend complete\n"); + + return 0; +} + +static int pxa168_icr_resume(struct platform_device *pdev) +{ + struct pxa168_icr *icr = platform_get_drvdata(pdev); + + pxa168_icr_initdev(icr); + + DEBUG(KERN_INFO "ICR resume complete\n"); + + return 0; +} +#else +#define pxa168_icr_suspend NULL +#define pxa168_icr_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver pxa168_icr_driver = { + .probe = pxa168_icr_probe, + .remove = __devexit_p(pxa168_icr_remove), + .suspend = pxa168_icr_suspend, + .resume = pxa168_icr_resume, + .driver = { + .name = "pxa168-icr", + .owner = THIS_MODULE, + }, +}; + +static int __init pxa168_icr_init(void) +{ + int ret; + DEBUG("ICR init"); + ret = platform_driver_register(&pxa168_icr_driver); + if (ret) { + pr_init(KERN_ERR PFX "unable to register driver\n"); + DEBUG("icr error - 0x%x\n", ret); + return ret; + } + return 0; +} + +static void __exit pxa168_icr_exit(void) +{ + DEBUG("ICR exit"); + platform_driver_unregister(&pxa168_icr_driver); +} + +module_init(pxa168_icr_init); +module_exit(pxa168_icr_exit); + +MODULE_DESCRIPTION("ICR driver for PXA168"); +MODULE_AUTHOR("Alan Guenther"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/icr/pxa168_icr.h b/drivers/char/icr/pxa168_icr.h new file mode 100644 index 00000000000000..02b566c0cd3316 --- /dev/null +++ b/drivers/char/icr/pxa168_icr.h @@ -0,0 +1,424 @@ +/* + * linux/drivers/icr/pxa168_icr.h + * + * Copyright: (C) Copyright 2009 Marvell International Ltd. + * + * Author: Alan Guenther <> + * + * 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 + * publishhed by the Free Software Foundation. + * + */ + +/* + * ICR hardware control register addresses + */ + +#ifndef __PXA168_ICR_H +#define __PXA168_ICR_H + +#define ICR_CONTROL_REG_BASE_ADDR 0xC0802000 +#define ICR_BASE_ADDR 0 +#define ICR_DMA_BASE_ADDR ICR_BASE_ADDR +#define ICR_SVF0RGB_STRT_ADDR_REG (ICR_DMA_BASE_ADDR+0x0) //CFG_DMA_SA_RGB0 +#define ICR_SVF1RGB_STRT_ADDR_REG (ICR_DMA_BASE_ADDR+0x4) //CFG_DMA_SA_RGB1 +#define ICR_SVF0_SIZ_REG (ICR_DMA_BASE_ADDR+0x8) +#define ICR_SVF1_SIZ_REG (ICR_DMA_BASE_ADDR+0xc) +#define ICR_DF0DISP_STRT_ADDR_REG (ICR_DMA_BASE_ADDR+0x10) +#define ICR_DF1DISP_STRT_ADDR_REG (ICR_DMA_BASE_ADDR+0x14) +#define ICR_DF0DISP_SIZE_REG (ICR_DMA_BASE_ADDR+0x18) +#define ICR_DF1DISP_SIZE_REG (ICR_DMA_BASE_ADDR+0x1c) +#define ICR_DF0DISP_SPSCR_REG (ICR_DMA_BASE_ADDR+0x20) +#define ICR_DF1DISP_SPSCR_REG (ICR_DMA_BASE_ADDR+0x24) +#define ICR_DF0_VID_SIZ_REG (ICR_DMA_BASE_ADDR+0x28) +#define ICR_DF1_VID_SIZ_REG (ICR_DMA_BASE_ADDR+0x2C) +#define ICR_RX_DMA_CTRL0_REG (ICR_DMA_BASE_ADDR+0x30) +#define ICR_RX_DMA_CTRL1_REG (ICR_DMA_BASE_ADDR+0x34) +#define ICR_TX_DMA_CTRL0_REG (ICR_DMA_BASE_ADDR+0x38) +#define ICR_TX_DMA_CTRL1_REG (ICR_DMA_BASE_ADDR+0x3c) +#define ICR_DMA_GCR_REG (ICR_DMA_BASE_ADDR+0x40) +#define ICR_INTR_MASK_REG (ICR_DMA_BASE_ADDR+0x44) +#define ICR_INTR_CLR_SEL_REG (ICR_DMA_BASE_ADDR+0x48) +#define ICR_INTR_STAT_MASK_REG (ICR_DMA_BASE_ADDR+0x4c) +#define ICR_INTR_STATUS_REG (ICR_DMA_BASE_ADDR+0x50) +//#define ICR_MEM_CF_CFG_REG (ICR_DMA_BASE_ADDR+0x40) +#define ICR_SRAM_WTC_RTC_REG (ICR_DMA_BASE_ADDR+0x54) +//#define ICR_INTR_CTRL_REG (ICR_DMA_BASE_ADDR+0x48) +//#define ICR_INTR_STAT_REG (ICR_DMA_BASE_ADDR+0x4c) +#define ICR_DBL_BUF_TRG_REG (ICR_DMA_BASE_ADDR+0x58) + +#define ICR_ICSC_BASE_ADDR ICR_BASE_ADDR+0x400 +#define ICR_ICSC_M_C0_L (ICR_ICSC_BASE_ADDR+4*0x00) +#define ICR_ICSC_M_C0_H (ICR_ICSC_BASE_ADDR+4*0x01) +#define ICR_ICSC_M_C1_L (ICR_ICSC_BASE_ADDR+4*0x02) +#define ICR_ICSC_M_C1_H (ICR_ICSC_BASE_ADDR+4*0x03) +#define ICR_ICSC_M_C2_L (ICR_ICSC_BASE_ADDR+4*0x04) +#define ICR_ICSC_M_C2_H (ICR_ICSC_BASE_ADDR+4*0x05) +#define ICR_ICSC_M_C3_L (ICR_ICSC_BASE_ADDR+4*0x06) +#define ICR_ICSC_M_C3_H (ICR_ICSC_BASE_ADDR+4*0x07) +#define ICR_ICSC_M_C4_L (ICR_ICSC_BASE_ADDR+4*0x08) +#define ICR_ICSC_M_C4_H (ICR_ICSC_BASE_ADDR+4*0x09) +#define ICR_ICSC_M_C5_L (ICR_ICSC_BASE_ADDR+4*0x0A) +#define ICR_ICSC_M_C5_H (ICR_ICSC_BASE_ADDR+4*0x0B) +#define ICR_ICSC_M_C6_L (ICR_ICSC_BASE_ADDR+4*0x0C) +#define ICR_ICSC_M_C6_H (ICR_ICSC_BASE_ADDR+4*0x0D) +#define ICR_ICSC_M_C7_L (ICR_ICSC_BASE_ADDR+4*0x0E) +#define ICR_ICSC_M_C7_H (ICR_ICSC_BASE_ADDR+4*0x0F) +#define ICR_ICSC_M_C8_L (ICR_ICSC_BASE_ADDR+4*0x10) +#define ICR_ICSC_M_C8_H (ICR_ICSC_BASE_ADDR+4*0x11) +#define ICR_ICSC_M_O1_0 (ICR_ICSC_BASE_ADDR+4*0x14) +#define ICR_ICSC_M_O1_1 (ICR_ICSC_BASE_ADDR+4*0x15) +#define ICR_ICSC_M_O1_2 (ICR_ICSC_BASE_ADDR+4*0x16) +#define ICR_ICSC_M_O2_0 (ICR_ICSC_BASE_ADDR+4*0x18) +#define ICR_ICSC_M_O2_1 (ICR_ICSC_BASE_ADDR+4*0x19) +#define ICR_ICSC_M_O2_2 (ICR_ICSC_BASE_ADDR+4*0x1A) +#define ICR_ICSC_M_O3_0 (ICR_ICSC_BASE_ADDR+4*0x1C) +#define ICR_ICSC_M_O3_1 (ICR_ICSC_BASE_ADDR+4*0x1D) +#define ICR_ICSC_M_O3_2 (ICR_ICSC_BASE_ADDR+4*0x1E) +#define ICR_ICSC_P_C0_L (ICR_ICSC_BASE_ADDR+4*0x20) +#define ICR_ICSC_P_C0_H (ICR_ICSC_BASE_ADDR+4*0x21) +#define ICR_ICSC_P_C1_L (ICR_ICSC_BASE_ADDR+4*0x22) +#define ICR_ICSC_P_C1_H (ICR_ICSC_BASE_ADDR+4*0x23) +#define ICR_ICSC_P_C2_L (ICR_ICSC_BASE_ADDR+4*0x24) +#define ICR_ICSC_P_C2_H (ICR_ICSC_BASE_ADDR+4*0x25) +#define ICR_ICSC_P_C3_L (ICR_ICSC_BASE_ADDR+4*0x26) +#define ICR_ICSC_P_C3_H (ICR_ICSC_BASE_ADDR+4*0x27) +#define ICR_ICSC_P_C4_L (ICR_ICSC_BASE_ADDR+4*0x28) +#define ICR_ICSC_P_C4_H (ICR_ICSC_BASE_ADDR+4*0x29) +#define ICR_ICSC_P_C5_L (ICR_ICSC_BASE_ADDR+4*0x2A) +#define ICR_ICSC_P_C5_H (ICR_ICSC_BASE_ADDR+4*0x2B) +#define ICR_ICSC_P_C6_L (ICR_ICSC_BASE_ADDR+4*0x2C) +#define ICR_ICSC_P_C6_H (ICR_ICSC_BASE_ADDR+4*0x2D) +#define ICR_ICSC_P_C7_L (ICR_ICSC_BASE_ADDR+4*0x2E) +#define ICR_ICSC_P_C7_H (ICR_ICSC_BASE_ADDR+4*0x2F) +#define ICR_ICSC_P_C8_L (ICR_ICSC_BASE_ADDR+4*0x30) +#define ICR_ICSC_P_C8_H (ICR_ICSC_BASE_ADDR+4*0x31) +#define ICR_ICSC_P_O1_0 (ICR_ICSC_BASE_ADDR+4*0x34) +#define ICR_ICSC_P_O1_1 (ICR_ICSC_BASE_ADDR+4*0x35) +#define ICR_ICSC_P_O1_2 (ICR_ICSC_BASE_ADDR+4*0x36) +#define ICR_ICSC_P_O2_0 (ICR_ICSC_BASE_ADDR+4*0x38) +#define ICR_ICSC_P_O2_1 (ICR_ICSC_BASE_ADDR+4*0x39) +#define ICR_ICSC_P_O2_2 (ICR_ICSC_BASE_ADDR+4*0x3A) +#define ICR_ICSC_P_O3_0 (ICR_ICSC_BASE_ADDR+4*0x3C) +#define ICR_ICSC_P_O3_1 (ICR_ICSC_BASE_ADDR+4*0x3D) +#define ICR_ICSC_P_O3_2 (ICR_ICSC_BASE_ADDR+4*0x3E) + +#define ICR_FTDC_M_EN (ICR_ICSC_BASE_ADDR+4*0xA0) +#define ICR_FTDC_P_EN (ICR_ICSC_BASE_ADDR+4*0xA1) +#define ICR_FTDC_INLOW_L (ICR_ICSC_BASE_ADDR+4*0xA2) +#define ICR_FTDC_INLOW_H (ICR_ICSC_BASE_ADDR+4*0xA3) +#define ICR_FTDC_INHIGH_L (ICR_ICSC_BASE_ADDR+4*0xA4) +#define ICR_FTDC_INHIGH_H (ICR_ICSC_BASE_ADDR+4*0xA5) +#define ICR_FTDC_OUTLOW_L (ICR_ICSC_BASE_ADDR+4*0xA6) +#define ICR_FTDC_OUTLOW_H (ICR_ICSC_BASE_ADDR+4*0xA7) +#define ICR_FTDC_OUTHIGH_L (ICR_ICSC_BASE_ADDR+4*0xA8) +#define ICR_FTDC_OUTHIGH_H (ICR_ICSC_BASE_ADDR+4*0xA9) +#define ICR_FTDC_YLOW (ICR_ICSC_BASE_ADDR+4*0xAA) +#define ICR_FTDC_YHIGH (ICR_ICSC_BASE_ADDR+4*0xAB) +#define ICR_FTDC_CH1 (ICR_ICSC_BASE_ADDR+4*0xAC) +#define ICR_FTDC_CH2_L (ICR_ICSC_BASE_ADDR+4*0xAE) +#define ICR_FTDC_CH2_H (ICR_ICSC_BASE_ADDR+4*0xAF) +#define ICR_FTDC_CH3_L (ICR_ICSC_BASE_ADDR+4*0xB0) +#define ICR_FTDC_CH3_H (ICR_ICSC_BASE_ADDR+4*0xB1) +#define ICR_FTDC_1_C00 (ICR_ICSC_BASE_ADDR+4*0xB2) +#define ICR_FTDC_2_C00 (ICR_ICSC_BASE_ADDR+4*0xB3) +#define ICR_FTDC_3_C00 (ICR_ICSC_BASE_ADDR+4*0xB4) +#define ICR_FTDC_4_C00 (ICR_ICSC_BASE_ADDR+4*0xB5) +#define ICR_FTDC_5_C00 (ICR_ICSC_BASE_ADDR+4*0xB6) +#define ICR_FTDC_6_C00 (ICR_ICSC_BASE_ADDR+4*0xB7) +#define ICR_FTDC_1_C01 (ICR_ICSC_BASE_ADDR+4*0xB8) +#define ICR_FTDC_2_C01 (ICR_ICSC_BASE_ADDR+4*0xB9) +#define ICR_FTDC_3_C01 (ICR_ICSC_BASE_ADDR+4*0xBA) +#define ICR_FTDC_4_C01 (ICR_ICSC_BASE_ADDR+4*0xBB) +#define ICR_FTDC_5_C01 (ICR_ICSC_BASE_ADDR+4*0xBC) +#define ICR_FTDC_6_C01 (ICR_ICSC_BASE_ADDR+4*0xBD) +#define ICR_FTDC_1_C11 (ICR_ICSC_BASE_ADDR+4*0xBE) +#define ICR_FTDC_2_C11 (ICR_ICSC_BASE_ADDR+4*0xBF) +#define ICR_FTDC_3_C11 (ICR_ICSC_BASE_ADDR+4*0xC0) +#define ICR_FTDC_4_C11 (ICR_ICSC_BASE_ADDR+4*0xC1) +#define ICR_FTDC_5_C11 (ICR_ICSC_BASE_ADDR+4*0xC2) +#define ICR_FTDC_6_C11 (ICR_ICSC_BASE_ADDR+4*0xC3) +#define ICR_FTDC_1_C10 (ICR_ICSC_BASE_ADDR+4*0xC4) +#define ICR_FTDC_2_C10 (ICR_ICSC_BASE_ADDR+4*0xC5) +#define ICR_FTDC_3_C10 (ICR_ICSC_BASE_ADDR+4*0xC6) +#define ICR_FTDC_4_C10 (ICR_ICSC_BASE_ADDR+4*0xC7) +#define ICR_FTDC_5_C10 (ICR_ICSC_BASE_ADDR+4*0xC8) +#define ICR_FTDC_6_C10 (ICR_ICSC_BASE_ADDR+4*0xC9) +#define ICR_FTDC_1_OFF00 (ICR_ICSC_BASE_ADDR+4*0xCA) +#define ICR_FTDC_2_OFF00 (ICR_ICSC_BASE_ADDR+4*0xCB) +#define ICR_FTDC_3_OFF00 (ICR_ICSC_BASE_ADDR+4*0xCC) +#define ICR_FTDC_4_OFF00 (ICR_ICSC_BASE_ADDR+4*0xCD) +#define ICR_FTDC_5_OFF00 (ICR_ICSC_BASE_ADDR+4*0xCE) +#define ICR_FTDC_6_OFF00 (ICR_ICSC_BASE_ADDR+4*0xCF) +#define ICR_FTDC_1_OFF10 (ICR_ICSC_BASE_ADDR+4*0xD0) +#define ICR_FTDC_2_OFF10 (ICR_ICSC_BASE_ADDR+4*0xD1) +#define ICR_FTDC_3_OFF10 (ICR_ICSC_BASE_ADDR+4*0xD2) +#define ICR_FTDC_4_OFF10 (ICR_ICSC_BASE_ADDR+4*0xD3) +#define ICR_FTDC_5_OFF10 (ICR_ICSC_BASE_ADDR+4*0xD4) +#define ICR_FTDC_6_OFF10 (ICR_ICSC_BASE_ADDR+4*0xD5) + +#define ICR_HSC_BASE_ADDR ICR_BASE_ADDR+0x800 +#define ICR_HS_M_EN (ICR_HSC_BASE_ADDR+4*0x00) +#define ICR_HS_M_AX1_L (ICR_HSC_BASE_ADDR+4*0x02) +#define ICR_HS_M_AX1_H (ICR_HSC_BASE_ADDR+4*0x03) +#define ICR_HS_M_AX2_L (ICR_HSC_BASE_ADDR+4*0x04) +#define ICR_HS_M_AX2_H (ICR_HSC_BASE_ADDR+4*0x05) +#define ICR_HS_M_AX3_L (ICR_HSC_BASE_ADDR+4*0x06) +#define ICR_HS_M_AX3_H (ICR_HSC_BASE_ADDR+4*0x07) +#define ICR_HS_M_AX4_L (ICR_HSC_BASE_ADDR+4*0x08) +#define ICR_HS_M_AX4_H (ICR_HSC_BASE_ADDR+4*0x09) +#define ICR_HS_M_AX5_L (ICR_HSC_BASE_ADDR+4*0x0A) +#define ICR_HS_M_AX5_H (ICR_HSC_BASE_ADDR+4*0x0B) +#define ICR_HS_M_AX6_L (ICR_HSC_BASE_ADDR+4*0x0C) +#define ICR_HS_M_AX6_H (ICR_HSC_BASE_ADDR+4*0x0D) +#define ICR_HS_M_AX7_L (ICR_HSC_BASE_ADDR+4*0x0E) +#define ICR_HS_M_AX7_H (ICR_HSC_BASE_ADDR+4*0x0F) +#define ICR_HS_M_AX8_L (ICR_HSC_BASE_ADDR+4*0x10) +#define ICR_HS_M_AX8_H (ICR_HSC_BASE_ADDR+4*0x11) +#define ICR_HS_M_AX9_L (ICR_HSC_BASE_ADDR+4*0x12) +#define ICR_HS_M_AX9_H (ICR_HSC_BASE_ADDR+4*0x13) +#define ICR_HS_M_AX10_L (ICR_HSC_BASE_ADDR+4*0x14) +#define ICR_HS_M_AX10_H (ICR_HSC_BASE_ADDR+4*0x15) +#define ICR_HS_M_AX11_L (ICR_HSC_BASE_ADDR+4*0x16) +#define ICR_HS_M_AX11_H (ICR_HSC_BASE_ADDR+4*0x17) +#define ICR_HS_M_AX12_L (ICR_HSC_BASE_ADDR+4*0x18) +#define ICR_HS_M_AX12_H (ICR_HSC_BASE_ADDR+4*0x19) +#define ICR_HS_M_AX13_L (ICR_HSC_BASE_ADDR+4*0x1A) +#define ICR_HS_M_AX13_H (ICR_HSC_BASE_ADDR+4*0x1B) +#define ICR_HS_M_AX14_L (ICR_HSC_BASE_ADDR+4*0x1C) +#define ICR_HS_M_AX14_H (ICR_HSC_BASE_ADDR+4*0x1D) +#define ICR_HS_M_H1 (ICR_HSC_BASE_ADDR+4*0x1E) +#define ICR_HS_M_H2 (ICR_HSC_BASE_ADDR+4*0x1F) +#define ICR_HS_M_H3 (ICR_HSC_BASE_ADDR+4*0x20) +#define ICR_HS_M_H4 (ICR_HSC_BASE_ADDR+4*0x21) +#define ICR_HS_M_H5 (ICR_HSC_BASE_ADDR+4*0x22) +#define ICR_HS_M_H6 (ICR_HSC_BASE_ADDR+4*0x23) +#define ICR_HS_M_H7 (ICR_HSC_BASE_ADDR+4*0x24) +#define ICR_HS_M_H8 (ICR_HSC_BASE_ADDR+4*0x25) +#define ICR_HS_M_H9 (ICR_HSC_BASE_ADDR+4*0x26) +#define ICR_HS_M_H10 (ICR_HSC_BASE_ADDR+4*0x27) +#define ICR_HS_M_H11 (ICR_HSC_BASE_ADDR+4*0x28) +#define ICR_HS_M_H12 (ICR_HSC_BASE_ADDR+4*0x29) +#define ICR_HS_M_H13 (ICR_HSC_BASE_ADDR+4*0x2A) +#define ICR_HS_M_H14 (ICR_HSC_BASE_ADDR+4*0x2B) +#define ICR_HS_M_S1 (ICR_HSC_BASE_ADDR+4*0x2C) +#define ICR_HS_M_S2 (ICR_HSC_BASE_ADDR+4*0x2D) +#define ICR_HS_M_S3 (ICR_HSC_BASE_ADDR+4*0x2E) +#define ICR_HS_M_S4 (ICR_HSC_BASE_ADDR+4*0x2F) +#define ICR_HS_M_S5 (ICR_HSC_BASE_ADDR+4*0x30) +#define ICR_HS_M_S6 (ICR_HSC_BASE_ADDR+4*0x31) +#define ICR_HS_M_S7 (ICR_HSC_BASE_ADDR+4*0x32) +#define ICR_HS_M_S8 (ICR_HSC_BASE_ADDR+4*0x33) +#define ICR_HS_M_S9 (ICR_HSC_BASE_ADDR+4*0x34) +#define ICR_HS_M_S10 (ICR_HSC_BASE_ADDR+4*0x35) +#define ICR_HS_M_S11 (ICR_HSC_BASE_ADDR+4*0x36) +#define ICR_HS_M_S12 (ICR_HSC_BASE_ADDR+4*0x37) +#define ICR_HS_M_S13 (ICR_HSC_BASE_ADDR+4*0x38) +#define ICR_HS_M_S14 (ICR_HSC_BASE_ADDR+4*0x39) +#define ICR_HS_M_GL (ICR_HSC_BASE_ADDR+4*0x3A) +#define ICR_HS_M_MAXSAT_RGB_Y_L (ICR_HSC_BASE_ADDR+4*0x3C) +#define ICR_HS_M_MAXSAT_RGB_Y_H (ICR_HSC_BASE_ADDR+4*0x3D) +#define ICR_HS_M_MAXSAT_RCR_L (ICR_HSC_BASE_ADDR+4*0x3E) +#define ICR_HS_M_MAXSAT_RCR_H (ICR_HSC_BASE_ADDR+4*0x3F) +#define ICR_HS_M_MAXSAT_RCB_L (ICR_HSC_BASE_ADDR+4*0x40) +#define ICR_HS_M_MAXSAT_RCB_H (ICR_HSC_BASE_ADDR+4*0x41) +#define ICR_HS_M_MAXSAT_GCR_L (ICR_HSC_BASE_ADDR+4*0x42) +#define ICR_HS_M_MAXSAT_GCR_H (ICR_HSC_BASE_ADDR+4*0x43) +#define ICR_HS_M_MAXSAT_GCB_L (ICR_HSC_BASE_ADDR+4*0x44) +#define ICR_HS_M_MAXSAT_GCB_H (ICR_HSC_BASE_ADDR+4*0x45) +#define ICR_HS_M_MAXSAT_BCR_L (ICR_HSC_BASE_ADDR+4*0x46) +#define ICR_HS_M_MAXSAT_BCR_H (ICR_HSC_BASE_ADDR+4*0x47) +#define ICR_HS_M_MAXSAT_BCB_L (ICR_HSC_BASE_ADDR+4*0x48) +#define ICR_HS_M_MAXSAT_BCB_H (ICR_HSC_BASE_ADDR+4*0x49) +#define ICR_HS_M_ROFF_L (ICR_HSC_BASE_ADDR+4*0x4A) +#define ICR_HS_M_ROFF_H (ICR_HSC_BASE_ADDR+4*0x4B) +#define ICR_HS_M_GOFF_L (ICR_HSC_BASE_ADDR+4*0x4C) +#define ICR_HS_M_GOFF_H (ICR_HSC_BASE_ADDR+4*0x4D) +#define ICR_HS_M_BOFF_L (ICR_HSC_BASE_ADDR+4*0x4E) +#define ICR_HS_M_BOFF_H (ICR_HSC_BASE_ADDR+4*0x4F) +#define ICR_HS_P_EN (ICR_HSC_BASE_ADDR+4*0x50) +#define ICR_HS_P_AX1_L (ICR_HSC_BASE_ADDR+4*0x52) +#define ICR_HS_P_AX1_H (ICR_HSC_BASE_ADDR+4*0x53) +#define ICR_HS_P_AX2_L (ICR_HSC_BASE_ADDR+4*0x54) +#define ICR_HS_P_AX2_H (ICR_HSC_BASE_ADDR+4*0x55) +#define ICR_HS_P_AX3_L (ICR_HSC_BASE_ADDR+4*0x56) +#define ICR_HS_P_AX3_H (ICR_HSC_BASE_ADDR+4*0x57) +#define ICR_HS_P_AX4_L (ICR_HSC_BASE_ADDR+4*0x58) +#define ICR_HS_P_AX4_H (ICR_HSC_BASE_ADDR+4*0x59) +#define ICR_HS_P_AX5_L (ICR_HSC_BASE_ADDR+4*0x5A) +#define ICR_HS_P_AX5_H (ICR_HSC_BASE_ADDR+4*0x5B) +#define ICR_HS_P_AX6_L (ICR_HSC_BASE_ADDR+4*0x5C) +#define ICR_HS_P_AX6_H (ICR_HSC_BASE_ADDR+4*0x5D) +#define ICR_HS_P_AX7_L (ICR_HSC_BASE_ADDR+4*0x5E) +#define ICR_HS_P_AX7_H (ICR_HSC_BASE_ADDR+4*0x5F) +#define ICR_HS_P_AX8_L (ICR_HSC_BASE_ADDR+4*0x60) +#define ICR_HS_P_AX8_H (ICR_HSC_BASE_ADDR+4*0x61) +#define ICR_HS_P_AX9_L (ICR_HSC_BASE_ADDR+4*0x62) +#define ICR_HS_P_AX9_H (ICR_HSC_BASE_ADDR+4*0x63) +#define ICR_HS_P_AX10_L (ICR_HSC_BASE_ADDR+4*0x64) +#define ICR_HS_P_AX10_H (ICR_HSC_BASE_ADDR+4*0x65) +#define ICR_HS_P_AX11_L (ICR_HSC_BASE_ADDR+4*0x66) +#define ICR_HS_P_AX11_H (ICR_HSC_BASE_ADDR+4*0x67) +#define ICR_HS_P_AX12_L (ICR_HSC_BASE_ADDR+4*0x68) +#define ICR_HS_P_AX12_H (ICR_HSC_BASE_ADDR+4*0x69) +#define ICR_HS_P_AX13_L (ICR_HSC_BASE_ADDR+4*0x6A) +#define ICR_HS_P_AX13_H (ICR_HSC_BASE_ADDR+4*0x6B) +#define ICR_HS_P_AX14_L (ICR_HSC_BASE_ADDR+4*0x6C) +#define ICR_HS_P_AX14_H (ICR_HSC_BASE_ADDR+4*0x6D) +#define ICR_HS_P_H1 (ICR_HSC_BASE_ADDR+4*0x6E) +#define ICR_HS_P_H2 (ICR_HSC_BASE_ADDR+4*0x6F) +#define ICR_HS_P_H3 (ICR_HSC_BASE_ADDR+4*0x70) +#define ICR_HS_P_H4 (ICR_HSC_BASE_ADDR+4*0x71) +#define ICR_HS_P_H5 (ICR_HSC_BASE_ADDR+4*0x72) +#define ICR_HS_P_H6 (ICR_HSC_BASE_ADDR+4*0x73) +#define ICR_HS_P_H7 (ICR_HSC_BASE_ADDR+4*0x74) +#define ICR_HS_P_H8 (ICR_HSC_BASE_ADDR+4*0x75) +#define ICR_HS_P_H9 (ICR_HSC_BASE_ADDR+4*0x76) +#define ICR_HS_P_H10 (ICR_HSC_BASE_ADDR+4*0x77) +#define ICR_HS_P_H11 (ICR_HSC_BASE_ADDR+4*0x78) +#define ICR_HS_P_H12 (ICR_HSC_BASE_ADDR+4*0x79) +#define ICR_HS_P_H13 (ICR_HSC_BASE_ADDR+4*0x7A) +#define ICR_HS_P_H14 (ICR_HSC_BASE_ADDR+4*0x7B) +#define ICR_HS_P_S1 (ICR_HSC_BASE_ADDR+4*0x7C) +#define ICR_HS_P_S2 (ICR_HSC_BASE_ADDR+4*0x7D) +#define ICR_HS_P_S3 (ICR_HSC_BASE_ADDR+4*0x7E) +#define ICR_HS_P_S4 (ICR_HSC_BASE_ADDR+4*0x7F) +#define ICR_HS_P_S5 (ICR_HSC_BASE_ADDR+4*0x80) +#define ICR_HS_P_S6 (ICR_HSC_BASE_ADDR+4*0x81) +#define ICR_HS_P_S7 (ICR_HSC_BASE_ADDR+4*0x82) +#define ICR_HS_P_S8 (ICR_HSC_BASE_ADDR+4*0x83) +#define ICR_HS_P_S9 (ICR_HSC_BASE_ADDR+4*0x84) +#define ICR_HS_P_S10 (ICR_HSC_BASE_ADDR+4*0x85) +#define ICR_HS_P_S11 (ICR_HSC_BASE_ADDR+4*0x86) +#define ICR_HS_P_S12 (ICR_HSC_BASE_ADDR+4*0x87) +#define ICR_HS_P_S13 (ICR_HSC_BASE_ADDR+4*0x88) +#define ICR_HS_P_S14 (ICR_HSC_BASE_ADDR+4*0x89) +#define ICR_HS_P_GL (ICR_HSC_BASE_ADDR+4*0x8A) +#define ICR_HS_P_MAXSAT_RGB_Y_L (ICR_HSC_BASE_ADDR+4*0x8C) +#define ICR_HS_P_MAXSAT_RGB_Y_H (ICR_HSC_BASE_ADDR+4*0x8D) +#define ICR_HS_P_MAXSAT_RCR_L (ICR_HSC_BASE_ADDR+4*0x8E) +#define ICR_HS_P_MAXSAT_RCR_H (ICR_HSC_BASE_ADDR+4*0x8F) +#define ICR_HS_P_MAXSAT_RCB_L (ICR_HSC_BASE_ADDR+4*0x90) +#define ICR_HS_P_MAXSAT_RCB_H (ICR_HSC_BASE_ADDR+4*0x91) +#define ICR_HS_P_MAXSAT_GCR_L (ICR_HSC_BASE_ADDR+4*0x92) +#define ICR_HS_P_MAXSAT_GCR_H (ICR_HSC_BASE_ADDR+4*0x93) +#define ICR_HS_P_MAXSAT_GCB_L (ICR_HSC_BASE_ADDR+4*0x94) +#define ICR_HS_P_MAXSAT_GCB_H (ICR_HSC_BASE_ADDR+4*0x95) +#define ICR_HS_P_MAXSAT_BCR_L (ICR_HSC_BASE_ADDR+4*0x96) +#define ICR_HS_P_MAXSAT_BCR_H (ICR_HSC_BASE_ADDR+4*0x97) +#define ICR_HS_P_MAXSAT_BCB_L (ICR_HSC_BASE_ADDR+4*0x98) +#define ICR_HS_P_MAXSAT_BCB_H (ICR_HSC_BASE_ADDR+4*0x99) +#define ICR_HS_P_ROFF_L (ICR_HSC_BASE_ADDR+4*0x9A) +#define ICR_HS_P_ROFF_H (ICR_HSC_BASE_ADDR+4*0x9B) +#define ICR_HS_P_GOFF_L (ICR_HSC_BASE_ADDR+4*0x9C) +#define ICR_HS_P_GOFF_H (ICR_HSC_BASE_ADDR+4*0x9D) +#define ICR_HS_P_BOFF_L (ICR_HSC_BASE_ADDR+4*0x9E) +#define ICR_HS_P_BOFF_H (ICR_HSC_BASE_ADDR+4*0x9F) + +#define ICR_GCSC_BASE_ADDR ICR_HSC_BASE_ADDR+0x0 +#define ICR_GCSC_M_C0_L (ICR_GCSC_BASE_ADDR+4*0xA0) +#define ICR_GCSC_M_C0_H (ICR_GCSC_BASE_ADDR+4*0xA1) +#define ICR_GCSC_M_C1_L (ICR_GCSC_BASE_ADDR+4*0xA2) +#define ICR_GCSC_M_C1_H (ICR_GCSC_BASE_ADDR+4*0xA3) +#define ICR_GCSC_M_C2_L (ICR_GCSC_BASE_ADDR+4*0xA4) +#define ICR_GCSC_M_C2_H (ICR_GCSC_BASE_ADDR+4*0xA5) +#define ICR_GCSC_M_C3_L (ICR_GCSC_BASE_ADDR+4*0xA6) +#define ICR_GCSC_M_C3_H (ICR_GCSC_BASE_ADDR+4*0xA7) +#define ICR_GCSC_M_C4_L (ICR_GCSC_BASE_ADDR+4*0xA8) +#define ICR_GCSC_M_C4_H (ICR_GCSC_BASE_ADDR+4*0xA9) +#define ICR_GCSC_M_C5_L (ICR_GCSC_BASE_ADDR+4*0xAA) +#define ICR_GCSC_M_C5_H (ICR_GCSC_BASE_ADDR+4*0xAB) +#define ICR_GCSC_M_C6_L (ICR_GCSC_BASE_ADDR+4*0xAC) +#define ICR_GCSC_M_C6_H (ICR_GCSC_BASE_ADDR+4*0xAD) +#define ICR_GCSC_M_C7_L (ICR_GCSC_BASE_ADDR+4*0xAE) +#define ICR_GCSC_M_C7_H (ICR_GCSC_BASE_ADDR+4*0xAF) +#define ICR_GCSC_M_C8_L (ICR_GCSC_BASE_ADDR+4*0xB0) +#define ICR_GCSC_M_C8_H (ICR_GCSC_BASE_ADDR+4*0xB1) +#define ICR_GCSC_M_O1_0 (ICR_GCSC_BASE_ADDR+4*0xB4) +#define ICR_GCSC_M_O1_1 (ICR_GCSC_BASE_ADDR+4*0xB5) +#define ICR_GCSC_M_O1_2 (ICR_GCSC_BASE_ADDR+4*0xB6) +#define ICR_GCSC_M_O2_0 (ICR_GCSC_BASE_ADDR+4*0xB8) +#define ICR_GCSC_M_O2_1 (ICR_GCSC_BASE_ADDR+4*0xB9) +#define ICR_GCSC_M_O2_2 (ICR_GCSC_BASE_ADDR+4*0xBA) +#define ICR_GCSC_M_O3_0 (ICR_GCSC_BASE_ADDR+4*0xBC) +#define ICR_GCSC_M_O3_1 (ICR_GCSC_BASE_ADDR+4*0xBD) +#define ICR_GCSC_M_O3_2 (ICR_GCSC_BASE_ADDR+4*0xBE) +#define ICR_GCSC_P_C0_L (ICR_GCSC_BASE_ADDR+4*0xC0) +#define ICR_GCSC_P_C0_H (ICR_GCSC_BASE_ADDR+4*0xC1) +#define ICR_GCSC_P_C1_L (ICR_GCSC_BASE_ADDR+4*0xC2) +#define ICR_GCSC_P_C1_H (ICR_GCSC_BASE_ADDR+4*0xC3) +#define ICR_GCSC_P_C2_L (ICR_GCSC_BASE_ADDR+4*0xC4) +#define ICR_GCSC_P_C2_H (ICR_GCSC_BASE_ADDR+4*0xC5) +#define ICR_GCSC_P_C3_L (ICR_GCSC_BASE_ADDR+4*0xC6) +#define ICR_GCSC_P_C3_H (ICR_GCSC_BASE_ADDR+4*0xC7) +#define ICR_GCSC_P_C4_L (ICR_GCSC_BASE_ADDR+4*0xC8) +#define ICR_GCSC_P_C4_H (ICR_GCSC_BASE_ADDR+4*0xC9) +#define ICR_GCSC_P_C5_L (ICR_GCSC_BASE_ADDR+4*0xCA) +#define ICR_GCSC_P_C5_H (ICR_GCSC_BASE_ADDR+4*0xCB) +#define ICR_GCSC_P_C6_L (ICR_GCSC_BASE_ADDR+4*0xCC) +#define ICR_GCSC_P_C6_H (ICR_GCSC_BASE_ADDR+4*0xCD) +#define ICR_GCSC_P_C7_L (ICR_GCSC_BASE_ADDR+4*0xCE) +#define ICR_GCSC_P_C7_H (ICR_GCSC_BASE_ADDR+4*0xCF) +#define ICR_GCSC_P_C8_L (ICR_GCSC_BASE_ADDR+4*0xD0) +#define ICR_GCSC_P_C8_H (ICR_GCSC_BASE_ADDR+4*0xD1) +#define ICR_GCSC_P_O1_0 (ICR_GCSC_BASE_ADDR+4*0xD4) +#define ICR_GCSC_P_O1_1 (ICR_GCSC_BASE_ADDR+4*0xD5) +#define ICR_GCSC_P_O1_2 (ICR_GCSC_BASE_ADDR+4*0xD6) +#define ICR_GCSC_P_O2_0 (ICR_GCSC_BASE_ADDR+4*0xD8) +#define ICR_GCSC_P_O2_1 (ICR_GCSC_BASE_ADDR+4*0xD9) +#define ICR_GCSC_P_O2_2 (ICR_GCSC_BASE_ADDR+4*0xDA) +#define ICR_GCSC_P_O3_0 (ICR_GCSC_BASE_ADDR+4*0xDC) +#define ICR_GCSC_P_O3_1 (ICR_GCSC_BASE_ADDR+4*0xDD) +#define ICR_GCSC_P_O3_2 (ICR_GCSC_BASE_ADDR+4*0xDE) + +#define ICR_CPCB_PIXVAL_M_EN (ICR_GCSC_BASE_ADDR+4*0xE0) +#define ICR_CPCB_PIXVAL_P_EN (ICR_GCSC_BASE_ADDR+4*0xE1) + +#define ICR_REG_SIZE 4096 + +///////////////////////////////////////////////////////////////////////// +// GCR Control bits + +#define ICR_GCR_SOFT_RESET 0x1000 +#define ICR_GCR_RX_WPTR_RESET 0x0800 +#define ICR_GCR_RX_RPTR_RESET 0x0400 +#define ICR_GCR_TX_WPTR_RESET 0x0200 +#define ICR_GCR_TX_RPTR_RESET 0x0100 +#define ICR_GCR_DMA_RX_MOA_ENABLE 0x0080 +#define ICR_GCR_DMA_TX_MOA_ENABLE 0x0040 +#define ICR_GCR_SRC_FORMAT 0x0020 +#define ICR_GCR_DST_FORMAT 0x0010 +#define ICR_GCR_DMA1_RX_ENABLE 0x0008 +#define ICR_GCR_DMA0_RX_ENABLE 0x0004 +#define ICR_GCR_DMA1_TX_ENABLE 0x0002 +#define ICR_GCR_DMA0_TX_ENABLE 0x0001 +#define ICR_GCR_FULL_RESET ( ICR_GCR_SOFT_RESET \ + | ICR_GCR_RX_WPTR_RESET \ + | ICR_GCR_RX_RPTR_RESET \ + | ICR_GCR_TX_WPTR_RESET \ + | ICR_GCR_TX_RPTR_RESET ) +#define ICR_GCR_FULL_ENABLE ( 0 \ + /*| ICR_GCR_DMA_RX_MOA_ENABLE */ \ + /*| ICR_GCR_DMA_TX_MOA_ENABLE */ \ + /*| ICR_GCR_SRC_FORMAT*/ \ + /*| ICR_GCR_DST_FORMAT*/ \ + | ICR_GCR_DMA1_RX_ENABLE \ + | ICR_GCR_DMA0_RX_ENABLE \ + | ICR_GCR_DMA1_TX_ENABLE \ + | ICR_GCR_DMA0_TX_ENABLE ) +#define ICR_DMA_TRANSFER_32 (0x1<<1) +#define ICR_DMA_TRANSFER_64 (0x2<<1) +#define ICR_DMA_TRANSFER_128 (0x3<<1) + +#define ICR_INTR_STATUS_ERRORS(a) (((a) & 0xfffffff0) != 0) +#define ICR_INTR_STATUS_DMA0_DONE(a) (((a) & 5) == 5) +#define ICR_INTR_STATUS_DMA0_READ_DONE(a) (((a) & 4) == 4) +#define ICR_INTR_STATUS_DMA0_CLEAR(a) (a) &= ~5 +#define ICR_INTR_STATUS_DMA1_DONE(a) (((a) & 0xa) == 0xa) +#define ICR_INTR_STATUS_DMA1_READ_DONE(a) (((a) & 0x8) == 0x8) +#define ICR_INTR_STATUS_DMA1_CLEAR(a) (a) &= ~0xa + +#define ICR_DMA_FORMAT_MASK (ICR_GCR_SRC_FORMAT | ICR_GCR_DST_FORMAT) + +#endif /* __PXA168_ICR_H */ diff --git a/drivers/char/imm.c b/drivers/char/imm.c new file mode 100644 index 00000000000000..7bb924fd9e9c70 --- /dev/null +++ b/drivers/char/imm.c @@ -0,0 +1,202 @@ +/* + * linux/arch/arm/mach-pxa/imm.c + * + * Intel Memory Management + * + * User level driver interface to IMM APIs + * + * Todd Brandt + * Copyright (c) 2004, Intel Corporation (todd.e.brandt@intel.com) + * + * 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. + + *(C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct priv_struct { + u32 virt_sram_base; + int virt_sram_base_valid; + u32 pid; +}; + +static int num_instances = 0; + +static int imm_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct priv_struct *pv = filp->private_data; + struct imm_memory_block mb, *pmb = (struct imm_memory_block *)arg; + unsigned long ret; + int res = 0; + char *s, *u; + u32 size; + + /* we must have a struct included */ + if(arg == 0) return 0; + memcpy(&mb, pmb, sizeof(struct imm_memory_block)); + + switch(cmd) { +#ifdef CONFIG_IMM_API_SRAM + case IMM_MALLOC: + if(!pv->virt_sram_base_valid) { + imm_debug("Cannot malloc until mmap is called"); + res = -1; + break; + } + res = (int)imm_malloc(mb.size, mb.flag, pv->pid); + imm_debug("IMM_MALLOC RETURN STATUS = %s\n", imm_error(pv->pid)); + break; + case IMM_GET_FREE_SPACE: + if(!pv->virt_sram_base_valid) { + imm_debug("Cannot check free space until mmap is called"); + res = -1; + break; + } + size = imm_get_freespace(mb.flag, pv->pid); + ret = copy_to_user(&pmb->size, &size, sizeof(unsigned int)); + imm_debug("IMM_GET_FREE_SPACE RETURN STATUS = %s\n", imm_error(pv->pid)); + break; + case IMM_FREE: + if(!pv->virt_sram_base_valid) { + imm_debug("Cannot free memory until mmap is called"); + res = -1; + break; + } + res = imm_free((void *)mb.start, pv->pid); + imm_debug("IMM_FREE RETURN STATUS = %s\n", imm_error(pv->pid)); + break; +#else + case IMM_MALLOC: + imm_debug("Unsupported IMM command"); + res = 0; + break; +#endif + case IMM_GET_PHYS_SRAM_SIZE: + size = phys_sram_size; + ret = copy_to_user(&pmb->size, &size, sizeof(unsigned int)); + break; + case IMM_GET_VIRT_SRAM_SIZE: + size = imm_malloc_map_size; + ret = copy_to_user(&pmb->size, &size, sizeof(unsigned int)); + break; + case IMM_ERROR: + s = (char *)imm_error(pv->pid); + u = (char *)mb.start; + ret = copy_to_user(u, s, ((strlen(s)+1) > mb.size)?mb.size:(strlen(s)+1)); + break; + case IMM_GET_VIRTUAL: + res = (int)imm_get_virtual((u32)mb.start, current); + break; + case IMM_GET_PHYSICAL: + res = (int)imm_get_physical((void *)mb.start, pv->pid); + break; + default: + imm_debug("Unsupported IMM command"); + return -1; + } + return res; +} + +static int imm_mmap(struct file *filp, struct vm_area_struct * vma) +{ + struct priv_struct *pv = filp->private_data; + + imm_debug("vma->vm_pgoff = %08X\n", vma->vm_pgoff << PAGE_SHIFT); + if((vma->vm_end-vma->vm_start) < imm_malloc_map_size) { + printk("IMM Driver mmap failure, size must be at least %d bytes\n", + imm_malloc_map_size); + return -EINVAL; + } + + vma->vm_page_prot = (pgprot_t)PAGE_SHARED; + + /* Don't try to swap out physical pages */ + vma->vm_flags |= VM_RESERVED | VM_LOCKED; + + zap_page_range(vma, vma->vm_start, vma->vm_end-vma->vm_start, NULL); + imm_debug("Virtual sram mmap from v(0x%08X) of %d bytes\n", (u32)vma->vm_start, vma->vm_end-vma->vm_start); + if(!pv->virt_sram_base_valid) + imm_register_user((u32)vma->vm_start, current->pid); + pv->virt_sram_base = (u32)vma->vm_start; + pv->virt_sram_base_valid = 1; + return 0; +} + +static int imm_driver_open(struct inode * inode, struct file * filp) +{ + struct priv_struct *pv; + + pv = kmalloc (sizeof (struct priv_struct), GFP_KERNEL); + if (!pv) return -ENOMEM; + + num_instances++; + pv->virt_sram_base = 0; + pv->virt_sram_base_valid = 0; + pv->pid = current->pid; + + imm_debug("imm_driver_open(current->pid = %d)\n", current->pid); + + filp->private_data = pv; + + return 0; +} + +static int imm_driver_release(struct inode * inode, struct file * filp) +{ + struct priv_struct *pv = filp->private_data; + + num_instances--; + if(num_instances < 0) num_instances = 0; + if(pv) kfree(pv); + filp->private_data = NULL; + return 0; +} + +static struct file_operations imm_fops = { + mmap: imm_mmap, + open: imm_driver_open, + release: imm_driver_release, + ioctl: imm_ioctl, +}; + +static struct miscdevice imm_misc = { + minor : IMM_MINOR, + name : "imm", + fops : &imm_fops, +}; + +static int __init imm_init(void) { + if (misc_register(&imm_misc)) { + printk(KERN_ERR "Error registering device /dev/%s\n", imm_misc.name); + return -EFAULT; + } + return 0; +} + +static void __exit imm_exit(void) { + misc_deregister(&imm_misc); +} + +module_init(imm_init) +module_exit(imm_exit) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index f54dab8acdcd65..faaa3a8e1f0d51 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -59,6 +59,7 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) } #endif +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) #ifdef CONFIG_STRICT_DEVMEM static inline int range_is_allowed(unsigned long pfn, unsigned long size) { @@ -84,7 +85,9 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) return 1; } #endif +#endif +#ifdef CONFIG_DEVMEM void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr) { } @@ -211,6 +214,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, *ppos += written; return written; } +#endif /* CONFIG_DEVMEM */ + +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) int __weak phys_mem_access_prot_allowed(struct file *file, unsigned long pfn, unsigned long size, pgprot_t *vma_prot) @@ -332,6 +338,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) } return 0; } +#endif /* CONFIG_DEVMEM */ #ifdef CONFIG_DEVKMEM static int mmap_kmem(struct file *file, struct vm_area_struct *vma) @@ -696,6 +703,8 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig) return file->f_pos = 0; } +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) + /* * The memory devices use the full 32/64 bits of the offset, and so we cannot * check against negative addresses: they are ok. The return value is weird, @@ -729,10 +738,14 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) return ret; } +#endif + +#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT) static int open_port(struct inode * inode, struct file * filp) { return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; } +#endif #define zero_lseek null_lseek #define full_lseek null_lseek @@ -742,6 +755,7 @@ static int open_port(struct inode * inode, struct file * filp) #define open_kmem open_mem #define open_oldmem open_mem +#ifdef CONFIG_DEVMEM static const struct file_operations mem_fops = { .llseek = memory_lseek, .read = read_mem, @@ -750,6 +764,7 @@ static const struct file_operations mem_fops = { .open = open_mem, .get_unmapped_area = get_unmapped_area_mem, }; +#endif #ifdef CONFIG_DEVKMEM static const struct file_operations kmem_fops = { diff --git a/drivers/char/pxa-cnm.c b/drivers/char/pxa-cnm.c new file mode 100644 index 00000000000000..5d4930e0d87562 --- /dev/null +++ b/drivers/char/pxa-cnm.c @@ -0,0 +1,460 @@ +/* + * pxa-cnm main driver for chip&media codad8x codec + * + * Copyright (C) 2009, Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#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 +#include + +#include +#include + + + +/*FPGA addr*/ +//#define CNM_MEM_START 0x80000000 + +/*TTC EVB*/ +#define CNM_MEM_START 0xD420D000 +#define CNM_MEM_SIZE 0x010000 +#define MAX_NUM_INSTANCE 4 + +/*Power On*/ +//////////////////////////////////////////////////////////// +///// +// // DX8_CLK_RES definition: + // bit[1:0]: {func_reset, axi_reset} + // bit[4:3]: {func_clk_en, axi_clk_en} + // bit[10:6]: dx8_clk_div + // bit[18:16]: {pwr_on2, pwr_on1, isob} + // bit[19]: hw_mode +#define DX8_AXI_RST 0x1 +#define DX8_FUNC_RST 0x2 +#define DX8_RST_OFF 0x3 +#define DX8_FUNC_CLK 0x10 +#define DX8_AXI_CLK 0x8 +#define DX8_CLK_DIV 0x40 +#define DX8_CLK_SEL_208 0x20 +#define DX8_CLK_EN 0x58 +#define DX8_HW_MODE 0x80000 +#define DX8_ON2 0x40000 +#define DX8_ON1 0x20000 +#define DX8_PWR_ON 0x60000 +#define DX8_ISO 0x10000 + +#if 0 +#define PMUA_PWR_STAT 0xd42828F0 +#define PMUA_DX8_CLK_RES 0xd42828A4 + +#define PMU_GC_CLK_RES_CTRL 0xD42828CC +//Dx8 clk_reset ctrl +//#define ADDR_VPRO_CLK_RES_CTRL 0xD42828A4 +#define PMU_DX8_CLK_RES_CTRL 0xD42828A4 +#define PMUA_PWR_TIMER 0xd42828DC +#define PMUA_PWR_CTRL 0xd42828D8 + +//#define APMU_CF APMU_REG(0x0f0) +//#define APMU_DX8_CLK_RES_CTRL APMU_REG(0x00a4) +//#define APMU_XD_CLK_RES_CTRL APMU_REG(0x00dc) +#endif + + +struct vpu_info { + unsigned int off; + void *value; +}; + +enum { + VPU_READREG = 1, + VPU_WRITEREG, + VPU_WAITFORTIMEOUT, +}; + +struct pxa_cnm { + struct clk *clk; + void __iomem *mmio_base; + int irq; + int vpu_downloaded; + int current_act; + int ins_num; + struct mutex lock; +}; + +static struct pxa_cnm pxa_cnm; +void VpuWriteReg(unsigned int off, unsigned int value) +{ + __raw_writel((value), pxa_cnm.mmio_base + (off)); +} + +unsigned int VpuReadReg(unsigned int off) +{ + return __raw_readl(pxa_cnm.mmio_base + (off)); +} + +static int pxa_cnm_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + lock_kernel(); + if (pxa_cnm.ins_num < MAX_NUM_INSTANCE) + pxa_cnm.ins_num ++; + else { + printk("Current CNM cannot accept more instance!!\n"); + ret = -EACCES; + goto out; + } + + printk("Current CNM Get %d instance!!\n", pxa_cnm.ins_num); + file->private_data = &pxa_cnm; +out: + unlock_kernel(); + return ret; +} + +static int pxa_cnm_close(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + mutex_lock(&pxa_cnm.lock); + pxa_cnm.ins_num --; + mutex_unlock(&pxa_cnm.lock); + + return 0; +} + +static int pxa_cnm_ioctl +(struct inode *inode, struct file *file, u_int cmd, u_long arg) +{ + void __user *argp = (void __user *)arg; + struct vpu_info vpu_info; + unsigned int value; + + if (copy_from_user(&vpu_info, argp, sizeof(struct vpu_info))) + return -EFAULT; + + switch (cmd) { + case VPU_READREG: + { + value = VpuReadReg(vpu_info.off); + if (copy_to_user(vpu_info.value, &value, sizeof(unsigned int))) + return -EFAULT; + break; + } + case VPU_WRITEREG: + { + if (copy_from_user(&value, vpu_info.value, sizeof(unsigned int))) + return -EFAULT; + + VpuWriteReg(vpu_info.off, value); + break; + } + case VPU_WAITFORTIMEOUT: + { + break; + } + default: + { + return -EFAULT; + } + } + + return 0; +} + +static struct file_operations pxa_cnm_fops = { + .owner = THIS_MODULE, + .open = pxa_cnm_open, + .release = pxa_cnm_close, + .ioctl = pxa_cnm_ioctl, +}; + +static struct miscdevice pxa_cnm_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cnm", + .fops = &pxa_cnm_fops, + .this_device = NULL, +}; + +static irqreturn_t pxa_cnm_irq (int irq, void *devid) +{ + return IRQ_HANDLED; +} + +static int pxa_cnm_clock(int enable) +{ + if(enable == 1){ + /*Enable*/ + /*Power On*/ + printk("pxa_cnm_clock enable\n"); + + /*DX8_CLK check*/ + printk("Dx8 Read ...APMU_DX8_CLK_RES_CTRL = 0x%.8x\n", __raw_readl(APMU_DX8_CLK_RES_CTRL)); + + // DX8_CLK_RES definition: + // bit[1:0]: {func_reset, axi_reset} + // bit[4:3]: {func_clk_en, axi_clk_en} + // bit[10:6]: dx8_clk_div + // bit[18:16]: {pwr_on2, pwr_on1, isob} + // bit[19]: hw_mode + // set power timer@vctcxo (HW mode only) + // SW turn-on GC/Dx8 power, and enable clock + // BU_REG_WRITE(PMUA_PWR_TIMER,0x0f0f0f); // on1, on2, off timer are 0x0F. + __raw_writel(0x0f0f0f, APMU_XD_CLK_RES_CTRL); + // SW turn-on GC/Dx8 power, and enable clock + // BU_REG_WRITE(PMUA_DX8_CLK_RES, DX8_CLK_DIV | DX8_FUNC_CLK | DX8_AXI_CLK); // clock enable + // Enable Bus Clock and Function clocks + __raw_writel(DX8_CLK_DIV | DX8_FUNC_CLK | DX8_AXI_CLK, APMU_DX8_CLK_RES_CTRL); + // BU_REG_WRITE(PMUA_DX8_CLK_RES, DX8_CLK_EN | DX8_ON1); // on1 + // BU_REG_WRITE(PMUA_DX8_CLK_RES, DX8_CLK_EN | DX8_ON1 | DX8_ON2); // on2, 200us latency required + // Enable power_on1 wait for at least 200us , eanble power_on2 + __raw_writel(DX8_CLK_EN | DX8_ON1, APMU_DX8_CLK_RES_CTRL); + + //printk("Dx8 Power On1, wait 20\n"); + schedule_timeout(20); + + __raw_writel(DX8_CLK_EN | DX8_ON1 | DX8_ON2, APMU_DX8_CLK_RES_CTRL); + + //printk("Dx8 Power On2, wait 20\n"); + schedule_timeout(20); + + //printk("Dx8 Reset ...APMU_DX8_CLK_RES_CTRL = 0x%.8x\n", __raw_readl(APMU_DX8_CLK_RES_CTRL)); + + // SW release reset + // BU_REG_WRITE(PMUA_DX8_CLK_RES, DX8_CLK_EN | DX8_PWR_ON | DX8_FUNC_RST); // resetPin_ first + // BU_REG_WRITE(PMUA_DX8_CLK_RES, DX8_CLK_EN | DX8_PWR_ON | DX8_FUNC_RST | DX8_AXI_RST); // areset/hreset 48 cycles needed + __raw_writel(DX8_CLK_EN | DX8_PWR_ON | DX8_FUNC_RST, APMU_DX8_CLK_RES_CTRL); + __raw_writel(DX8_CLK_EN | DX8_PWR_ON | DX8_FUNC_RST | DX8_AXI_RST, APMU_DX8_CLK_RES_CTRL); + + // SW enable FW + // BU_REG_WRITE(PMUA_DX8_CLK_RES, DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF | DX8_ISO); // GC is fully functional now + __raw_writel(DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF | DX8_ISO , APMU_DX8_CLK_RES_CTRL); + //BU_REG_READ(PMUA_DX8_CLK_RES) != (DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF | DX8_ISO + if( __raw_readl(APMU_DX8_CLK_RES_CTRL) != (DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF | DX8_ISO ) ) { + /*Enable 208M AXI bridget*/ + //__raw_writel(DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF | DX8_ISO | DX8_CLK_SEL_208, APMU_DX8_CLK_RES_CTRL); + //if( __raw_readl(APMU_DX8_CLK_RES_CTRL) != (DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF | DX8_ISO | DX8_CLK_SEL_208) ) { + printk("---------PMUA_DX8_CLK_RES error!--------\n"); // reg read test + return -1; + }else{ + printk("Dx8 Reset ...APMU_DX8_CLK_RES_CTRL = 0x%.8x\n", __raw_readl(APMU_DX8_CLK_RES_CTRL)); + } + + /*Test*/ +#if 0 + { + unsigned int retval = 0; + printk("test register 0x120, init 0x%x\n", VpuReadReg(0x120)); + + VpuWriteReg(0x120, 0x12345678); + retval = VpuReadReg(0x120); + printk("ret value 0x%x\n", retval); + + } +#endif + + }else{ + // *PMUA_DX8_CLK_RES = DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF; // shutdown FW + // *PMUA_DX8_CLK_RES = DX8_CLK_EN | DX8_ON1 | DX8_RST_OFF; // on2 off + //*PMUA_DX8_CLK_RES = DX8_CLK_EN | DX8_RST_OFF ; // on1 off, GC power down now. + //if(*PMUA_DX8_CLK_RES != (DX8_CLK_EN | DX8_RST_OFF) ) { + // *ARM_MSG = 0xf0f0; *ARM_MSG = SIM_END; // reg read test + //} + /*Disable*/ + printk("pxa_cnm_clock disable\n"); + __raw_writel(DX8_CLK_EN | DX8_PWR_ON | DX8_RST_OFF , APMU_DX8_CLK_RES_CTRL); + __raw_writel(DX8_CLK_EN | DX8_ON1 | DX8_RST_OFF , APMU_DX8_CLK_RES_CTRL); + __raw_writel(DX8_CLK_EN | DX8_RST_OFF , APMU_DX8_CLK_RES_CTRL); + if( __raw_readl(APMU_DX8_CLK_RES_CTRL) != (DX8_CLK_EN | DX8_RST_OFF ) ) { + printk("error in close\n"); + return -1; + } + } + return 0; +} + +static int __init pxa_cnm_probe(struct platform_device *pdev) +{ + struct resource *r; + struct pxa_cnm *cnm = &pxa_cnm; + int irq, ret; + + platform_set_drvdata(pdev, cnm); +#if 0 + cnm->clk = clk_get(&pdev->dev, "CNMCLK"); + if (IS_ERR(cnm->clk)) { + dev_err(&pdev->dev, "failed to get cnm clock\n"); + goto out; + } + clk_enable(cnm->clk); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + goto fail_put_clk; + } + + pxa_cnm.irq = irq; + ret = request_irq(irq, pxa_cnm_irq, 0, "cnm", NULL); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto fail_free_irq; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) + goto fail_free_irq; +#endif + + r = request_mem_region(CNM_MEM_START, CNM_MEM_SIZE, "cnm"); + if (r == NULL) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + goto fail_free_irq; + } + + cnm->mmio_base = ioremap(CNM_MEM_START, CNM_MEM_SIZE); + if(cnm->mmio_base == NULL) + goto fail_free_res; + + ret = misc_register(&pxa_cnm_miscdev); + if (ret < 0) { + dev_err(&pdev->dev, + "unable to register device node /dev/cnm\n"); + goto fail_free_res; + } + + mutex_init(&pxa_cnm.lock); + + /*Probe*/ + printk(" CnM Probe\n"); + /*CLock Enable*/ + + if(pxa_cnm_clock(1) != 0){ + goto fail_free_res; + }; + + return 0; + +fail_free_res: + release_mem_region(r->start, r->end - r->start + 1); +fail_free_irq: + free_irq(irq, &pxa_cnm); +fail_put_clk: + clk_disable(pxa_cnm.clk); + clk_put(pxa_cnm.clk); +out: + return -ENODEV; +} + +static int pxa_cnm_remove(struct platform_device *pdev) +{ + misc_deregister(&pxa_cnm_miscdev); + //free_irq(pxa_cnm.irq, &pxa_cnm); + printk("CnM Remove\n"); + + return 0; +} + +#define pxa_cnm_suspend NULL +#define pxa_cnm_resume NULL + +static struct resource cnm_resource[] = { + [0] = { + .start = CNM_MEM_START, + .end = CNM_MEM_START + CNM_MEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_driver cnm_driver = { + .driver = { + .name = "pxa-cnm", + }, + .probe = pxa_cnm_probe, + .remove = pxa_cnm_remove, + .suspend = pxa_cnm_suspend, + .resume = pxa_cnm_resume, +}; + +static struct platform_device cnm_device = { + .name = "pxa-cnm", + .id = 0, + .dev = { + }, + .num_resources = ARRAY_SIZE(cnm_resource), + .resource = cnm_resource, +}; + +static int __init pxa_cnm_init(void) +{ + int ret; +//#if defined(CONFIG_PXA3xx_DVFM) +// dvfm_register("MVED", &dvfm_lock.dev_idx); +// dvfm_register_notifier(¬ifier_freq_block, +// DVFM_FREQUENCY_NOTIFIER); +//#endif + if(!cpu_is_pxa910_Ax()) + return -1; + ret = platform_driver_register(&cnm_driver); + platform_device_register(&cnm_device); + return ret; +} + +static void __exit pxa_cnm_exit(void) +{ +//#if defined(CONFIG_PXA3xx_DVFM) +// dvfm_unregister_notifier(¬ifier_freq_block, +// DVFM_FREQUENCY_NOTIFIER); +// dvfm_unregister("CNM", &dvfm_lock.dev_idx); +//#endif + platform_driver_unregister(&cnm_driver); +} + +module_init(pxa_cnm_init); +module_exit(pxa_cnm_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lei Wen (leiwen@marvell.com)"); +MODULE_DESCRIPTION("Chip and Media CodaDx8 codec Driver"); diff --git a/drivers/char/pxa910_ire.c b/drivers/char/pxa910_ire.c new file mode 100644 index 00000000000000..9724f72dcea22f --- /dev/null +++ b/drivers/char/pxa910_ire.c @@ -0,0 +1,1096 @@ +/* + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + + * (C) Copyright 2007 Marvell International Ltd. + * All Rights Reserved + */ +#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 + +#include +#include + +#define IRE_CTRL0 0x0000 +#define IRE_Y0STARTADDR 0x0010 +#define IRE_Y1STARTADDR 0x0014 +#define IRE_Y2STARTADDR 0x0018 +#define IRE_U0STARTADDR 0x001C +#define IRE_U1STARTADDR 0x0020 +#define IRE_U2STARTADDR 0x0024 +#define IRE_V0STARTADDR 0x0028 +#define IRE_V1STARTADDR 0x002C +#define IRE_V2STARTADDR 0x0030 +#define IRE_SIZE 0x0034 +#define IRE_PREROTATE_PITCH 0x0038 +#define IRE_Y0P0_START_ADDR 0x003C +#define IRE_Y1P0_START_ADDR 0x0040 +#define IRE_Y2P0_START_ADDR 0x0044 +#define IRE_U0P0_START_ADDR 0x0048 +#define IRE_U1P0_START_ADDR 0x004C +#define IRE_U2P0_START_ADDR 0x0050 +#define IRE_V0P0_START_ADDR 0x0054 +#define IRE_V1P0_START_ADDR 0x0058 +#define IRE_V2P0_START_ADDR 0x005C +#define IRE_POSTROTATE_PITCH 0x0060 +#define IRE_SRAM_CTRL 0x0070 +#define IRE_IRQ_RAW 0x0080 +#define IRE_IRQ_MASK 0x0084 +#define IRE_IRQ_STAT 0x0088 + +#define ire_write(dev, value, reg) writel(value, dev->ire_regs_base + reg) +#define ire_read(dev, reg) readl(dev->ire_regs_base + reg) + +#define EOF_TIMEOUT 50 + +/* #define DEBUG */ +struct ire_buffer_pair { + struct ire_buffer src; + struct ire_buffer dst; +}; + +struct ire_device { + struct device *dev; + struct clk *clk; + + unsigned char __iomem *ire_regs_base; + + struct ire_context *last_context; + + spinlock_t context_lock; + struct list_head context_list; + unsigned long context_count; + + unsigned long total_gmem_size; + + struct ire_fmt fmt; + unsigned int buf_count; + unsigned int buf_list; + struct ire_buffer_pair buf_pair[MAX_IRE_BUF_COUNT]; + struct completion eof[MAX_IRE_BUF_COUNT]; + struct semaphore buf_sem; + struct semaphore submit_sem; + + unsigned int irq; +}; + +struct ire_gmem { + struct list_head gmem_list; + atomic_t gmem_count; + struct page *gmem_pages; + size_t gmem_size; + unsigned long gmem_virt_addr; + unsigned long gmem_phys_addr; + struct ire_device *gmem_dev; +}; + +struct ire_mem_map { + struct list_head mmap_list; + struct ire_gmem *mmap_gmem; + unsigned long mmap_type; + unsigned long mmap_addr; + unsigned long mmap_pgoff; + unsigned long mmap_size; +}; + +struct ire_context { + struct list_head list; + struct ire_device *dev; + struct task_struct *task; + + struct list_head mem_map_list; + struct ire_mem_map *mem_map; + + unsigned long total_gmem_size; + unsigned long corner_y, corner_uv; +}; + +static struct ire_context *ire_create_context(struct ire_device *dev); +static void ire_free_context(struct ire_context *ctx); +static struct ire_gmem *ire_alloc_gmem(struct ire_context *ctx, size_t rsize); +static void ire_free_gmem(struct ire_context *ctx, struct ire_gmem *gmem); +static int ire_request_mem(struct file *file, struct ire_context *ctx, + struct ire_mem_req *req); +static void ire_release_mem(struct ire_context *ctx, unsigned long mmap_addr); +static int ire_submit(struct ire_context *ctx, struct ire_buffer *src, + struct ire_buffer *dst); +static int ire_release(struct inode *inode, struct file *file); +static int ire_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int ire_mmap(struct file *file, struct vm_area_struct *vma); +static int ire_fsync(struct file *file, struct dentry *dentry, int datasync); +static int ire_open(struct inode *inode, struct file *file); + + +static struct ire_device g_ire_dev; +#ifdef DEBUG +static void dump_ire_context(struct ire_context *ctx) +{ + struct ire_device *dev = ctx->dev; + int i; + + printk(KERN_INFO "=================ire_context==================\n"); + printk(KERN_INFO "dev:0x%08x\n", ctx); + printk(KERN_INFO "ire_regs_base: 0x%08x\n", dev->ire_regs_base); + printk(KERN_INFO "buf_count:%d\n", dev->buf_count); + printk(KERN_INFO "buf_list:%d\n", dev->buf_list); + printk(KERN_INFO "fmt.width: %d\n", dev->fmt.width); + printk(KERN_INFO "fmt.height: %d\n", dev->fmt.height); + printk(KERN_INFO "fmt.fmt: 0x%08x\n", dev->fmt.fmt); + printk(KERN_INFO "fmt.angle: 0x%08x\n", dev->fmt.rot_angle); + printk(KERN_INFO "fmt.y_pitch: %d\n", dev->fmt.y_pitch); + printk(KERN_INFO "fmt.uv_pitch: %d\n", dev->fmt.uv_pitch); + printk(KERN_INFO "fmt.yp0_pitch: %d\n", dev->fmt.yp0_pitch); + printk(KERN_INFO "fmt.uvp0_pitch: %d\n", dev->fmt.uvp0_pitch); + for (i = 0; i < dev->buf_count && dev->buf_count < MAX_IRE_BUF_COUNT; + i++) { + printk(KERN_INFO "buf_pair.src[%d].y_paddr:0x%08x\n", + i, dev->buf_pair[i].src.y_paddr); + printk(KERN_INFO "buf_pair.src[%d].u_paddr:0x%08x\n", + i, dev->buf_pair[i].src.u_paddr); + printk(KERN_INFO "buf_pair.src[%d].v_paddr:0x%08x\n", + i, dev->buf_pair[i].src.v_paddr); + printk(KERN_INFO "buf_pair.dst[%d].y_paddr:0x%08x\n", + i, dev->buf_pair[i].dst.y_paddr); + printk(KERN_INFO "buf_pair.dst[%d].u_paddr:0x%08x\n", + i, dev->buf_pair[i].dst.u_paddr); + printk(KERN_INFO "buf_pair.dst[%d].v_paddr:0x%08x\n", + i, dev->buf_pair[i].dst.v_paddr); + } + printk(KERN_INFO "\n"); + +} + +static void dump_ire_regs(struct ire_context *ctx) +{ + struct ire_device *dev = ctx->dev; + + printk(KERN_INFO "=================ire registers==================\n"); + printk(KERN_INFO "IRE_CTRL0:0x%08x\n", ire_read(dev, IRE_CTRL0)); + printk(KERN_INFO "IRE_Y0STARTADDR:0x%08x\n", + ire_read(dev, IRE_Y0STARTADDR)); + printk(KERN_INFO "IRE_Y1STARTADDR:0x%08x\n", + ire_read(dev, IRE_Y1STARTADDR)); + printk(KERN_INFO "IRE_Y2STARTADDR:0x%08x\n", + ire_read(dev, IRE_Y2STARTADDR)); + printk(KERN_INFO "IRE_U0STARTADDR:0x%08x\n", + ire_read(dev, IRE_U0STARTADDR)); + printk(KERN_INFO "IRE_U1STARTADDR:0x%08x\n", + ire_read(dev, IRE_U1STARTADDR)); + printk(KERN_INFO "IRE_U2STARTADDR:0x%08x\n", + ire_read(dev, IRE_U2STARTADDR)); + printk(KERN_INFO "IRE_V0STARTADDR:0x%08x\n", + ire_read(dev, IRE_V0STARTADDR)); + printk(KERN_INFO "IRE_V1STARTADDR:0x%08x\n", + ire_read(dev, IRE_V1STARTADDR)); + printk(KERN_INFO "IRE_V2STARTADDR:0x%08x\n", + ire_read(dev, IRE_V2STARTADDR)); + printk(KERN_INFO "IRE_SIZE:0x%08x\n", + ire_read(dev, IRE_SIZE)); + printk(KERN_INFO "IRE_PREROTATE_PITCH:0x%08x\n", + ire_read(dev, IRE_PREROTATE_PITCH)); + printk(KERN_INFO "IRE_Y0P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_Y0P0_START_ADDR)); + printk(KERN_INFO "IRE_Y1P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_Y1P0_START_ADDR)); + printk(KERN_INFO "IRE_Y2P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_Y2P0_START_ADDR)); + printk(KERN_INFO "IRE_U0P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_U0P0_START_ADDR)); + printk(KERN_INFO "IRE_U1P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_U1P0_START_ADDR)); + printk(KERN_INFO "IRE_U2P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_U2P0_START_ADDR)); + printk(KERN_INFO "IRE_V0P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_V0P0_START_ADDR)); + printk(KERN_INFO "IRE_V1P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_V1P0_START_ADDR)); + printk(KERN_INFO "IRE_V2P0_START_ADDR:0x%08x\n", + ire_read(dev, IRE_V2P0_START_ADDR)); + printk(KERN_INFO "IRE_POSTROTATE_PITCH:0x%08x\n", + ire_read(dev, IRE_POSTROTATE_PITCH)); + printk(KERN_INFO "IRE_SRAM_CTRL:0x%08x\n", + ire_read(dev, IRE_SRAM_CTRL)); + printk(KERN_INFO "IRE_IRQ_RAW:0x%08x\n", ire_read(dev, IRE_IRQ_RAW)); + printk(KERN_INFO "IRE_IRQ_MASK:0x%08x\n", ire_read(dev, IRE_IRQ_MASK)); + printk(KERN_INFO "IRE_IRQ_STAT:0x%08x\n", ire_read(dev, IRE_IRQ_STAT)); + printk(KERN_INFO "\n"); +} +#else +#define dump_ire_context(arg...) +#define dump_ire_regs(arg...) +#endif + +static struct ire_context *ire_create_context(struct ire_device *dev) +{ + struct ire_context *ctx; + unsigned long flags; + int i; + + if (dev == NULL) + return NULL; + + ctx = kzalloc(sizeof(struct ire_context), GFP_KERNEL); + if (ctx == NULL) + return NULL; + + ctx->dev = dev; + ctx->task = current; + + INIT_LIST_HEAD(&ctx->mem_map_list); + init_MUTEX(&dev->buf_sem); + init_MUTEX(&dev->submit_sem); + spin_lock_irqsave(&dev->context_lock, flags); + INIT_LIST_HEAD(&dev->context_list); + list_add_tail(&ctx->list, &dev->context_list); + dev->context_count++; + dev->buf_list = 0; + spin_unlock_irqrestore(&dev->context_lock, flags); + for (i = 0; i < MAX_IRE_BUF_COUNT; i++) + init_completion(&dev->eof[i]); + + return ctx; +} + +static void ire_free_context(struct ire_context *ctx) +{ + struct ire_mem_map *mmap, *m; + struct ire_device *dev; + unsigned long flags; + + if (ctx == NULL || ctx->dev == NULL) + return; + + dev = ctx->dev; + + list_for_each_entry_safe(mmap, m, &ctx->mem_map_list, mmap_list) { + if (mmap->mmap_gmem) + ire_free_gmem(ctx, mmap->mmap_gmem); + + list_del(&mmap->mmap_list); + kfree(mmap); + } + + spin_lock_irqsave(&dev->context_lock, flags); + list_del(&ctx->list); + dev->context_count--; + if (dev->last_context == ctx) + dev->last_context = NULL; + spin_unlock_irqrestore(&dev->context_lock, flags); + + kfree(ctx); +} + +static struct ire_gmem *ire_alloc_gmem(struct ire_context *ctx, size_t rsize) +{ + struct ire_device *dev = ctx->dev; + unsigned long size, order, kaddr; + struct page *page, *end_page; + struct ire_gmem *gmem; + + if (dev == NULL) + return NULL; + + gmem = kmalloc(sizeof(struct ire_gmem), GFP_KERNEL); + if (gmem == NULL) + return NULL; + + size = PAGE_ALIGN(rsize); + order = get_order(size); + + page = alloc_pages(GFP_KERNEL | GFP_DMA, order); + if (page == NULL) { + kfree(gmem); + return NULL; + } + + kaddr = (unsigned long)page_address(page); + end_page = page + (1 << order); + + memset(page_address(page), 0, size); + __cpuc_flush_kern_all(); + dmac_flush_range(kaddr, kaddr + PAGE_ALIGN(size)); + + gmem->gmem_size = size; + gmem->gmem_pages = page; + gmem->gmem_virt_addr = kaddr; + gmem->gmem_phys_addr = __pa(kaddr); + ctx->total_gmem_size += size; + dev->total_gmem_size += size; + + do { + atomic_set(&(page->_count), 1); + SetPageReserved(page); + page++; + } while (size -= PAGE_SIZE); + + /* free the otherwise unused pages. */ + while (page < end_page) { + atomic_set(&(page->_count), 1); + __free_page(page); + page++; + } + + atomic_set(&gmem->gmem_count, 1); + + return gmem; +} + +static void ire_free_gmem(struct ire_context *ctx, struct ire_gmem *gmem) +{ + struct ire_device *dev = ctx->dev; + struct page *page = gmem->gmem_pages; + size_t size = gmem->gmem_size; + + if (!atomic_dec_and_test(&gmem->gmem_count)) + return; + + ctx->total_gmem_size -= size; + dev->total_gmem_size -= size; + + for (; size > 0; size -= PAGE_SIZE, page++) { + ClearPageReserved(page); + __free_page(page); + } + + kfree(gmem); +} + +static int ire_request_mem( + struct file *file, + struct ire_context *ctx, + struct ire_mem_req *req) +{ + struct mm_struct *mm = current->mm; + struct ire_mem_map *mmap = NULL; + struct ire_gmem *gmem; + + + int ret; + + mmap = kmalloc(sizeof(struct ire_mem_map), GFP_KERNEL); + if (mmap == NULL) + return -ENOMEM; + + memset(mmap, 0, sizeof(struct ire_mem_map)); + + mmap->mmap_type = req->req_type; + if (req->req_size == 0) { + req->phys_addr = 0l; + req->mmap_addr = 0l; + req->mmap_size = 0l; + return 0; + } + + gmem = ire_alloc_gmem(ctx, req->req_size); + + if (gmem == NULL) + return -ENOMEM; + + mmap->mmap_gmem = gmem; + mmap->mmap_pgoff = gmem->gmem_phys_addr >> PAGE_SHIFT; + mmap->mmap_size = gmem->gmem_size; + list_add(&mmap->mmap_list, &ctx->mem_map_list); + + down_write(&mm->mmap_sem); + ctx->mem_map = mmap; + ret = do_mmap_pgoff(file, 0L, + mmap->mmap_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + mmap->mmap_pgoff); + ctx->mem_map = NULL; + up_write(&mm->mmap_sem); + + if (ret < 0 && ret > -1024) { + if (mmap->mmap_gmem) + ire_free_gmem(ctx, mmap->mmap_gmem); + + req->mmap_addr = 0l; + req->mmap_size = 0l; + kfree(mmap); + return ret; + } + + mmap->mmap_addr = (unsigned long)ret; + + req->mmap_addr = (unsigned long)ret; + req->mmap_size = mmap->mmap_size; + req->phys_addr = mmap->mmap_pgoff << PAGE_SHIFT; + + return 0; +} + +static void ire_release_mem(struct ire_context *ctx, unsigned long mmap_addr) +{ + struct mm_struct *mm = current->mm; + struct ire_mem_map *mmap, *m; + + list_for_each_entry_safe(mmap, m, &ctx->mem_map_list, mmap_list) { + if (mmap->mmap_addr == mmap_addr) { + down_write(&mm->mmap_sem); + do_munmap(mm, mmap->mmap_addr, mmap->mmap_size); + up_write(&mm->mmap_sem); + + if (mmap->mmap_gmem) + ire_free_gmem(ctx, mmap->mmap_gmem); + + list_del(&mmap->mmap_list); + kfree(mmap); + } + } +} + +static int ire_submit(struct ire_context *ctx, struct ire_buffer *src, + struct ire_buffer *dst) +{ + struct ire_device *dev; + unsigned int buf_id; + + dev = ctx->dev; + + down(&dev->submit_sem); + buf_id = find_first_zero_bit(&dev->buf_list, MAX_IRE_BUF_COUNT); + if (buf_id == MAX_IRE_BUF_COUNT) { + up(&dev->submit_sem); + return -EBUSY; + } + + set_bit(buf_id, &dev->buf_list); + memcpy(&dev->buf_pair[buf_id].src, src, sizeof(struct ire_buffer)); + memcpy(&dev->buf_pair[buf_id].dst, dst, sizeof(struct ire_buffer)); + + if (cpu_is_pxa910_Ax()){ + /* set src and dst buffer */ + unsigned long y_paddr, u_paddr, v_paddr; + + y_paddr = src->y_paddr + ctx->corner_y; + u_paddr = src->u_paddr + ctx->corner_uv; + v_paddr = src->v_paddr + ctx->corner_uv; + ire_write(dev, y_paddr, IRE_Y0STARTADDR + buf_id*4); + ire_write(dev, u_paddr, IRE_U0STARTADDR + buf_id*4); + ire_write(dev, v_paddr, IRE_V0STARTADDR + buf_id*4); + ire_write(dev, dst->y_paddr, IRE_Y0P0_START_ADDR + buf_id*4); + ire_write(dev, dst->u_paddr, IRE_U0P0_START_ADDR + buf_id*4); + ire_write(dev, dst->v_paddr, IRE_V0P0_START_ADDR + buf_id*4); + }else{ + /* set src and dst buffer */ + ire_write(dev, src->y_paddr, IRE_Y0STARTADDR + buf_id*4); + ire_write(dev, src->u_paddr, IRE_U0STARTADDR + buf_id*4); + ire_write(dev, src->v_paddr, IRE_V0STARTADDR + buf_id*4); + ire_write(dev, dst->y_paddr, IRE_Y0P0_START_ADDR + buf_id*4); + ire_write(dev, dst->u_paddr, IRE_U0P0_START_ADDR + buf_id*4); + ire_write(dev, dst->v_paddr, IRE_V0P0_START_ADDR + buf_id*4); + } + up(&dev->submit_sem); + + return buf_id; +} + +static int ire_desubmit(struct ire_context *ctx, unsigned int buf_id) +{ + struct ire_device *dev; + + dev = ctx->dev; + + down(&dev->submit_sem); + clear_bit(buf_id, &dev->buf_list); + up(&dev->submit_sem); + + return buf_id; +} + +static int ire_set_format(struct ire_context *ctx, struct ire_fmt *fmt) +{ + struct ire_device *dev = ctx->dev; + unsigned long y_pitch, uv_pitch; + unsigned long yp0_pitch, uvp0_pitch; + + y_pitch = 0; + uv_pitch = 0; + yp0_pitch = 0; + uv_pitch = 0; + + switch(fmt->fmt){ + case IRE_PIXFMT_RGB565: + y_pitch = fmt->width * 2; + yp0_pitch = fmt->height* 2; + if(fmt->rot_angle == IRE_ROT_180) + yp0_pitch = y_pitch; + break; + case IRE_PIXFMT_RGB888: + y_pitch = fmt->width * 4; + yp0_pitch = fmt->height* 4; + if(fmt->rot_angle == IRE_ROT_180) + yp0_pitch = y_pitch; + break; + case IRE_PIXFMT_YUV422_PACKED_UYVY:/*YUV422 packed mode => YUV 444 packed*/ + case IRE_PIXFMT_YUV422_PACKED_VYUV:/*YUV422 packed mode => YUV 444 packed*/ + case IRE_PIXFMT_YUV422_PACKED_YUYV:/*YUV422 packed mode => YUV 444 packed*/ + case IRE_PIXFMT_YUV422_PACKED_YVYU:/*YUV422 packed mode => YUV 444 packed*/ + y_pitch = fmt->width * 2; + if(fmt->rot_angle == IRE_ROT_180) + yp0_pitch = y_pitch; + else + yp0_pitch = fmt->height * 4; + break; + case IRE_PIXFMT_YUV420_PLANAR: + y_pitch = fmt->width; + uv_pitch = fmt->width/2; + yp0_pitch = fmt->height; + uvp0_pitch = fmt->height/2; + if(fmt->rot_angle == IRE_ROT_180) + printk(KERN_ERR "IRE dosen't support YUV420 180 degree rotation!\n"); + break; + + default: + printk(KERN_ERR "IRE dosen't support this type of rotation!\n"); + break; + } + + switch(fmt->rot_angle){ + case IRE_ROT_90: + ctx->corner_y = (fmt->height-1)*y_pitch; /*bottom/left corner*/ + break; + case IRE_ROT_270: + ctx->corner_y = 0; /*top/left corner*/ + break; + case IRE_ROT_180: + ctx->corner_y = (fmt->height)*y_pitch-1; /*bottom/right corner*/ + break; + } + + if((fmt->rot_angle == IRE_ROT_90) && (fmt->fmt == IRE_PIXFMT_YUV420_PLANAR)){ + ctx->corner_uv = uv_pitch*(fmt->height/2-1); + }else + ctx->corner_uv = 0; + + /* set format */ + ire_write(dev, fmt->height << 16 | fmt->width, IRE_SIZE);/*in pixels*/ + ire_write(dev, uv_pitch << 16 | y_pitch, IRE_PREROTATE_PITCH);/*in bytes*/ + ire_write(dev, uvp0_pitch << 16 | yp0_pitch, IRE_POSTROTATE_PITCH);/*in bytes*/ + + if (cpu_is_pxa910_Ax() && ((fmt->fmt == IRE_PIXFMT_YUV422_PACKED_UYVY) + || (fmt->fmt == IRE_PIXFMT_YUV422_PACKED_VYUV) + || (fmt->fmt == IRE_PIXFMT_YUV422_PACKED_YUYV) + || (fmt->fmt == IRE_PIXFMT_YUV422_PACKED_YVYU))){ + if(fmt->rot_angle == IRE_ROT_180) + ire_write(dev, fmt->fmt | 2 << 21 | fmt->rot_angle << 11 | 0 << 30, + IRE_CTRL0); + else + ire_write(dev, fmt->fmt | 1 << 21 | fmt->rot_angle << 11 | 0 << 30, + IRE_CTRL0); + }else{ + ire_write(dev, fmt->fmt | fmt->rot_angle << 11 | 0 << 30, + IRE_CTRL0); + } + + return 0; +} + +static int ire_dequeue(struct ire_context *ctx, unsigned int buf_id) +{ + struct ire_device *dev = ctx->dev; + unsigned int value; + int ret; + + down(&dev->buf_sem); + if (buf_id >= dev->buf_count) { + up(&dev->buf_sem); + return 0; + } + + ret = wait_for_completion_timeout(&dev->eof[buf_id], EOF_TIMEOUT); + if (!ret) { + INIT_COMPLETION(dev->eof[buf_id]); + printk(KERN_WARNING "%s: wait for EOF timeout!\n", __func__); + } + + /* disable eof interrupt*/ + value = ire_read(dev, IRE_IRQ_MASK); + value &= ~(1 << buf_id); + ire_write(dev, value, IRE_IRQ_MASK); + + dev->buf_count--; + up(&dev->buf_sem); + + return 0; +} + +static int +ire_enqueue(struct ire_context *ctx, unsigned int buf_id) +{ + struct ire_device *dev = ctx->dev; + unsigned int value; + + down(&dev->buf_sem); + if (buf_id >= MAX_IRE_BUF_COUNT) { + up(&dev->buf_sem); + return -EIO; + } + + /* enable interrupt*/ + value = ire_read(dev, IRE_IRQ_MASK); + value |= 1 << buf_id; + ire_write(dev, value, IRE_IRQ_MASK); + + dev->buf_count++; + up(&dev->buf_sem); + + return 0; +} + +static int +ire_enable(struct ire_context *ctx) +{ + struct ire_device *dev = ctx->dev; + unsigned int value; + + if (dev->buf_count == 0) + return -EIO; + + dump_ire_context(ctx); + dump_ire_regs(ctx); + /* enable IRE */ + value = ire_read(dev, IRE_CTRL0); + value |= 0x1; + ire_write(dev, value, IRE_CTRL0); + + return 0; +} + +static int +ire_disable(struct ire_context *ctx) +{ + struct ire_device *dev = ctx->dev; + unsigned int value; + + /* disable IRE */ + value = ire_read(dev, IRE_CTRL0); + value &= ~0x1; + ire_write(dev, value, IRE_CTRL0); + + return 0; +} + +static irqreturn_t +ire_irq_handler(int irq, void *dev_id) +{ + struct ire_device *dev = dev_id; + int status; + + status = ire_read(dev, IRE_IRQ_STAT); + + if (status & 0x1) + complete(&dev->eof[0]); + if (status & 0x2) + complete(&dev->eof[1]); + if (status & 0x4) + complete(&dev->eof[2]); + + ire_write(dev, status, IRE_IRQ_STAT); + + return IRQ_HANDLED; +} + +static int +ire_open(struct inode *inode, struct file *file) +{ + struct ire_context *ctx = file->private_data; + struct ire_device *dev; + + dev = &g_ire_dev; + ctx = ire_create_context(dev); + if (!ctx) + return -ENOMEM; + + file->private_data = ctx; + + return 0; +} + +static int +ire_release(struct inode *inode, struct file *file) +{ + struct ire_context *ctx = file->private_data; + + ire_free_context(ctx); + + return 0; +} + +static struct page *__ire_follow_page(struct mm_struct *mm, + unsigned long address) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep, pte; + unsigned long pfn; + struct page *page; + + pgd = pgd_offset(mm, address); + if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) + goto out; + + pud = pud_offset(pgd, address); + if (pud_none(*pud) || unlikely(pud_bad(*pud))) + goto out; + + pmd = pmd_offset(pud, address); + if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) + goto out; + + ptep = pte_offset_map(pmd, address); + if (!ptep) + goto out; + + pte = *ptep; + pte_unmap(ptep); + if (pte_present(pte)) { + pfn = pte_pfn(pte); + if (pfn_valid(pfn)) { + page = pfn_to_page(pfn); + return page; + } + } + +out: + return NULL; +} + +static int ire_fsync(struct file *file, struct dentry *dentry, int datasync) +{ + struct ire_context *ctx; + struct ire_device *dev; + int error = 0; + int i; + + ctx = (struct ire_context *)(file->private_data); + dev = ctx->dev; + for (i = 0; i < dev->buf_count; i++) + error = ire_dequeue(ctx, i); + + return error; +} +static int ire_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ire_context *ctx = file->private_data; + struct ire_device *dev = (struct ire_device *)ctx->dev; + + if (dev == NULL) + return -ENODEV; + + switch (cmd) { + /* request buffer */ + case IREIO_REQUEST_MEM: + { + struct ire_mem_req mem_req; + void __user *argp = (void *)arg; + int ret; + + if (copy_from_user(&mem_req, argp, sizeof(mem_req))) + return -EFAULT; + + ret = ire_request_mem(file, ctx, &mem_req); + if (ret < 0) + return ret; + + if (copy_to_user(argp, &mem_req, sizeof(mem_req))) { + ire_release_mem(ctx, mem_req.mmap_addr); + return -EFAULT; + } + + return 0; + } + + /* release memory */ + case IREIO_RELEASE_MEM: + { + unsigned long mmap_addr = arg; + ire_release_mem(ctx, mmap_addr); + return 0; + } + + case IREIO_FLUSH_MEM: + { + struct ire_mem_map *mmap; + list_for_each_entry(mmap, &ctx->mem_map_list, mmap_list) { + if (mmap->mmap_addr == arg) + __cpuc_flush_user_range( + mmap->mmap_addr, + mmap->mmap_size, + 0); + } + return 0; + } + + case IREIO_S_FMT: + { + void __user *argp = (void *)arg; + if (copy_from_user(&dev->fmt, argp, sizeof(struct ire_fmt))) + return -EFAULT; + return ire_set_format(ctx, &dev->fmt); + } + + case IREIO_G_FMT: + { + void __user *argp = (void *)arg; + if (copy_to_user((void *)argp, &dev->fmt, + sizeof(struct ire_fmt))) + return -EFAULT; + return 0; + } + + case IREIO_SUBMIT: + { + struct ire_submit_req submit_req; + void __user *argp = (void *)arg; + int ret; + + if (copy_from_user(&submit_req, argp, sizeof(submit_req))) + return -EFAULT; + + ret = ire_submit(ctx, &submit_req.src, &submit_req.dst); + submit_req.buf_id = ret; + if (copy_to_user(argp, &submit_req, sizeof(submit_req))) + return -EFAULT; + if (ret < 0) + return -EBUSY; + return 0; + } + + case IREIO_DESUBMIT: + { + unsigned int buf_id = arg; + + return ire_desubmit(ctx, buf_id); + } + + case IREIO_ENQUEUE: + { + unsigned int buf_id = arg; + + return ire_enqueue(ctx, buf_id); + } + + + case IREIO_DEQUEUE: + { + unsigned int buf_id = arg; + + return ire_dequeue(ctx, buf_id); + } + + case IREIO_ENABLE: + return ire_enable(ctx); + + case IREIO_DISABLE: + return ire_disable(ctx); + + case IREIO_GET_BUS_ADDR: + { + /* assume user provided virtual memory are physical + * contiguous + */ + unsigned long virt_addr; + unsigned long phys_addr; + struct page *page; + + if (copy_from_user(&virt_addr, (void *)arg, sizeof(virt_addr))) + return -EFAULT; + + page = __ire_follow_page(current->mm, virt_addr); + if (page == NULL) + return -EINVAL; + phys_addr = __pa(page_address(page)) + virt_addr % PAGE_SIZE; + + if (copy_to_user((void *)arg, &phys_addr, sizeof(phys_addr))) + return -EFAULT; + + return 0; + } + default: + break; + } + + return 0; +} + +static int ire_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ire_context *ctx = file->private_data; + struct ire_mem_map *mmap = ctx->mem_map; + pgprot_t pgprot; + + if (ctx == NULL) + return -ENODEV; + + /* Within 4GB address space? */ + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + + /* DO NOT allow arbitrary memory map request */ + if (mmap == NULL) + return -EPERM; + + if (mmap->mmap_size != (vma->vm_end - vma->vm_start)) + return -EINVAL; + + switch (IRE_MEM_REQ_ATTR(mmap->mmap_type)) { + case IRE_ATTR_COHERENT: + pgprot = pgprot_noncached(vma->vm_page_prot); + break; + case IRE_ATTR_WRITECOMBINE: + pgprot = pgprot_writecombine(vma->vm_page_prot); + break; + case IRE_ATTR_CACHEABLE: + default: + pgprot = vma->vm_page_prot; + break; + } + + vma->vm_page_prot = pgprot; + vma->vm_flags |= (VM_IO | VM_RESERVED); + + if (remap_pfn_range(vma, vma->vm_start, + mmap->mmap_pgoff, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + /* ire_flush_gmem(NULL); FIXME later*/ + +#ifdef DEBUG_CACHE_COHERENCY + dump_page_table_entries(vma->vm_start, vma->vm_end, mmap->mmap_gmem); +#endif + return 0; +} + + +static struct file_operations pxa910_ire_fops = { + .owner = THIS_MODULE, + .open = ire_open, + .release = ire_release, + .ioctl = ire_ioctl, + .mmap = ire_mmap, + .fsync = ire_fsync, +}; + +static struct miscdevice pxa910_ire_miscdev = { + .minor = PXA910_IRE_MINOR, + .name = "ire", + .fops = &pxa910_ire_fops, +}; + +static int __devinit pxa910_ire_probe(struct platform_device *pdev) +{ + struct resource *res; + struct ire_device *dev = &g_ire_dev; + int ret, size, irq; + + ret = misc_register(&pxa910_ire_miscdev); + if (ret) { + printk(KERN_WARNING "%s: failed to register misc device\n", + __func__); + goto exit; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + size = res->end - res->start + 1; + dev->ire_regs_base = ioremap_nocache(res->start, size); + if (dev->ire_regs_base == NULL) { + printk(KERN_WARNING "failed to request register memory\n"); + ret = -EBUSY; + goto failed_deregmisc; + } + irq = platform_get_irq(pdev, 0); + ret = request_irq(irq, ire_irq_handler, + IRQF_DISABLED, "ire", dev); + dev->irq = irq; + + if (ret) { + printk(KERN_WARNING "failed to request IRQ\n"); + goto failed_freemem; + } + + platform_set_drvdata(pdev, dev); + + return 0; + +failed_freemem: + iounmap(dev->ire_regs_base); +failed_deregmisc: + misc_deregister(&pxa910_ire_miscdev); +exit: + return ret; +} + +static int __devexit pxa910_ire_remove(struct platform_device *pdev) +{ + struct ire_device *dev = platform_get_drvdata(pdev); + int irq; + + irq = platform_get_irq(pdev, 0); + free_irq(irq, dev); + iounmap(dev->ire_regs_base); + misc_deregister(&pxa910_ire_miscdev); + + return 0; +} + +static struct platform_driver pxa910_ire_driver = { + .driver = { + .name = "pxa910-ire", + }, + .probe = pxa910_ire_probe, + .remove = __devexit_p(pxa910_ire_remove) +}; + +static int __init pxa910_ire_init(void) +{ + return platform_driver_register(&pxa910_ire_driver); +} + +static void __exit pxa910_ire_exit(void) +{ + platform_driver_unregister(&pxa910_ire_driver); +} + +module_init(pxa910_ire_init); +module_exit(pxa910_ire_exit); + +MODULE_DESCRIPTION("PXA910 Image Rotation"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/char/pxa930_acipc.c b/drivers/char/pxa930_acipc.c new file mode 100644 index 00000000000000..e727562c894f04 --- /dev/null +++ b/drivers/char/pxa930_acipc.c @@ -0,0 +1,653 @@ +/* + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + + * (C) Copyright 2006 Marvell International Ltd. + * All Rights Reserved + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct pxa930_acipc { + struct resource *mem; + void __iomem *mmio_base; + int irq[5]; + char irq_name[5][20]; + u32 IIR_val; + acipc_database acipc_db; + u32 bind_event_arg; + wait_queue_head_t acipc_wait_q; + int poll_status; + spinlock_t poll_lock; + u32 historical_events; + struct DDR_status g_ddr_status; +}; + +const acipc_events pxa910_acipc_priority_table[ACIPC_NUMBER_OF_EVENTS] = { + ACIPC_RINGBUF_TX_STOP, + ACIPC_RINGBUF_TX_RESUME, + ACIPC_PORT_FLOWCONTROL, + ACIPC_SPARE, + ACIPC_SPARE, + ACIPC_SPARE, + ACIPC_SPARE, + ACIPC_SPARE, + ACIPC_SHM_PACKET_NOTIFY, + ACIPC_IPM +}; + +const acipc_events acipc_priority_table[ACIPC_NUMBER_OF_EVENTS] = { + ACIPC_DDR_READY_REQ, + ACIPC_DDR_260_READY_REQ, + ACIPC_DDR_RELQ_REQ, + ACIPC_DDR_260_RELQ_REQ, + ACIPC_DATA_IND, + ACIPC_MSL_WAKEUP_REQ, + ACIPC_MSL_WAKEUP_ACK, + ACIPC_MSL_SLEEP_ALLOW, + ACIPC_SPARE, + ACIPC_SPARE +}; + +struct pxa930_acipc *acipc; + +/*PXA930 ACIPC registers*/ +#define IPC_WDR 0x0004 +#define IPC_ISRW 0x0008 +#define IPC_ICR 0x000C +#define IPC_IIR 0x0010 +#define IPC_RDR 0x0014 + +#define acipc_readl(off) __raw_readl(acipc->mmio_base + (off)) +#define acipc_writel(off, v) __raw_writel((v), acipc->mmio_base + (off)) + +/* read IIR*/ +#define ACIPC_IIR_READ(IIR_val) \ +{ \ + /* dummy write to latch the IIR value*/ \ + acipc_writel(IPC_IIR, 0x0); \ + barrier(); \ + (IIR_val) = acipc_readl(IPC_IIR); \ +} + +static u32 acipc_default_callback(u32 status) +{ + IPC_ENTER(); + /* getting here means that the client didn't yet bind his callback. + * we will save the event until the bind occur + */ + acipc->acipc_db.historical_event_status += status; + + IPC_LEAVE(); + return 0 ; +} + +acipc_return_code acipc_event_set(acipc_events user_event) +{ + IPC_ENTER(); + acipc_writel(IPC_ISRW, user_event); + IPCTRACE("acipc_event_set userEvent 0x%x\n", user_event); + + IPC_LEAVE(); + return ACIPC_RC_OK; +} + +static void acipc_int_enable(acipc_events event) +{ + IPCTRACE("acipc_int_enable event 0x%x\n", event); + + if (event & ACIPC_INT0_EVENTS) { + /* for the first 8 bits we enable the INTC_AC_IPC_0 + * only if this the first event that has been binded + */ + if (!(acipc->acipc_db.int0_events_cnt)) + enable_irq(acipc->irq[0]); + + acipc->acipc_db.int0_events_cnt++; + return; + } + if (cpu_is_pxa910()) { + if (event & PXA910_ACIPC_INT1_EVENTS) { + enable_irq(acipc->irq[1]); + return; + } + if (event & PXA910_ACIPC_INT2_EVENTS) { + enable_irq(acipc->irq[2]); + return; + } + } else { + if (event & ACIPC_INT1_EVENTS) { + enable_irq(acipc->irq[1]); + return; + } + if (event & ACIPC_INT2_EVENTS) { + enable_irq(acipc->irq[2]); + return; + } + } +} + +static void acipc_int_disable(acipc_events event) +{ + IPC_ENTER(); + + IPCTRACE("acipc_int_disable event 0x%x\n", event); + if (event & ACIPC_INT0_EVENTS) { + acipc->acipc_db.int0_events_cnt--; + /* for the first 8 bits we disable INTC_AC_IPC_0 + * only if this is the last unbind event + */ + if (!(acipc->acipc_db.int0_events_cnt)) + disable_irq(acipc->irq[0]); + return; + } + if (cpu_is_pxa910()) { + if (event & PXA910_ACIPC_INT1_EVENTS) { + disable_irq(acipc->irq[1]); + return; + } + if (event & PXA910_ACIPC_INT2_EVENTS) { + disable_irq(acipc->irq[2]); + return; + } + } else { + if (event & ACIPC_INT1_EVENTS) { + disable_irq(acipc->irq[1]); + return; + } + if (event & ACIPC_INT2_EVENTS) { + disable_irq(acipc->irq[2]); + return; + } + } + +} + +void acipc_change_driver_state(int is_DDR_ready) +{ + IPC_ENTER(); + + IPCTRACE("acipc_change_driver_state isDDRReady %d\n", is_DDR_ready); + if (is_DDR_ready) + acipc->acipc_db.driver_mode = ACIPC_CB_NORMAL; + else + acipc->acipc_db.driver_mode = ACIPC_CB_ALWAYS_NO_DDR; + + IPC_LEAVE(); +} + +acipc_return_code acipc_data_send(acipc_events user_event, acipc_data data) +{ + IPC_ENTER(); + IPCTRACE("acipc_data_send userEvent 0x%x, data 0x%x\n", + user_event, data); + /* writing the data to WDR*/ + acipc_writel(IPC_WDR, data); + + /* fire the event to the other subsystem + * to indicate the data is ready for read + */ + acipc_writel(IPC_ISRW, user_event); + + IPC_LEAVE(); + return ACIPC_RC_OK; +} + +acipc_return_code acipc_data_read(acipc_data *data) +{ + IPC_ENTER(); + /* reading the data from RDR*/ + *data = acipc_readl(IPC_RDR); + + IPC_LEAVE(); + + return ACIPC_RC_OK ; +} + +acipc_return_code acipc_event_status_get(u32 user_event, u32 *status) +{ + u32 IIR_local_val; + + IPC_ENTER(); + /* reading the status from IIR*/ + ACIPC_IIR_READ(IIR_local_val); + + /* clear the events hw status*/ + acipc_writel(IPC_ICR, user_event); + + /* verify that this event will be cleared from the global IIR variable. + * for cases this API is called from user callback + */ + acipc->IIR_val &= ~(user_event); + + *status = IIR_local_val & user_event; + + IPC_LEAVE(); + return ACIPC_RC_OK; +} + +acipc_return_code acipc_event_bind(u32 user_event, acipc_rec_event_callback cb, + + acipc_callback_mode cb_mode, u32 *historical_event_status) +{ + u32 i; + + IPC_ENTER(); + for (i = 0; i < ACIPC_NUMBER_OF_EVENTS; i++) { + if (acipc->acipc_db.event_db[i].IIR_bit & user_event) { + if (acipc->acipc_db.event_db[i].cb != + acipc_default_callback) + return ACIPC_EVENT_ALREADY_BIND; + else{ + acipc->acipc_db.event_db[i].cb = cb; + acipc->acipc_db.event_db[i].mode = cb_mode; + acipc->acipc_db.event_db[i].mask = user_event & + acipc->acipc_db.event_db[i].IIR_bit; + acipc_int_enable( + acipc->acipc_db.event_db[i].IIR_bit); + } + } + } + /* if there were historical events*/ + if (acipc->acipc_db.historical_event_status & user_event) { + *historical_event_status = + acipc->acipc_db.historical_event_status & user_event; + /* clear the historical events from the database*/ + acipc->acipc_db.historical_event_status &= ~user_event; + return ACIPC_HISTORICAL_EVENT_OCCUR; + } + + IPC_LEAVE(); + return ACIPC_RC_OK; +} + +acipc_return_code acipc_event_unbind(u32 user_event) +{ + u32 i; + + IPC_ENTER(); + for (i = 0; i < ACIPC_NUMBER_OF_EVENTS; i++) { + if (acipc->acipc_db.event_db[i].mask & user_event) { + if (acipc->acipc_db.event_db[i].IIR_bit & user_event) { + acipc->acipc_db.event_db[i].cb = + acipc_default_callback; + acipc->acipc_db.event_db[i].mode = + ACIPC_CB_NORMAL; + acipc->acipc_db.event_db[i].mask = 0; + acipc_int_disable( + acipc->acipc_db.event_db[i].IIR_bit); + } + /*clean this event from other event's mask*/ + acipc->acipc_db.event_db[i].mask &= ~user_event; + } + } + + IPC_LEAVE(); + return ACIPC_RC_OK; +} + +static irqreturn_t acipc_interrupt_handler(int irq, void *dev_id) +{ + u32 i, on_events; + + IPC_ENTER(); + ACIPC_IIR_READ(acipc->IIR_val); /* read the IIR*/ + + /*using ACIPCEventStatusGet might cause getting here with IIR=0*/ + if (acipc->IIR_val) { + for (i = 0; i < ACIPC_NUMBER_OF_EVENTS; i++) { + if ((acipc->acipc_db.event_db[i].IIR_bit & + acipc->IIR_val) && (acipc->acipc_db. + event_db[i].mode == ACIPC_CB_NORMAL)) { + on_events = (acipc->IIR_val)& + (acipc->acipc_db.event_db[i].mask); + /* call the user callback with the status of + * other events as define when the user called + * ACIPCEventBind + */ + acipc->acipc_db.event_db[i].cb(on_events); + /* clean the event(s)*/ + acipc_writel(IPC_ICR, on_events); + /* if more then one event exist we clear + * the rest of the bits from the global + * IIR_val so user callback will be called + * only once. */ + + acipc->IIR_val &= (~on_events); + } + } + } + + IPC_LEAVE(); + + return IRQ_HANDLED; +} + +u32 user_callback(u32 events_status) +{ + acipc->bind_event_arg = events_status; + acipc->poll_status = 1; + wake_up_interruptible(&acipc->acipc_wait_q); + + return 0; +} + +static void set_constraint(void) {} + +u32 acipc_kernel_callback(u32 events_status) +{ + if (cpu_is_pxa910()) + return 0; + + IPC_ENTER(); + + if (events_status & ACIPC_DDR_READY_REQ) { + acipc_event_set(ACIPC_DDR_READY_ACK); + /* Remove incorrect bit */ + if (events_status & ACIPC_DDR_RELQ_REQ) + events_status &= ~ACIPC_DDR_RELQ_REQ; + acipc_change_driver_state(1); + } + if (events_status & ACIPC_DDR_RELQ_REQ) { + acipc_event_set(ACIPC_DDR_RELQ_ACK); + acipc_change_driver_state(0); + } + if (events_status & ACIPC_DDR_260_READY_REQ) { + acipc_event_set(ACIPC_DDR_260_READY_ACK); + acipc_change_driver_state(1); + } + + if (events_status & ACIPC_DDR_260_RELQ_REQ) { + acipc_event_set(ACIPC_DDR_260_RELQ_ACK); + acipc_change_driver_state(0); + } + IPC_LEAVE(); + + return 0; +} + +void register_pm_events(void) +{ + if (cpu_is_pxa910()) + return; + + acipc_event_bind(ACIPC_DDR_RELQ_REQ | ACIPC_DDR_260_RELQ_REQ | + ACIPC_DDR_260_READY_REQ | ACIPC_DDR_READY_REQ, + acipc_kernel_callback, ACIPC_CB_NORMAL, + &acipc->historical_events); +} + +static int acipc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct acipc_ioctl_arg acipc_arg; + u32 status; + int ret = 0; + + IPC_ENTER(); + + copy_from_user(&acipc_arg, (void *)arg, + sizeof(struct acipc_ioctl_arg)); + + switch (cmd) { + case ACIPC_SET_EVENT: + acipc_event_set(acipc_arg.set_event); + break; + case ACIPC_GET_EVENT: + acipc_event_status_get(acipc_arg.arg, &status); + acipc_arg.arg = status; + break; + case ACIPC_SEND_DATA: + acipc_data_send(acipc_arg.set_event, acipc_arg.arg); + break; + case ACIPC_READ_DATA: + acipc_data_read(&acipc_arg.arg); + break; + case ACIPC_BIND_EVENT: + acipc_event_bind(acipc_arg.arg, user_callback, + acipc_arg.cb_mode, &status); + acipc_arg.arg = status; + break; + case ACIPC_UNBIND_EVENT: + acipc_event_unbind(acipc_arg.arg); + break; + case ACIPC_GET_BIND_EVENT_ARG: + acipc_arg.arg = acipc->bind_event_arg; + break; + default: + ret = -1; + break; + } + + copy_to_user((void *)arg, &acipc_arg, sizeof(struct acipc_ioctl_arg)); + + IPC_LEAVE(); + + return ret; +} + +static unsigned int acipc_poll(struct file *file, poll_table *wait) +{ + IPC_ENTER(); + + poll_wait(file, &acipc->acipc_wait_q, wait); + + IPC_LEAVE(); + + if (acipc->poll_status == 0) { + return 0; + } else{ + acipc->poll_status = 0; + return POLLIN | POLLRDNORM; + } +} + +static struct file_operations acipc_fops = { + .owner = THIS_MODULE, + .ioctl = acipc_ioctl, + .poll = acipc_poll, +}; + +static struct miscdevice acipc_miscdev = { + .minor = ACIPC_MINOR, + .name = "acipc", + .fops = &acipc_fops, +}; + +static int __devinit pxa930_acipc_probe(struct platform_device *pdev) +{ + struct resource *res; + int size; + int ret = -EINVAL, irq, *irq_name; + int i; + + ret = misc_register(&acipc_miscdev); + if (ret < 0) + return ret; + + acipc = kzalloc(sizeof(struct pxa930_acipc), GFP_KERNEL); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + size = res->end - res->start + 1; + acipc->mem = request_mem_region(res->start, size, pdev->name); + if (acipc->mem == NULL) { + dev_err(&pdev->dev, "failed to request register memory\n"); + ret = -EBUSY; + goto failed_deregmisc; + } + + acipc->mmio_base = ioremap_nocache(res->start, size); + if (acipc->mmio_base == NULL) { + dev_err(&pdev->dev, "failed to ioremap registers\n"); + ret = -ENXIO; + goto failed_freemem; + } + + set_constraint(); + + /*init driver database*/ + if (cpu_is_pxa910()) { + for (i = 0; i < ACIPC_NUMBER_OF_EVENTS; i++) { + acipc->acipc_db.event_db[i].IIR_bit = + pxa910_acipc_priority_table[i]; + acipc->acipc_db.event_db[i].cb = acipc_default_callback; + acipc->acipc_db.event_db[i].mode = ACIPC_CB_NORMAL; + acipc->acipc_db.event_db[i].mask = + pxa910_acipc_priority_table[i]; + } + } else{ + for (i = 0; i < ACIPC_NUMBER_OF_EVENTS; i++) { + acipc->acipc_db.event_db[i].IIR_bit = + acipc_priority_table[i]; + acipc->acipc_db.event_db[i].cb = + acipc_default_callback; + acipc->acipc_db.event_db[i].mode = ACIPC_CB_NORMAL; + acipc->acipc_db.event_db[i].mask = + acipc_priority_table[i]; + } + } + acipc->acipc_db.driver_mode = ACIPC_CB_NORMAL; + acipc->acipc_db.int0_events_cnt = 0; + + init_waitqueue_head(&acipc->acipc_wait_q); + spin_lock_init(&acipc->poll_lock); + + platform_set_drvdata(pdev, acipc); + + for (i = 0; i < 3; i++) { + irq = platform_get_irq(pdev, i); + acipc->irq[i] = irq; + irq_name = acipc->irq_name[i]; + memset(irq_name, 0, 20); + sprintf(irq_name, "pxa930-ACIPC%d", i); + if (irq >= 0) { + ret = request_irq(irq, acipc_interrupt_handler, + IRQF_DISABLED | IRQF_TRIGGER_HIGH, + irq_name, acipc); + disable_irq(irq); + } + if (irq < 0 || ret < 0) { + ret = -ENXIO; + goto failed_freeirqs; + } + acipc->irq[i] = irq; + } + + register_pm_events(); + + pr_info("pxa930 AC-IPC initialized!\n"); + + return 0; + +failed_freeirqs: + for (i = 0; i < 3; i++) + if (acipc->irq[i] >= 0) + free_irq(acipc->irq[i], acipc); +failed_freemem: + iounmap(acipc->mmio_base); + release_resource(acipc->mem); + kfree(acipc->mem); +failed_deregmisc: + misc_deregister(&acipc_miscdev); + return ret; +} + +static int __devexit pxa930_acipc_remove(struct platform_device *pdev) +{ + struct pxa930_acipc *acipc = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < 3; i++) { + disable_irq(acipc->irq[i]); + free_irq(acipc->irq[i], acipc); + } + + iounmap(acipc->mmio_base); + + release_resource(acipc->mem); + kfree(acipc->mem); + + kfree(acipc); + return 0; +} + +static struct platform_driver pxa930_acipc_driver = { + .driver = { + .name = "pxa930-acipc", + }, + .probe = pxa930_acipc_probe, + .remove = __devexit_p(pxa930_acipc_remove) +}; + +static int __init pxa930_acipc_init(void) +{ + return platform_driver_register(&pxa930_acipc_driver); +} + +static void __exit pxa930_acipc_exit(void) +{ + platform_driver_unregister(&pxa930_acipc_driver); +} + +acipc_return_code ACIPCEventBind(u32 user_event, acipc_rec_event_callback cb, + acipc_callback_mode cb_mode, u32 *historical_event_status) +{ + return acipc_event_bind(user_event, cb, cb_mode, historical_event_status); +} +EXPORT_SYMBOL(ACIPCEventBind); + +acipc_return_code ACIPCEventUnBind(u32 user_event) +{ + return acipc_event_unbind(user_event); +} +EXPORT_SYMBOL(ACIPCEventUnBind); + +acipc_return_code ACIPCEventSet(acipc_events user_event) +{ + return acipc_event_set(user_event); +} +EXPORT_SYMBOL(ACIPCEventSet); + +acipc_return_code ACIPCDataSend(acipc_events user_event, acipc_data data) +{ + return acipc_data_send(user_event, data); +} +EXPORT_SYMBOL(ACIPCDataSend); + +acipc_return_code ACIPCDataRead(acipc_data *data) +{ + return acipc_data_read(data); +} +EXPORT_SYMBOL(ACIPCDataRead); + +module_init(pxa930_acipc_init); +module_exit(pxa930_acipc_exit); +MODULE_AUTHOR("MARVELL"); +MODULE_DESCRIPTION("AC-IPC driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index a8c8d9c19d740d..335f28b9fa280f 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -190,4 +190,17 @@ config CPU_FREQ_GOV_CONSERVATIVE If in doubt, say N. +config CPU_FREQ_MIN_TICKS + int "Ticks between governor polling interval." + default 10 + help + Minimum number of ticks between polling interval for governors. + +config CPU_FREQ_SAMPLING_LATENCY_MULTIPLIER + int "Sampling rate multiplier for governors." + default 1000 + help + Sampling latency rate multiplied by the cpu switch latency. + Affects governor polling. + endif # CPU_FREQ diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index bd444dc93cf2eb..30433f05bb21e7 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -579,7 +579,9 @@ static void do_dbs_timer(struct work_struct *work) /* We want all CPUs to do sampling nearly on same jiffy */ int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); - delay -= jiffies % delay; + if (num_online_cpus() > 1) + delay -= jiffies % delay; + mutex_lock(&dbs_info->timer_mutex); /* Common NORMAL_SAMPLE setup */ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index fee678f74a1919..162d3b4623cbb4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -157,6 +157,14 @@ config GPIO_PCA953X This driver can also be built as a module. If so, the module will be called pca953x. +config GPIO_PCA953X_GENERIC_IRQ + bool "Generic IRQ support for PCA953X" + depends on GPIO_PCA953X=y && GENERIC_HARDIRQS + help + Say yes here to support the Generic IRQ for the PCA953X on-chip + GPIO lines. Only pin-changed IRQs (IRQ_TYPE_EDGE_BOTH) are + supported in hardware. + config GPIO_PCA953X_IRQ bool "Interrupt controller support for PCA953x" depends on GPIO_PCA953X=y @@ -235,6 +243,17 @@ config GPIO_ADP5588 To compile this driver as a module, choose M here: the module will be called adp5588-gpio. +config MAX8660 + boolean "Max8660 chip" + depends on I2C && I2C_PXA + select PXA3xx_PMIC + default n + help + Max8660 is a PMIC chip. + If you say yes here, you support for it. + + This driver can NOT be built as a module. + comment "PCI GPIO expanders:" config GPIO_CS5535 @@ -291,6 +310,27 @@ config GPIO_MCP23S08 SPI driver for Microchip MCP23S08 I/O expander. This provides a GPIO interface supporting inputs and outputs. +config GPIO_PCA9575 + tristate "PCA9575 I/O ports" + depends on I2C + help + Say yes here to provide access to several register-oriented + SMBus I/O expanders, made mostly by NXP or TI. Compatible + models include: + + 16 bits: pca9575 + + This driver can also be built as a module. If so, the module + will be called pca9575 + +config GPIO_PCA9575_GENERIC_IRQ + bool "Generic IRQ support for PCA9575" + depends on GPIO_PCA9575=y && GENERIC_HARDIRQS + help + Say yes here to support the Generic IRQ for the PCA9575 on-chip + GPIO lines. Only pin-changed IRQs (IRQ_TYPE_EDGE_BOTH) are + supported in hardware. + config GPIO_MC33880 tristate "Freescale MC33880 high-side/low-side switch" depends on SPI_MASTER @@ -310,4 +350,5 @@ config GPIO_UCB1400 To compile this driver as a module, choose M here: the module will be called ucb1400_gpio. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 10f3f8d958b1f7..d3f469ac346fc6 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -22,9 +22,10 @@ obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o +obj-$(CONFIG_GPIO_PCA9575) += pca9575.o obj-$(CONFIG_GPIO_IT8761E) += it8761e_gpio.o obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o obj-$(CONFIG_GPIO_WM8350) += wm8350-gpiolib.o obj-$(CONFIG_GPIO_WM8994) += wm8994-gpio.o -obj-$(CONFIG_GPIO_SCH) += sch_gpio.o \ No newline at end of file +obj-$(CONFIG_GPIO_SCH) += sch_gpio.o diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index eb0c3fe44b29b3..1c0d90b3798cf2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -99,8 +99,8 @@ static int gpio_ensure_requested(struct gpio_desc *desc, unsigned offset) const struct gpio_chip *chip = desc->chip; const int gpio = chip->base + offset; - if (WARN(test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0, - "autorequest GPIO-%d\n", gpio)) { +#warning Temporarily removed warning for GPIO autorequest + if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { if (!try_module_get(chip->owner)) { pr_err("GPIO-%d: module can't be gotten \n", gpio); clear_bit(FLAG_REQUESTED, &desc->flags); diff --git a/drivers/gpio/max8660.c b/drivers/gpio/max8660.c new file mode 100644 index 00000000000000..68c7b7fa603b0a --- /dev/null +++ b/drivers/gpio/max8660.c @@ -0,0 +1,472 @@ +/* + * * PXA168 MAX8660 PMIC Management Routines + * * + * * + * * Copyright (C) 2009, Marvell Corporation(bshen9@marvell.com). + * * + * * This software program is licensed subject to the GNU General Public License + * * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +extern int get_pm_state(void); +static struct pxa3xx_pmic_regs max8660_regs[MAX8660_REG_NUM]; +static struct power_supply_module *max8660_power_module; +static struct i2c_client *g_client; + + +/* max8660 do not support read to the register */ +int max8660_read(u8 reg, u8 *pval) +{ + /* Cache read of the max8660 register.*/ + *pval = max8660_regs[reg].data; + + return 0; +} + +int max8660_write(u8 reg, u8 val) +{ + struct max8660_platform_data *pdata = g_client->dev.platform_data; + int ret; + int status; + int map; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + + spin_lock(&pdata->lock); + map = max8660_reg_map[reg]; + ret = i2c_smbus_write_byte_data(g_client, map, val); + if (ret == 0) { + max8660_regs[reg].data = val; + status = 0; + } else + status = -EIO; + spin_unlock(&pdata->lock); + + return status; +} + +static int max8660_initchip(void) +{ + memset(&max8660_regs, 0, + (sizeof(struct pxa3xx_pmic_regs) * MAX8660_REG_NUM)); + + + max8660_regs[MAX8660_OVER1].data = 0x0; + max8660_regs[MAX8660_OVER2].data = 0x0; + max8660_regs[MAX8660_VCC1].data = 0x0; + max8660_regs[MAX8660_ADTV1].data = 0x1b; + max8660_regs[MAX8660_ADTV2].data = 0x1b; + max8660_regs[MAX8660_SDTV1].data = 0x1b; + max8660_regs[MAX8660_SDTV2].data = 0x1b; + max8660_regs[MAX8660_MDTV1].data = 0x4; + max8660_regs[MAX8660_MDTV2].data = 0x4; + max8660_regs[MAX8660_L12VCR].data = 0x0; + max8660_regs[MAX8660_FPWM].data = 0x0; + + return 0; +} + +static int get_power_supply_module(int cmd) +{ + int power_module, i; + + power_module = -EINVAL; + + if (cmd < VCC_CORE || cmd >= MAX_CMD) { + printk(KERN_WARNING "input wrong command: %d\n", cmd); + return -EINVAL; + } + + + for(i = 0; max8660_power_module[i].command != 0; i++ ){ + if(max8660_power_module[i].command == cmd){ + power_module = max8660_power_module[i].power_module; + break; + } + } + + return power_module; +} + +static int set_max8660_voltage(unsigned int cmd, unsigned int mv) +{ + unsigned char val; + int status = 0; + int power_module = get_power_supply_module(cmd); + + + switch (power_module){ + case MAX8660_V1: + break; + + case MAX8660_V2: + break; + + case MAX8660_V3: + if (mv == 0) + { /* Disable V3 output */ + max8660_read(MAX8660_OVER1, &val); + val &= ~OVER1_EN3; + status = max8660_write(MAX8660_OVER1, val); + return status; + } + else if ((mv < MAX8660_V3_BASE) || (mv > MAX8660_V3_MAX)) + return -EINVAL; + + max8660_read(MAX8660_VCC1, &val); + + if( val & VCC1_AVS){ + status = max8660_write(MAX8660_ADTV2, (mv - MAX8660_V3_BASE)/MAX8660_V3_STEP); + + } else { + status = max8660_write(MAX8660_ADTV1, (mv - MAX8660_V3_BASE)/MAX8660_V3_STEP); + } + + val |= VCC1_AGO; + status= max8660_write(MAX8660_VCC1, val); + + max8660_read(MAX8660_OVER1, &val); + val |= OVER1_EN3; + status = max8660_write(MAX8660_OVER1, val); + + break; + + case MAX8660_V4: + if (mv == 0) + { /* Disable V4 output */ + status = max8660_read(MAX8660_OVER1, &val); + val &= ~OVER1_EN4; + status = max8660_write(MAX8660_OVER1, val); + return status; + } + else if ((mv < MAX8660_V4_BASE) || (mv > MAX8660_V4_MAX)) + return -EINVAL; + + max8660_read(MAX8660_VCC1, &val); + + if( val & VCC1_SVS){ + status = max8660_write(MAX8660_SDTV2, (mv - MAX8660_V4_BASE)/MAX8660_V4_STEP); + } else { + status = max8660_write(MAX8660_SDTV1, (mv - MAX8660_V4_BASE)/MAX8660_V4_STEP); + } + + val |= VCC1_SGO; + status= max8660_write(MAX8660_VCC1, val); + + max8660_read(MAX8660_OVER1, &val); + val |= OVER1_EN4; + status = max8660_write(MAX8660_OVER1, val); + + break; + + case MAX8660_V5: + if ((mv < MAX8660_V5_BASE) || (mv > MAX8660_V5_MAX)) + return -EINVAL; + + max8660_read(MAX8660_VCC1, &val); + + if( val & VCC1_MVS){ + status = max8660_write(MAX8660_MDTV2, (mv - MAX8660_V5_BASE)/MAX8660_V5_STEP); + } else { + status = max8660_write(MAX8660_MDTV1, (mv - MAX8660_V5_BASE)/MAX8660_V5_STEP); + } + + val |= VCC1_MGO; + status= max8660_write(MAX8660_VCC1, val); + break; + + case MAX8660_V6: + if (mv == 0) + { /* Disable V6 output */ + status = max8660_read(MAX8660_OVER2, &val); + val &= ~OVER2_EN6; + status = max8660_write(MAX8660_OVER2, val); + return status; + } + else if ((mv < MAX8660_V6_BASE) || (mv > MAX8660_V6_MAX)) + return -EINVAL; + + max8660_read(MAX8660_L12VCR, &val); + val = (val & 0xf0) | (mv-MAX8660_V6_BASE)/MAX8660_V6_STEP; + status = max8660_write(MAX8660_L12VCR, val); + + max8660_read(MAX8660_OVER2, &val); + val |= OVER2_EN6; + status = max8660_write(MAX8660_OVER2, val); + break; + + case MAX8660_V7: + if (mv == 0) + { /* Disable V7 output */ + status = max8660_read(MAX8660_OVER2, &val); + val &= ~OVER2_EN7; + status = max8660_write(MAX8660_OVER2, val); + return status; + } + else if ((mv < MAX8660_V7_BASE) || (mv > MAX8660_V7_MAX)) + return -EINVAL; + + max8660_read(MAX8660_L12VCR, &val); + val = (val & 0xf) | (mv - MAX8660_V7_BASE)/MAX8660_V7_STEP << 4; + status = max8660_write(MAX8660_L12VCR, val); + + max8660_read(MAX8660_OVER2, &val); + val |= OVER2_EN7; + status = max8660_write(MAX8660_OVER2, val); + break; + + case MAX8660_V8: + break; + + } + + return status; +} + + +static int get_max8660_voltage(unsigned int cmd, int *pmv) +{ + unsigned char val; + int status = 0; + int power_module = get_power_supply_module(cmd); + + *pmv = 0; + + switch (power_module){ + case MAX8660_V1: + *pmv = MAX8660_V1_DEFAULT; + break; + + case MAX8660_V2: + *pmv = MAX8660_V2_DEFAULT; + break; + + case MAX8660_V3: + status = max8660_read(MAX8660_OVER1, &val); + if( val & OVER1_EN3){ + status = max8660_read(MAX8660_VCC1,&val); + if (val & VCC1_AGO) + { + if (val & VCC1_AVS) + { + status = max8660_read(MAX8660_ADTV2, &val); + *pmv = MAX8660_V3_BASE + MAX8660_V3_STEP * val; + } else { + status = max8660_read(MAX8660_ADTV1, &val); + *pmv = MAX8660_V3_BASE + MAX8660_V3_STEP * val; + } + } else { + *pmv = MAX8660_V3_DEFAULT; + } + } else { + /*V3 output disable */ + *pmv = 0; + } + break; + + case MAX8660_V4: + status = max8660_read(MAX8660_OVER1, &val); + if( val & OVER1_EN4){ + status = max8660_read(MAX8660_VCC1,&val); + if (val & VCC1_SGO) + { + if (val & VCC1_SVS) + { + status = max8660_read(MAX8660_SDTV2, &val); + *pmv = MAX8660_V4_BASE + MAX8660_V4_STEP * val; + } else { + status = max8660_read(MAX8660_SDTV1, &val); + *pmv = MAX8660_V4_BASE + MAX8660_V4_STEP * val; + } + } else { + *pmv = MAX8660_V4_DEFAULT; + } + } else { + /*V4 output disable */ + *pmv = 0; + } + + break; + + case MAX8660_V5: + status = max8660_read(MAX8660_VCC1, &val); + if (val & VCC1_MGO) + { + if (val & VCC1_MVS) + { + status = max8660_read(MAX8660_MDTV2, &val); + *pmv = MAX8660_V5_BASE + MAX8660_V5_STEP * val; + } else { + status = max8660_read(MAX8660_MDTV1, &val); + *pmv = MAX8660_V5_BASE + MAX8660_V5_STEP * val; + } + } else { + *pmv = MAX8660_V5_DEFAULT; + } + + break; + + case MAX8660_V6: + status = max8660_read(MAX8660_OVER2, &val); + if (val & OVER2_EN6) + { + status = max8660_read(MAX8660_L12VCR, &val); + val &= 0xf; + *pmv = MAX8660_V6_BASE + MAX8660_V6_STEP * val; + } else { + *pmv = 0; + } + break; + + case MAX8660_V7: + status = max8660_read(MAX8660_OVER2, &val); + if (val & OVER2_EN7) + { + status = max8660_read(MAX8660_L12VCR, &val); + val &= 0xf0; + val = val >> 4; + *pmv = MAX8660_V6_BASE + MAX8660_V6_STEP * val; + } else { + *pmv = 0; + } + break; + + case MAX8660_V8: + *pmv = MAX8660_V8_DEFAULT; + break; + + } + + return status; +} + + +#ifdef CONFIG_PM +static unsigned int vcc_misc1, vcc_misc2, hdmi_1p2v; + +static int max8660_suspend(struct i2c_client *client, pm_message_t state) +{ + get_max8660_voltage(VCC_MISC1, &vcc_misc1); + get_max8660_voltage(VCC_MISC2, &vcc_misc2); + get_max8660_voltage(HDMI_1P2V, &hdmi_1p2v); + set_max8660_voltage(VCC_MISC1, 0); + set_max8660_voltage(VCC_MISC2, 0); + set_max8660_voltage(HDMI_1P2V, 0); + return 0; +} + +static int max8660_resume(struct i2c_client *client) +{ + set_max8660_voltage(VCC_MISC1, vcc_misc1); + set_max8660_voltage(VCC_MISC2, vcc_misc2); + set_max8660_voltage(HDMI_1P2V, hdmi_1p2v); + return 0; +} +#else +#define max8660_suspend NULL +#define max8660_resume NULL +#endif + +static struct pmic_ops max8660_pmic_ops = { + .get_voltage = get_max8660_voltage, + .set_voltage = set_max8660_voltage, +}; + +static int __devinit max8660_probe(struct i2c_client *client, + const struct i2c_device_id* id) +{ + struct max8660_platform_data *pdata; + int ret; + int i; + + g_client = client; + + ret = max8660_initchip(); + if (ret != 0) + printk(KERN_WARNING "Initialize max8660 failed with ret 0x%x\n", + ret); + + pdata = client->dev.platform_data; + /* init spinlock */ + spin_lock_init(&pdata->lock); + + + for (i = 0; pdata->power_chips[i].power_supply_modules != NULL; i++) { + if (pdata->power_chips[i].chip_id == MAX8660_ID ){ + max8660_power_module = pdata->power_chips[i].power_supply_modules; + break; + } + } + + if (pdata->power_chips[i].power_supply_modules == NULL) + panic("This max8660 chip is not supported"); + + pmic_set_ops(&max8660_pmic_ops); + + pdata->platform_init(); + + return 0; +} + +static int max8660_remove(struct i2c_client *client) +{ + pmic_set_ops(NULL); + + free_irq(client->irq, NULL); + + return 0; +} + +static const struct i2c_device_id max8660_id[] = { + { "max8660", 0x34 }, + {}, +}; + +static struct i2c_driver max8660_driver = { + .driver = { + .name = "max8660", + }, + .probe = max8660_probe, + .remove = max8660_remove, + .suspend = max8660_suspend, + .resume = max8660_resume, + .id_table = max8660_id, +}; + +static int __init max8660_init(void) +{ + return i2c_add_driver(&max8660_driver); +} + +static void __exit max8660_exit(void) +{ + i2c_del_driver(&max8660_driver); +} + +subsys_initcall(max8660_init); +module_exit(max8660_exit); + +MODULE_DESCRIPTION("Max8660 PMIC Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/gpio/pca953x.c b/drivers/gpio/pca953x.c index b827c976dc6214..2ccedf542c3a0b 100644 --- a/drivers/gpio/pca953x.c +++ b/drivers/gpio/pca953x.c @@ -13,45 +13,33 @@ #include #include -#include -#include #include +#include +#include #include #include #include -#ifdef CONFIG_OF_GPIO -#include -#include -#endif + +#include #define PCA953X_INPUT 0 #define PCA953X_OUTPUT 1 #define PCA953X_INVERT 2 #define PCA953X_DIRECTION 3 -#define PCA953X_GPIOS 0x00FF -#define PCA953X_INT 0x0100 - static const struct i2c_device_id pca953x_id[] = { - { "pca9534", 8 | PCA953X_INT, }, - { "pca9535", 16 | PCA953X_INT, }, + { "pca9534", 8, }, + { "pca9535", 16, }, { "pca9536", 4, }, - { "pca9537", 4 | PCA953X_INT, }, - { "pca9538", 8 | PCA953X_INT, }, - { "pca9539", 16 | PCA953X_INT, }, - { "pca9554", 8 | PCA953X_INT, }, - { "pca9555", 16 | PCA953X_INT, }, - { "pca9556", 8, }, + { "pca9537", 4, }, + { "pca9538", 8, }, + { "pca9539", 16, }, + { "pca9554", 8, }, + { "pca9555", 16, }, { "pca9557", 8, }, { "max7310", 8, }, - { "max7312", 16 | PCA953X_INT, }, - { "max7313", 16 | PCA953X_INT, }, - { "max7315", 8 | PCA953X_INT, }, - { "pca6107", 8 | PCA953X_INT, }, - { "tca6408", 8 | PCA953X_INT, }, - { "tca6416", 16 | PCA953X_INT, }, - /* NYET: { "tca6424", 24, }, */ + { "max7312", 16, }, { } }; MODULE_DEVICE_TABLE(i2c, pca953x_id); @@ -61,19 +49,24 @@ struct pca953x_chip { uint16_t reg_output; uint16_t reg_direction; -#ifdef CONFIG_GPIO_PCA953X_IRQ - struct mutex irq_lock; - uint16_t irq_mask; - uint16_t irq_stat; - uint16_t irq_trig_raise; - uint16_t irq_trig_fall; - int irq_base; -#endif - struct i2c_client *client; - struct pca953x_platform_data *dyn_pdata; struct gpio_chip gpio_chip; - char **names; +#ifdef CONFIG_GPIO_PCA953X_GENERIC_IRQ + uint16_t last_input; + /* + * Note: Generic IRQ is not accessible within module code, the IRQ + * support will thus _only_ be available if the driver is built-in + */ + int irq; /* IRQ for the chip itself */ + int irq_start; /* starting IRQ for the on-chip GPIO lines */ + + uint16_t irq_mask; + uint16_t irq_falling_edge; + uint16_t irq_rising_edge; + + struct irq_chip irq_chip; + struct work_struct irq_work; +#endif }; static int pca953x_write_reg(struct pca953x_chip *chip, int reg, uint16_t val) @@ -216,272 +209,162 @@ static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios) gc->label = chip->client->name; gc->dev = &chip->client->dev; gc->owner = THIS_MODULE; - gc->names = chip->names; } -#ifdef CONFIG_GPIO_PCA953X_IRQ -static int pca953x_gpio_to_irq(struct gpio_chip *gc, unsigned off) +#ifdef CONFIG_GPIO_PCA953X_GENERIC_IRQ +/* FIXME: change to schedule_delayed_work() here if reading out of + * registers does not reflect the actual pin levels + */ + +static void pca953x_irq_work(struct work_struct *work) { struct pca953x_chip *chip; + uint16_t input, mask, rising, falling; + int ret, i; - chip = container_of(gc, struct pca953x_chip, gpio_chip); - return chip->irq_base + off; -} + chip = container_of(work, struct pca953x_chip, irq_work); -static void pca953x_irq_mask(unsigned int irq) -{ - struct pca953x_chip *chip = get_irq_chip_data(irq); + ret = pca953x_read_reg(chip, PCA953X_INPUT, &input); + if (ret < 0) + return; - chip->irq_mask &= ~(1 << (irq - chip->irq_base)); -} + mask = (input ^ chip->last_input) & chip->irq_mask; + rising = (input & mask) & chip->irq_rising_edge; + falling = (~input & mask) & chip->irq_falling_edge; -static void pca953x_irq_unmask(unsigned int irq) -{ - struct pca953x_chip *chip = get_irq_chip_data(irq); + irq_enter(); - chip->irq_mask |= 1 << (irq - chip->irq_base); -} + for (i = 0; i < chip->gpio_chip.ngpio; i++) { + if ((rising | falling) & (1u << i)) { + int irq = chip->irq_start + i; + struct irq_desc *desc; -static void pca953x_irq_bus_lock(unsigned int irq) -{ - struct pca953x_chip *chip = get_irq_chip_data(irq); + desc = irq_desc + irq; + desc_handle_irq(irq, desc); + } + } - mutex_lock(&chip->irq_lock); + irq_exit(); + + chip->last_input = input; } -static void pca953x_irq_bus_sync_unlock(unsigned int irq) +static void +pca953x_irq_demux(unsigned int irq, struct irq_desc *desc) { - struct pca953x_chip *chip = get_irq_chip_data(irq); - uint16_t new_irqs; - uint16_t level; - - /* Look for any newly setup interrupt */ - new_irqs = chip->irq_trig_fall | chip->irq_trig_raise; - new_irqs &= ~chip->reg_direction; - - while (new_irqs) { - level = __ffs(new_irqs); - pca953x_gpio_direction_input(&chip->gpio_chip, level); - new_irqs &= ~(1 << level); - } + struct pca953x_chip *chip = desc->handler_data; - mutex_unlock(&chip->irq_lock); + desc->chip->mask(chip->irq); + desc->chip->ack(chip->irq); + schedule_work(&chip->irq_work); + desc->chip->unmask(chip->irq); } -static int pca953x_irq_set_type(unsigned int irq, unsigned int type) +static void pca953x_irq_mask(unsigned int irq) { - struct pca953x_chip *chip = get_irq_chip_data(irq); - uint16_t level = irq - chip->irq_base; - uint16_t mask = 1 << level; - - if (!(type & IRQ_TYPE_EDGE_BOTH)) { - dev_err(&chip->client->dev, "irq %d: unsupported type %d\n", - irq, type); - return -EINVAL; - } + struct irq_desc *desc = irq_desc + irq; + struct pca953x_chip *chip = desc->chip_data; - if (type & IRQ_TYPE_EDGE_FALLING) - chip->irq_trig_fall |= mask; - else - chip->irq_trig_fall &= ~mask; - - if (type & IRQ_TYPE_EDGE_RISING) - chip->irq_trig_raise |= mask; - else - chip->irq_trig_raise &= ~mask; - - return 0; + chip->irq_mask &= ~(1u << (irq - chip->irq_start)); } -static struct irq_chip pca953x_irq_chip = { - .name = "pca953x", - .mask = pca953x_irq_mask, - .unmask = pca953x_irq_unmask, - .bus_lock = pca953x_irq_bus_lock, - .bus_sync_unlock = pca953x_irq_bus_sync_unlock, - .set_type = pca953x_irq_set_type, -}; - -static uint16_t pca953x_irq_pending(struct pca953x_chip *chip) +static void pca953x_irq_unmask(unsigned int irq) { - uint16_t cur_stat; - uint16_t old_stat; - uint16_t pending; - uint16_t trigger; - int ret; - - ret = pca953x_read_reg(chip, PCA953X_INPUT, &cur_stat); - if (ret) - return 0; + struct irq_desc *desc = irq_desc + irq; + struct pca953x_chip *chip = desc->chip_data; - /* Remove output pins from the equation */ - cur_stat &= chip->reg_direction; - - old_stat = chip->irq_stat; - trigger = (cur_stat ^ old_stat) & chip->irq_mask; - - if (!trigger) - return 0; - - chip->irq_stat = cur_stat; - - pending = (old_stat & chip->irq_trig_fall) | - (cur_stat & chip->irq_trig_raise); - pending &= trigger; - - return pending; + chip->irq_mask |= 1u << (irq - chip->irq_start); } -static irqreturn_t pca953x_irq_handler(int irq, void *devid) +static void pca953x_irq_ack(unsigned int irq) { - struct pca953x_chip *chip = devid; - uint16_t pending; - uint16_t level; - - pending = pca953x_irq_pending(chip); - - if (!pending) - return IRQ_HANDLED; - - do { - level = __ffs(pending); - handle_nested_irq(level + chip->irq_base); - - pending &= ~(1 << level); - } while (pending); - - return IRQ_HANDLED; + /* unfortunately, we have to provide an empty irq_chip.ack even + * if we do nothing here, Generic IRQ will complain otherwise + */ } -static int pca953x_irq_setup(struct pca953x_chip *chip, - const struct i2c_device_id *id) +static int pca953x_irq_set_type(unsigned int irq, unsigned int type) { - struct i2c_client *client = chip->client; - struct pca953x_platform_data *pdata = client->dev.platform_data; - int ret; - - if (pdata->irq_base && (id->driver_data & PCA953X_INT)) { - int lvl; - - ret = pca953x_read_reg(chip, PCA953X_INPUT, - &chip->irq_stat); - if (ret) - goto out_failed; - - /* - * There is no way to know which GPIO line generated the - * interrupt. We have to rely on the previous read for - * this purpose. - */ - chip->irq_stat &= chip->reg_direction; - chip->irq_base = pdata->irq_base; - mutex_init(&chip->irq_lock); - - for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) { - int irq = lvl + chip->irq_base; - - set_irq_chip_data(irq, chip); - set_irq_chip_and_handler(irq, &pca953x_irq_chip, - handle_edge_irq); - set_irq_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - set_irq_noprobe(irq); -#endif - } - - ret = request_threaded_irq(client->irq, - NULL, - pca953x_irq_handler, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(&client->dev), chip); - if (ret) { - dev_err(&client->dev, "failed to request irq %d\n", - client->irq); - goto out_failed; - } - - chip->gpio_chip.to_irq = pca953x_gpio_to_irq; + struct irq_desc *desc = irq_desc + irq; + struct pca953x_chip *chip = desc->chip_data; + uint16_t mask = 1u << (irq - chip->irq_start); + int gpio = irq_to_gpio(irq); + + if (type == IRQ_TYPE_PROBE) { + if ((mask & chip->irq_rising_edge) || + (mask & chip->irq_falling_edge) || + (mask & ~chip->reg_direction)) + return 0; + + type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; } - return 0; - -out_failed: - chip->irq_base = 0; - return ret; -} + if (gpio_request(gpio, "gpio expander pin")) { + printk(KERN_ERR "Request GPIO failed, gpio: 0x%x\n", gpio); + return -1; + } + gpio_direction_input(gpio); + gpio_free(gpio); -static void pca953x_irq_teardown(struct pca953x_chip *chip) -{ - if (chip->irq_base) - free_irq(chip->client->irq, chip); -} -#else /* CONFIG_GPIO_PCA953X_IRQ */ -static int pca953x_irq_setup(struct pca953x_chip *chip, - const struct i2c_device_id *id) -{ - struct i2c_client *client = chip->client; - struct pca953x_platform_data *pdata = client->dev.platform_data; + if (type & IRQ_TYPE_EDGE_RISING) + chip->irq_rising_edge |= mask; + else + chip->irq_rising_edge &= ~mask; - if (pdata->irq_base && (id->driver_data & PCA953X_INT)) - dev_warn(&client->dev, "interrupt support not compiled in\n"); + if (type & IRQ_TYPE_EDGE_FALLING) + chip->irq_falling_edge |= mask; + else + chip->irq_falling_edge &= ~mask; return 0; } -static void pca953x_irq_teardown(struct pca953x_chip *chip) +static int pca953x_init_irq(struct pca953x_chip *chip) { -} -#endif + struct irq_chip *ic = &chip->irq_chip; + int irq, irq_start = chip->irq_start; -/* - * Handlers for alternative sources of platform_data - */ -#ifdef CONFIG_OF_GPIO -/* - * Translate OpenFirmware node properties into platform_data - */ -static struct pca953x_platform_data * -pca953x_get_alt_pdata(struct i2c_client *client) -{ - struct pca953x_platform_data *pdata; - struct device_node *node; - const uint16_t *val; + chip->irq = chip->client->irq; + chip->irq_start = irq_start = gpio_to_irq(chip->gpio_start); - node = dev_archdata_get_node(&client->dev.archdata); - if (node == NULL) - return NULL; + /* do not install GPIO interrupts for the chip if + * 1. the PCA953X interrupt line is not used + * 2. or the GPIO interrupt number exceeds NR_IRQS + */ + if (chip->irq <= 0 || irq_start + chip->gpio_chip.ngpio >= NR_IRQS) + return -EINVAL; - pdata = kzalloc(sizeof(struct pca953x_platform_data), GFP_KERNEL); - if (pdata == NULL) { - dev_err(&client->dev, "Unable to allocate platform_data\n"); - return NULL; - } + chip->irq_mask = 0; + chip->irq_rising_edge = 0; + chip->irq_falling_edge = 0; - pdata->gpio_base = -1; - val = of_get_property(node, "linux,gpio-base", NULL); - if (val) { - if (*val < 0) - dev_warn(&client->dev, - "invalid gpio-base in device tree\n"); - else - pdata->gpio_base = *val; + ic->ack = pca953x_irq_ack; + ic->mask = pca953x_irq_mask; + ic->unmask = pca953x_irq_unmask; + ic->set_type = pca953x_irq_set_type; + + for (irq = irq_start; irq < irq_start + chip->gpio_chip.ngpio; irq++) { + set_irq_chip(irq, ic); + set_irq_chip_data(irq, chip); + set_irq_handler(irq, handle_edge_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - val = of_get_property(node, "polarity", NULL); - if (val) - pdata->invert = *val; + set_irq_type(chip->irq, IRQ_TYPE_EDGE_FALLING); + set_irq_data(chip->irq, chip); + set_irq_chained_handler(chip->irq, pca953x_irq_demux); - return pdata; + INIT_WORK(&chip->irq_work, pca953x_irq_work); + return 0; } #else -static struct pca953x_platform_data * -pca953x_get_alt_pdata(struct i2c_client *client) +static inline int pca953x_init_irq(struct pca953x_chip *chip) { - return NULL; + return 0; } -#endif +#endif /* CONFIG_GPIO_PCA953X_GENERIC_IRQ */ + + static int __devinit pca953x_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -489,37 +372,33 @@ static int __devinit pca953x_probe(struct i2c_client *client, struct pca953x_platform_data *pdata; struct pca953x_chip *chip; int ret; - - chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; + uint16_t val; pdata = client->dev.platform_data; - if (pdata == NULL) { - pdata = pca953x_get_alt_pdata(client); - /* - * Unlike normal platform_data, this is allocated - * dynamically and must be freed in the driver - */ - chip->dyn_pdata = pdata; - } - if (pdata == NULL) { dev_dbg(&client->dev, "no platform data\n"); - ret = -EINVAL; - goto out_failed; + return -EINVAL; } + chip = kzalloc(sizeof(struct pca953x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + chip->client = client; chip->gpio_start = pdata->gpio_base; - chip->names = pdata->names; - /* initialize cached registers from their original values. * we can't share this chip with another i2c master. */ - pca953x_setup_gpio(chip, id->driver_data & PCA953X_GPIOS); + pca953x_setup_gpio(chip, id->driver_data); + + /* some chips need read first to validate address, + * if SCL SDA is used for address + */ + ret = pca953x_read_reg(chip, PCA953X_INPUT, &val); + if (ret) + goto out_failed; ret = pca953x_read_reg(chip, PCA953X_OUTPUT, &chip->reg_output); if (ret) @@ -534,9 +413,6 @@ static int __devinit pca953x_probe(struct i2c_client *client, if (ret) goto out_failed; - ret = pca953x_irq_setup(chip, id); - if (ret) - goto out_failed; ret = gpiochip_add(&chip->gpio_chip); if (ret) @@ -549,16 +425,45 @@ static int __devinit pca953x_probe(struct i2c_client *client, dev_warn(&client->dev, "setup failed, %d\n", ret); } +#if defined(CONFIG_WLAN_8688_SDIO) + if (pdata->poweron) { + ret = pdata->poweron(); + if (ret < 0) + dev_warn(&client->dev, "poweron failed, %d\n", ret); + } +#endif + + if(client->irq > 0){ + /* clear the interrupt */ + ret = pca953x_read_reg(chip, PCA953X_INPUT, &chip->last_input); + if (ret) + goto out_failed; + + + ret = pca953x_init_irq(chip); + if (ret) { + ret = gpiochip_remove(&chip->gpio_chip); + goto out_failed; + } + + } + i2c_set_clientdata(client, chip); return 0; out_failed: - pca953x_irq_teardown(chip); - kfree(chip->dyn_pdata); kfree(chip); return ret; } +#ifdef CONFIG_GPIO_PCA953X_GENERIC_IRQ +static int pca953x_remove(struct i2c_client *client) +{ + printk(KERN_ERR "failed to unload the driver with IRQ support\n"); + return -EINVAL; +} +#else + static int pca953x_remove(struct i2c_client *client) { struct pca953x_platform_data *pdata = client->dev.platform_data; @@ -582,11 +487,10 @@ static int pca953x_remove(struct i2c_client *client) return ret; } - pca953x_irq_teardown(chip); - kfree(chip->dyn_pdata); kfree(chip); return 0; } +#endif /* CONFIG_GPIO_PCA953X_GENERIC_IRQ */ static struct i2c_driver pca953x_driver = { .driver = { @@ -604,7 +508,7 @@ static int __init pca953x_init(void) /* register after i2c postcore initcall and before * subsys initcalls that may rely on these GPIOs */ -subsys_initcall(pca953x_init); +subsys_initcall_sync(pca953x_init); static void __exit pca953x_exit(void) { diff --git a/drivers/gpio/pca9575.c b/drivers/gpio/pca9575.c new file mode 100644 index 00000000000000..92bf5733c800e7 --- /dev/null +++ b/drivers/gpio/pca9575.c @@ -0,0 +1,522 @@ +/* + * pca9575.c - 4/8/16 bit I/O ports + * + * Copyright (C) 2005 Ben Gardner + * Copyright (C) 2007 Marvell International Ltd. + * + * Derived from drivers/i2c/chips/pca9539.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PCA9575_IN 0 +#define PCA9575_INVRT 1 +#define PCA9575_BKEN 2 +#define PCA9575_PUPD 3 +#define PCA9575_CFG 4 +#define PCA9575_OUT 5 +#define PCA9575_MSK 6 +#define PCA9575_INTS 7 + +static const struct i2c_device_id pca9575_id[] = { + { "pca9575", 16, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pca9575_id); + +struct pca9575_chip { + unsigned gpio_start; + uint16_t reg_output; + uint16_t reg_direction; + + struct i2c_client *client; + struct gpio_chip gpio_chip; +#ifdef CONFIG_GPIO_PCA9575_GENERIC_IRQ + uint16_t last_input; + /* + * Note: Generic IRQ is not accessible within module code, the IRQ + * support will thus _only_ be available if the driver is built-in + */ + int irq; /* IRQ for the chip itself */ + int irq_start; /* starting IRQ for the on-chip GPIO lines */ + + uint16_t irq_mask; + uint16_t irq_falling_edge; + uint16_t irq_rising_edge; + + struct irq_chip irq_chip; + struct work_struct irq_work; +#endif +}; + +static int pca9575_write_reg(struct pca9575_chip *chip, int reg, uint16_t val) +{ + int ret; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_write_byte_data(chip->client, reg, val); + else { + reg = reg << 1; + ret = i2c_smbus_write_byte_data(chip->client, reg, val & 0xff); + ret = i2c_smbus_write_byte_data(chip->client, reg + 1, (val & 0xff00) >> 8); + } + + if (ret < 0) { + dev_err(&chip->client->dev, "failed writing register\n"); + return ret; + } + + return 0; +} + +static int pca9575_read_reg(struct pca9575_chip *chip, int reg, uint16_t *val) +{ + int ret, reth, retl; + + if (chip->gpio_chip.ngpio <= 8) + ret = i2c_smbus_read_byte_data(chip->client, reg); + else { + reg = reg << 1; + retl = i2c_smbus_read_byte_data(chip->client, reg); + reth = i2c_smbus_read_byte_data(chip->client, reg + 1); + ret = (retl & 0xff) | (reth << 8); + } + + if (ret < 0) { + dev_err(&chip->client->dev, "failed reading register\n"); + return ret; + } + + *val = (uint16_t)ret; + return 0; +} + +static int pca9575_gpio_direction_input(struct gpio_chip *gc, unsigned off) +{ + struct pca9575_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca9575_chip, gpio_chip); + + reg_val = chip->reg_direction | (1u << off); + ret = pca9575_write_reg(chip, PCA9575_CFG, reg_val); + if (ret) + return ret; + + chip->reg_direction = reg_val; + return 0; +} + +static int pca9575_gpio_direction_output(struct gpio_chip *gc, + unsigned off, int val) +{ + struct pca9575_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca9575_chip, gpio_chip); + + /* set output level */ + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + ret = pca9575_write_reg(chip, PCA9575_OUT, reg_val); + if (ret) + return ret; + + chip->reg_output = reg_val; + + /* then direction */ + reg_val = chip->reg_direction & ~(1u << off); + ret = pca9575_write_reg(chip, PCA9575_CFG, reg_val); + if (ret) + return ret; + + chip->reg_direction = reg_val; + return 0; +} + +static int pca9575_gpio_get_value(struct gpio_chip *gc, unsigned off) +{ + struct pca9575_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca9575_chip, gpio_chip); + + ret = pca9575_read_reg(chip, PCA9575_IN, ®_val); + if (ret < 0) { + /* NOTE: diagnostic already emitted; that's all we should + * do unless gpio_*_value_cansleep() calls become different + * from their nonsleeping siblings (and report faults). + */ + return 0; + } + + return (reg_val & (1u << off)) ? 1 : 0; +} + +static void pca9575_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +{ + struct pca9575_chip *chip; + uint16_t reg_val; + int ret; + + chip = container_of(gc, struct pca9575_chip, gpio_chip); + + if (val) + reg_val = chip->reg_output | (1u << off); + else + reg_val = chip->reg_output & ~(1u << off); + + ret = pca9575_write_reg(chip, PCA9575_OUT, reg_val); + if (ret) + return; + + chip->reg_output = reg_val; +} + +static void pca9575_setup_gpio(struct pca9575_chip *chip, int gpios) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->direction_input = pca9575_gpio_direction_input; + gc->direction_output = pca9575_gpio_direction_output; + gc->get = pca9575_gpio_get_value; + gc->set = pca9575_gpio_set_value; + gc->can_sleep = 1; + + gc->base = chip->gpio_start; + gc->ngpio = gpios; + gc->label = chip->client->name; + gc->dev = &chip->client->dev; + gc->owner = THIS_MODULE; +} + +#ifdef CONFIG_GPIO_PCA9575_GENERIC_IRQ +/* FIXME: change to schedule_delayed_work() here if reading out of + * registers does not reflect the actual pin levels + */ + +static void pca9575_irq_work(struct work_struct *work) +{ + struct pca9575_chip *chip; + uint16_t input, mask, rising, falling; + int ret, i; + + chip = container_of(work, struct pca9575_chip, irq_work); + + ret = pca9575_read_reg(chip, PCA9575_IN, &input); + if (ret < 0) + return; + + mask = (input ^ chip->last_input) & chip->irq_mask; + rising = (input & mask) & chip->irq_rising_edge; + falling = (~input & mask) & chip->irq_falling_edge; + + irq_enter(); + + for (i = 0; i < chip->gpio_chip.ngpio; i++) { + if ((rising | falling) & (1u << i)) { + int irq = chip->irq_start + i; + struct irq_desc *desc; + + desc = irq_desc + irq; + desc_handle_irq(irq, desc); + } + } + + irq_exit(); + + chip->last_input = input; +} + +static void +pca9575_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct pca9575_chip *chip = desc->handler_data; + + desc->chip->mask(chip->irq); + desc->chip->ack(chip->irq); + schedule_work(&chip->irq_work); + desc->chip->unmask(chip->irq); +} + +static void pca9575_irq_mask(unsigned int irq) +{ + struct irq_desc *desc = irq_desc + irq; + struct pca9575_chip *chip = desc->chip_data; + + chip->irq_mask &= ~(1u << (irq - chip->irq_start)); +} + +static void pca9575_irq_unmask(unsigned int irq) +{ + struct irq_desc *desc = irq_desc + irq; + struct pca9575_chip *chip = desc->chip_data; + + chip->irq_mask |= 1u << (irq - chip->irq_start); +} + +static void pca9575_irq_ack(unsigned int irq) +{ + /* unfortunately, we have to provide an empty irq_chip.ack even + * if we do nothing here, Generic IRQ will complain otherwise + */ +} + +static int pca9575_irq_set_type(unsigned int irq, unsigned int type) +{ + struct irq_desc *desc = irq_desc + irq; + struct pca9575_chip *chip = desc->chip_data; + uint16_t mask = 1u << (irq - chip->irq_start); + int gpio = irq_to_gpio(irq); + + if (type == IRQ_TYPE_PROBE) { + if ((mask & chip->irq_rising_edge) || + (mask & chip->irq_falling_edge) || + (mask & ~chip->reg_direction)) + return 0; + + type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; + } + + if (gpio_request(gpio, "gpio expander pin")) { + printk(KERN_ERR "Request GPIO failed, gpio: 0x%x\n", gpio); + return -1; + } + gpio_direction_input(gpio); + gpio_free(gpio); + + if (type & IRQ_TYPE_EDGE_RISING) + chip->irq_rising_edge |= mask; + else + chip->irq_rising_edge &= ~mask; + + if (type & IRQ_TYPE_EDGE_FALLING) + chip->irq_falling_edge |= mask; + else + chip->irq_falling_edge &= ~mask; + + return 0; +} + +static int pca9575_init_irq(struct pca9575_chip *chip) +{ + struct irq_chip *ic = &chip->irq_chip; + int irq, irq_start = chip->irq_start; + + chip->irq = chip->client->irq; + chip->irq_start = irq_start = gpio_to_irq(chip->gpio_start); + + /* do not install GPIO interrupts for the chip if + * 1. the PCA9575 interrupt line is not used + * 2. or the GPIO interrupt number exceeds NR_IRQS + */ + if (chip->irq <= 0 || irq_start + chip->gpio_chip.ngpio >= NR_IRQS) + return -EINVAL; + + chip->irq_mask = 0; + chip->irq_rising_edge = 0; + chip->irq_falling_edge = 0; + + ic->ack = pca9575_irq_ack; + ic->mask = pca9575_irq_mask; + ic->unmask = pca9575_irq_unmask; + ic->set_type = pca9575_irq_set_type; + + for (irq = irq_start; irq < irq_start + chip->gpio_chip.ngpio; irq++) { + set_irq_chip(irq, ic); + set_irq_chip_data(irq, chip); + set_irq_handler(irq, handle_edge_irq); + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); + } + + set_irq_type(chip->irq, IRQ_TYPE_EDGE_FALLING); + set_irq_data(chip->irq, chip); + set_irq_chained_handler(chip->irq, pca9575_irq_demux); + + INIT_WORK(&chip->irq_work, pca9575_irq_work); + return 0; +} +#else +static inline int pca9575_init_irq(struct pca9575_chip *chip) +{ + return 0; +} +#endif /* CONFIG_GPIO_PCA9575_GENERIC_IRQ */ + + + +static int __devinit pca9575_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pca9575_platform_data *pdata; + struct pca9575_chip *chip; + int ret; + uint16_t val; + + pdata = client->dev.platform_data; + if (pdata == NULL) { + dev_dbg(&client->dev, "no platform data\n"); + return -EINVAL; + } + + chip = kzalloc(sizeof(struct pca9575_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->client = client; + + chip->gpio_start = pdata->gpio_base; + + /* initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + pca9575_setup_gpio(chip, id->driver_data); + + /* some chips need read first to validate address, + * if SCL SDA is used for address + */ + ret = pca9575_read_reg(chip, PCA9575_IN, &val); + if (ret) + goto out_failed; + + ret = pca9575_read_reg(chip, PCA9575_OUT, &chip->reg_output); + if (ret) + goto out_failed; + + ret = pca9575_read_reg(chip, PCA9575_CFG, &chip->reg_direction); + if (ret) + goto out_failed; + + /* set platform specific polarity inversion */ + ret = pca9575_write_reg(chip, PCA9575_INVRT, pdata->invert); + if (ret) + goto out_failed; + + + ret = gpiochip_add(&chip->gpio_chip); + if (ret) + goto out_failed; + + if (pdata->setup) { + ret = pdata->setup(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) + dev_warn(&client->dev, "setup failed, %d\n", ret); + } + + printk("%s:detected\n", __func__); + +#if defined(CONFIG_WLAN_8688_SDIO) + if (pdata->poweron) { + ret = pdata->poweron(); + if (ret < 0) + dev_warn(&client->dev, "poweron failed, %d\n", ret); + } +#endif + + if(client->irq > 0){ + /* clear the interrupt */ + ret = pca9575_read_reg(chip, PCA9575_IN, &val); + if (ret) + goto out_failed; + + + ret = pca9575_init_irq(chip); + if (ret) { + ret = gpiochip_remove(&chip->gpio_chip); + goto out_failed; + } + + } + + i2c_set_clientdata(client, chip); + return 0; + +out_failed: + kfree(chip); + return ret; +} + +#ifdef CONFIG_GPIO_PCA9575_GENERIC_IRQ +static int pca9575_remove(struct i2c_client *client) +{ + printk(KERN_ERR "failed to unload the driver with IRQ support\n"); + return -EINVAL; +} +#else + +static int pca9575_remove(struct i2c_client *client) +{ + struct pca9575_platform_data *pdata = client->dev.platform_data; + struct pca9575_chip *chip = i2c_get_clientdata(client); + int ret = 0; + + if (pdata->teardown) { + ret = pdata->teardown(client, chip->gpio_chip.base, + chip->gpio_chip.ngpio, pdata->context); + if (ret < 0) { + dev_err(&client->dev, "%s failed, %d\n", + "teardown", ret); + return ret; + } + } + + ret = gpiochip_remove(&chip->gpio_chip); + if (ret) { + dev_err(&client->dev, "%s failed, %d\n", + "gpiochip_remove()", ret); + return ret; + } + + kfree(chip); + return 0; +} +#endif /* CONFIG_GPIO_PCA9575_GENERIC_IRQ */ + +static struct i2c_driver pca9575_driver = { + .driver = { + .name = "pca9575", + }, + .probe = pca9575_probe, + .remove = pca9575_remove, + .id_table = pca9575_id, +}; + +static int __init pca9575_init(void) +{ + return i2c_add_driver(&pca9575_driver); +} +/* register after i2c postcore initcall and before + * subsys initcalls that may rely on these GPIOs + */ +subsys_initcall_sync(pca9575_init); + +static void __exit pca9575_exit(void) +{ + i2c_del_driver(&pca9575_driver); +} +module_exit(pca9575_exit); + +MODULE_AUTHOR("Jack Ren "); +MODULE_DESCRIPTION("GPIO expander driver for PCA9575"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 9be8e1754a0baf..cd2509217f2f3d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -28,6 +28,12 @@ config HWMON_VID tristate default n +config BMA020 + tristate "BOSCH bma020 acceleration sensor" + depends on I2C + help + support for bosch g-sensor + config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" default n @@ -1056,6 +1062,30 @@ config SENSORS_LIS3_I2C will be called lis3lv02d and a specific module for the I2C transport is called lis3lv02d_i2c. +config SENSORS_LIS3LV02D_I2C + tristate "STMicroeletronics LIS3LV02Dx(I2C) three-axis digital accelerometer" + depends on I2C && INPUT + default n + help + This driver provides support for the LIS3LV02Dx accelerometer. In + particular, it can be found in a number of HP laptops, which have the + "Mobile Data Protection System 3D" or "3D DriveGuard" feature. On such + systems the driver should load automatically (via ACPI). The + accelerometer might also be found in other systems, connected via SPI + or acpi. The accelerometer data is readable via + /sys/devices/platform/lis3lv02d. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + This driver can also be built as a module. If so, the module + will be called lis3lv02d. + +config SENSORS_CM3602 + tristate "CM3602 short distance proximity sensor with ambient light sensor" + depends on INPUT + default n + config SENSORS_APPLESMC tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" depends on INPUT && X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4aa1a3d112ad99..816b4349e7e9d4 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -56,6 +56,8 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o +obj-$(CONFIG_SENSORS_LIS3LV02D_I2C) += lis3lv02d_i2c.o +obj-$(CONFIG_SENSORS_CM3602) += cm3602.o obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o obj-$(CONFIG_SENSORS_LIS3_I2C) += lis3lv02d.o lis3lv02d_i2c.o obj-$(CONFIG_SENSORS_LM63) += lm63.o @@ -101,6 +103,8 @@ obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_BMA020) += bma020/ + ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG endif diff --git a/drivers/hwmon/bma020/Makefile b/drivers/hwmon/bma020/Makefile new file mode 100644 index 00000000000000..2a70093d321d59 --- /dev/null +++ b/drivers/hwmon/bma020/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for sensor chip drivers. +# + + +obj-n += bma020.o +obj-$(CONFIG_BMA020) += bma020_fs.o + + + + + diff --git a/drivers/hwmon/bma020/bma020.c b/drivers/hwmon/bma020/bma020.c new file mode 100644 index 00000000000000..368965e891bfe0 --- /dev/null +++ b/drivers/hwmon/bma020/bma020.c @@ -0,0 +1,1959 @@ +/* $Date: 2007/08/07 16:05:10 $ + * $Revision: 1.4 $ + * + */ + +/* +* Copyright (C) 2007 Bosch Sensortec GmbH +* +* BMA020 acceleration sensor API +* +* Usage: Application Programming Interface for BMA020 configuration and data read out +* +* Author: Lars.Beseke@bosch-sensortec.com +* +* Disclaimer +* +* Common: +* Bosch Sensortec products are developed for the consumer goods industry. They may only be used +* within the parameters of the respective valid product data sheet. Bosch Sensortec products are +* provided with the express understanding that there is no warranty of fitness for a particular purpose. +* They are not fit for use in life-sustaining, safety or security sensitive systems or any system or device +* that may lead to bodily harm or property damage if the system or device malfunctions. In addition, +* Bosch Sensortec products are not fit for use in products which interact with motor vehicle systems. +* The resale and/or use of products are at the purchaser�s own risk and his own responsibility. The +* examination of fitness for the intended use is the sole responsibility of the Purchaser. +* +* The purchaser shall indemnify Bosch Sensortec from all third party claims, including any claims for +* incidental, or consequential damages, arising from any product use not covered by the parameters of +* the respective valid product data sheet or not approved by Bosch Sensortec and reimburse Bosch +* Sensortec for all costs in connection with such claims. +* +* The purchaser must monitor the market for the purchased products, particularly with regard to +* product safety and inform Bosch Sensortec without delay of all security relevant incidents. +* +* Engineering Samples are marked with an asterisk (*) or (e). Samples may vary from the valid +* technical specifications of the product series. They are therefore not intended or fit for resale to third +* parties or for use in end products. Their sole purpose is internal client testing. The testing of an +* engineering sample may in no way replace the testing of a product series. Bosch Sensortec +* assumes no liability for the use of engineering samples. By accepting the engineering samples, the +* Purchaser agrees to indemnify Bosch Sensortec from all claims arising from the use of engineering +* samples. +* +* Special: +* This software module (hereinafter called "Software") and any information on application-sheets +* (hereinafter called "Information") is provided free of charge for the sole purpose to support your +* application work. The Software and Information is subject to the following terms and conditions: +* +* The Software is specifically designed for the exclusive use for Bosch Sensortec products by +* personnel who have special experience and training. Do not use this Software if you do not have the +* proper experience or training. +* +* This Software package is provided `` as is `` and without any expressed or implied warranties, +* including without limitation, the implied warranties of merchantability and fitness for a particular +* purpose. +* +* Bosch Sensortec and their representatives and agents deny any liability for the functional impairment +* of this Software in terms of fitness, performance and safety. Bosch Sensortec and their +* representatives and agents shall not be liable for any direct or indirect damages or injury, except as +* otherwise stipulated in mandatory applicable law. +* +* The Information provided is believed to be accurate and reliable. Bosch Sensortec assumes no +* responsibility for the consequences of use of such Information nor for any infringement of patents or +* other rights of third parties which may result from its use. No license is granted by implication or +* otherwise under any patent or patent rights of Bosch. Specifications mentioned in the Information are +* subject to change without notice. +* +* It is not allowed to deliver the source code of the Software to any third party without permission of +* Bosch Sensortec. +*/ + + +/*! \file bma020.c + \brief This file contains all function implementations for the BMA020 API + + Details. +*/ + +#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 */ + + + + +/***************************************************************************** + * BMA020 I2C Client Driver + *****************************************************************************/ +/*#define INPUT_POLL */ /* for input_poll interface, otherwise ioctl interface */ + +/* #define DEBUG_INFO */ + +/** select one of below **/ +/* #define DEBUG_INPUT_POLL //for input_poll interface */ +/* #define DEBUG_IOCTL //for ioctl interface */ + +#if 0 +/* old i2c attach method */ +#define BMA020_ADDRESS (0x38) /* 0x70/71 */ +static unsigned short ignore[] = {I2C_CLIENT_END}; +static unsigned short normal_addr[] = {BMA020_ADDRESS, I2C_CLIENT_END,}; +static struct i2c_client_address_data addr_data = { + .normal_i2c = normal_addr, + .probe = ignore, + .ignore = ignore, +}; +/* static unsigned short normal_i2c[] = {BMA020_ADDRESS, I2C_CLIENT_END }; */ +#endif + +static struct i2c_client *g_client; + +static DEFINE_MUTEX(bma020_lock); + +static bma020_t *p_bma020 = NULL; /**< pointer to BMA020 device structure */ + + +//***************************i2c bus function**************************************// + +char i2c_bma020_read_buf(u8 dev_addr, u8 reg_addr, u8 *buf, u8 cnt) +{ + i2c_master_send(g_client, ®_addr, 1); /* i2c_smbus_write_byte(g_client, reg_addr); */ + return i2c_master_recv(g_client, buf, cnt); +} + +char i2c_bma020_write_buf(u8 dev_addr, u8 reg_addr, u8 *buf, u8 cnt) +{ + /* here issue cnt always be 1! */ + return i2c_smbus_write_byte_data(g_client, reg_addr, *buf); +} + + +//*************************bma020 hw configuration***********************************// +int bma020_detect(void) +{ + int comres = 0; + unsigned char data; + + + /* get chip info. */ + p_bma020->dev_addr = BMA020_I2C_ADDR; /* preset SM380 I2C_addr */ + p_bma020->bus_write = i2c_bma020_write_buf; + p_bma020->bus_read = i2c_bma020_read_buf; + + comres += p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, CHIP_ID__REG, &data, 1);/* read Chip Id */ + + p_bma020->chip_id = BMA020_GET_BITSLICE(data, CHIP_ID); /* get bitslice */ + + comres += p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ML_VERSION__REG, &data, 1); /* read Version reg */ + p_bma020->ml_version = BMA020_GET_BITSLICE(data, ML_VERSION); /* get ML Version */ + p_bma020->al_version = BMA020_GET_BITSLICE(data, AL_VERSION);/* get AL Version */ + + return comres; +} + +/** Perform soft reset of BMA020 via bus command +*/ +int bma020_soft_reset(void) +{ + int comres; + unsigned char data = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + data = BMA020_SET_BITSLICE(data, SOFT_RESET, 1); + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, SOFT_RESET__REG, &data, 1); + return comres; +} + +/** start BMA020s integrated selftest function + \param st 1 = selftest0, 3 = selftest1 (see also) + \return result of bus communication function + + \see BMA020_SELF_TEST0_ON + \see BMA020_SELF_TEST1_ON + + */ +int bma020_selftest(unsigned char st) +{ + int comres; + unsigned char data; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, SELF_TEST__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, SELF_TEST, st); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, SELF_TEST__REG, &data, 1); + return comres; + +} + + + +/** set bma020s range + \param range + \return result of bus communication function + + \see BMA020_RANGE_2G + \see BMA020_RANGE_4G + \see BMA020_RANGE_8G +*/ +int bma020_set_range(char range) +{ + int comres = 0; + unsigned char data; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + if (range < 3) { + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, RANGE__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, RANGE, range); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, RANGE__REG, &data, 1); + } + return comres; + +} + + +/* readout select range from BMA020 + \param *range pointer to range setting + \return result of bus communication function + \see BMA020_RANGE_2G, BMA020_RANGE_4G, BMA020_RANGE_8G + \see bma020_set_range() +*/ +int bma020_get_range(unsigned char *range) +{ + + int comres = 0; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, RANGE__REG, range, 1); + + *range = BMA020_GET_BITSLICE(*range, RANGE); + + return comres; + +} + + + +/** set BMA020s operation mode + \param mode 0 = normal, 2 = sleep, 3 = auto wake up + \return result of bus communication function + \note Available constants see below + \see BMA020_MODE_NORMAL, BMA020_MODE_SLEEP, BMA020_MODE_WAKE_UP + \see bma020_get_mode() +*/ +int bma020_set_mode(unsigned char mode) +{ + int comres = 0; + unsigned char data1, data2; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + if (mode < 4 || mode != 1) { + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, WAKE_UP__REG, &data1, 1); + data1 = BMA020_SET_BITSLICE(data1, WAKE_UP, mode); + comres += p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, SLEEP__REG, &data2, 1); + data2 = BMA020_SET_BITSLICE(data2, SLEEP, (mode>>1)); + + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, WAKE_UP__REG, &data1, 1); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, SLEEP__REG, &data2, 1); + p_bma020->mode = mode; + } + return comres; + +} + + + +/** get selected mode + \return used mode + \note this function returns the mode stored in \ref bma020_t structure + \see BMA020_MODE_NORMAL, BMA020_MODE_SLEEP, BMA020_MODE_WAKE_UP + \see bma020_set_mode() + +*/ +unsigned char bma020_get_mode(void) +{ + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + return p_bma020->mode; + +} + +/** set BMA020 internal filter bandwidth + \param bw bandwidth (see bandwidth constants) + \return result of bus communication function + \see #define BMA020_BW_25HZ, BMA020_BW_50HZ, BMA020_BW_100HZ, BMA020_BW_190HZ, BMA020_BW_375HZ, + \BMA020_BW_750HZ, BMA020_BW_1500HZ + \see bma020_get_bandwidth() +*/ +int bma020_set_bandwidth(char bw) +{ + int comres = 0; + unsigned char data; + + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + if (bw < 8) { + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BANDWIDTH__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, BANDWIDTH, bw); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, BANDWIDTH__REG, &data, 1); + } + return comres; + + +} + +/** read selected bandwidth from BMA020 + \param *bw pointer to bandwidth return value + \return result of bus communication function + \see #define BMA020_BW_25HZ, BMA020_BW_50HZ, BMA020_BW_100HZ, BMA020_BW_190HZ, BMA020_BW_375HZ, + \BMA020_BW_750HZ, BMA020_BW_1500HZ + \see bma020_set_bandwidth() +*/ +int bma020_get_bandwidth(unsigned char *bw) +{ + int comres = 1; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BANDWIDTH__REG, bw, 1); + + *bw = BMA020_GET_BITSLICE(*bw, BANDWIDTH); + + return comres; + +} + +/** set BMA020 auto wake up pause + \param wup wake_up_pause parameters + \return result of bus communication function + \see BMA020_WAKE_UP_PAUSE_20MS, BMA020_WAKE_UP_PAUSE_80MS, BMA020_WAKE_UP_PAUSE_320MS, + \BMA020_WAKE_UP_PAUSE_2560MS + \see bma020_get_wake_up_pause() +*/ + +int bma020_set_wake_up_pause(unsigned char wup) +{ + int comres = 0; + unsigned char data; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, WAKE_UP_PAUSE__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, WAKE_UP_PAUSE, wup); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, WAKE_UP_PAUSE__REG, &data, 1); + return comres; +} + +/** read BMA020 auto wake up pause from image + \param *wup wake up pause read back pointer + \see BMA020_WAKE_UP_PAUSE_20MS, BMA020_WAKE_UP_PAUSE_80MS, BMA020_WAKE_UP_PAUSE_320MS, + \BMA020_WAKE_UP_PAUSE_2560MS + \see bma020_set_wake_up_pause() +*/ +int bma020_get_wake_up_pause(unsigned char *wup) +{ + int comres = 1; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, WAKE_UP_PAUSE__REG, &data, 1); + + *wup = BMA020_GET_BITSLICE(data, WAKE_UP_PAUSE); + + return comres; + +} + + +/* Thresholds and Interrupt Configuration */ + + +/** set low-g interrupt threshold + \param th set the threshold + \note the threshold depends on configured range. A macro \ref BMA020_LG_THRES_IN_G() for range to + \register value conversion is available. + \see BMA020_LG_THRES_IN_G() + \see bma020_get_low_g_threshold() +*/ +int bma020_set_low_g_threshold(unsigned char th) +{ + + int comres; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, LG_THRES__REG, &th, 1); + return comres; + +} + + +/** get low-g interrupt threshold + \param *th get the threshold value from sensor image + \see bma020_set_low_g_threshold() +*/ +int bma020_get_low_g_threshold(unsigned char *th) +{ + + int comres = 1; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, LG_THRES__REG, th, 1); + + return comres; + +} + + +/** set low-g interrupt countdown + \param cnt get the countdown value from sensor image + \see bma020_get_low_g_countdown() +*/ +int bma020_set_low_g_countdown(unsigned char cnt) +{ + int comres = 0; + unsigned char data; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, COUNTER_LG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, COUNTER_LG, cnt); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, COUNTER_LG__REG, &data, 1); + return comres; +} + + +/** get low-g interrupt countdown + \param cnt get the countdown value from sensor image + \see bma020_set_low_g_countdown() +*/ +int bma020_get_low_g_countdown(unsigned char *cnt) +{ + int comres = 1; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, COUNTER_LG__REG, &data, 1); + *cnt = BMA020_GET_BITSLICE(data, COUNTER_LG); + + return comres; +} + +/** set high-g interrupt countdown + \param cnt get the countdown value from sensor image + \see bma020_get_high_g_countdown() +*/ +int bma020_set_high_g_countdown(unsigned char cnt) +{ + int comres = 1; + unsigned char data; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, COUNTER_HG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, COUNTER_HG, cnt); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, COUNTER_HG__REG, &data, 1); + return comres; +} + +/** get high-g interrupt countdown + \param cnt get the countdown value from sensor image + \see bma020_set_high_g_countdown() +*/ +int bma020_get_high_g_countdown(unsigned char *cnt) +{ + int comres = 0; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, COUNTER_HG__REG, &data, 1); + + *cnt = BMA020_GET_BITSLICE(data, COUNTER_HG); + + return comres; + +} + + +/** configure low-g duration value + \param dur low-g duration in miliseconds + \see bma020_get_low_g_duration(), bma020_get_high_g_duration(), bma020_set_high_g_duration() + +*/ +int bma020_set_low_g_duration(unsigned char dur) +{ + int comres = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, LG_DUR__REG, &dur, 1); + + return comres; +} + + +int bma020_set_low_g_hyst(unsigned char hyst) +{ + int comres = 0; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, LG_HYST__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, LG_HYST , hyst); + #ifdef DEBUG_INFO + printk("##!!## hyst=0x%x\n", data); + #endif + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, LG_HYST__REG, &data, 1); + + return comres; +} + +int bma020_set_high_g_hyst(unsigned char hyst) +{ + int comres = 0; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, HG_HYST__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, HG_HYST , hyst); + #ifdef DEBUG_INFO + printk("##!!## hyst=0x%x\n", data); + #endif + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, HG_HYST__REG, &data, 1); + + return comres; +} + +/** read out low-g duration value from sensor image + \param dur low-g duration in miliseconds + \see bma020_set_low_g_duration(), bma020_get_high_g_duration(), bma020_set_high_g_duration() + +*/ +int bma020_get_low_g_duration(unsigned char *dur) +{ + int comres = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, LG_DUR__REG, dur, 1); + return comres; + +} + + + + +/** set low-g interrupt threshold + \param th set the threshold + \note the threshold depends on configured range. A macro \ref BMA020_HG_THRES_IN_G() for range to + \register value conversion is available. + \see BMA020_HG_THRES_IN_G() + \see bma020_get_high_g_threshold() +*/ +int bma020_set_high_g_threshold(unsigned char th) +{ + + int comres = 0; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, HG_THRES__REG, &th, 1); + return comres; + +} + +/** get high-g interrupt threshold + \param *th get the threshold value from sensor image + \see bma020_set_high_g_threshold() +*/ +int bma020_get_high_g_threshold(unsigned char *th) +{ + + int comres = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, HG_THRES__REG, th, 1); + + return comres; + +} + + + +/** configure high-g duration value + \param dur high-g duration in miliseconds + \see bma020_get_high_g_duration(), bma020_set_low_g_duration(), bma020_get_low_g_duration() + +*/ +int bma020_set_high_g_duration(unsigned char dur) +{ + int comres = 0; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, HG_DUR__REG, &dur, 1); + return comres; +} + + +/** read out high-g duration value from sensor image + \param dur high-g duration in miliseconds + \see bma020_set_high_g_duration(), bma020_get_low_g_duration(), bma020_set_low_g_duration(), + +*/ +int bma020_get_high_g_duration(unsigned char *dur) +{ + int comres = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, HG_DUR__REG, dur, 1); + + return comres; + +} + + + + +/** set threshold value for any_motion feature + \param th set the threshold a macro \ref BMA020_ANY_MOTION_THRES_IN_G() is available for that + \see BMA020_ANY_MOTION_THRES_IN_G() +*/ +int bma020_set_any_motion_threshold(unsigned char th) +{ + int comres = 0; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, ANY_MOTION_THRES__REG, &th, 1); + + return comres; +} + + +/** get threshold value for any_motion feature + \param *th read back any_motion threshold from image register + \see BMA020_ANY_MOTION_THRES_IN_G() +*/ +int bma020_get_any_motion_threshold(unsigned char *th) +{ + + int comres = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ANY_MOTION_THRES__REG, th, 1); + + return comres; + +} + +/** set counter value for any_motion feature + \param amc set the counter value, constants are available for that + \see BMA020_ANY_MOTION_DUR_1, BMA020_ANY_MOTION_DUR_3, BMA020_ANY_MOTION_DUR_5, + \BMA020_ANY_MOTION_DUR_7 +*/ +int bma020_set_any_motion_count(unsigned char amc) +{ + int comres = 0; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ANY_MOTION_DUR__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ANY_MOTION_DUR, amc); + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, ANY_MOTION_DUR__REG, &data, 1); + return comres; +} + + +/** get counter value for any_motion feature from image register + \param *amc readback pointer for counter value + \see BMA020_ANY_MOTION_DUR_1, BMA020_ANY_MOTION_DUR_3, BMA020_ANY_MOTION_DUR_5, + \BMA020_ANY_MOTION_DUR_7 +*/ +int bma020_get_any_motion_count(unsigned char *amc) +{ + int comres = 0; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ANY_MOTION_DUR__REG, &data, 1); + + *amc = BMA020_GET_BITSLICE(data, ANY_MOTION_DUR); + return comres; + +} + + + +/** set the interrupt mask for BMA020's interrupt features in one mask + \param mask input for interrupt mask + \see BMA020_INT_ALERT, BMA020_INT_ANY_MOTION, BMA020_INT_EN_ADV_INT, BMA020_INT_NEW_DATA, + \BMA020_INT_LATCH, BMA020_INT_HG, BMA020_INT_LG +*/ +int bma020_set_interrupt_mask(unsigned char mask) +{ + int comres = 0; + unsigned char data[4]; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + data[0] = mask & BMA020_CONF1_INT_MSK; + data[2] = ((mask<<1) & BMA020_CONF2_INT_MSK); + + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BMA020_CONF1_REG, &data[1], 1); + comres += p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BMA020_CONF2_REG, &data[3], 1); + data[1] &= (~BMA020_CONF1_INT_MSK); + data[1] |= data[0]; + data[3] &= (~(BMA020_CONF2_INT_MSK)); + data[3] |= data[2]; + + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, BMA020_CONF1_REG, &data[1], 1); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, BMA020_CONF2_REG, &data[3], 1); + + return comres; +} + + + + +/** set the shadow_dis for BMA020's shadow_dis bit + \param ssd input for on/off control + \see BMA020_SHADOW_DIS_OFF, BMA020_SHADOW_DIS_ON +*/ +int bma020_set_shadow_dis(unsigned char ssd) +{ + int comres = 0; + unsigned char data; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + if ((ssd == 1) || (ssd == 0)) { + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BMA020_CONF2_REG, &data, 1); + data = BMA020_SET_BITSLICE(data, SHADOW_DIS, ssd); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, BMA020_CONF2_REG, &data, 1); + } + return comres; + +} + +/** get the current interrupt mask settings from BMA020 image registers + \param *mask return variable pointer for interrupt mask + \see BMA020_INT_ALERT, BMA020_INT_ANY_MOTION, BMA020_INT_EN_ADV_INT, BMA020_INT_NEW_DATA, + \BMA020_INT_LATCH, BMA020_INT_HG, BMA020_INT_LG +*/ +int bma020_get_interrupt_mask(unsigned char *mask) +{ + int comres = 0; + unsigned char data; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BMA020_CONF1_REG, &data, 1); + *mask = data & BMA020_CONF1_INT_MSK; + p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BMA020_CONF2_REG, &data, 1); + *mask = *mask | ((data & BMA020_CONF2_INT_MSK)>>1); + + return comres; +} + + +/** resets the BMA020 interrupt status + \note this feature can be used to reset a latched interrupt + +*/ +int bma020_reset_interrupt(void) +{ + #ifdef DEBUG_INFO + printk("##!!## file=%s,func=%s\n", __FILE__, __FUNCTION__); + #endif + int comres = 0; + unsigned char data = (1 << RESET_INT__POS); + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, RESET_INT__REG, &data, 1); + return comres; + +} + + + + + +/* Data Readout */ + +/** X-axis acceleration data readout + \param *a_x pointer for 16 bit 2's complement data output (LSB aligned) +*/ +int bma020_read_accel_x(short *a_x) +{ + int comres; + unsigned char data[2]; + + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ACC_X_LSB__REG, data, 2); + *a_x = BMA020_GET_BITSLICE(data[0], ACC_X_LSB) | BMA020_GET_BITSLICE(data[1], ACC_X_MSB) << ACC_X_LSB__LEN; + *a_x = *a_x << (sizeof(short)*8-(ACC_X_LSB__LEN+ACC_X_MSB__LEN)); + *a_x = *a_x >> (sizeof(short)*8-(ACC_X_LSB__LEN+ACC_X_MSB__LEN)); + return comres; + +} + + + +/** Y-axis acceleration data readout + \param *a_y pointer for 16 bit 2's complement data output (LSB aligned) +*/ +int bma020_read_accel_y(short *a_y) +{ + int comres; + unsigned char data[2]; + + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ACC_Y_LSB__REG, data, 2); + *a_y = BMA020_GET_BITSLICE(data[0], ACC_Y_LSB) | BMA020_GET_BITSLICE(data[1], ACC_Y_MSB) << ACC_Y_LSB__LEN; + *a_y = *a_y << (sizeof(short)*8-(ACC_Y_LSB__LEN+ACC_Y_MSB__LEN)); + *a_y = *a_y >> (sizeof(short)*8-(ACC_Y_LSB__LEN+ACC_Y_MSB__LEN)); + return comres; +} + + +/** Z-axis acceleration data readout + \param *a_z pointer for 16 bit 2's complement data output (LSB aligned) +*/ +int bma020_read_accel_z(short *a_z) +{ + int comres; + unsigned char data[2]; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ACC_Z_LSB__REG, data, 2); + *a_z = BMA020_GET_BITSLICE(data[0], ACC_Z_LSB) | BMA020_GET_BITSLICE(data[1], ACC_Z_MSB) << ACC_Z_LSB__LEN; + *a_z = *a_z << (sizeof(short)*8-(ACC_Z_LSB__LEN+ACC_Z_MSB__LEN)); + *a_z = *a_z >> (sizeof(short)*8-(ACC_Z_LSB__LEN+ACC_Z_MSB__LEN)); + return comres; +} + + +/** X,Y and Z-axis acceleration data readout + \param *acc pointer to \ref bma020acc_t structure for x,y,z data readout + \note data will be read by multi-byte protocol into a 6 byte structure +*/ +int bma020_read_accel_xyz(bma020acc_t *acc) +{ + int comres; + unsigned char data[6]; + + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ACC_X_LSB__REG, &data[0], 6); + + acc->x = BMA020_GET_BITSLICE(data[0], ACC_X_LSB) | (BMA020_GET_BITSLICE(data[1], ACC_X_MSB) << ACC_X_LSB__LEN); + acc->x = acc->x << (sizeof(short)*8-(ACC_X_LSB__LEN+ACC_X_MSB__LEN)); + acc->x = acc->x >> (sizeof(short)*8-(ACC_X_LSB__LEN+ACC_X_MSB__LEN)); + + acc->y = BMA020_GET_BITSLICE(data[2], ACC_Y_LSB) | (BMA020_GET_BITSLICE(data[3], ACC_Y_MSB) << ACC_Y_LSB__LEN); + acc->y = acc->y << (sizeof(short)*8-(ACC_Y_LSB__LEN + ACC_Y_MSB__LEN)); + acc->y = acc->y >> (sizeof(short)*8-(ACC_Y_LSB__LEN + ACC_Y_MSB__LEN)); + + + acc->z = BMA020_GET_BITSLICE(data[4], ACC_Z_LSB) | (BMA020_GET_BITSLICE(data[5], ACC_Z_MSB) << ACC_Z_LSB__LEN); + acc->z = acc->z << (sizeof(short)*8-(ACC_Z_LSB__LEN+ACC_Z_MSB__LEN)); + acc->z = acc->z >> (sizeof(short)*8-(ACC_Z_LSB__LEN+ACC_Z_MSB__LEN)); + + return comres; + +} + + + +/** check current interrupt status from interrupt status register in BMA020 image register + \param *ist pointer to interrupt status byte + \see BMA020_INT_STATUS_HG, BMA020_INT_STATUS_LG, BMA020_INT_STATUS_HG_LATCHED, + \BMA020_INT_STATUS_LG_LATCHED, BMA020_INT_STATUS_ALERT, BMA020_INT_STATUS_ST_RESULT +*/ +int bma020_get_interrupt_status(unsigned char *ist) +{ + + int comres = 0; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, BMA020_STATUS_REG, ist, 1); + return comres; +} + +/** enable/ disable low-g interrupt feature + \param onoff enable=1, disable=0 +*/ + +int bma020_set_low_g_int(unsigned char onoff) +{ + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ENABLE_LG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ENABLE_LG, onoff); + + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, ENABLE_LG__REG, &data, 1); + + return comres; +} + +/** enable/ disable high-g interrupt feature + \param onoff enable=1, disable=0 +*/ +int bma020_set_high_g_int(unsigned char onoff) +{ + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ENABLE_HG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ENABLE_HG, onoff); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, ENABLE_HG__REG, &data, 1); + + return comres; +} + +/** enable/ disable any_motion interrupt feature + \param onoff enable=1, disable=0 + \note for any_motion interrupt feature usage see also \ref bma020_set_advanced_int() +*/ +int bma020_set_any_motion_int(unsigned char onoff) +{ + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, EN_ANY_MOTION__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, EN_ANY_MOTION, onoff); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, EN_ANY_MOTION__REG, &data, 1); + + return comres; + +} + +/** enable/ disable alert-int interrupt feature + \param onoff enable=1, disable=0 + \note for any_motion interrupt feature usage see also \ref bma020_set_advanced_int() +*/ +int bma020_set_alert_int(unsigned char onoff) +{ + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ALERT__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ALERT, onoff); + + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, ALERT__REG, &data, 1); + + return comres; + +} + +/** enable/ disable advanced interrupt feature + \param onoff enable=1, disable=0 + \see bma020_set_any_motion_int() + \see bma020_set_alert_int() +*/ +int bma020_set_advanced_int(unsigned char onoff) +{ + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, ENABLE_ADV_INT__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, EN_ANY_MOTION, onoff); + + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, ENABLE_ADV_INT__REG, &data, 1); + + return comres; + +} + +/** enable/disable latched interrupt for all interrupt feature (global option) + \param latched (=1 for latched interrupts), (=0 for unlatched interrupts) +*/ +int bma020_latch_int(unsigned char latched) +{ + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, LATCH_INT__REG, &data, 1); + + #ifdef DEBUG_INFO + printk("read1=0x%x\n", data); + #endif + data = BMA020_SET_BITSLICE(data, LATCH_INT, latched); + #ifdef DEBUG_INFO + printk("read2=0x%x\n", data); + #endif + + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, LATCH_INT__REG, &data, 1); + + return comres; + +} + + + +/** enable/ disable new data interrupt feature + \param onoff enable=1, disable=0 +*/ + +int bma020_set_new_data_int(unsigned char onoff) +{ +/* printk("bma020_set_new_data_int onoff:%d \n",onoff); */ + + int comres; + unsigned char data; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, NEW_DATA_INT__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, NEW_DATA_INT, onoff); + comres += p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, NEW_DATA_INT__REG, &data, 1); + + return comres; + +} + + + +/** read function for raw register access + + \param addr register address + \param *data pointer to data array for register read back + \param len number of bytes to be read starting from addr + +*/ + +int bma020_read_reg(unsigned char addr, unsigned char *data, unsigned char len) +{ + + int comres; + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, addr, data, len); + return comres; + +} + + +/** write function for raw register access + \param addr register address + \param *data pointer to data array for register write + \param len number of bytes to be written starting from addr +*/ +int bma020_write_reg(unsigned char addr, unsigned char *data, unsigned char len) +{ + + int comres; + + if (p_bma020 == 0) + return E_BMA_NULL_PTR; + + comres = p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, addr, data, len); + + return comres; + +} + + +/* the new_data_int occur every 330us, CNT_MAX is adopted to reduce the sampling rate for new data int */ +#define CNT_MAX (100) +#if 0 +static irqreturn_t bma020_interrupt(int irq, void *dev_id) +{ + static int cnt = 0; + if (p_bma020->int_kind == NORMAL_INT) { + if (cnt >= CNT_MAX) { + cnt = 0; + wake_up_interruptible(&(p_bma020->int_wait)); + } + else { + cnt++; + } + /* wake_up_interruptible(&(p_bma020->int_wait)); */ + bma020_reset_interrupt(); + } else if (p_bma020->int_kind == ADV_INT) { + wake_up_interruptible(&(p_bma020->int_wait)); + } + + /* bma020_reset_interrupt(); */ +/* schedule_work(&p_bma020->irq_work); */ + return IRQ_HANDLED; +} + +static void bma020_irq_work(struct work_struct *work) +{ + /* + bma020_read_accel_xyz(bma020_dev.mem+cnt); + static int cnt = 0; + cnt++; + if (cnt == 2000) { + cnt=0; + printk("-->%d---\n",cnt); + } + */ + bma020acc_t data; + static int cnt = 0; + cnt++; + if(cnt == 2000){ + bma020_read_accel_xyz(&data); + printk("x=%d: ", data.x); + printk("y=%d: ", data.y); + printk("z=%d: ", data.z); + printk("\n"); + } +} +#endif +static struct fasync_struct *async = NULL; + + +static int app_fasync(int fd, struct file *filp, int mode) +{ + #ifdef DEBUG_INFO + printk("##!!## file=%s, func=%s\n", __FILE__, __FUNCTION__); + #endif + return fasync_helper(fd, filp, mode, &async); + +} + +static irqreturn_t bma020_interrupt(int irq, void *dev_id) +{ + + if (async) + kill_fasync(&async, SIGIO, POLL_IN); + #ifdef DEBUG_INFO + printk("##!!## file=%s,func=%s\n", __FILE__, __FUNCTION__); + printk("##!!## bma int fasync\n"); + #endif + +/* bma020_reset_interrupt(); */ + + return IRQ_HANDLED; + +} + +/*=======================================================================================*/ + + +static struct i2c_device_id bma020_idtable[] = +{ + {"bma020", 0}, + {}, +}; + + +struct i2c_driver bma020_driver = { + .driver = { + .name = "bma020", + }, + .id_table = bma020_idtable, + .probe = bma020_i2c_probe, + .remove = bma020_i2c_remove, +}; + + + +void bma020_driver_test(void) +{ + bma020acc_t data; + int tmp; + char s; + + bma020_soft_reset(); + + tmp = bma020_selftest(0); + printk("bustest num=%d\n", tmp); + + bma020_set_range(BMA020_RANGE_2G); + /* bma020_set_range(BMA020_RANGE_8G); */ + bma020_set_mode(0); + bma020_set_bandwidth(BMA020_BW_25HZ); + + for (;;) { + bma020_get_range(&s); + printk("range:%d\n", s); + bma020_read_accel_xyz(&data); + printk("x=%d: ", data.x); + printk("y=%d: ", data.y); + printk("z=%d: ", data.z); + printk("\n"); + msleep(1000); + } +} + +/* static int __devinit bma020_i2c_probe(struct i2c_client *client) */ +static int __devinit bma020_i2c_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) +{ + int ret; + + #ifdef DEBUG_INFO + printk("##!!## bma020_i2c_probe \n"); + #endif + + /* initialize global ptr */ + g_client = client; + + /* make sure it's a bma020 sensor */ + bma020_detect(); + if (p_bma020->chip_id != 0x02) { + printk(KERN_ERR "bma020 chip id error\n"); + return -1; + } + + #ifdef DEBUG_INFO + printk(KERN_NOTICE "bma020 detect by i2c bus\n"); + #endif + + printk("bma020: bma020 attatch to i2c bus, addr=0x%x, chip id=0x%x \n", + p_bma020->dev_addr, p_bma020->chip_id); + + #ifdef DEBUG_INFO + printk("chip id: 0x%x\n", p_bma020->chip_id); + printk("ml version: 0x%x\n", p_bma020->ml_version); + printk("al version: 0x%x\n", p_bma020->al_version); + #endif + + /* bma gpio interrupt initialization */ + p_bma020->gpio = mfp_to_gpio(MFP_PIN_GPIO111); + g_client->irq = gpio_to_irq(p_bma020->gpio); + + gpio_direction_input(p_bma020->gpio); /* set gpio pin as input */ + + #ifdef DEBUG_INFO + printk("irq=%d\n", g_client->irq); + printk("gpio=%d\n", p_bma020->gpio); + #endif + + /* IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING, IRQF_TRIGGER_HIGH, IRQF_TRIGGER_LOW + ret = request_irq(g_client->irq, bma020_interrupt, IRQF_DISABLED | IRQF_TRIGGER_RISING, + "bma020_irq", NULL); */ + ret = request_irq(g_client->irq, bma020_interrupt, IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING, + "bma020_irq", NULL); + + if (ret) { + printk(KERN_ERR "bma020 request irq failed\n"); + goto IRQ_ERR; + } + + /*************************for simple test**************************/ + #ifdef DEBUG_IOCTL + bma020_driver_test(); + #endif + + +/* INIT_WORK(&p_bma020->irq_work, bma020_irq_work); + bma020_latch_int(1); + bma020_set_mode(BMA020_MODE_SLEEP); //set to sleep mode +*/ + return 0; + +IRQ_ERR: + return ret; + +} + +static int __devexit bma020_i2c_remove(struct i2c_client *client) +{ + if( client->irq) { + free_irq(client->irq, client); + client->irq = 0; + } + + g_client = NULL; + return 0; +} + + + + +static int i2c_bma020_init(void) +{ + #ifdef DEBUG_INFO + printk("bma020 try attach to i2c bus.\n"); + #endif + + int ret; + if ((ret = i2c_add_driver(&bma020_driver))) { + printk(KERN_ERR "bma020: Driver registration failed," + "module not inserted.\n"); + return ret; + } + return 0; +} + +static void i2c_bma020_exit(void) +{ + i2c_del_driver(&bma020_driver); +} + + +//**************************application interface*************************// + +int app_open(struct inode *inode, struct file *filp) +{ +/* printk("##!!## open entry\n"); */ + unsigned char data1, data2; + + bma020_set_mode(BMA020_MODE_NORMAL); /* set to normal mode */ + msleep(2); /* delay for 1ms */ + bma020_soft_reset(); + msleep(5); /* delay for >=1.3ms; */ + bma020_set_range(BMA020_RANGE_2G); + bma020_set_bandwidth(BMA020_BW_25HZ); + bma020_set_shadow_dis(BMA020_SHADOW_DIS_ON); /* free read sequence */ + + /* bma020_selftest(0); */ + + /* test customer reserved reg at addr CUSTOMER1_REG, CUSTOMER2_REG */ + data1 = 0x55; + data2 = 0xAA; + p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, CUSTOMER_RESERVED1__REG, &data1, 1); + p_bma020->BMA020_BUS_WRITE_FUNC(p_bma020->dev_addr, CUSTOMER_RESERVED2__REG, &data2, 1); + p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, CUSTOMER_RESERVED1__REG, &data1, 1); + p_bma020->BMA020_BUS_READ_FUNC(p_bma020->dev_addr, CUSTOMER_RESERVED2__REG, &data2, 1); + + if ((0x55 != data1) || (0xAA != data2)) { + printk("bma020 open err.\n"); + return -1; + } + + /* initialize the wait queue head */ + init_waitqueue_head(&p_bma020->int_wait); + + bma020_set_mode(BMA020_MODE_NORMAL); /* set to normal mode */ + msleep(1); /* delay for 1ms */ + bma020_soft_reset(); + msleep(5); /* delay for >=1.3ms; */ + +/* printk("##!!## open quit \n"); */ + return 0; +} + +int app_release(struct inode *inode, struct file *filp) +{ +/* printk("##!!## release entry\n"); */ + bma020_set_mode(BMA020_MODE_NORMAL); /* set to normal mode */ + msleep(1); /* delay for 1ms */ + bma020_soft_reset(); + msleep(5); /* delay for >=1.3ms; */ + bma020_set_range(BMA020_RANGE_2G); + bma020_set_bandwidth(BMA020_BW_25HZ); + /* bma020_set_shadow_dis(BMA020_SHADOW_DIS_ON); //free read sequence */ + + bma020_set_mode(BMA020_MODE_SLEEP);/* sleep */ + + return app_fasync(-1, filp, 0); + +/* printk("##!!## release quit\n"); */ + + return 0; +} + + + + +/* ppos: range in [0~0x15], assign address offset in mem map of BMA020 */ +static ssize_t app_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) +{ + + char local[0x16]; + int pos = *(ppos); + int read_len = count; + int ret; + + if((pos > 0x15L) || (pos < 0)) + return 0; + if((pos + read_len) > (0x16L)) + read_len = (0x16L - pos); + + ret = bma020_read_reg(pos, local, read_len); + if (copy_to_user((char *)buf, local, ret)) { + return -EFAULT; + } + + return ret; + +} + + +/* arg: data format should be 'char' for 'SET_XXX' and 'char *' for 'GET_XXX' */ +static int app_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg) +{ + unsigned char val; + bma020acc_t acc_data; + +/* printk("##!!##ioctl entry\n"); */ + + switch (cmd) { + case SET_RST: + case SET_NORMAL: + bma020_set_mode(BMA020_MODE_NORMAL); + msleep(1); /* delay for 1ms */ + /* bma020_soft_reset(); */ + msleep(2); /* delay for >=1.3ms */ + bma020_reset_interrupt(); + break; + + case GET_MODE:/* return BMA020_MODE_NORMAL, BMA020_MODE_SLEEP, BMA020_MODE_WAKE_UP */ + val = bma020_get_mode(); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_SLEEP: + bma020_set_mode(BMA020_MODE_SLEEP); + break; + + case SET_WAKEUP_DUR:/* para BMA020_WAKE_UP_PAUSE_20MS, BMA020_WAKE_UP_PAUSE_80MS, */ + /* BMA020_WAKE_UP_PAUSE_320MS, BMA020_WAKE_UP_PAUSE_2560MS */ + val = (unsigned char)arg; + bma020_set_wake_up_pause(val); + break; + + case SET_AUTO_WAKEUP: + bma020_set_mode(BMA020_MODE_WAKE_UP); + break; + + case GET_G:/* return BMA020_RANGE_2G;BMA020_RANGE_4G;BMA020_RANGE_8G */ + bma020_get_range(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_G:/* para BMA020_RANGE_2G;BMA020_RANGE_4G;BMA020_RANGE_8G */ + val = (unsigned char)arg; + bma020_set_range(val); + break; + + case SET_BANDWIDTH: + val = (unsigned char)arg; + bma020_set_bandwidth(val); + break; + + case GET_BANDWIDTH: + bma020_get_bandwidth(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case GET_WAKEUP_DUR:/* BMA020_WAKE_UP_PAUSE_20MS, BMA020_WAKE_UP_PAUSE_80MS, */ + /*BMA020_WAKE_UP_PAUSE_320MS, BMA020_WAKE_UP_PAUSE_2560MS */ + bma020_get_wake_up_pause(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_LOW_G_THRESHOULD:/* ref to BMA020_LG_THRES_IN_G() */ + val = (unsigned char)arg; +#ifdef DEBUG_INFO + printk("##!!## low g thr= %d \n", val); +#endif + bma020_set_low_g_threshold(val); + break; + + case GET_LOW_G_THRESHOULD: + bma020_get_low_g_threshold(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_LOW_G_COUNTDOWN: + val = (unsigned char)arg; + bma020_set_low_g_countdown(val); + break; + + case GET_LOW_G_COUNTDOWN: + bma020_get_low_g_countdown(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + + case SET_LOW_G_DUR: + val = (unsigned char)arg; + bma020_set_low_g_duration(val); + break; + + case GET_LOW_G_DUR: + bma020_get_low_g_duration(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_HIGH_G_DUR: + val = (unsigned char)arg; + bma020_set_high_g_duration(val); + break; + + case GET_HIGH_G_DUR: + bma020_get_high_g_duration(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_HIGH_G_THRESHOULD:/* BMA020_HG_THRES_IN_G() */ + val = (unsigned char)arg; +#ifdef DEBUG_INFO + printk("##!!## high g thr= %d \n", val); +#endif + bma020_set_high_g_threshold(val); + break; + + case GET_HIGH_G_THRESHOULD: + bma020_get_high_g_threshold(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_HIGH_G_COUNTDOWN: + val = (unsigned char)arg; + bma020_set_high_g_countdown(val); + break; + + case GET_HIGH_G_COUNTDOWN: + bma020_get_high_g_countdown(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_HIGH_G_HYST: + val = (unsigned char)arg; + bma020_set_high_g_hyst(val); + break; + case SET_LOW_G_HYST: + val = (unsigned char)arg; + bma020_set_low_g_hyst(val); + break; + + case SET_ANY_MOTION_THRESHOULD:/* BMA020_ANY_MOTION_THRES_IN_G() */ + val = (unsigned char)arg; + bma020_set_any_motion_threshold(val); + break; + + case GET_ANY_MOTION_THRESHOULD:/* BMA020_ANY_MOTION_THRES_IN_G() */ + bma020_get_any_motion_threshold(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_ANY_MOTION_COUNT:/* BMA020_ANY_MOTION_DUR_1, BMA020_ANY_MOTION_DUR_3, */ + /* BMA020_ANY_MOTION_DUR_5, BMA020_ANY_MOTION_DUR_7 */ + val = (unsigned char)arg; + bma020_set_any_motion_count(val); + break; + + case GET_ANY_MOTION_COUNT:/* BMA020_ANY_MOTION_DUR_1, BMA020_ANY_MOTION_DUR_3, */ + /*BMA020_ANY_MOTION_DUR_5, BMA020_ANY_MOTION_DUR_7 */ + bma020_get_any_motion_count(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + /* + case SET_INTERRUPT_MASK://BMA020_INT_ALERT, BMA020_INT_ANY_MOTION, BMA020_INT_EN_ADV_INT, + //BMA020_INT_NEW_DATA, BMA020_INT_LATCH, BMA020_INT_HG, BMA020_INT_LG + val = (unsigned char)arg; + bma020_set_interrupt_mask(val); + break; + + case GET_INTERRUPT_MASK://BMA020_INT_ALERT, BMA020_INT_ANY_MOTION, BMA020_INT_EN_ADV_INT, + //BMA020_INT_NEW_DATA, BMA020_INT_LATCH, BMA020_INT_HG, BMA020_INT_LG + bma020_get_interrupt_mask(&val); + if (copy_to_user((char *)arg,&val,sizeof(val))) + return -EFAULT; + break; + */ + + case RESET_INT:/* resets the BMA020 interrupt status, can be used to reset a latched interrupt */ + bma020_reset_interrupt(); + break; + + case READ_ACCEL_XYZ:/* X,Y and Z-axis acceleration data readout, ref bma020acc_t structure for x,y,z data readout */ + bma020_read_accel_xyz(&acc_data); + /* debug */ + /* + printk("x=%d: ",acc_data.x); + printk("y=%d: ",acc_data.y); + printk("z=%d: ",acc_data.z); + printk("\n"); + */ + if (copy_to_user((char *)arg, &acc_data, sizeof(acc_data))) + return -EFAULT; + break; + + case GET_INT_STATUS:/* BMA020_INT_STATUS_HG, BMA020_INT_STATUS_LG, BMA020_INT_STATUS_HG_LATCHED, */ + /*BMA020_INT_STATUS_LG_LATCHED, BMA020_INT_STATUS_ALERT, BMA020_INT_STATUS_ST_RESULT */ + bma020_get_interrupt_status(&val); + if (copy_to_user((char *)arg, &val, sizeof(val))) + return -EFAULT; + break; + + case SET_LOW_G_INT:/* param onoff, enable=1, disable=0 */ + val = (unsigned char)arg; + bma020_set_low_g_int(val); + break; + + case SET_HIGH_G_INT:/* param onoff enable=1, disable=0 */ + val = (unsigned char)arg; + bma020_set_high_g_int(val) ; + break; + + case SET_ANY_MOTION_INT:/* param onoff enable=1, disable=0 */ + val = (unsigned char)arg; + bma020_set_any_motion_int(val); + break; + + case SET_ADVANCED_INT:/* param onoff enable=1, disable=0 */ + val = (unsigned char)arg; + bma020_set_advanced_int(val) ; + break; + + case LATCH_INT:/* param latched (=1 for latched interrupts), (=0 for unlatched interrupts) */ + val = (unsigned char)arg; + bma020_latch_int(val) ; + break; + + case SET_NEW_DATA_INT: /* param onoff enable=1, disable=0 */ + val = (unsigned char)arg; + bma020_set_new_data_int(val); + break; + + case WAIT_FOR_INT: + + interruptible_sleep_on(&(p_bma020->int_wait)); + + if (signal_pending(current)) + return -ERESTARTSYS; + bma020_read_accel_xyz(&acc_data); + if (copy_to_user((char *)arg, &acc_data, sizeof(acc_data))) + return -EFAULT; + /* debug */ + /* + printk("x=%d: ",acc_data.x); + printk("y=%d: ",acc_data.y); + printk("z=%d: ",acc_data.z); + printk("\n"); + */ + break; + + default: + return -EINVAL; + } +/* printk("##!!##ioctl quit\n"); */ + return 0; +} + + +/*************************************************************/ +#ifdef INPUT_POLL +static void bma020_idev_poll(struct input_polled_dev *dev) +{ + bma020acc_t acc_data; + struct input_dev *idev; + + mutex_lock(&bma020_lock); + + idev = dev->input; + + bma020_read_accel_xyz(&acc_data); + + /* debug */ +#ifdef DEBUG_INPUT_POLL + printk("x=%d: ", acc_data.x); + printk("y=%d: ", acc_data.y); + printk("z=%d: ", acc_data.z); + printk("\n"); +#endif + + input_report_rel(idev, REL_X, acc_data.x); + input_report_rel(idev, REL_Y, acc_data.y); + input_report_rel(idev, REL_Z, acc_data.z); + input_sync(idev); + + mutex_unlock(&bma020_lock); + +} +#endif + + +#define BMA020_POLL_INTERVAL 200 /* msec */ + + +//***************************cdev register****************************// +static const struct file_operations app_fops = +{ + .owner = THIS_MODULE, + .read = app_read, + /* .write=, */ + .ioctl = app_ioctl, + /* .poll = , */ + .open = app_open, + .release = app_release, + .fasync = app_fasync, +}; + + +static int register_as_cdev(void)/* return 0 for success */ +{ + int ret, devno; + + p_bma020->app_major = 55; /* major dev no. */ + + /* apply for dev no. */ + devno = MKDEV(p_bma020->app_major, 0); + ret = register_chrdev_region(devno, 1, "bma020_device"); + +#if 0 + if (p_bma020->app_major) { + devno = MKDEV(p_bma020->app_major, 0); + ret = register_chrdev_region(devno, 1, "bma020_device"); + } else { + ret = alloc_chrdev_region(&devno, 0, 1, "bma020_device"); + p_bma020->app_major = MAJOR(devno); + } +#endif + + if (ret < 0) { + printk(KERN_ERR "Failed to register bma020 cdev.\n"); + goto ERROR0; + } + printk("register bma cdev, major: %d \n", p_bma020->app_major); + + cdev_init(&(p_bma020->bma_cdev), &app_fops); + p_bma020->bma_cdev.owner = THIS_MODULE; + + ret = cdev_add(&(p_bma020->bma_cdev), devno, 1); + if (ret) { + printk(KERN_ERR "Error cdev bma020 added\n"); + goto ERROR1; + } + + return 0; +ERROR1: + unregister_chrdev_region(MKDEV(p_bma020->app_major, 0), 1); +ERROR0: + return ret; +} + +static void unregister_as_cdev(void) +{ + cdev_del(&(p_bma020->bma_cdev)); + unregister_chrdev_region(MKDEV(p_bma020->app_major, 0), 1); +} +#ifdef INPUT_POLL +static int register_as_hwmon(void) +{ + struct input_dev *idev; + int ret; + + /* register input poll dev */ + + p_bma020->bma020_idev = input_allocate_polled_device(); + if (!(p_bma020->bma020_idev)) { + ret = -ENOMEM; + goto ERROR2; + } + + p_bma020->bma020_idev->poll = bma020_idev_poll; + p_bma020->bma020_idev->poll_interval = BMA020_POLL_INTERVAL; + + /* initialize the input device */ + idev = p_bma020->bma020_idev->input; + idev->name = "bma020_input"; + idev->phys = "bma020/input0"; + idev->dev.parent = &g_client->dev; + + idev->id.bustype = BUS_HOST; + + input_set_capability(idev, EV_REL, REL_X); + input_set_capability(idev, EV_REL, REL_Y); + input_set_capability(idev, EV_REL, REL_Z); + + ret = input_register_polled_device(p_bma020->bma020_idev); + + if (ret) + goto ERROR3; + + /* initialize sensor to normal operation mode */ + bma020_set_mode(BMA020_MODE_NORMAL); /* set to normal mode */ + msleep(2); /* delay for 1ms */ + bma020_soft_reset(); + msleep(2); /* delay for >=1.3ms; */ + bma020_set_range(BMA020_RANGE_2G); + bma020_set_bandwidth(BMA020_BW_25HZ); + bma020_set_shadow_dis(BMA020_SHADOW_DIS_ON); /* free read sequence */ + + return 0; + +ERROR3: + input_free_polled_device(p_bma020->bma020_idev); +ERROR2: + return ret; + +} +#endif + +void unregister_as_hwmon(void) +{ + input_unregister_polled_device(p_bma020->bma020_idev); + input_free_polled_device(p_bma020->bma020_idev); +} + + + + +static int __init app_init(void) +{ + int ret; + bma020_t *new_bma020 = NULL; + + printk("bma020 init \n"); + /*****************initialize bma020 device*******************/ + new_bma020 = kzalloc(sizeof(bma020_t), GFP_KERNEL); + + if (!new_bma020) { + printk(KERN_ERR "Failed to allocate bma020 device.\n"); + ret = -ENOMEM; + goto ERROR0; + } + /* initialize global ptr */ + p_bma020 = new_bma020; + + /*===================================================*/ +#ifndef INPUT_POLL + /* register as char device, combine file_operations */ + ret = register_as_cdev(); + if (ret) + goto ERROR1; +#endif + /* attach to i2c bus */ + ret = i2c_bma020_init(); + if (ret) { +#ifdef INPUT_POLL + goto ERROR1; +#else + goto ERROR2; +#endif + } +#ifdef INPUT_POLL + /* register as input hardware monitor device, create input poll event */ + ret = register_as_hwmon(); + if (ret) + goto ERROR3; +#endif + + return 0; + +#ifdef INPUT_POLL +ERROR3: + i2c_bma020_exit(); +#endif + +#ifndef INPUT_POLL +ERROR2: + unregister_as_cdev(); +#endif + +ERROR1: + kfree(new_bma020); + +ERROR0: + p_bma020 = NULL; + return ret; + +} + +static void __exit app_exit(void) +{ +#ifdef INPUT_POLL + unregister_as_hwmon(); +#endif + i2c_bma020_exit(); +#ifndef INPUT_POLL + unregister_as_cdev(); +#endif + kfree(p_bma020); + p_bma020 = NULL; +} + + + +module_init(app_init); +module_exit(app_exit); + +MODULE_DESCRIPTION("BMA020 I2C Client driver"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/hwmon/bma020/bma020_fs.c b/drivers/hwmon/bma020/bma020_fs.c new file mode 100644 index 00000000000000..d168b462ca2503 --- /dev/null +++ b/drivers/hwmon/bma020/bma020_fs.c @@ -0,0 +1,1471 @@ +/* +* bma020_fs.c - ST BMA020 accelerometer driver +* +* Copyright (C) 2007-2008 Yan Burman +* Copyright (C) 2008 Eric Piel +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bma020_fs.h" +/* #define DEBUG_INFO */ + +static struct i2c_bma020 bma_dev; +static struct i2c_client *g_client; + +//**********************i2c bus function*****************************// + +static int i2c_bma020_read_buf(u8 reg_addr, u8 *buf, u8 cnt)/* ret 0 for success */ +{ + int ret = 0; + + if (NULL == g_client) /* No global client pointer? */ + return -EFAULT; + /* + if (-1 == set_bus_busy()) + return -EINVAL; + */ + ret = i2c_master_send(g_client, ®_addr, 1);/* i2c_smbus_write_byte(g_client,reg_addr); */ + /* set_bus_free(); */ + + if (ret < 0) + return -EIO; + + /* + if (-1 == set_bus_busy()) + return -EINVAL; + */ + ret = i2c_master_recv(g_client, buf, cnt); + /* set_bus_free(); */ + + if (ret < 0) + return -EIO; + + return 0; +} + +static int i2c_bma020_write_buf(u8 reg_addr, u8 *buf, u8 cnt)/* ret 0 for success */ +{ + int ret = 0; + int status; + + if (NULL == g_client) /* No global client pointer? */ + return -EFAULT; + + /* + if (-1 == set_bus_busy()) + return -EINVAL; + */ + ret = i2c_smbus_write_byte_data(g_client, reg_addr, *buf);/* 0 for success */ + /* set_bus_free(); */ + + if (0 == ret) + return 0; + else + return -EIO; +} + +//*************************** low level operation **************************************// +static int bma020_detect(void) +{ + int comres = 0; + unsigned char data; + /* get chip info. */ + bma_dev.dev_addr = BMA020_I2C_ADDR; /* preset SM380 I2C_addr */ + comres |= i2c_bma020_read_buf(CHIP_ID__REG, &data, 1);/* read Chip Id */ + bma_dev.chip_id = BMA020_GET_BITSLICE(data, CHIP_ID); /* get bitslice */ + comres |= i2c_bma020_read_buf(ML_VERSION__REG, &data, 1); /* read Version reg */ + bma_dev.ml_version = BMA020_GET_BITSLICE(data, ML_VERSION); /* get ML Version */ + bma_dev.al_version = BMA020_GET_BITSLICE(data, AL_VERSION);/* get AL Version */ + + return comres; +} + +/** Perform soft reset of BMA020 via bus command +*/ +int bma020_soft_reset(void) +{ + int comres; + unsigned char data = 0; + data = BMA020_SET_BITSLICE(data, SOFT_RESET, 1); + comres = i2c_bma020_write_buf(SOFT_RESET__REG, &data, 1); + return comres; +} + +/** start BMA020s integrated selftest function +\param st 1 = selftest0, 3 = selftest1 (see also) +\return result of bus communication function +\see BMA020_SELF_TEST0_ON +\see BMA020_SELF_TEST1_ON +*/ +int bma020_selftest(unsigned char st) +{ + int comres; + unsigned char data; + comres = i2c_bma020_read_buf(SELF_TEST__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, SELF_TEST, st); + comres |= i2c_bma020_write_buf(SELF_TEST__REG, &data, 1); + return comres; +} + +/** set bma020s range +\param range +\return result of bus communication function +\see BMA020_RANGE_2G +\see BMA020_RANGE_4G +\see BMA020_RANGE_8G +*/ +int bma020_set_range(char range) +{ + int comres = 0; + unsigned char data; + + if (range < 3) { + comres = i2c_bma020_read_buf(RANGE__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, RANGE, range); + comres |= i2c_bma020_write_buf(RANGE__REG, &data, 1); + } + return comres; +} + +/* readout select range from BMA020 +\param *range pointer to range setting +\return result of bus communication function +\see BMA020_RANGE_2G, BMA020_RANGE_4G, BMA020_RANGE_8G +\see bma020_set_range() +*/ +int bma020_get_range(unsigned char *range) +{ + int comres = 0; + + comres = i2c_bma020_read_buf(RANGE__REG, range, 1); + *range = BMA020_GET_BITSLICE(*range, RANGE); + return comres; +} + +/** set BMA020s operation mode +\param mode 0 = normal, 2 = sleep, 3 = auto wake up +\return result of bus communication function +\note Available constants see below +\see BMA020_MODE_NORMAL, BMA020_MODE_SLEEP, BMA020_MODE_WAKE_UP +\see bma020_get_mode() +*/ +int bma020_set_mode(unsigned char mode) +{ + int comres = 0; + unsigned char data1, data2; + + if (mode < 4 || mode != 1) { + comres = i2c_bma020_read_buf(WAKE_UP__REG, &data1, 1); + data1 = BMA020_SET_BITSLICE(data1, WAKE_UP, mode); + comres |= i2c_bma020_read_buf(SLEEP__REG, &data2, 1); + data2 = BMA020_SET_BITSLICE(data2, SLEEP, (mode >> 1)); + + comres |= i2c_bma020_write_buf(WAKE_UP__REG, &data1, 1); + comres |= i2c_bma020_write_buf(SLEEP__REG, &data2, 1); + bma_dev.mode = mode; + } + return comres; +} + +/** get selected mode +\return used mode +\note this function returns the mode stored in \ref bma020_t structure +\see BMA020_MODE_NORMAL, BMA020_MODE_SLEEP, BMA020_MODE_WAKE_UP +\see bma020_set_mode() +*/ +unsigned char bma020_get_mode(void) +{ + return bma_dev.mode; +} + +/** set BMA020 internal filter bandwidth +\param bw bandwidth (see bandwidth constants) +\return result of bus communication function +\see #define BMA020_BW_25HZ, BMA020_BW_50HZ, BMA020_BW_100HZ, +\BMA020_BW_190HZ, BMA020_BW_375HZ, +\BMA020_BW_750HZ, BMA020_BW_1500HZ +\see bma020_get_bandwidth() +*/ +int bma020_set_bandwidth(char bw) +{ + int comres = 0; + unsigned char data; + if (bw < 8) { + comres = i2c_bma020_read_buf(BANDWIDTH__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, BANDWIDTH, bw); + comres |= i2c_bma020_write_buf(BANDWIDTH__REG, &data, 1); + } + return comres; +} + +/** read selected bandwidth from BMA020 +\param *bw pointer to bandwidth return value +\return result of bus communication function +\see #define BMA020_BW_25HZ, BMA020_BW_50HZ, BMA020_BW_100HZ, +\BMA020_BW_190HZ, BMA020_BW_375HZ,BMA020_BW_750HZ, BMA020_BW_1500HZ +\see bma020_set_bandwidth() +*/ +int bma020_get_bandwidth(unsigned char *bw) +{ + int comres = 1; + + comres = i2c_bma020_read_buf(BANDWIDTH__REG, bw, 1); + *bw = BMA020_GET_BITSLICE(*bw, BANDWIDTH); + return comres; +} + +/** set BMA020 auto wake up pause +\param wup wake_up_pause parameters +\return result of bus communication function +\see BMA020_WAKE_UP_PAUSE_20MS, BMA020_WAKE_UP_PAUSE_80MS, +\BMA020_WAKE_UP_PAUSE_320MS,BMA020_WAKE_UP_PAUSE_2560MS +\see bma020_get_wake_up_pause() +*/ + +int bma020_set_wake_up_pause(unsigned char wup) +{ + int comres = 0; + unsigned char data; + + comres = i2c_bma020_read_buf(WAKE_UP_PAUSE__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, WAKE_UP_PAUSE, wup); + comres |= i2c_bma020_write_buf(WAKE_UP_PAUSE__REG, &data, 1); + return comres; +} + +/** read BMA020 auto wake up pause from image +\param *wup wake up pause read back pointer +\see BMA020_WAKE_UP_PAUSE_20MS, BMA020_WAKE_UP_PAUSE_80MS, +\BMA020_WAKE_UP_PAUSE_320MS,BMA020_WAKE_UP_PAUSE_2560MS +\see bma020_set_wake_up_pause() +*/ +int bma020_get_wake_up_pause(unsigned char *wup) +{ + int comres = 1; + unsigned char data; + comres = i2c_bma020_read_buf(WAKE_UP_PAUSE__REG, &data, 1); + *wup = BMA020_GET_BITSLICE(data, WAKE_UP_PAUSE); + return comres; +} + +/* Thresholds and Interrupt Configuration */ +/** set low-g interrupt threshold +\param th set the threshold +\note the threshold depends on configured range. A macro +\ref BMA020_LG_THRES_IN_G() for range to +\register value conversion is available. +\see BMA020_LG_THRES_IN_G() +\see bma020_get_low_g_threshold() +*/ +int bma020_set_low_g_threshold(unsigned char th) +{ + int comres; + comres = i2c_bma020_write_buf(LG_THRES__REG, &th, 1); + return comres; +} + +/** get low-g interrupt threshold +\param *th get the threshold value from sensor image +\see bma020_set_low_g_threshold() +*/ +int bma020_get_low_g_threshold(unsigned char *th) +{ + int comres = 1; + comres = i2c_bma020_read_buf(LG_THRES__REG, th, 1); + return comres; +} + +/** set low-g interrupt countdown +\param cnt get the countdown value from sensor image +\see bma020_get_low_g_countdown() +*/ +int bma020_set_low_g_countdown(unsigned char cnt) +{ + int comres = 0; + unsigned char data; + comres = i2c_bma020_read_buf(COUNTER_LG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, COUNTER_LG, cnt); + comres |= i2c_bma020_write_buf(COUNTER_LG__REG, &data, 1); + return comres; +} + +/** get low-g interrupt countdown +\param cnt get the countdown value from sensor image +\see bma020_set_low_g_countdown() +*/ +int bma020_get_low_g_countdown(unsigned char *cnt) +{ + int comres = 1; + unsigned char data; + comres = i2c_bma020_read_buf(COUNTER_LG__REG, &data, 1); + *cnt = BMA020_GET_BITSLICE(data, COUNTER_LG); + return comres; +} + +/** set high-g interrupt countdown +\param cnt get the countdown value from sensor image +\see bma020_get_high_g_countdown() +*/ +int bma020_set_high_g_countdown(unsigned char cnt) +{ + int comres = 1; + unsigned char data; + comres = i2c_bma020_read_buf(COUNTER_HG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, COUNTER_HG, cnt); + comres |= i2c_bma020_write_buf(COUNTER_HG__REG, &data, 1); + return comres; +} + +/** get high-g interrupt countdown +\param cnt get the countdown value from sensor image +\see bma020_set_high_g_countdown() +*/ +int bma020_get_high_g_countdown(unsigned char *cnt) +{ + int comres = 0; + unsigned char data; + comres = i2c_bma020_read_buf(COUNTER_HG__REG, &data, 1); + *cnt = BMA020_GET_BITSLICE(data, COUNTER_HG); + return comres; +} + +/** configure low-g duration value +\param dur low-g duration in miliseconds +\see bma020_get_low_g_duration(), bma020_get_high_g_duration(), +\bma020_set_high_g_duration() +*/ +int bma020_set_low_g_duration(unsigned char dur) +{ + int comres = 0; + comres = i2c_bma020_write_buf(LG_DUR__REG, &dur, 1); + return comres; +} + +int bma020_set_low_g_hyst(unsigned char hyst) +{ + int comres = 0; + unsigned char data; + + comres = i2c_bma020_read_buf(LG_HYST__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, LG_HYST , hyst); +#ifdef DEBUG_INFO + printk(KERN_INFO"hyst=0x%x\n", data); +#endif + comres |= i2c_bma020_write_buf(LG_HYST__REG, &data, 1); + return comres; +} + +int bma020_set_high_g_hyst(unsigned char hyst) +{ + int comres = 0; + unsigned char data; + + comres = i2c_bma020_read_buf(HG_HYST__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, HG_HYST , hyst); +#ifdef DEBUG_INFO + printk(KERN_INFO"hyst=0x%x\n", data); +#endif + comres |= i2c_bma020_write_buf(HG_HYST__REG, &data, 1); + return comres; +} + +/** read out low-g duration value from sensor image +\param dur low-g duration in miliseconds +\see bma020_set_low_g_duration(), bma020_get_high_g_duration(), +\bma020_set_high_g_duration() +*/ +int bma020_get_low_g_duration(unsigned char *dur) +{ + int comres = 0; + comres = i2c_bma020_read_buf(LG_DUR__REG, dur, 1); + return comres; +} + +/** set low-g interrupt threshold +\param th set the threshold +\note the threshold depends on configured range. A macro +\ref BMA020_HG_THRES_IN_G() for range to +\register value conversion is available. +\see BMA020_HG_THRES_IN_G() +\see bma020_get_high_g_threshold() +*/ +int bma020_set_high_g_threshold(unsigned char th) +{ + int comres = 0; + comres = i2c_bma020_write_buf(HG_THRES__REG, &th, 1); + return comres; +} + +/** get high-g interrupt threshold +\param *th get the threshold value from sensor image +\see bma020_set_high_g_threshold() +*/ +int bma020_get_high_g_threshold(unsigned char *th) +{ + int comres = 0; + comres = i2c_bma020_read_buf(HG_THRES__REG, th, 1); + return comres; +} + +/** configure high-g duration value +\param dur high-g duration in miliseconds +\see bma020_get_high_g_duration(), bma020_set_low_g_duration(), +\bma020_get_low_g_duration() +*/ +int bma020_set_high_g_duration(unsigned char dur) +{ + int comres = 0; + comres = i2c_bma020_write_buf(HG_DUR__REG, &dur, 1); + return comres; +} + +/** read out high-g duration value from sensor image +\param dur high-g duration in miliseconds +\see bma020_set_high_g_duration(), bma020_get_low_g_duration(), bma020_set_low_g_duration(), +*/ +int bma020_get_high_g_duration(unsigned char *dur) +{ + int comres = 0; + comres = i2c_bma020_read_buf(HG_DUR__REG, dur, 1); + return comres; +} + +/** set threshold value for any_motion feature +\param th set the threshold a macro \ref BMA020_ANY_MOTION_THRES_IN_G() is available for that +\see BMA020_ANY_MOTION_THRES_IN_G() +*/ +int bma020_set_any_motion_threshold(unsigned char th) +{ + int comres = 0; + comres = i2c_bma020_write_buf(ANY_MOTION_THRES__REG, &th, 1); + return comres; +} + +/** get threshold value for any_motion feature +\param *th read back any_motion threshold from image register +\see BMA020_ANY_MOTION_THRES_IN_G() +*/ +int bma020_get_any_motion_threshold(unsigned char *th) +{ + int comres = 0; + comres = i2c_bma020_read_buf(ANY_MOTION_THRES__REG, th, 1); + return comres; +} + +/** set counter value for any_motion feature +\param amc set the counter value, constants are available for that +\see BMA020_ANY_MOTION_DUR_1, BMA020_ANY_MOTION_DUR_3, BMA020_ANY_MOTION_DUR_5, +\BMA020_ANY_MOTION_DUR_7 +*/ +int bma020_set_any_motion_count(unsigned char amc) +{ + int comres = 0; + unsigned char data; + comres = i2c_bma020_read_buf(ANY_MOTION_DUR__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ANY_MOTION_DUR, amc); + comres = i2c_bma020_write_buf(ANY_MOTION_DUR__REG, &data, 1); + return comres; +} + +/** get counter value for any_motion feature from image register +\param *amc readback pointer for counter value +\see BMA020_ANY_MOTION_DUR_1, BMA020_ANY_MOTION_DUR_3, BMA020_ANY_MOTION_DUR_5, +\BMA020_ANY_MOTION_DUR_7 +*/ +int bma020_get_any_motion_count(unsigned char *amc) +{ + int comres = 0; + unsigned char data; + comres = i2c_bma020_read_buf(ANY_MOTION_DUR__REG, &data, 1); + *amc = BMA020_GET_BITSLICE(data, ANY_MOTION_DUR); + return comres; +} + +/** set the interrupt mask for BMA020's interrupt features in one mask +\param mask input for interrupt mask +\see BMA020_INT_ALERT, BMA020_INT_ANY_MOTION, BMA020_INT_EN_ADV_INT, BMA020_INT_NEW_DATA, +\BMA020_INT_LATCH, BMA020_INT_HG, BMA020_INT_LG +*/ +int bma020_set_interrupt_mask(unsigned char mask) +{ + int comres = 0; + unsigned char data[4]; + data[0] = mask & BMA020_CONF1_INT_MSK; + data[2] = ((mask<<1) & BMA020_CONF2_INT_MSK); + comres = i2c_bma020_read_buf(BMA020_CONF1_REG, &data[1], 1); + comres |= i2c_bma020_read_buf(BMA020_CONF2_REG, &data[3], 1); + data[1] &= (~BMA020_CONF1_INT_MSK); + data[1] |= data[0]; + data[3] &= (~(BMA020_CONF2_INT_MSK)); + data[3] |= data[2]; + comres |= i2c_bma020_write_buf(BMA020_CONF1_REG, &data[1], 1); + comres |= i2c_bma020_write_buf(BMA020_CONF2_REG, &data[3], 1); + return comres; +} + +/** set the shadow_dis for BMA020's shadow_dis bit +\param ssd input for on/off control +\see BMA020_SHADOW_DIS_OFF, BMA020_SHADOW_DIS_ON +*/ +int bma020_set_shadow_dis(unsigned char ssd) +{ + int comres = 0; + unsigned char data; + if ((ssd == 1) || (ssd == 0)) { + comres = i2c_bma020_read_buf(BMA020_CONF2_REG, &data, 1); + data = BMA020_SET_BITSLICE(data, SHADOW_DIS, ssd); + comres |= i2c_bma020_write_buf(BMA020_CONF2_REG, &data, 1); + } + return comres; +} + +/** get the current interrupt mask settings from BMA020 image registers +\param *mask return variable pointer for interrupt mask +\see BMA020_INT_ALERT, BMA020_INT_ANY_MOTION, BMA020_INT_EN_ADV_INT, BMA020_INT_NEW_DATA, +\BMA020_INT_LATCH, BMA020_INT_HG, BMA020_INT_LG +*/ +int bma020_get_interrupt_mask(unsigned char *mask) +{ + int comres = 0; + unsigned char data; + comres = i2c_bma020_read_buf(BMA020_CONF1_REG, &data, 1); + *mask = data & BMA020_CONF1_INT_MSK; + comres |= i2c_bma020_read_buf(BMA020_CONF2_REG, &data, 1); + *mask = *mask | ((data & BMA020_CONF2_INT_MSK)>>1); + return comres; +} + +/** resets the BMA020 interrupt status +\note this feature can be used to reset a latched interrupt +*/ +int bma020_reset_interrupt(void) +{ +#ifdef DEBUG_INFO + printk(KERN_INFO"file=%s, func=%s\n", __FILE__, __FUNCTION__); +#endif + int comres = 0; + unsigned char data = (1 << RESET_INT__POS); + comres = i2c_bma020_write_buf(RESET_INT__REG, &data, 1); + return comres; +} + +/* Data Readout */ +/** X-axis acceleration data readout +\param *a_x pointer for 16 bit 2's complement data output (LSB aligned) +*/ +int bma020_read_accel_x(short *a_x) +{ + int comres; + unsigned char data[2]; + comres = i2c_bma020_read_buf(ACC_X_LSB__REG, data, 2); + if (comres) + return -EIO; + *a_x = BMA020_GET_BITSLICE(data[0], ACC_X_LSB) | + BMA020_GET_BITSLICE(data[1], ACC_X_MSB) << ACC_X_LSB__LEN; + *a_x = *a_x << (sizeof(short)*8 - (ACC_X_LSB__LEN + ACC_X_MSB__LEN)); + *a_x = *a_x >> (sizeof(short)*8 - (ACC_X_LSB__LEN + ACC_X_MSB__LEN)); + return comres; +} + +/** Y-axis acceleration data readout +\param *a_y pointer for 16 bit 2's complement data output (LSB aligned) +*/ +int bma020_read_accel_y(short *a_y) +{ + int comres; + unsigned char data[2]; + + comres = i2c_bma020_read_buf(ACC_Y_LSB__REG, data, 2); + if (comres) + return -EIO; + *a_y = BMA020_GET_BITSLICE(data[0], ACC_Y_LSB) | + BMA020_GET_BITSLICE(data[1], ACC_Y_MSB) << ACC_Y_LSB__LEN; + *a_y = *a_y << (sizeof(short)*8 - (ACC_Y_LSB__LEN + ACC_Y_MSB__LEN)); + *a_y = *a_y >> (sizeof(short)*8 - (ACC_Y_LSB__LEN + ACC_Y_MSB__LEN)); + return comres; +} + +/** Z-axis acceleration data readout +\param *a_z pointer for 16 bit 2's complement data output (LSB aligned) +*/ +int bma020_read_accel_z(short *a_z) +{ + int comres; + unsigned char data[2]; + + comres = i2c_bma020_read_buf(ACC_Z_LSB__REG, data, 2); + if (comres) + return -EIO; + *a_z = BMA020_GET_BITSLICE(data[0], ACC_Z_LSB) | + BMA020_GET_BITSLICE(data[1], ACC_Z_MSB) << ACC_Z_LSB__LEN; + *a_z = *a_z << (sizeof(short)*8 - (ACC_Z_LSB__LEN + ACC_Z_MSB__LEN)); + *a_z = *a_z >> (sizeof(short)*8 - (ACC_Z_LSB__LEN + ACC_Z_MSB__LEN)); + return comres; +} + +/** X,Y and Z-axis acceleration data readout +\param *acc pointer to \ref bma020acc_t structure for x,y,z data readout +\note data will be read by multi-byte protocol into a 6 byte structure +*/ +int bma020_read_accel_xyz(bma020acc_t *acc) +{ + int comres; + unsigned char data[6]; + comres = i2c_bma020_read_buf(ACC_X_LSB__REG, &data[0], 6); + if (comres) + return -EIO; + acc->x = BMA020_GET_BITSLICE(data[0], ACC_X_LSB) | + (BMA020_GET_BITSLICE(data[1], ACC_X_MSB) << ACC_X_LSB__LEN); + acc->x = acc->x << (sizeof(short)*8 - (ACC_X_LSB__LEN + ACC_X_MSB__LEN)); + acc->x = acc->x >> (sizeof(short)*8 - (ACC_X_LSB__LEN + ACC_X_MSB__LEN)); + acc->y = BMA020_GET_BITSLICE(data[2], ACC_Y_LSB) | + (BMA020_GET_BITSLICE(data[3], ACC_Y_MSB) << ACC_Y_LSB__LEN); + acc->y = acc->y << (sizeof(short)*8 - (ACC_Y_LSB__LEN + ACC_Y_MSB__LEN)); + acc->y = acc->y >> (sizeof(short)*8 - (ACC_Y_LSB__LEN + ACC_Y_MSB__LEN)); + acc->z = BMA020_GET_BITSLICE(data[4], ACC_Z_LSB) | + (BMA020_GET_BITSLICE(data[5], ACC_Z_MSB) << ACC_Z_LSB__LEN); + acc->z = acc->z << (sizeof(short)*8 - (ACC_Z_LSB__LEN + ACC_Z_MSB__LEN)); + acc->z = acc->z >> (sizeof(short)*8 - (ACC_Z_LSB__LEN + ACC_Z_MSB__LEN)); + return comres; +} + +/** check current interrupt status from interrupt status register in BMA020 image register +\param *ist pointer to interrupt status byte +\see BMA020_INT_STATUS_HG, BMA020_INT_STATUS_LG, BMA020_INT_STATUS_HG_LATCHED, +\BMA020_INT_STATUS_LG_LATCHED, BMA020_INT_STATUS_ALERT, BMA020_INT_STATUS_ST_RESULT +*/ +int bma020_get_interrupt_status(unsigned char *ist) +{ + int comres = 0; + comres = i2c_bma020_read_buf(BMA020_STATUS_REG, ist, 1); + return comres; +} + +/** enable/ disable low-g interrupt feature +\param onoff enable=1, disable=0 +*/ +int bma020_set_low_g_int(unsigned char onoff) +{ + int comres; + unsigned char data; + + comres = i2c_bma020_read_buf(ENABLE_LG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ENABLE_LG, onoff); + comres |= i2c_bma020_write_buf(ENABLE_LG__REG, &data, 1); + return comres; +} + +/** enable/ disable high-g interrupt feature +\param onoff enable=1, disable=0 +*/ +int bma020_set_high_g_int(unsigned char onoff) +{ + int comres; + unsigned char data; + + comres = i2c_bma020_read_buf(ENABLE_HG__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ENABLE_HG, onoff); + comres |= i2c_bma020_write_buf(ENABLE_HG__REG, &data, 1); + return comres; +} + +/** enable/ disable any_motion interrupt feature +\param onoff enable=1, disable=0 +\note for any_motion interrupt feature usage see also \ref bma020_set_advanced_int() +*/ +int bma020_set_any_motion_int(unsigned char onoff) +{ + int comres; + unsigned char data; + comres = i2c_bma020_read_buf(EN_ANY_MOTION__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, EN_ANY_MOTION, onoff); + comres |= i2c_bma020_write_buf(EN_ANY_MOTION__REG, &data, 1); + return comres; +} + +/** enable/ disable alert-int interrupt feature +\param onoff enable=1, disable=0 +\note for any_motion interrupt feature usage see also \ref bma020_set_advanced_int() +*/ +int bma020_set_alert_int(unsigned char onoff) +{ + int comres; + unsigned char data; + + comres = i2c_bma020_read_buf(ALERT__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, ALERT, onoff); + comres |= i2c_bma020_write_buf(ALERT__REG, &data, 1); + return comres; +} + +/** enable/ disable advanced interrupt feature +\param onoff enable=1, disable=0 +\see bma020_set_any_motion_int() +\see bma020_set_alert_int() +*/ +int bma020_set_advanced_int(unsigned char onoff) +{ + int comres; + unsigned char data; + comres = i2c_bma020_read_buf(ENABLE_ADV_INT__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, EN_ANY_MOTION, onoff); + comres |= i2c_bma020_write_buf(ENABLE_ADV_INT__REG, &data, 1); + return comres; +} + +/** enable/disable latched interrupt for all interrupt feature (global option) +\param latched (=1 for latched interrupts), (=0 for unlatched interrupts) +*/ + +int bma020_latch_int(unsigned char latched) +{ + int comres; + unsigned char data; + comres = i2c_bma020_read_buf(LATCH_INT__REG, &data, 1); + +#ifdef DEBUG_INFO + printk("read1=0x%x\n", data); +#endif + data = BMA020_SET_BITSLICE(data, LATCH_INT, latched); +#ifdef DEBUG_INFO + printk("read2=0x%x\n", data); +#endif + comres |= i2c_bma020_write_buf(LATCH_INT__REG, &data, 1); + return comres; +} + +int bma020_set_new_data_int(unsigned char onoff) +{ + /* printk("bma020_set_new_data_int onoff:%d \n", onoff); */ + int comres; + unsigned char data; + comres = i2c_bma020_read_buf(NEW_DATA_INT__REG, &data, 1); + data = BMA020_SET_BITSLICE(data, NEW_DATA_INT, onoff); + comres |= i2c_bma020_write_buf(NEW_DATA_INT__REG, &data, 1); + return comres; +} + +/** read function for raw register access +\param addr register address +\param *data pointer to data array for register read back +\param len number of bytes to be read starting from addr +*/ +int bma020_read_reg(unsigned char addr, unsigned char *data, unsigned char len) +{ + int comres; + + comres = i2c_bma020_read_buf(addr, data, len); + return comres; +} + +/** write function for raw register access +\param addr register address +\param *data pointer to data array for register write +\param len number of bytes to be written starting from addr +*/ +int bma020_write_reg(unsigned char addr, unsigned char *data, unsigned char len) +{ + int comres; + comres = i2c_bma020_write_buf(addr, data, len); + return comres; +} + +/* Maximum value our axis may get for the input device (signed 10 bits) */ +#define ACC_MAX_VAL 512 + +static int bma020_remove_fs(void); +static int bma020_add_fs(struct device *device); + +/** +* bma020_get_axis - For the given axis, give the value converted +* @axis: 1,2,3 - can also be negative +* @hw_values: raw values returned by the hardware +* +* Returns the converted value. +*/ +static inline int bma020_get_axis(s8 axis, int hw_values[3]) +{ + if (axis > 0) + return hw_values[axis - 1]; + else + return -hw_values[-axis - 1]; +} + +#ifdef DEBUG_INFO +void bma020_driver_test(void) + +{ + bma020acc_t data; + int tmp; + char s; + int i = 20; + bma020_soft_reset(); + tmp = bma020_selftest(0); + printk("bustest num=%d\n", tmp); + bma020_set_range(BMA020_RANGE_2G); + /* bma020_set_range(BMA020_RANGE_8G); */ + bma020_set_mode(0); + bma020_set_bandwidth(BMA020_BW_25HZ); + + while (i-- > 0) { + bma020_get_range(&s); + printk("range:%d\n", s); + bma020_read_accel_xyz(&data); + printk("x=%d: ", data.x); + printk("y=%d: ", data.y); + printk("z=%d: ", data.z); + printk("\n"); + msleep(1000); + } +} +#endif + +/** +* bma020_get_xyz_axis_adjusted - Get X, Y and Z axis values from the accelerometer +* @handle: the handle to the device +* @x: where to store the X axis value +* @y: where to store the Y axis value +* @z: where to store the Z axis value +* +* Note that 40Hz input device can eat up about 10% CPU at 800MHZ +*/ +static int bma020_get_xyz_axis_adjusted(int *x, int *y, int *z) +{ + int position[3]; + int ret; + + bma020acc_t acc; + ret = bma020_read_accel_xyz(&acc); + if (ret) + return -EIO; + position[0] = acc.x; + position[1] = acc.y; + position[2] = acc.z; + + *x = bma020_get_axis(bma_dev.ac.x, position) * (-1); + *y = bma020_get_axis(bma_dev.ac.y, position); + *z = bma020_get_axis(bma_dev.ac.z, position); + return 0; +} + +static int bma020_poweroff(void) +{ + printk(KERN_INFO"bma020 poweroff.\n"); + bma020_set_mode(BMA020_MODE_SLEEP);/* set to sleep mode instead of power off */ + return 0; +} + +static int bma020_poweron(void) +{ + unsigned char data1, data2; + int ret = 0; + printk(KERN_INFO"bma020 poweron.\n"); + + ret |= bma020_set_mode(BMA020_MODE_NORMAL); /* set to normal mode */ + msleep(2); /* delay for 1ms */ + ret |= bma020_soft_reset(); + msleep(5); /* delay for >=1.3ms; */ + ret |= bma020_set_range(bma_dev.range); + ret |= bma020_set_bandwidth(bma_dev.bandwidth); + /* bma020_set_shadow_dis(BMA020_SHADOW_DIS_ON); //free read sequence */ + + /* bma020_selftest(0); */ + /* test customer reserved reg at addr CUSTOMER1_REG, CUSTOMER2_REG */ + data1 = 0x55; + data2 = 0xAA; + ret |= i2c_bma020_write_buf(CUSTOMER_RESERVED1__REG, &data1, 1); + ret |= i2c_bma020_write_buf(CUSTOMER_RESERVED2__REG, &data2, 1); + ret |= i2c_bma020_read_buf(CUSTOMER_RESERVED1__REG, &data1, 1); + ret |= i2c_bma020_read_buf(CUSTOMER_RESERVED2__REG, &data2, 1); + if (ret) + return ret; + + if ((0x55 != data1) || (0xAA != data2)) { + printk(KERN_ERR"bma020 poweron err.\n"); + bma020_set_mode(BMA020_MODE_SLEEP); /* set to normal mode */ + return -1; + } + + ret |= bma020_set_mode(BMA020_MODE_NORMAL); /* set to normal mode */ + msleep(1); /* delay for 1ms */ + return ret; + /* bma020_soft_reset(); */ + /* msleep(5); //delay for >=1.3ms; */ +} + + +static int bma020_kthread(void *data) +{ + int x, y, z; + int ret; + static int x_last = 0, y_last = 0, z_last = 0; + + while (!kthread_should_stop()) { + ret = bma020_get_xyz_axis_adjusted(&x, &y, &z); + if (ret) {/* retry if this time failed */ + try_to_freeze(); + msleep_interruptible(bma_dev.sample_interval); + return; + } + + x = (x * 1 + x_last * 3) / 4; + y = (y * 1 + y_last * 3) / 4; + z = (z * 1 + z_last * 3) / 4; + x_last = x; + y_last = y; + z_last = z; + +#ifdef DEBUG_INFO + printk(KERN_INFO "x=%d, y=%d, z=%d \n", x, y, z); +#endif +#if 1 + input_report_abs(bma_dev.idev, ABS_X, y - bma_dev.xcalib); + input_report_abs(bma_dev.idev, ABS_Y, x - bma_dev.ycalib); + input_report_abs(bma_dev.idev, ABS_Z, -z - bma_dev.zcalib); +#else + input_report_abs(bma_dev.idev, ABS_X, x); + input_report_abs(bma_dev.idev, ABS_Y, y); + input_report_abs(bma_dev.idev, ABS_Z, z); +#endif + input_sync(bma_dev.idev); + + try_to_freeze(); + msleep_interruptible(bma_dev.sample_interval); + } + +} + + +/* +* To be called before starting to use the device. It makes sure that the +* device will always be on until a call to bma020_decrease_use(). Not to be +* used from interrupt context. +*/ +static void bma020_increase_use(void) +{ + printk(KERN_INFO"%s\n", __FUNCTION__); + mutex_lock(&bma_dev.lock); + bma_dev.usage++; + if (bma_dev.usage == 1) { + if (!bma_dev.is_on) { + if (!bma020_poweron()) { + /* 0 for success power on */ + bma_dev.is_on = 1; + bma_dev.kthread = kthread_run(bma020_kthread, + NULL, "bma020"); + } else { + bma_dev.is_on = 0; + bma_dev.usage = 0; + } + } + } + mutex_unlock(&bma_dev.lock); +} + +/* +* To be called whenever a usage of the device is stopped. +* It will make sure to turn off the device when there is not usage. +*/ +static void bma020_decrease_use(void) +{ + printk(KERN_INFO"%s\n", __FUNCTION__); + mutex_lock(&bma_dev.lock); + if (bma_dev.usage == 0) { + mutex_unlock(&bma_dev.lock); + return; + } + bma_dev.usage--; + if (bma_dev.usage == 0) { + bma020_poweroff(); + bma_dev.is_on = 0; + kthread_stop(bma_dev.kthread); + } + mutex_unlock(&bma_dev.lock); +} + +static inline void bma020_calibrate_joystick(void) +{ + bma020_get_xyz_axis_adjusted(&bma_dev.xcalib, &bma_dev.ycalib, &bma_dev.zcalib); +} + +static int bma020_joystick_enable(void) +{ + int err; + + if (bma_dev.idev) + return -EINVAL; + + bma_dev.idev = input_allocate_device(); + if (!bma_dev.idev) + return -ENOMEM; + + bma_dev.idev->name = "BOSCH BMA020 Accelerometer"; + bma_dev.idev->name = "bma020_input"; + bma_dev.idev->phys = "bma020/input0"; + bma_dev.idev->id.bustype = BUS_I2C; + bma_dev.idev->id.vendor = 0; + bma_dev.idev->dev.parent = bma_dev.device; + /* + bma_dev.idev->open = bma020_joystick_open; + bma_dev.idev->close = bma020_joystick_close; + */ + __set_bit(EV_ABS, bma_dev.idev->evbit); + __set_bit(ABS_X, bma_dev.idev->absbit); + __set_bit(ABS_Y, bma_dev.idev->absbit); + __set_bit(ABS_Z, bma_dev.idev->absbit); + + __set_bit(EV_SYN, bma_dev.idev->evbit); + __set_bit(EV_KEY, bma_dev.idev->evbit); + + input_set_abs_params(bma_dev.idev, ABS_X, + -ACC_MAX_VAL, ACC_MAX_VAL, 0, 0); + input_set_abs_params(bma_dev.idev, ABS_Y, + -ACC_MAX_VAL, ACC_MAX_VAL, 0, 0); + input_set_abs_params(bma_dev.idev, ABS_Z, + -ACC_MAX_VAL, ACC_MAX_VAL, 0, 0); + + err = input_register_device(bma_dev.idev); + if (err) { + printk(KERN_ERR "regist input driver error\n"); + input_free_device(bma_dev.idev); + bma_dev.idev = NULL; + } + + return err; +} + +static void bma020_joystick_disable(void) +{ + if (!bma_dev.idev) + return; + + input_unregister_device(bma_dev.idev); + bma_dev.idev = NULL; +} + + +/* +* Initialise the accelerometer and the various subsystems. +* Should be rather independant of the bus system. +*/ +static int bma020_init_device(struct i2c_bma020 *dev) +{ + bma020_add_fs(dev->device); + + if (bma020_joystick_enable()) + printk(KERN_ERR "bma020: joystick initialization failed\n"); + + return 0; +} + +static int bma020_dmi_matched(const struct dmi_system_id *dmi) +{ + bma_dev.ac = *((struct axis_conversion *)dmi->driver_data); + printk(KERN_INFO "bma020: hardware type %s found.\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). +* If the value is negative, the opposite of the hw value is used. */ +static struct axis_conversion bma020_axis_normal = {1, 2, 3}; +static struct axis_conversion bma020_axis_y_inverted = {1, -2, 3}; +static struct axis_conversion bma020_axis_x_inverted = {-1, 2, 3}; +static struct axis_conversion bma020_axis_z_inverted = {1, 2, -3}; +static struct axis_conversion bma020_axis_xy_rotated_left = {-2, 1, 3}; +static struct axis_conversion bma020_axis_xy_swap_inverted = {-2, -1, 3}; + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = bma020_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &bma020_axis_##_axis \ +} +static struct dmi_system_id bma020_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + { NULL, } + /* Laptop models without axis info (yet): + * "NC651xx" "HP Compaq 651" + * "NC671xx" "HP Compaq 671" + * "NC6910" "HP Compaq 6910" + * HP Compaq 8710x Notebook PC / Mobile Workstation + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + +static int bma020_add(struct device *device) +{ + if (!device) + return -EINVAL; + bma_dev.device = device; + + /* ##!!## axis direction should be checked */ + /* If possible use a "standard" axes order */ + if (dmi_check_system(bma020_dmi_ids) == 0) { + printk(KERN_INFO "bma020" ": laptop model unknown, " + "using default axes configuration\n"); + bma_dev.ac = bma020_axis_normal; + } + return bma020_init_device(&bma_dev); +} + + +/*********************** Sysfs stuff ***********************/ +static ssize_t bma020_acceleration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int x, y, z; + if (bma_dev.is_on != 0) { + bma020_get_xyz_axis_adjusted(&x, &y, &z); + } else { + x = 0; y = 0; z = 0; + } + + if (x < ACC_MAX_VAL && x > -ACC_MAX_VAL && + y < ACC_MAX_VAL && y > -ACC_MAX_VAL && + z < ACC_MAX_VAL && z > -ACC_MAX_VAL) { + return sprintf(buf, "(%d,%d,%d)\n", x, y, z); + } else + return 0; +} + +static ssize_t bma020_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d,%d)\n", bma_dev.xcalib, bma_dev.ycalib, bma_dev.zcalib); +} + +static ssize_t bma020_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef DEBUG_INFO + printk(KERN_INFO"func=%s\n", __FUNCTION__); + printk(KERN_INFO"msg=%s\n", buf); +#endif + if (bma_dev.is_on != 0) + bma020_calibrate_joystick(); + + return count; +} + +static ssize_t bma020_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (bma_dev.is_on == 0) + return sprintf(buf, "off\n"); + else + return sprintf(buf, "on,user %d\n", bma_dev.usage); +} + +static ssize_t bma020_status_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (strcmp(buf, "on\n") == 0) + bma020_increase_use(); + else if (strcmp(buf, "off\n") == 0) + bma020_decrease_use(); + else + printk(KERN_INFO"unrecognized, using on/off\n"); + return count; +} + + +static ssize_t bma020_interval_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + char msg[256] = "\0"; + if (count > 256) + count = 256; + memcpy(msg, buf, count-1);/* skip last "\n" */ + msg[count-1] = '\0'; + val = (unsigned int) simple_strtoul(msg, NULL, 10); + if (val < 5) /* TODO:avoid i2c too busy */ + val = 5; + bma_dev.sample_interval = val;/* double increase sample rate */ + printk(KERN_INFO "bma020 sample interval %d ms\n", bma_dev.sample_interval); + return count; +} + +static ssize_t bma020_interval_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", bma_dev.sample_interval); +} + +static ssize_t bma020_range_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + char msg[10] = "\0"; + if (count > 10) + count = 10; + memcpy(msg, buf, count-1);/* skip last "\n" */ + msg[count-1] = '\0'; + val = (unsigned int) simple_strtoul(msg, NULL, 10); + if (val >= 2) + bma_dev.range = BMA020_RANGE_8G; + else + bma_dev.range = val;/* 0 for BMA020_RANGE_2G, 1 for BMA020_RANGE_4G */ + if (bma_dev.is_on)/* activate immediately? */ + bma020_set_range(bma_dev.range); + + printk(KERN_INFO"bma020 range %d\n", bma_dev.range); + return count; +} + +static ssize_t bma020_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", bma_dev.range); +} + +static ssize_t bma020_bandwidth_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + char msg[10] = "\0"; + if (count > 10) + count = 10; + memcpy(msg, buf, count-1);/* skip last "\n" */ + msg[count-1] = '\0'; + val = (unsigned int) simple_strtoul(msg, NULL, 10); + printk("val=%d\n", val); + if (val >= 6) + bma_dev.bandwidth = BMA020_BW_1500HZ; + else + bma_dev.bandwidth = val; + if (bma_dev.is_on)/* activate immediately? */ + bma020_set_bandwidth(bma_dev.bandwidth); + + printk(KERN_INFO"bma020 bandwidth %d\n", bma_dev.bandwidth); + return count; +} + +static ssize_t bma020_bandwidth_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", bma_dev.bandwidth); +} + + +static DEVICE_ATTR(status, S_IRUGO|S_IWUGO, bma020_status_show, bma020_status_set); +static DEVICE_ATTR(acceleration, S_IRUGO, bma020_acceleration_show, NULL); +static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUGO, bma020_calibrate_show, + bma020_calibrate_store); +static DEVICE_ATTR(interval, S_IRUGO|S_IWUGO, bma020_interval_show, + bma020_interval_set); +static DEVICE_ATTR(range, S_IRUGO|S_IWUGO, bma020_range_show, + bma020_range_set); +static DEVICE_ATTR(bandwidth, S_IRUGO|S_IWUGO, bma020_bandwidth_show, + bma020_bandwidth_set); +/* static DEVICE_ATTR(rate, S_IRUGO, bma020_rate_show, NULL); */ + +static struct attribute *bma020_attributes[] = { + &dev_attr_status.attr, + &dev_attr_acceleration.attr, + &dev_attr_calibrate.attr, + &dev_attr_interval.attr, + &dev_attr_range.attr, + &dev_attr_bandwidth.attr, + NULL +}; + +static struct attribute_group bma020_attribute_group = { + .attrs = bma020_attributes +}; + +static int bma020_add_fs(struct device *device) +{ + return sysfs_create_group(&bma_dev.device->kobj, &bma020_attribute_group); +} + +static int bma020_remove_fs(void) +{ + sysfs_remove_group(&bma_dev.device->kobj, &bma020_attribute_group); + return 0; +} + + +#ifdef CONFIG_PM +static int bma020_suspend(struct i2c_client *client, pm_message_t state) +{ + bma020_poweroff(); + return 0; +} + +static int bma020_resume(struct i2c_client *client) +{ + bma020_poweron(); + return 0; +} +#else +#define bma020_suspend NULL +#define bma020_resume NULL +#endif + +static int __devinit bma020_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ +#ifdef DEBUG_INFO + printk(KERN_INFO"bma020_i2c_probe \n"); +#endif + int retry; + + /* initialize global ptr */ + g_client = client; + + bma_dev.usage = 0; + bma_dev.is_on = 0; + bma_dev.range = BMA020_RANGE_2G; + bma_dev.bandwidth = BMA020_BW_25HZ; + bma_dev.sample_interval = 1000;/* ms */ + mutex_init(&bma_dev.lock);/* be called before bma020_increase_use() */ + + /* make sure it's a bma020 sensor */ + for (retry = 0; retry < 30; retry++) { + bma020_poweron(); + bma020_detect(); + bma020_poweroff(); + if (bma_dev.chip_id == 0x02) + break; + } + if (bma_dev.chip_id != 0x02) { + printk(KERN_ERR "bma020 chip id error\n"); + return -1; + } + + printk(KERN_INFO "bma020:attatch to i2c bus, addr=0x%x, chip id=0x%x \n", + bma_dev.dev_addr, bma_dev.chip_id); + +#ifdef DEBUG_INFO + printk(KERN_INFO "chip id: 0x%x\n", bma_dev.chip_id); + printk(KERN_INFO "ml version: 0x%x\n", bma_dev.ml_version); + printk(KERN_INFO "al version: 0x%x\n", bma_dev.al_version); +#endif + + /* bma gpio interrupt initialization */ + bma_dev.gpio = mfp_to_gpio(MFP_PIN_GPIO111); + g_client->irq = gpio_to_irq(bma_dev.gpio); + + gpio_direction_input(bma_dev.gpio); /* set gpio pin as input */ + +#ifdef DEBUG_INFO + printk(KERN_INFO "irq=%d\n", g_client->irq); + printk(KERN_INFO "gpio=%d\n", bma_dev.gpio); +#endif + +#ifdef DEBUG_INFO + bma020_poweron(); + bma020_driver_test();/* show some measured data for simple test */ + bma020_poweroff(); +#endif + + bma020_add(&client->dev); + return 0; +} + +static int bma020_remove(struct i2c_client *client) +{ + bma020_joystick_disable(); + bma020_remove_fs(); + bma020_poweroff(); + bma_dev.usage = 0; + bma_dev.is_on = 0; + + return 0; +} + +static const struct i2c_device_id bma020_id[] = { + { "bma020", 0 }, + { } +}; + +static struct i2c_driver bma020_driver = { + .driver = { + .name = "bma020", + }, + .id_table = bma020_id, + .probe = bma020_probe, + .remove = bma020_remove, + .suspend = bma020_suspend, + .resume = bma020_resume, +}; + +static int __init bma020_i2c_init(void) +{ +#ifdef DEBUG_INFO + printk(KERN_INFO"func=%s \n", __FUNCTION__); +#endif + return i2c_add_driver(&bma020_driver); +} + +static void __exit bma020_i2c_exit(void) +{ + i2c_del_driver(&bma020_driver); +} + +/*====================================================================*/ + +MODULE_DESCRIPTION("BOSCH BMA020 three-axis digital accelerometer (I2C) driver"); +MODULE_AUTHOR("Tony Teng "); +MODULE_LICENSE("GPL"); + +module_init(bma020_i2c_init); +module_exit(bma020_i2c_exit); + +/*====================================================================*/ diff --git a/drivers/hwmon/bma020/bma020_fs.h b/drivers/hwmon/bma020/bma020_fs.h new file mode 100644 index 00000000000000..13b0b810cbe640 --- /dev/null +++ b/drivers/hwmon/bma020/bma020_fs.h @@ -0,0 +1,710 @@ +/* +* bma020.c - ST BMA020 accelerometer driver +* +* Copyright (C) 2007-2008 Yan Burman +* Copyright (C) 2008 Eric Piel +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* 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. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __BMA020_H__ +#define __BMA020_H__ + +/** BMA020 I2C Address*/ +#define BMA020_I2C_ADDR 0x38 + +/** register definitions*/ +#define CHIP_ID_REG 0x00 +#define VERSION_REG 0x01 +#define X_AXIS_LSB_REG 0x02 +#define X_AXIS_MSB_REG 0x03 +#define Y_AXIS_LSB_REG 0x04 +#define Y_AXIS_MSB_REG 0x05 +#define Z_AXIS_LSB_REG 0x06 +#define Z_AXIS_MSB_REG 0x07 +#define TEMP_RD_REG 0x08 +#define BMA020_STATUS_REG 0x09 +#define BMA020_CTRL_REG 0x0a +#define BMA020_CONF1_REG 0x0b +#define LG_THRESHOLD_REG 0x0c +#define LG_DURATION_REG 0x0d +#define HG_THRESHOLD_REG 0x0e +#define HG_DURATION_REG 0x0f +#define MOTION_THRS_REG 0x10 +#define HYSTERESIS_REG 0x11 +#define CUSTOMER1_REG 0x12 +#define CUSTOMER2_REG 0x13 +#define RANGE_BWIDTH_REG 0x14 +#define BMA020_CONF2_REG 0x15 + +#define OFFS_GAIN_X_REG 0x16 +#define OFFS_GAIN_Y_REG 0x17 +#define OFFS_GAIN_Z_REG 0x18 +#define OFFS_GAIN_T_REG 0x19 +#define OFFSET_X_REG 0x1a +#define OFFSET_Y_REG 0x1b +#define OFFSET_Z_REG 0x1c +#define OFFSET_T_REG 0x1d + +/** bma020 typedef structure + \brief This structure holds all relevant information about BMA020 and links communication to the +*/ +#define BUF_LEN (20*1) + +typedef struct { + short x, /**< holds x-axis acceleration data sign extended. Range -512 to 511. */ + y, /**< holds y-axis acceleration data sign extended. Range -512 to 511. */ + z; /**< holds z-axis acceleration data sign extended. Range -512 to 511. */ +} bma020acc_t; + +struct axis_conversion { + s8 x; + s8 y; + s8 z; +}; + +struct i2c_bma020 { + unsigned int sample_interval; + int gpio; /* gpio pin for g-sensor's interrupt wire */ + /* bma020acc_t mem[BUF_LEN]; //data buffer */ + unsigned char mode; /*< save current BMA020 operation mode */ + unsigned char range; + unsigned char bandwidth; + + unsigned char chip_id, /*< save BMA020's chip id which has to be 0x02 after calling bma020_init() */ + ml_version, /*< holds the BMA020 ML_version number */ + al_version; /*< holds the BMA020 AL_version number */ + unsigned char dev_addr; /*< initializes BMA020's I2C device address 0x38 */ + unsigned char int_mask; /*< stores the current BMA020 API generated interrupt mask */ + + struct device *device; /* The device */ + struct input_dev *idev; /* input device */ + struct task_struct *kthread; /* kthread for input */ + struct completion thread_exit_complete; + int thread_exit; + struct mutex lock; + atomic_t count; /* interrupt count after last read */ + int xcalib; /* calibrated null value for x */ + int ycalib; /* calibrated null value for y */ + int zcalib; /* calibrated null value for z */ + unsigned char is_on; /* whether the device is on or off */ + unsigned char usage; /* usage counter */ + struct axis_conversion ac; /* hw -> logical axis */ + unsigned int delay; + wait_queue_head_t wait; +}; + +/* +* bit slice positions in registers +*/ + +/** \cond BITSLICE */ + +#define CHIP_ID__POS 0 +#define CHIP_ID__MSK 0x07 +#define CHIP_ID__LEN 3 +#define CHIP_ID__REG CHIP_ID_REG + +#define ML_VERSION__POS 0 +#define ML_VERSION__LEN 4 +#define ML_VERSION__MSK 0x0F +#define ML_VERSION__REG VERSION_REG + +#define AL_VERSION__POS 4 +#define AL_VERSION__LEN 4 +#define AL_VERSION__MSK 0xF0 +#define AL_VERSION__REG VERSION_REG + +/* DATA REGISTERS */ + +#define NEW_DATA_X__POS 0 +#define NEW_DATA_X__LEN 1 +#define NEW_DATA_X__MSK 0x01 +#define NEW_DATA_X__REG X_AXIS_LSB_REG + +#define ACC_X_LSB__POS 6 +#define ACC_X_LSB__LEN 2 +#define ACC_X_LSB__MSK 0xC0 +#define ACC_X_LSB__REG X_AXIS_LSB_REG + +#define ACC_X_MSB__POS 0 +#define ACC_X_MSB__LEN 8 +#define ACC_X_MSB__MSK 0xFF +#define ACC_X_MSB__REG X_AXIS_MSB_REG + +#define NEW_DATA_Y__POS 0 +#define NEW_DATA_Y__LEN 1 +#define NEW_DATA_Y__MSK 0x01 +#define NEW_DATA_Y__REG Y_AXIS_LSB_REG + +#define ACC_Y_LSB__POS 6 +#define ACC_Y_LSB__LEN 2 +#define ACC_Y_LSB__MSK 0xC0 +#define ACC_Y_LSB__REG Y_AXIS_LSB_REG + +#define ACC_Y_MSB__POS 0 +#define ACC_Y_MSB__LEN 8 +#define ACC_Y_MSB__MSK 0xFF +#define ACC_Y_MSB__REG Y_AXIS_MSB_REG + +#define NEW_DATA_Z__POS 0 +#define NEW_DATA_Z__LEN 1 +#define NEW_DATA_Z__MSK 0x01 +#define NEW_DATA_Z__REG Z_AXIS_LSB_REG + +#define ACC_Z_LSB__POS 6 +#define ACC_Z_LSB__LEN 2 +#define ACC_Z_LSB__MSK 0xC0 +#define ACC_Z_LSB__REG Z_AXIS_LSB_REG + +#define ACC_Z_MSB__POS 0 +#define ACC_Z_MSB__LEN 8 +#define ACC_Z_MSB__MSK 0xFF +#define ACC_Z_MSB__REG Z_AXIS_MSB_REG + +/* STATUS BITS */ + +#define STATUS_HG__POS 0 +#define STATUS_HG__LEN 1 +#define STATUS_HG__MSK 0x01 +#define STATUS_HG__REG BMA020_STATUS_REG + +#define STATUS_LG__POS 1 +#define STATUS_LG__LEN 1 +#define STATUS_LG__MSK 0x02 +#define STATUS_LG__REG BMA020_STATUS_REG + +#define HG_LATCHED__POS 2 +#define HG_LATCHED__LEN 1 +#define HG_LATCHED__MSK 0x04 +#define HG_LATCHED__REG BMA020_STATUS_REG + +#define LG_LATCHED__POS 3 +#define LG_LATCHED__LEN 1 +#define LG_LATCHED__MSK 8 +#define LG_LATCHED__REG BMA020_STATUS_REG + +#define ALERT_PHASE__POS 4 +#define ALERT_PHASE__LEN 1 +#define ALERT_PHASE__MSK 0x10 +#define ALERT_PHASE__REG BMA020_STATUS_REG + +#define ST_RESULT__POS 7 +#define ST_RESULT__LEN 1 +#define ST_RESULT__MSK 0x80 +#define ST_RESULT__REG BMA020_STATUS_REG + +/* CONTROL BITS */ + +#define SLEEP__POS 0 +#define SLEEP__LEN 1 +#define SLEEP__MSK 0x01 +#define SLEEP__REG BMA020_CTRL_REG + +#define SOFT_RESET__POS 1 +#define SOFT_RESET__LEN 1 +#define SOFT_RESET__MSK 0x02 +#define SOFT_RESET__REG BMA020_CTRL_REG + +#define SELF_TEST__POS 2 +#define SELF_TEST__LEN 2 +#define SELF_TEST__MSK 0x0C +#define SELF_TEST__REG BMA020_CTRL_REG + +#define SELF_TEST0__POS 2 +#define SELF_TEST0__LEN 1 +#define SELF_TEST0__MSK 0x04 +#define SELF_TEST0__REG BMA020_CTRL_REG + +#define SELF_TEST1__POS 3 +#define SELF_TEST1__LEN 1 +#define SELF_TEST1__MSK 0x08 +#define SELF_TEST1__REG BMA020_CTRL_REG + +#define RESET_INT__POS 6 +#define RESET_INT__LEN 1 +#define RESET_INT__MSK 0x40 +#define RESET_INT__REG BMA020_CTRL_REG + +/* LOW-G, HIGH-G settings */ + +#define ENABLE_LG__POS 0 +#define ENABLE_LG__LEN 1 +#define ENABLE_LG__MSK 0x01 +#define ENABLE_LG__REG BMA020_CONF1_REG + +#define ENABLE_HG__POS 1 +#define ENABLE_HG__LEN 1 +#define ENABLE_HG__MSK 0x02 +#define ENABLE_HG__REG BMA020_CONF1_REG + +/* LG/HG counter */ + +#define COUNTER_LG__POS 2 +#define COUNTER_LG__LEN 2 +#define COUNTER_LG__MSK 0x0C +#define COUNTER_LG__REG BMA020_CONF1_REG + +#define COUNTER_HG__POS 4 +#define COUNTER_HG__LEN 2 +#define COUNTER_HG__MSK 0x30 +#define COUNTER_HG__REG BMA020_CONF1_REG + +/* LG/HG duration is in ms */ + +#define LG_DUR__POS 0 +#define LG_DUR__LEN 8 +#define LG_DUR__MSK 0xFF +#define LG_DUR__REG LG_DURATION_REG + +#define HG_DUR__POS 0 +#define HG_DUR__LEN 8 +#define HG_DUR__MSK 0xFF +#define HG_DUR__REG HG_DURATION_REG + +#define LG_THRES__POS 0 +#define LG_THRES__LEN 8 +#define LG_THRES__MSK 0xFF +#define LG_THRES__REG LG_THRESHOLD_REG + +#define HG_THRES__POS 0 +#define HG_THRES__LEN 8 +#define HG_THRES__MSK 0xFF +#define HG_THRES__REG HG_THRESHOLD_REG + +#define LG_HYST__POS 0 +#define LG_HYST__LEN 3 +#define LG_HYST__MSK 0x07 +#define LG_HYST__REG HYSTERESIS_REG + +#define HG_HYST__POS 3 +#define HG_HYST__LEN 3 +#define HG_HYST__MSK 0x38 +#define HG_HYST__REG HYSTERESIS_REG + +/* ANY MOTION and ALERT settings */ + +#define EN_ANY_MOTION__POS 6 +#define EN_ANY_MOTION__LEN 1 +#define EN_ANY_MOTION__MSK 0x40 +#define EN_ANY_MOTION__REG BMA020_CONF1_REG + +/* ALERT settings */ +#define ALERT__POS 7 +#define ALERT__LEN 1 +#define ALERT__MSK 0x80 +#define ALERT__REG BMA020_CONF1_REG + +/* ANY MOTION Duration */ + +#define ANY_MOTION_THRES__POS 0 +#define ANY_MOTION_THRES__LEN 8 +#define ANY_MOTION_THRES__MSK 0xFF +#define ANY_MOTION_THRES__REG MOTION_THRS_REG + +#define ANY_MOTION_DUR__POS 6 +#define ANY_MOTION_DUR__LEN 2 +#define ANY_MOTION_DUR__MSK 0xC0 +#define ANY_MOTION_DUR__REG HYSTERESIS_REG + +#define CUSTOMER_RESERVED1__POS 0 +#define CUSTOMER_RESERVED1__LEN 8 +#define CUSTOMER_RESERVED1__MSK 0xFF +#define CUSTOMER_RESERVED1__REG CUSTOMER1_REG + +#define CUSTOMER_RESERVED2__POS 0 +#define CUSTOMER_RESERVED2__LEN 8 +#define CUSTOMER_RESERVED2__MSK 0xFF +#define CUSTOMER_RESERVED2__REG CUSTOMER2_REG + +/* BANDWIDTH dependend definitions */ + +#define BANDWIDTH__POS 0 +#define BANDWIDTH__LEN 3 +#define BANDWIDTH__MSK 0x07 +#define BANDWIDTH__REG RANGE_BWIDTH_REG + +/* RANGE */ + +#define RANGE__POS 3 +#define RANGE__LEN 2 +#define RANGE__MSK 0x18 +#define RANGE__REG RANGE_BWIDTH_REG + +/* WAKE UP */ +#define WAKE_UP__POS 0 +#define WAKE_UP__LEN 1 +#define WAKE_UP__MSK 0x01 +#define WAKE_UP__REG BMA020_CONF2_REG + +#define WAKE_UP_PAUSE__POS 1 +#define WAKE_UP_PAUSE__LEN 2 +#define WAKE_UP_PAUSE__MSK 0x06 +#define WAKE_UP_PAUSE__REG BMA020_CONF2_REG + +/* ACCELERATION DATA SHADOW */ +#define SHADOW_DIS__POS 3 +#define SHADOW_DIS__LEN 1 +#define SHADOW_DIS__MSK 0x08 +#define SHADOW_DIS__REG BMA020_CONF2_REG + +/* LATCH Interrupt */ + +#define LATCH_INT__POS 4 +#define LATCH_INT__LEN 1 +#define LATCH_INT__MSK 0x10 +#define LATCH_INT__REG BMA020_CONF2_REG + +/* new data interrupt */ +#define NEW_DATA_INT__POS 5 +#define NEW_DATA_INT__LEN 1 +#define NEW_DATA_INT__MSK 0x20 +#define NEW_DATA_INT__REG BMA020_CONF2_REG + +#define ENABLE_ADV_INT__POS 6 +#define ENABLE_ADV_INT__LEN 1 +#define ENABLE_ADV_INT__MSK 0x40 +#define ENABLE_ADV_INT__REG BMA020_CONF2_REG + +/* +#define BMA020_SPI4_OFF 0 +#define BMA020_SPI4_ON 1 + +#define SPI4__POS 7 +#define SPI4__LEN 1 +#define SPI4__MSK 0x80 +#define SPI4__REG BMA020_CONF2_REG +*/ +#define GAIN_X__POS 0 +#define GAIN_X__LEN 6 +#define GAIN_X__MSK 0x3f +#define GAIN_X__REG OFFS_GAIN_X_REG + +#define GAIN_Y__POS 0 +#define GAIN_Y__LEN 6 +#define GAIN_Y__MSK 0x3f +#define GAIN_Y__REG OFFS_GAIN_Y_REG + +#define GAIN_Z__POS 0 +#define GAIN_Z__LEN 6 +#define GAIN_Z__MSK 0x3f +#define GAIN_Z__REG OFFS_GAIN_Z_REG + +#define OFFSET_T_LSB__POS 6 +#define OFFSET_T_LSB__LEN 2 +#define OFFSET_T_LSB__MSK 0xC0 +#define OFFSET_T_LSB__REG OFFS_GAIN_T_REG + +#define GAIN_T__POS 0 +#define GAIN_T__LEN 6 +#define GAIN_T__MSK 0x3f +#define GAIN_T__REG OFFS_GAIN_T_REG + +#define OFFSET_X_MSB__POS 0 +#define OFFSET_X_MSB__LEN 8 +#define OFFSET_X_MSB__MSK 0xFF +#define OFFSET_X_MSB__REG OFFSET_X_REG + +#define OFFSET_Y_MSB__POS 0 +#define OFFSET_Y_MSB__LEN 8 +#define OFFSET_Y_MSB__MSK 0xFF +#define OFFSET_Y_MSB__REG OFFSET_Y_REG + +#define OFFSET_Z_MSB__POS 0 +#define OFFSET_Z_MSB__LEN 8 +#define OFFSET_Z_MSB__MSK 0xFF +#define OFFSET_Z_MSB__REG OFFSET_Z_REG + +#define OFFSET_T_MSB__POS 0 +#define OFFSET_T_MSB__LEN 8 +#define OFFSET_T_MSB__MSK 0xFF +#define OFFSET_T_MSB__REG OFFSET_T_REG + +#define BMA020_GET_BITSLICE(regvar, bitname)\ + ((regvar & bitname##__MSK) >> bitname##__POS) + +#define BMA020_SET_BITSLICE(regvar, bitname, val)\ + ((regvar & ~bitname##__MSK) | ((val< + * Example: BMA020_LG_THRES_IN_G( 0.3, 2.0) generates the register value + * for 0.3G threshold in 2G mode. + * \brief convert g-values to 8-bit value + */ +#define BMA020_LG_THRES_IN_G(gthres, range) ((256 * gthres) / range) + +/** Macro to convert floating point high-g-thresholds in G to 8-bit register + * values.
+ * Example: BMA020_HG_THRES_IN_G( 1.4, 2.0) generates the register value + * for 1.4G threshold in 2G mode. + * \brief convert g-values to 8-bit value + */ +#define BMA020_HG_THRES_IN_G(gthres, range) ((256 * gthres) / range) + +/** Macro to convert floating point low-g-hysteresis in G to 8-bit register + * values.
+ * Example: BMA020_LG_HYST_THRES_IN_G( 0.2, 2.0) generates the register value + * for 0.2G threshold in 2G mode. + * \brief convert g-values to 8-bit value + */ +#define BMA020_LG_HYST_IN_G(ghyst, range) ((32 * ghyst) / range) + +/** Macro to convert floating point high-g-hysteresis in G to 8-bit register + * values.
+ * Example: BMA020_HG_HYST_THRES_IN_G( 0.2, 2.0) generates the register value + * for 0.2G threshold in 2G mode. + * \brief convert g-values to 8-bit value + */ +#define BMA020_HG_HYST_IN_G(ghyst, range) ((32 * ghyst) / range) + +/** Macro to convert floating point G-thresholds to 8-bit register values
+ * Example: BMA020_ANY_MOTION_THRES_IN_G( 1.2, 2.0) generates the register + * value for 1.2G threshold in 2G mode. + * \brief convert g-values to 8-bit value + */ +#define BMA020_ANY_MOTION_THRES_IN_G(gthres, range) ((128 * gthres) / range) + + +#define BMA020_ANY_MOTION_DUR_1 0 +#define BMA020_ANY_MOTION_DUR_3 1 +#define BMA020_ANY_MOTION_DUR_5 2 +#define BMA020_ANY_MOTION_DUR_7 3 + +#define BMA020_SHADOW_DIS_OFF 0 +#define BMA020_SHADOW_DIS_ON 1 + +#define BMA020_LATCH_INT_OFF 0 +#define BMA020_LATCH_INT_ON 1 + +#define BMA020_NEW_DATA_INT_OFF 0 +#define BMA020_NEW_DATA_INT_ON 1 + +#define BMA020_ENABLE_ADV_INT_OFF 0 +#define BMA020_ENABLE_ADV_INT_ON 1 + +#define BMA020_EN_ANY_MOTION_OFF 0 +#define BMA020_EN_ANY_MOTION_ON 1 + +#define BMA020_ALERT_OFF 0 +#define BMA020_ALERT_ON 1 + +#define BMA020_ENABLE_LG_OFF 0 +#define BMA020_ENABLE_LG_ON 1 + +#define BMA020_ENABLE_HG_OFF 0 +#define BMA020_ENABLE_HG_ON 1 + +#define BMA020_INT_ALERT (1<<7) +#define BMA020_INT_ANY_MOTION (1<<6) +#define BMA020_INT_EN_ADV_INT (1<<5) +#define BMA020_INT_NEW_DATA (1<<4) +#define BMA020_INT_LATCH (1<<3) +#define BMA020_INT_HG (1<<1) +#define BMA020_INT_LG (1<<0) + +#define BMA020_INT_STATUS_HG (1<<0) +#define BMA020_INT_STATUS_LG (1<<1) +#define BMA020_INT_STATUS_HG_LATCHED (1<<2) +#define BMA020_INT_STATUS_LG_LATCHED (1<<3) +#define BMA020_INT_STATUS_ALERT (1<<4) +#define BMA020_INT_STATUS_ST_RESULT (1<<7) + +#define BMA020_CONF1_INT_MSK ((1< + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct cm3602_platform_data *pd; + +static int power_use; + +static inline void cm3602_poweroff(void) +{ + power_use--; + if (power_use == 0) + pd->power(0); +} + +static void cm3602_poweron(void) +{ + if (power_use == 0) + pd->power(1); + power_use++; +} + +static void cm3602_report_ps(struct input_dev *idev) +{ + int pout; + + pout = pd->get_pout(); + input_report_abs(idev, ABS_DISTANCE, pout); + input_sync(idev); +} + +static void cm3602_report_as(struct input_dev *idev) +{ + int aout; + + aout = pd->get_aout(); + input_report_abs(idev, ABS_PRESSURE, aout); + input_sync(idev); +} + +static int __devinit cm3602_probe(struct platform_device *pdev) +{ + pd = pdev->dev.platform_data; + printk("****************sensor CM3602**********\n"); + sensor_input_add(INPUT_PROXIMITY_SENSOR, "cm3602_ps", cm3602_report_ps, NULL, cm3602_poweron, + cm3602_poweroff); + sensor_input_add(INPUT_AMBIENT_SENSOR, "cm3602_as", cm3602_report_as, NULL, cm3602_poweron, + cm3602_poweroff); + + return 0; +} + +static int cm3602_remove(struct platform_device *pdev) +{ + sensor_input_del("cm3602"); + + return 0; +} + +static struct platform_driver cm3602_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "cm3602", + }, + .probe = cm3602_probe, + .remove = cm3602_remove, +}; + +static int __init cm3602_init(void) +{ + return platform_driver_register(&cm3602_driver); +} + +static void __exit cm3602_exit(void) +{ + platform_device_unregister(&cm3602_driver); +} + +MODULE_DESCRIPTION("CM3602 short distance proximity sensor with ambient light sensor driver"); +MODULE_AUTHOR("Angela Wan "); +MODULE_LICENSE("GPL"); + +module_init(cm3602_init); +module_exit(cm3602_exit); + diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 14d249f5ed3f3d..d91c37e7d64652 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -35,10 +35,13 @@ #include #include +#include +#include #include #include #include +#undef CONFIG_PM /* * I2C register offsets will be shifted 0 or 1 bit left, depending on * different SoCs @@ -54,6 +57,45 @@ static const struct platform_device_id i2c_pxa_id_table[] = { }; MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); +#if defined(CONFIG_DVFM) +#include +static int dvfm_dev_idx; + +static void set_dvfm_constraint(void) +{ + /* Disable Low power mode */ + dvfm_disable_op_name("apps_idle", dvfm_dev_idx); + dvfm_disable_op_name("apps_sleep", dvfm_dev_idx); + dvfm_disable_op_name("sys_sleep", dvfm_dev_idx); +} + +static void unset_dvfm_constraint(void) +{ + /* Enable Low power mode */ + dvfm_enable_op_name("apps_idle", dvfm_dev_idx); + dvfm_enable_op_name("apps_sleep", dvfm_dev_idx); + dvfm_enable_op_name("sys_sleep", dvfm_dev_idx); +} + +static void disable_dvfm(void) +{ + dvfm_disable(dvfm_dev_idx); +} + +static void enable_dvfm(void) +{ + dvfm_enable(dvfm_dev_idx); +} +#else +static void set_dvfm_constraint(void) {} +static void unset_dvfm_constraint(void) {} +static void disable_dvfm(void) {} +static void enable_dvfm(void) {} +#endif + +#ifdef CONFIG_PM +int i2c_running = 0; +#endif /* * I2C registers and bit definitions */ @@ -63,6 +105,8 @@ MODULE_DEVICE_TABLE(platform, i2c_pxa_id_table); #define ISR (0x18) #define ISAR (0x20) +#define SCL (1 << 1) +#define SDA (1 << 0) #define ICR_START (1 << 0) /* start bit */ #define ICR_STOP (1 << 1) /* stop bit */ #define ICR_ACKNAK (1 << 2) /* send ACK(0) or NAK(1) */ @@ -100,6 +144,7 @@ struct pxa_i2c { unsigned int msg_idx; unsigned int msg_ptr; unsigned int slave_addr; + unsigned int req_slave_addr; struct i2c_adapter adap; struct clk *clk; @@ -224,6 +269,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id); static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) { +#ifdef DEBUG unsigned int i; printk(KERN_ERR "i2c: error: %s\n", why); printk(KERN_ERR "i2c: msg_num: %d msg_idx: %d msg_ptr: %d\n", @@ -234,6 +280,10 @@ static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) for (i = 0; i < i2c->irqlogidx; i++) printk("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]); printk("\n"); +#else + printk("warning: i2c: <%s> slave_0x%x %s\n", i2c->adap.name, + i2c->req_slave_addr >> 1, why); +#endif } static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) @@ -288,7 +338,7 @@ static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c) static int i2c_pxa_wait_master(struct pxa_i2c *i2c) { - unsigned long timeout = jiffies + HZ*4; + unsigned long timeout = jiffies + msecs_to_jiffies(DEF_TIMEOUT); while (time_before(jiffies, timeout)) { if (i2c_debug > 1) @@ -340,7 +390,7 @@ static int i2c_pxa_set_master(struct pxa_i2c *i2c) #ifdef CONFIG_I2C_PXA_SLAVE static int i2c_pxa_wait_slave(struct pxa_i2c *i2c) { - unsigned long timeout = jiffies + HZ*1; + unsigned long timeout = jiffies + msecs_to_jiffies(DEF_TIMEOUT); /* wait for stop */ @@ -594,6 +644,7 @@ static inline void i2c_pxa_start_message(struct pxa_i2c *i2c) * Step 1: target slave address into IDBR */ writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c)); + i2c->req_slave_addr = i2c_pxa_addr_byte(i2c->msg); /* * Step 2: initiate the write. @@ -643,11 +694,14 @@ static int i2c_pxa_pio_set_master(struct pxa_i2c *i2c) } static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, - struct i2c_msg *msg, int num) + struct i2c_msg *msg, int num, + struct i2c_pxa_platform_data *plat) { - unsigned long timeout = 500000; /* 5 seconds */ + unsigned long timeout = 1000; /* 100 ms */ int ret = 0; + unsigned long flags; + local_irq_save(flags); ret = i2c_pxa_pio_set_master(i2c); if (ret) goto out; @@ -658,14 +712,28 @@ static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, i2c->msg_ptr = 0; i2c->irqlogidx = 0; + set_dvfm_constraint(); +#ifdef CONFIG_PM + i2c_running = 1; +#endif i2c_pxa_start_message(i2c); while (i2c->msg_num > 0 && --timeout) { i2c_pxa_handler(0, i2c); + /* send clk to make isp release i2c data line */ + if(timeout == 0){ + i2c_pxa_reset(i2c); + timeout = 0; + goto out; + } udelay(10); } i2c_pxa_stop_message(i2c); + unset_dvfm_constraint(); +#ifdef CONFIG_PM + i2c_running = 0; +#endif /* * We place the return code in i2c->msg_idx. @@ -673,8 +741,11 @@ static int i2c_pxa_do_pio_xfer(struct pxa_i2c *i2c, ret = i2c->msg_idx; out: - if (timeout == 0) + local_irq_restore(flags); + if (timeout == 0) { i2c_pxa_scream_blue_murder(i2c, "timeout"); + ret = -EIO; + } return ret; } @@ -713,6 +784,10 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) i2c->msg_ptr = 0; i2c->irqlogidx = 0; + set_dvfm_constraint(); +#ifdef CONFIG_PM + i2c_running = 1; +#endif i2c_pxa_start_message(i2c); spin_unlock_irq(&i2c->lock); @@ -720,16 +795,23 @@ static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) /* * The rest of the processing occurs in the interrupt handler. */ - timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, msecs_to_jiffies(100)); i2c_pxa_stop_message(i2c); - + unset_dvfm_constraint(); +#ifdef CONFIG_PM + i2c_running = 0; +#endif /* * We place the return code in i2c->msg_idx. */ ret = i2c->msg_idx; - if (timeout == 0) + if (timeout == 0) { + i2c_pxa_reset(i2c); i2c_pxa_scream_blue_murder(i2c, "timeout"); + ret = -EIO; + } + out: return ret; @@ -739,28 +821,33 @@ static int i2c_pxa_pio_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct pxa_i2c *i2c = adap->algo_data; + struct i2c_pxa_platform_data *plat = adap->dev.parent->platform_data; int ret, i; - + if(plat->get_ripc) + plat->get_ripc(); /* If the I2C controller is disabled we need to reset it (probably due to a suspend/resume destroying state). We do this here as we can then avoid worrying about resuming the controller before its users. */ - if (!(readl(_ICR(i2c)) & ICR_IUE)) - i2c_pxa_reset(i2c); + i2c_pxa_reset(i2c); for (i = adap->retries; i >= 0; i--) { - ret = i2c_pxa_do_pio_xfer(i2c, msgs, num); + ret = i2c_pxa_do_pio_xfer(i2c, msgs, num, plat); if (ret != I2C_RETRY) goto out; - if (i2c_debug) dev_dbg(&adap->dev, "Retrying transmission\n"); + if (((!(readl(_IBMR(i2c)) + & SDA )) || (!(readl(_IBMR(i2c)) & SCL)))) + i2c_pxa_reset(i2c); udelay(100); } i2c_pxa_scream_blue_murder(i2c, "exhausted retries"); ret = -EREMOTEIO; out: i2c_pxa_set_slave(i2c, ret); + if(plat->release_ripc) + plat->release_ripc(); return ret; } @@ -863,6 +950,7 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) * Write the next address. */ writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c)); + i2c->req_slave_addr = i2c_pxa_addr_byte(i2c->msg); /* * And trigger a repeated start, and send the byte. @@ -951,7 +1039,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) if (isr & ISR_IRF) i2c_pxa_irq_rxfull(i2c, isr); } else { - i2c_pxa_scream_blue_murder(i2c, "spurious irq"); + //i2c_pxa_scream_blue_murder(i2c, "spurious irq"); } return IRQ_HANDLED; @@ -961,13 +1049,19 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct pxa_i2c *i2c = adap->algo_data; + struct i2c_pxa_platform_data *plat = adap->dev.parent->platform_data; int ret, i; + if (in_atomic() || irqs_disabled()) + return i2c_pxa_pio_xfer(adap, msgs, num); + + disable_dvfm(); + if(plat->get_ripc) + plat->get_ripc(); for (i = adap->retries; i >= 0; i--) { ret = i2c_pxa_do_xfer(i2c, msgs, num); - if (ret != I2C_RETRY) + if (ret >= 0) goto out; - if (i2c_debug) dev_dbg(&adap->dev, "Retrying transmission\n"); udelay(100); @@ -976,6 +1070,10 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num ret = -EREMOTEIO; out: i2c_pxa_set_slave(i2c, ret); + + if(plat->release_ripc) + plat->release_ripc(); + enable_dvfm(); return ret; } @@ -1137,9 +1235,10 @@ static int __exit i2c_pxa_remove(struct platform_device *dev) #ifdef CONFIG_PM static int i2c_pxa_suspend_noirq(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct pxa_i2c *i2c = platform_get_drvdata(pdev); - + struct pxa_i2c *i2c = platform_get_drvdata(dev); + if (i2c_running == 1) { + return 1; /* reject suspend when the i2c doing transaction */ + } clk_disable(i2c->clk); return 0; @@ -1179,12 +1278,18 @@ static struct platform_driver i2c_pxa_driver = { static int __init i2c_adap_pxa_init(void) { +#ifdef CONFIG_DVFM + dvfm_register("I2C", &dvfm_dev_idx); +#endif return platform_driver_register(&i2c_pxa_driver); } static void __exit i2c_adap_pxa_exit(void) { platform_driver_unregister(&i2c_pxa_driver); +#ifdef CONFIG_DVFM + dvfm_unregister("I2C", &dvfm_dev_idx); +#endif } MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig new file mode 100644 index 00000000000000..be0459d4e4f3a5 --- /dev/null +++ b/drivers/i2c/chips/Kconfig @@ -0,0 +1,156 @@ +# +# Miscellaneous I2C chip drivers configuration +# +# *** DEPRECATED! Do not add new entries! See Makefile *** +# + +menu "Miscellaneous I2C Chip support" + +config DS1682 + tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm" + depends on EXPERIMENTAL + help + If you say yes here you get support for Dallas Semiconductor + DS1682 Total Elapsed Time Recorder. + + This driver can also be built as a module. If so, the module + will be called ds1682. + +config SENSORS_PCF8574 + tristate "Philips PCF8574 and PCF8574A (DEPRECATED)" + depends on EXPERIMENTAL && GPIO_PCF857X = "n" + default n + help + If you say yes here you get support for Philips PCF8574 and + PCF8574A chips. These chips are 8-bit I/O expanders for the I2C bus. + + This driver can also be built as a module. If so, the module + will be called pcf8574. + + This driver is deprecated and will be dropped soon. Use + drivers/gpio/pcf857x.c instead. + + These devices are hard to detect and rarely found on mainstream + hardware. If unsure, say N. + +config PCF8575 + tristate "Philips PCF8575 (DEPRECATED)" + default n + depends on GPIO_PCF857X = "n" + help + If you say yes here you get support for Philips PCF8575 chip. + This chip is a 16-bit I/O expander for the I2C bus. Several other + chip manufacturers sell equivalent chips, e.g. Texas Instruments. + + This driver can also be built as a module. If so, the module + will be called pcf8575. + + This driver is deprecated and will be dropped soon. Use + drivers/gpio/pcf857x.c instead. + + This device is hard to detect and is rarely found on mainstream + hardware. If unsure, say N. + +config SENSORS_PCA9539 + tristate "Philips PCA9539 16-bit I/O port (DEPRECATED)" + depends on EXPERIMENTAL && GPIO_PCA953X = "n" + help + If you say yes here you get support for the Philips PCA9539 + 16-bit I/O port. + + This driver can also be built as a module. If so, the module + will be called pca9539. + + This driver is deprecated and will be dropped soon. Use + drivers/gpio/pca953x.c instead. + +config SENSORS_PCF8591 + tristate "Philips PCF8591" + depends on EXPERIMENTAL + default n + help + If you say yes here you get support for Philips PCF8591 chips. + + This driver can also be built as a module. If so, the module + will be called pcf8591. + + These devices are hard to detect and rarely found on mainstream + hardware. If unsure, say N. + +config SENSORS_MAX6875 + tristate "Maxim MAX6875 Power supply supervisor" + depends on EXPERIMENTAL + help + If you say yes here you get support for the Maxim MAX6875 + EEPROM-programmable, quad power-supply sequencer/supervisor. + + This provides an interface to program the EEPROM and reset the chip. + + This driver also supports the Maxim MAX6874 hex power-supply + sequencer/supervisor if found at a compatible address. + + This driver can also be built as a module. If so, the module + will be called max6875. + +config SENSORS_TSL2550 + tristate "Taos TSL2550 ambient light sensor" + depends on EXPERIMENTAL + help + If you say yes here you get support for the Taos TSL2550 + ambient light sensor. + + This driver can also be built as a module. If so, the module + will be called tsl2550. + +config MENELAUS + bool "TWL92330/Menelaus PM chip" + depends on I2C=y && ARCH_OMAP24XX + help + If you say yes here you get support for the Texas Instruments + TWL92330/Menelaus Power Management chip. This include voltage + regulators, Dual slot memory card tranceivers, real-time clock + and other features that are often used in portable devices like + cell phones and PDAs. + +config MCU_MPC8349EMITX + tristate "MPC8349E-mITX MCU driver" + depends on I2C && PPC_83xx + select GENERIC_GPIO + select ARCH_REQUIRE_GPIOLIB + help + Say Y here to enable soft power-off functionality on the Freescale + boards with the MPC8349E-mITX-compatible MCU chips. This driver will + also register MCU GPIOs with the generic GPIO API, so you'll able + to use MCU pins as GPIOs. + +config SANREMO + boolean "Sanremo chip" + depends on I2C && I2C_PXA + select PXA3xx_PMIC + default n + help + Sanremo is a PMIC/Audio/Touch chip. + If you say yes here, you support for it. + + This driver can NOT be built as a module. + +config PORTOFINO + boolean "Portofino chip" + depends on I2C && I2C_PXA + default n + help + If you say yes here, you support for it. + This driver can NOT be built as a module. + +config MICCO + boolean "Micco chip" + depends on I2C && I2C_PXA + select PXA3xx_PMIC + default n + help + Micco is a PMIC/Audio/Touch chip. + If you say yes here, you support for it. + + This driver can NOT be built as a module. + +endmenu diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile new file mode 100644 index 00000000000000..cfb6d3b376a410 --- /dev/null +++ b/drivers/i2c/chips/Makefile @@ -0,0 +1,32 @@ +# +# Makefile for miscellaneous I2C chip drivers. +# +# Do not add new drivers to this directory! It is DEPRECATED. +# +# Device drivers are better grouped according to the functionality they +# implement rather than to the bus they are connected to. In particular: +# * Hardware monitoring chip drivers go to drivers/hwmon +# * RTC chip drivers go to drivers/rtc +# * I/O expander drivers go to drivers/gpio +# + +obj-$(CONFIG_DS1682) += ds1682.o +obj-$(CONFIG_SENSORS_MAX6875) += max6875.o +obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o +obj-$(CONFIG_SENSORS_PCA963X) += pca963x.o +obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o +obj-$(CONFIG_PCF8575) += pcf8575.o +obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o +obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o +obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o +obj-$(CONFIG_PORTOFINO) += portofino.o +obj-$(CONFIG_SANREMO) += sanremo.o +obj-$(CONFIG_MICCO) += micco.o +obj-$(CONFIG_MAX8660) += max8660.o + +obj-y += gpio_ec.o + +ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/i2c/chips/gpio_ec.c b/drivers/i2c/chips/gpio_ec.c new file mode 100644 index 00000000000000..dbba398dbb9f46 --- /dev/null +++ b/drivers/i2c/chips/gpio_ec.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(gpio_ec_lock); + +int set_bus_busy(void) +{ + if(!mutex_trylock(&gpio_ec_lock)) { + /* try lock reutrns 1 if the mutex is acquired successfully, + * and 0 on contention. + */ + return -1; + } + gpio_direction_output(MFP_PIN_GPIO88, 1); + + if ((gpio_get_value(MFP_PIN_GPIO89) >> 25) == 0) { + return 0; + } + + gpio_direction_output(MFP_PIN_GPIO88, 0); + mutex_unlock(&gpio_ec_lock); + return -1; + +} + +EXPORT_SYMBOL(set_bus_busy); + +int set_bus_free(void) +{ + gpio_direction_output(MFP_PIN_GPIO88, 0); + mutex_unlock(&gpio_ec_lock); + + return 0; + +} + +EXPORT_SYMBOL(set_bus_free); + + diff --git a/drivers/i2c/chips/micco.c b/drivers/i2c/chips/micco.c new file mode 100644 index 00000000000000..bdeec935159585 --- /dev/null +++ b/drivers/i2c/chips/micco.c @@ -0,0 +1,1863 @@ +/* + * Monahans Micco PMIC Management Routines + * + * + * Copyright (C) 2006, Marvell Corporation(fengwei.yin@marvell.com). + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#define MICCO_REG_NUM (0xC0) + +/* FIXME: until pxa3xx_pm.c is merged, this function points to nothing + * and returns PM_SUSPEND_MEM by default to fool micco_suspend/resume + */ +#if 0 +extern int get_pm_state(void); +#else +static inline int get_pm_state(void) { return PM_SUSPEND_MEM; } +#endif + +static struct pxa3xx_pmic_regs micco_regs[MICCO_REG_NUM]; +static struct power_supply_module *micco_power_module; +static unsigned int event; + +/* Make sure that Power I2C has been initialized before invoke this function */ +extern int pxa_i2c_set_speed(int speed); +extern int pxa_i2c_get_speed(void); +int micco_codec_disable_output(int type); + +/* Unique ID allocation */ +static struct i2c_client *g_client; + +int micco_read(u8 reg, u8 *pval) +{ + struct micco_platform_data *pdata = g_client->dev.platform_data; + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + + /* Cache read of the Micco register. Disabled temporary */ +#if 0 + if (micco_regs[reg].hit) { + *pval = micco_regs[reg].data; + return 0; + } +#endif + + spin_lock(&pdata->lock); + disable_irq(g_client->irq); + ret = i2c_smbus_read_byte_data(g_client, reg); + enable_irq(g_client->irq); + if (ret >= 0) { + *pval = ret; + micco_regs[reg].hit = ~micco_regs[reg].mask; + micco_regs[reg].data = ret; + status = 0; + } else + status = -EIO; + spin_unlock(&pdata->lock); + + return status; +} + +int micco_write(u8 reg, u8 val) +{ + struct micco_platform_data *pdata = g_client->dev.platform_data; + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + + spin_lock(&pdata->lock); + disable_irq(g_client->irq); + ret = i2c_smbus_write_byte_data(g_client, reg, val); + enable_irq(g_client->irq); + if (ret == 0) { + micco_regs[reg].hit = ~micco_regs[reg].mask; + micco_regs[reg].data = val; + status = 0; + } else + status = -EIO; + spin_unlock(&pdata->lock); + + return status; +} + +static int micco_initchip(void) +{ + int i; + + memset(&micco_regs, 0, + (sizeof(struct pxa3xx_pmic_regs) * MICCO_REG_NUM)); + /* TODO: Mask all micco registers uncacheable now. + * We can do some optimization here later. + */ + for (i = 0; i < MICCO_REG_NUM; i++) + micco_regs[i].mask = 1; + + return micco_write(MICCO_SYSCTRL_A, 0xE8); +} + +/* Micco TSI functions */ +int micco_enable_pen_down_irq(int enable) +{ + int ret; + u8 val; + + if (enable) { + /* enable pen down IRQ */ + ret = micco_read(MICCO_IRQ_MASK_C, &val); + val &= ~0x10; + ret = micco_write(MICCO_IRQ_MASK_C, val); + } else { + /* disable pen down IRQ */ + ret = micco_read(MICCO_IRQ_MASK_C, &val); + if (!(val & IRQ_MASK_C_PEN_DOWN)) { + val |= 0x10; + ret = micco_write(MICCO_IRQ_MASK_C, val); + } + } + return ret; +} + +int micco_tsi_poweron(void) +{ + int status; + u8 val; + + val = 0x10; + status = micco_write(MICCO_ADC_MAN_CONTROL, val); + + status = micco_read(MICCO_ADC_AUTO_CONTROL_2, &val); + if (!(val & MICCO_ADC_AUTO_2_PENDET_EN)) { + val |= MICCO_ADC_AUTO_2_PENDET_EN; + status = micco_write(MICCO_ADC_AUTO_CONTROL_2, val); + } + + /* TSI_DEABY: 3 slot. TSI_SKIP: 3 slot */ + val = 0x1B; + status = micco_write(MICCO_TSI_CONTROL_1, val); + + val = 0x00; + status = micco_write(MICCO_TSI_CONTROL_2, val); + if (status) + return -EIO; + + return 0; +} + +int micco_tsi_poweroff(void) +{ + int status; + u8 val; + + status = micco_read(MICCO_ADC_AUTO_CONTROL_2, &val); + if (status) + return -EIO; + + val &= ~(MICCO_ADC_AUTO_2_TSI_EN | MICCO_ADC_AUTO_2_PENDET_EN); + status = micco_write(MICCO_ADC_AUTO_CONTROL_2, val); + + if (status) + return -EIO; + + return 0; +} + +int micco_tsi_readxy(u16 *x, u16 *y, int pen_state) +{ + int status; + u8 val; + u16 mx, my, lxy; + + status = micco_read(MICCO_TSI_X_MSB, &val); + if (status) + return -EIO; + mx = val; + + status = micco_read(MICCO_TSI_Y_MSB, &val); + if (status) + return -EIO; + my = val; + + status = micco_read(MICCO_TSI_XY_MSB, &val); + if (status) + return -EIO; + lxy = val; + + *x = ((mx << 2) & 0x3fc) + (lxy & 0x03); + *y = ((my << 2) & 0x3fc) + ((lxy & 0x0c) >> 2); + + return 0; +} + +int micco_tsi_enable_pen(int pen_en) +{ + int status; + u8 val; + + status = micco_read(MICCO_ADC_AUTO_CONTROL_2, &val); + if (status) + return -EIO; + + if (pen_en) + val |= MICCO_ADC_AUTO_2_PENDET_EN; + else + val &= ~MICCO_ADC_AUTO_2_PENDET_EN; + + status = micco_write(MICCO_ADC_AUTO_CONTROL_2, val); + if (status) + return -EIO; + + return 0; +} + +int micco_tsi_enable_tsi(int tsi_en) +{ + int status; + u8 val; + + status = micco_read(MICCO_ADC_AUTO_CONTROL_2, &val); + if (status) + return -EIO; + + if (tsi_en) + val |= MICCO_ADC_AUTO_2_TSI_EN; + else + val &= ~MICCO_ADC_AUTO_2_TSI_EN; + + status = micco_write(MICCO_ADC_AUTO_CONTROL_2, val); + if (status) + return -EIO; + + return 0; +} +/* Micco TSI functions end */ + +/* Micco Audio functions */ +static volatile u8 micco_audio_regs[MICCO_AUDIO_REGS_NUM]; + +int micco_codec_read(u8 reg, u8 *val) +{ + return micco_read(MICCO_AUDIO_REG_BASE + reg, val); +} + +int micco_codec_write(u8 reg, u8 val) +{ + return micco_write(MICCO_AUDIO_REG_BASE + reg, val); +} + +void micco_read_codec(void) +{ + int i; + u8 val; + + for (i = 0; i < MICCO_AUDIO_REGS_NUM; i++) { + micco_codec_read(i, &val); + micco_audio_regs[i] = val; + } +} + +void micco_dump_codec(void) +{ + int i; + + micco_read_codec(); + + for (i = 0; i < MICCO_AUDIO_REGS_NUM; i++) { + printk(KERN_ALERT "%s: Micco_audio_reg[%d] = 0x%x\n", + __func__, i, micco_audio_regs[i]); + } +} +EXPORT_SYMBOL(micco_dump_codec); + +int micco_audio_init(void) +{ + int i; + + /* The default setting for Micco */ + micco_audio_regs[MICCO_MUX_MONO] = 0x00; /* MONO */ + micco_audio_regs[MICCO_MUX_BEAR] = 0x00; /* BEAR */ + micco_audio_regs[MICCO_MUX_LINE_OUT] = 0x00; /* LINE OUT */ + micco_audio_regs[MICCO_MUX_STEREO_CH1] = + 0x00; /* STEREO_CH1 */ + micco_audio_regs[MICCO_MUX_STEREO_CH2] = + 0x00; /* STEREO_CH2 */ + + micco_audio_regs[MICCO_AUDIO_LINE_AMP] = + MICCO_AUDIO_LINE_AMP_EN; /* Enable the Line AMP */ + + /* Gain for both channel controlled separately. Enable Setero */ + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] = + MICCO_STEREO_GAIN_SEPARATE | MICCO_STEREO_EN; + + /* Soft startup of the Stereo amplifiers */ + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH2] = 0x0; + + micco_audio_regs[MICCO_HIFI_DAC_CONTROL] = + MICCO_HIFI_DAC_ON; + + micco_audio_regs[MICCO_MONO_VOL] = + MICCO_MONO_EN | 0xa; + micco_audio_regs[MICCO_BEAR_VOL] = + MICCO_BEAR_EN | 0xa; + + /* Micco as I2S slave. Use I2S MSB normal mode */ + micco_audio_regs[MICCO_I2S_CONTROL] = + MICCO_I2S_MSB_JU_MODE; + + micco_audio_regs[MICCO_TX_PGA] = + 0x0c; /* 0 dB */ + micco_audio_regs[MICCO_MIC_PGA] = + MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN | + MICCO_MIC_PGA_SELMIC_2 | MICCO_MIC_PGA_AMP_EN | + 0x7; /* 30 dB*/ + + micco_audio_regs[MICCO_TX_PGA_MUX] = + 0xFF; + + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] = + MICCO_VCODEC_ADC_ON_EN | 0x08; + /* PCM_SDI normal operation, PCM_SDO enabled */ + micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] = + MICCO_VDAC_ON | MICCO_VDAC_HPF_BYPASS; + + micco_audio_regs[MICCO_SIDETONE] = + MICCO_SIDETONE_EN | MICCO_SIDETONE_GAIN_STEREO | 0x08; + + /* Enable AUX1,2. AUX1, 2 gain 0dB */ + micco_audio_regs[MICCO_PGA_AUX1_2] = + MICCO_PGA_AUX1_EN | MICCO_PGA_AUX2_EN; + + /* Enable AUX3. AUX3 gain 0 dB */ + micco_audio_regs[MICCO_PGA_AUX3] = + MICCO_PGA_AUX3_EN; + + /* DAC1, 2, 3 gain 0dB */ + micco_audio_regs[MICCO_PGA_DACS] = + 0x00; + + /*Soft start for MONO, BEAR LINE and STEREO is 61.5ms */ + micco_audio_regs[MICCO_SOFT_START_RAMP] = + 0x00; + + for (i = 0; i < MICCO_AUDIO_REGS_NUM; i++) + micco_codec_write(i, micco_audio_regs[i]); + + return 0; +} +EXPORT_SYMBOL(micco_audio_init); + +/* FIXME: The Stereo have left and right channel. Need add it later */ +int micco_codec_enable_output(int type) +{ + switch (type) { + case CODEC_BEAR: + if (!(micco_audio_regs[MICCO_BEAR_VOL] & MICCO_BEAR_EN)) { + micco_audio_regs[MICCO_BEAR_VOL] |= MICCO_BEAR_EN; + micco_codec_write(MICCO_BEAR_VOL, + micco_audio_regs[MICCO_BEAR_VOL]); + } + break; + + case CODEC_MONO: + if (!(micco_audio_regs[MICCO_MONO_VOL] & MICCO_MONO_EN)) { + micco_audio_regs[MICCO_MONO_VOL] |= MICCO_MONO_EN; + micco_codec_write(MICCO_MONO_VOL, + micco_audio_regs[MICCO_MONO_VOL]); + } + break; + + case CODEC_STEREO: + if (!(micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] & + MICCO_STEREO_EN)){ + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] |= + MICCO_STEREO_EN; + micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1, + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]); + } + break; + + case CODEC_LINE_OUT: + if (!(micco_audio_regs[MICCO_AUDIO_LINE_AMP] & + MICCO_AUDIO_LINE_AMP_EN)) { + micco_audio_regs[MICCO_AUDIO_LINE_AMP] |= + MICCO_AUDIO_LINE_AMP_EN; + micco_codec_write(MICCO_AUDIO_LINE_AMP, + micco_audio_regs[MICCO_AUDIO_LINE_AMP]); + } + break; + + case CODEC_ADC: + if (!(micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] & + MICCO_VCODEC_ADC_ON_EN)) { + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] |= + MICCO_VCODEC_ADC_ON_EN; + micco_codec_write(MICCO_VCODEC_ADC_CONTROL, + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]); + } + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(micco_codec_enable_output); + +int micco_codec_disable_output(int type) +{ + switch (type) { + case CODEC_BEAR: + if (micco_audio_regs[MICCO_BEAR_VOL] & MICCO_BEAR_EN) { + micco_audio_regs[MICCO_BEAR_VOL] &= ~MICCO_BEAR_EN; + micco_codec_write(MICCO_BEAR_VOL, + micco_audio_regs[MICCO_BEAR_VOL]); + } + break; + + case CODEC_MONO: + if (micco_audio_regs[MICCO_MONO_VOL] & MICCO_MONO_EN) { + micco_audio_regs[MICCO_MONO_VOL] &= ~MICCO_MONO_EN; + micco_codec_write(MICCO_MONO_VOL, + micco_audio_regs[MICCO_MONO_VOL]); + } + break; + + case CODEC_STEREO: + if (micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] & + MICCO_STEREO_EN){ + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] &= + ~MICCO_STEREO_EN; + micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1, + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]); + } + break; + + case CODEC_LINE_OUT: + if (micco_audio_regs[MICCO_AUDIO_LINE_AMP] & + MICCO_AUDIO_LINE_AMP_EN) { + micco_audio_regs[MICCO_AUDIO_LINE_AMP] &= + ~MICCO_AUDIO_LINE_AMP_EN; + micco_codec_write(MICCO_AUDIO_LINE_AMP, + micco_audio_regs[MICCO_AUDIO_LINE_AMP]); + } + break; + + case CODEC_ADC: + if (micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] & + MICCO_VCODEC_ADC_ON_EN) { + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] &= + ~MICCO_VCODEC_ADC_ON_EN; + micco_codec_write(MICCO_VCODEC_ADC_CONTROL, + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]); + } + break; + default: + return -EINVAL; + } + + return 0; +} + +/* We don't check the paramater. The caller need make sure + * that the vol is in legal range. + */ +int micco_codec_set_output_vol(int type, int vol) +{ + switch (type) { + case CODEC_BEAR: + micco_audio_regs[MICCO_BEAR_VOL] = (~0x3F & + micco_audio_regs[MICCO_BEAR_VOL]) | vol; + micco_codec_write(MICCO_BEAR_VOL, + micco_audio_regs[MICCO_BEAR_VOL]); + break; + + case CODEC_MONO: + micco_audio_regs[MICCO_MONO_VOL] = (~0x3F & + micco_audio_regs[MICCO_MONO_VOL]) | vol; + micco_codec_write(MICCO_MONO_VOL, + micco_audio_regs[MICCO_MONO_VOL]); + + case CODEC_STEREO: + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1] = + (~0x3F & micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]) | + vol; + micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1, + micco_audio_regs[MICCO_STEREO_AMPLITUDE_CH1]); + break; + + case CODEC_LINE_OUT: + micco_audio_regs[MICCO_AUDIO_LINE_AMP] = + (~0x0F & micco_audio_regs[MICCO_AUDIO_LINE_AMP]) | + vol; + micco_codec_write(MICCO_AUDIO_LINE_AMP, + micco_audio_regs[MICCO_AUDIO_LINE_AMP]); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(micco_codec_set_output_vol); + + +int micco_codec_enable_input(int type) +{ + switch (type) { + case CODEC_AUX1: + if (!(micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX1_EN)) { + micco_audio_regs[MICCO_PGA_AUX1_2] |= MICCO_PGA_AUX1_EN; + micco_codec_write(MICCO_PGA_AUX1_2, + micco_audio_regs[MICCO_PGA_AUX1_2]); + } + break; + + case CODEC_AUX2: + if (!(micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX2_EN)) { + micco_audio_regs[MICCO_PGA_AUX1_2] |= MICCO_PGA_AUX2_EN; + micco_codec_write(MICCO_PGA_AUX1_2, + micco_audio_regs[MICCO_PGA_AUX1_2]); + } + break; + + case CODEC_AUX3: + if (!(micco_audio_regs[MICCO_PGA_AUX3] & MICCO_PGA_AUX3_EN)) { + micco_audio_regs[MICCO_PGA_AUX3] |= MICCO_PGA_AUX3_EN; + micco_codec_write(MICCO_PGA_AUX3, + micco_audio_regs[MICCO_PGA_AUX3]); + } + break; + + case CODEC_MIC1: + micco_audio_regs[MICCO_MIC_PGA] &= ~MICCO_MIC_PGA_SELMIC_2; + micco_audio_regs[MICCO_MIC_PGA] = + micco_audio_regs[MICCO_MIC_PGA] | + MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN | + MICCO_MIC_PGA_AMP_EN; + micco_codec_write(MICCO_MIC_PGA, + micco_audio_regs[MICCO_MIC_PGA]); + break; + + case CODEC_MIC2: + micco_audio_regs[MICCO_MIC_PGA] |= MICCO_MIC_PGA_SELMIC_2; + micco_audio_regs[MICCO_MIC_PGA] = + micco_audio_regs[MICCO_MIC_PGA] | + MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN | + MICCO_MIC_PGA_AMP_EN; + micco_codec_write(MICCO_MIC_PGA, + micco_audio_regs[MICCO_MIC_PGA]); + break; + + case CODEC_PCM: + if (!(micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] & + MICCO_VDAC_ON)) { + micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] |= + MICCO_VDAC_ON; + micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] &= + ~MICCO_VDAC_HPF_MUTE; + micco_codec_write(MICCO_VCODEC_VDAC_CONTROL, + micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL]); + } + break; + + case CODEC_HIFI: + if (!(micco_audio_regs[MICCO_HIFI_DAC_CONTROL] & + MICCO_HIFI_DAC_ON)) { + micco_audio_regs[MICCO_HIFI_DAC_CONTROL] |= + MICCO_HIFI_DAC_ON; + micco_codec_write(MICCO_HIFI_DAC_CONTROL, + micco_audio_regs[MICCO_HIFI_DAC_CONTROL]); + } + break; + + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(micco_codec_enable_input); + +int micco_codec_disable_input(int type) +{ + switch (type) { + case CODEC_AUX1: + if (micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX1_EN) { + micco_audio_regs[MICCO_PGA_AUX1_2] &= + ~MICCO_PGA_AUX1_EN; + micco_codec_write(MICCO_PGA_AUX1_2, + micco_audio_regs[MICCO_PGA_AUX1_2]); + } + break; + + case CODEC_AUX2: + if (micco_audio_regs[MICCO_PGA_AUX1_2] & MICCO_PGA_AUX2_EN) { + micco_audio_regs[MICCO_PGA_AUX1_2] &= + ~MICCO_PGA_AUX2_EN; + micco_codec_write(MICCO_PGA_AUX1_2, + micco_audio_regs[MICCO_PGA_AUX1_2]); + } + break; + + case CODEC_AUX3: + if (micco_audio_regs[MICCO_PGA_AUX3] & MICCO_PGA_AUX3_EN) { + micco_audio_regs[MICCO_PGA_AUX3] &= ~MICCO_PGA_AUX3_EN; + micco_codec_write(MICCO_PGA_AUX3, + micco_audio_regs[MICCO_PGA_AUX3]); + } + break; + + case CODEC_MIC1: + micco_audio_regs[MICCO_MIC_PGA] = + micco_audio_regs[MICCO_MIC_PGA] & + ~(MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN | + MICCO_MIC_PGA_AMP_EN); + micco_codec_write(MICCO_MIC_PGA, + micco_audio_regs[MICCO_MIC_PGA]); + break; + + case CODEC_MIC2: + micco_audio_regs[MICCO_MIC_PGA] = + micco_audio_regs[MICCO_MIC_PGA] & + ~(MICCO_MIC_PGA_EXT_EN | MICCO_MIC_PGA_INT_EN | + MICCO_MIC_PGA_AMP_EN); + micco_codec_write(MICCO_MIC_PGA, + micco_audio_regs[MICCO_MIC_PGA]); + break; + + case CODEC_PCM: + if (micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] & + MICCO_VDAC_ON) { + micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL] &= + ~MICCO_VDAC_ON; + micco_codec_write(MICCO_VCODEC_VDAC_CONTROL, + micco_audio_regs[MICCO_VCODEC_VDAC_CONTROL]); + } + break; + + case CODEC_HIFI: + if (micco_audio_regs[MICCO_HIFI_DAC_CONTROL] & + MICCO_HIFI_DAC_ON) { + micco_audio_regs[MICCO_HIFI_DAC_CONTROL] &= + ~MICCO_HIFI_DAC_ON; + micco_codec_write(MICCO_HIFI_DAC_CONTROL, + micco_audio_regs[MICCO_HIFI_DAC_CONTROL]); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +/* We don't check the paramater. The caller need make sure + * that the vol is in legal range. + */ +int micco_codec_set_input_gain(int type, int gain) +{ + switch (type) { + case CODEC_AUX1: + micco_audio_regs[MICCO_PGA_AUX1_2] = ((~0x03 & + micco_audio_regs[MICCO_PGA_AUX1_2]) | gain); + micco_codec_write(MICCO_PGA_AUX1_2, + micco_audio_regs[MICCO_PGA_AUX1_2]); + break; + + case CODEC_AUX2: + micco_audio_regs[MICCO_PGA_AUX1_2] = ((~0x30 & + micco_audio_regs[MICCO_PGA_AUX1_2]) | (gain << 4)); + micco_codec_write(MICCO_PGA_AUX1_2, + micco_audio_regs[MICCO_PGA_AUX1_2]); + break; + + case CODEC_AUX3: + micco_audio_regs[MICCO_PGA_AUX3] = ((~0x03 & + micco_audio_regs[MICCO_PGA_AUX3]) | gain); + micco_codec_write(MICCO_PGA_AUX3, + micco_audio_regs[MICCO_PGA_AUX3]); + break; + + case CODEC_MIC1: + case CODEC_MIC2: + micco_audio_regs[MICCO_MIC_PGA] = ((~0x07 & + micco_audio_regs[MICCO_MIC_PGA]) | gain); + micco_codec_write(MICCO_MIC_PGA, + micco_audio_regs[MICCO_MIC_PGA]); + break; + + case CODEC_PCM: /* Need check whether HIFI and PCM support input? */ + case CODEC_HIFI: + break; + default: + return -EINVAL; + } + + return 0; +} + +int micco_codec_set_sample_rate(int port, int rate) +{ + switch (port) { + case MICCO_VOICE_PORT: + switch (rate) { + case 8000: + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] = + (~(0x03 << 3) & + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]); + break; + + case 16000: + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] = + (~(0x03 << 3) & + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]) | + (0x01 << 3); + break; + + case 32000: + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL] |= + (0x3 << 3); + break; + default: + return -EINVAL; + } + micco_codec_write(MICCO_VCODEC_ADC_CONTROL, + micco_audio_regs[MICCO_VCODEC_ADC_CONTROL]); + break; + + case MICCO_HIFI_PORT: + switch (rate) { + case 8000: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + break; + case 11025: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x01; + break; + case 12000: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x02; + break; + case 16000: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x03; + break; + case 22050: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x04; + break; + case 24000: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x05; + break; + case 32000: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x06; + break; + case 44100: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x07; + break; + case 48000: + micco_audio_regs[MICCO_I2S_CONTROL] &= 0xF0; + micco_audio_regs[MICCO_I2S_CONTROL] |= 0x0F; + break; + default: + return -EINVAL; + } + micco_codec_write(MICCO_I2S_CONTROL, + micco_audio_regs[MICCO_I2S_CONTROL]); + break; + default: + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(micco_codec_set_sample_rate); + +/* micco_set_vibrator parameter: + * SWITCH mode, repetition frequency controled by VIBRA_PWM<6:0> + * 00h : OFF + * >= 54h: 100% alwayes on (code 84 decimal) + */ +int micco_set_vibrator(unsigned char value) +{ + micco_write(MICCO_VIBRA_CONTROL, value & 0x3F); + return 0; +} +EXPORT_SYMBOL(micco_set_vibrator); + +int micco_sidetone_enable(void) +{ + if (!(micco_audio_regs[MICCO_SIDETONE] & MICCO_SIDETONE_EN)) { + micco_audio_regs[MICCO_SIDETONE] |= MICCO_SIDETONE_EN; + micco_codec_write(MICCO_SIDETONE, + micco_audio_regs[MICCO_SIDETONE]); + } + + return 0; +} + +int micco_sidetone_disable(void) +{ + if (micco_audio_regs[MICCO_SIDETONE] & MICCO_SIDETONE_EN) { + micco_audio_regs[MICCO_SIDETONE] &= ~MICCO_SIDETONE_EN; + micco_codec_write(MICCO_SIDETONE, + micco_audio_regs[MICCO_SIDETONE]); + } + + return 0; +} + +/* The Micco route rule: + * 1. DAC3 can't be route to TX_PGA. + * 2. MIC1 and MIC2 can't be enabled at the same time. + */ +int micco_check_route(u16 *routemap) +{ +/* TODO */ + return 0; +} + +int micco_set_route(u16 *rmap, u16 *current_rmap) +{ + int ret; + + ret = micco_check_route(current_rmap); + if (ret) + return ret; + + return 0; +} +/* Micco Audio primitive end here */ + +/* Initialization functions for system */ +/* LDO12 enabled for IO */ +void micco_enable_LDO12(void) +{ + u8 val; + + micco_read(MICCO_LDO1312, &val); + val = (0x0F & val); + micco_write(MICCO_LDO1312, val); + + return; +} + +/* USB Device/OTG functions */ +static int micco_set_pump(int enable) +{ + int status = 0; + u8 val; + unsigned long flags; + static int set_pump_count; + + local_irq_save(flags); + if (enable) { + if (set_pump_count++ != 0) + goto out; + status = micco_read(MICCO_MISC, &val); + if (status) + goto out; + val |= MICCO_MISC_VBUS_COMPS_EN; + status = micco_write(MICCO_MISC, val); + if (status) + goto out; + + /* FIXME: Littleton didn't connect EXTON as cable detection + * signal. We use USB_DEV detection in event B as cable + * detection. + */ + /* TODO: This is a platform related code. Need split + * to platform related code. + */ + status = micco_read(MICCO_IRQ_MASK_B, &val); + if (status) + goto out; + val &= ~IRQ_MASK_B_USB_DEV; + status = micco_write(MICCO_IRQ_MASK_B, val); + if (status) + goto out; + } else { + if (set_pump_count == 0 || set_pump_count-- != 0) + goto out; + + status = micco_read(MICCO_IRQ_MASK_B, &val); + if (status) + goto out; + val |= (IRQ_MASK_B_SESSION_VALID_1_8 | IRQ_MASK_B_VBUS_VALID_3_8 + | IRQ_MASK_B_VBUS_VALID_4_55); + status = micco_write(MICCO_IRQ_MASK_B, val); + if (status) + goto out; + + status = micco_read(MICCO_MISC, &val); + if (status) + goto out; + val &= ~MICCO_MISC_VBUS_COMPS_EN; + status = micco_write(MICCO_MISC, val); + if (status) + goto out; + + status = micco_read(MICCO_IRQ_MASK_B, &val); + if (status) + goto out; + val |= IRQ_MASK_B_USB_DEV; + status = micco_write(MICCO_IRQ_MASK_B, val); + if (status) + goto out; + } +out: + local_irq_restore(flags); + return status; +} + +/* 1. enable: 1, srp: 0, 100ma mode + * 2. enable: 1, srp: 1, 10ma mode + * + * Micco before e: 1:s: 1, must e:0, s:1. + */ +static int micco_set_vbus_supply(int enable, int srp) +{ + int ret; + u8 val, tmp; + + ret = micco_read(MICCO_MISC, &val); + if (ret) { + printk(KERN_ERR "I2C operation error 0x%x\n", ret); + return ret; + } + + if (enable) { + /* When enable the USB pump, need check the OTGCP_IOVER. */ + micco_read(MICCO_IRQ_MASK_B, &tmp); + tmp &= ~IRQ_MASK_B_OTGCP_IOVER; + micco_write(MICCO_IRQ_MASK_B, tmp); + + val |= MICCO_MISC_USBCP_EN; + if (srp) { + val |= MICCO_MISC_USBSR_EN; + } else { + val &= ~MICCO_MISC_USBSR_EN; + } + } else { + /* When disable the USB pump, needn't check the OTGCP_IOVER. */ + micco_read(MICCO_IRQ_MASK_B, &tmp); + tmp |= IRQ_MASK_B_OTGCP_IOVER; + micco_write(MICCO_IRQ_MASK_B, tmp); + + val &= ~(MICCO_MISC_USBCP_EN | MICCO_MISC_USBSR_EN); + } + ret = micco_write(MICCO_MISC, val); + if (ret) + printk(KERN_ERR "I2C operation error 0x%x\n", ret); + + return ret; +} + +static int micco_set_usbotg_a_mask(void) +{ + int ret; + u8 val; + + ret = micco_read(MICCO_IRQ_MASK_B, &val); + if (ret) { + printk(KERN_ERR "I2C operation error 0x%x\n", ret); + return ret; + } + + /* Enable the interrupt for VUBS > 4.4V and Session valid + * which A device care about. + * + * NOTESSSSSSSSS: We care about the SRP detection signal (0.8v ~ 2.0v) + * on Micco. Which map to SESSION_VALID_1_8. + */ + val |= (IRQ_MASK_B_VBUS_VALID_3_8 | IRQ_MASK_B_SRP_READY_0_6); + val &= ~(IRQ_MASK_B_VBUS_VALID_4_55 | IRQ_MASK_B_SESSION_VALID_1_8); + + ret = micco_write(MICCO_IRQ_MASK_B, val); + return ret; +} + +static int micco_set_usbotg_b_mask(void) +{ + int ret; + u8 val; + + ret = micco_read(MICCO_IRQ_MASK_B, &val); + if (ret) { + printk(KERN_ERR "I2C operation error 0x%x\n", ret); + return ret; + } + + /* Mask all USB VBUS interrupt for B device */ + val |= (IRQ_MASK_B_VBUS_VALID_3_8 | IRQ_MASK_B_SESSION_VALID_1_8 | \ + IRQ_MASK_B_VBUS_VALID_4_55 | IRQ_MASK_B_SRP_READY_0_6); + + ret = micco_write(MICCO_IRQ_MASK_B, val); + + return ret; +} + +static int is_micco_vbus_assert(void) +{ + int ret; + u8 val, level = MICCO_STATUS_B_VBUS_V_4P4; + + ret = micco_read(MICCO_STATUS_B, &val); + if (ret) { + printk(KERN_ERR "I2C operation error 0x%x\n", ret); + return ret; + } + + /* USBDEV interrupt is not so reliable for vbus assert check, + * replace with 4.4V check + * + if (val & MICCO_STATUS_B_USBDEV) */ + if (cpu_is_pxa910()) + level = MICCO_STATUS_B_VBUS_V_3P8; + + if (val & level) + return 1; + else + return 0; +} + +#ifdef CONFIG_LITTLETON_CHARGER +#define CHARGER_HOOK +#else +#undef CHARGER_HOOK +#endif + +static unsigned int micco_event_change(void) +{ + unsigned int ret = 0; + u8 val, mask; + + micco_read(MICCO_EVENT_A, &val); + if (val & MICCO_EA_CHDET) + ret |= PMIC_EVENT_CHDET; + + if (val & MICCO_EA_REV_IOVER) + ret |= PMIC_EVENT_REV_IOVER; + + if (val & MICCO_EA_IOVER) + ret |= PMIC_EVENT_IOVER; + + if (val & MICCO_EA_TBAT) + ret |= PMIC_EVENT_TBAT; + + if (val & MICCO_EA_VBATMON) + ret |= PMIC_EVENT_VBATMON; + + micco_read(MICCO_EVENT_B, &val); + if (val & MICCO_EB_USB_DEV) + ret |= PMIC_EVENT_VBUS; + + if (val & (MICCO_EB_VBUS_4P55|MICCO_EB_VBUS_3P8)) + ret |= PMIC_EVENT_VBUS; + + if (val & MICCO_EB_SESSION_1P8) { + micco_read(MICCO_IRQ_MASK_B, &mask); + if (!(mask & IRQ_MASK_B_SESSION_VALID_1_8)) + ret |= PMIC_EVENT_VBUS; + } + + if (val & MICCO_EB_OTGCP_IOVER) + ret |= PMIC_EVENT_OTGCP_IOVER; + + micco_read(MICCO_EVENT_C, &val); + if (val & MICCO_EC_PEN_DOWN) + ret |= PMIC_EVENT_TOUCH; + + micco_read(MICCO_EVENT_D, &val); + if (val & MICCO_ED_HEADSET) + { + ret |= PMIC_EVENT_HSDETECT; + } + + if (val & MICCO_ED_HOOKSWITCH) + { + ret |= PMIC_EVENT_HOOKSWITCH; + } + return ret; +} + +static int is_micco_avbusvld(void) +{ + u8 val; + + micco_read(MICCO_IRQ_MASK_B, &val); + if (val & IRQ_MASK_B_VBUS_VALID_4_55) + return 0; + + micco_read(MICCO_STATUS_B, &val); + if (val & MICCO_STATUS_B_VBUS_V_4P4) + return 1; + else + return 0; +} + +static int is_micco_asessvld(void) +{ + u8 val; + + micco_read(MICCO_IRQ_MASK_B, &val); + if (val & IRQ_MASK_B_SESSION_VALID_1_8) + return 0; + + micco_read(MICCO_STATUS_B, &val); + if (val & MICCO_STATUS_B_SESS_V_1P8) + return 1; + else + return 0; +} + +static int is_micco_bsessvld(void) +{ + u8 val; + + micco_read(MICCO_IRQ_MASK_B, &val); + if (val & IRQ_MASK_B_VBUS_VALID_3_8) + return 0; + + micco_read(MICCO_STATUS_B, &val); + if (val & MICCO_STATUS_B_VBUS_V_3P8) + return 1; + else + return 0; +} + +static int is_micco_srp_ready(void) +{ + u8 val; + + micco_read(MICCO_IRQ_MASK_B, &val); + if (val & IRQ_MASK_B_SRP_READY_0_6) + return 0; + + /* FIXME: When cable detached, the SESS Valid + * of STATUA B can not change to 0 immediately. + * That will cause potential problems. + */ + micco_read(MICCO_STATUS_B, &val); + if (val & MICCO_STATUS_B_SRP_READY) + return 1; + else + return 0; +} +static int get_power_supply_module(int cmd) +{ + int command, power_module; + + if (cmd < VCC_CORE || cmd >= MAX_CMD) { + printk(KERN_WARNING "input wrong command: %d\n", cmd); + return -EINVAL; + } + command = micco_power_module[cmd - VCC_CORE].command; + if (command != cmd) { + printk(KERN_WARNING "micco_power_module[] is error: %d\n", cmd); + return -EINVAL; + } + power_module = micco_power_module[cmd - VCC_CORE].power_module; + if (power_module == 0) { + printk(KERN_WARNING "the command not supported: %d\n", cmd); + return -EINVAL; + } + + return power_module; +} + +/* USB Device/OTG functions end here*/ +static int get_micco_voltage(int cmd, int *pmv) +{ + int power_module, status = 0; + u8 val; + + *pmv = 0; + + power_module = get_power_supply_module(cmd); + + start_calc_time(); + switch (power_module) { + case BUCK1: + status = micco_read(MICCO_BUCK1_DVC2, &val); + if (status) + return status; + + val &= 0x1f; + *pmv = val * MICCO_VBUCK1_STEP + MICCO_VBUCK1_BASE; + break; + case BUCK2: /* TODO */ + *pmv = 1800; + break; + case BUCK3: /* TODO */ + *pmv = 1800; + break; + case LDO1: + status = micco_read(MICCO_LDO1_MDTV1, &val); + if (status) + return status; + + val &= 0x0f; + *pmv = val * MICCO_VLDO1_STEP + MICCO_VLDO1_BASE; + break; + case LDO2: + status = micco_read(MICCO_SRAM_DVC2, &val); + if (status) + return status; + + val = val & 0x1f; + *pmv = val * MICCO_VSRAM_STEP + MICCO_VSRAM_BASE; + break; + case LDO3: + status = micco_read(MICCO_LDO643, &val); + if (status) + return status; + + val &= 0x0f; + *pmv = val * MICCO_VLDO3_STEP + MICCO_VLDO3_BASE; + break; + case LDO4: + status = micco_read(MICCO_LDO643, &val); + if (status) + return status; + + if (val & 0x10) + *pmv = 2900; + else + *pmv = 1800; + break; + case LDO5: /* TODO */ + *pmv = 3100; + break; + case LDO6: + status = micco_read(MICCO_LDO643, &val); + if (status) + return status; + + val = (val & 0xE0) >> 5; + *pmv = val * MICCO_VLDO6_STEP + MICCO_VLDO6_BASE; + break; + case LDO7: /* TODO */ + printk(KERN_WARNING "power supply module TODO: %d\n", cmd); + return -EINVAL; + case LDO8: /* TODO */ + printk(KERN_WARNING "power supply module TODO: %d\n", cmd); + return -EINVAL; + case LDO9: + status = micco_read(MICCO_LDO987, &val); + if (status) + return status; + + val = (val & 0xE0) >> 5; + *pmv = val * MICCO_VLDO9_STEP + MICCO_VLDO9_BASE; + break; + case LDO10: + status = micco_read(MICCO_LDO987, &val); + if (status) + return status; + + val &= 0x07; + *pmv = val * MICCO_VLDO10_STEP + MICCO_VLDO10_BASE; + break; + case LDO11: + status = micco_read(MICCO_LDO1110, &val); + if (status) + return status; + + val = (val & 0xf0) >> 4; + *pmv = val * MICCO_VLDO11_STEP + MICCO_VLDO11_BASE; + break; + case LDO12: + status = micco_read(MICCO_LDO1312, &val); + if (status) + return status; + + val &= 0x07; + if (val & 0x08) + *pmv = val * MICCO_VLDO12_STEP + MICCO_VLDO12_BASE1; + else + *pmv = val * MICCO_VLDO12_STEP + MICCO_VLDO12_BASE; + break; + case LDO13: + status = micco_read(MICCO_LDO1312, &val); + if (status) + return status; + + val = (val & 0xf0) >> 4; + *pmv = val * MICCO_VLDO13_STEP + MICCO_VLDO13_BASE; + break; + case LDO14: + status = micco_read(MICCO_LDO1514, &val); + if (status) + return status; + + val &= 0x0f; + *pmv = val * MICCO_VLDO14_STEP + MICCO_VLDO14_BASE; + break; + case LDO15: + status = micco_read(MICCO_LDO1514, &val); + if (status) + return status; + + val = (val & 0xF0) >> 4; + *pmv = val * MICCO_VLDO15_STEP + MICCO_VLDO15_BASE; + break; + case USB_OTG: /* TODO */ + case LDO_GPADC: /* TODO */ + case LDO_AUDIO: /* TODO */ + case LDO_PMCORE: /* TODO */ + case LDO_BBAT: /* TODO */ + printk(KERN_WARNING "power supply module TODO: %d\n", cmd); + return -EINVAL; + default: + printk(KERN_WARNING "input wrong command: %d\n", cmd); + return -EINVAL; + } + end_calc_time(); + return status; +} + +static int set_micco_voltage(int cmd, int mv) +{ + int power_module, status = 0; + u8 val; + + power_module = get_power_supply_module(cmd); + + start_calc_time(); + switch (power_module) { + case BUCK1: + if ((mv < MICCO_VBUCK1_BASE) || (mv > MICCO_VBUCK1_MAX)) + return -EINVAL; + status = micco_read(MICCO_BUCK1_DVC2, &val); + if (status) + return status; + + val &= 0xe0; + val = val | ((mv - MICCO_VBUCK1_BASE) / MICCO_VBUCK1_STEP); + status = micco_write(MICCO_BUCK1_DVC2, val); + /* Update the voltage output. Otherwise, voltage won't change */ + status = micco_write(MICCO_VCC1, 0x33); + break; + case BUCK2: /* TODO */ + return 0; + case BUCK3: /* TODO */ + return 0; + case LDO1: + if ((mv < MICCO_VLDO1_BASE) || (mv > MICCO_VLDO1_MAX)) + return -EINVAL; + status = micco_read(MICCO_LDO1_MDTV1, &val); + if (status) + return status; + + val &= 0xf0; + val = val | ((mv - MICCO_VLDO1_BASE) / MICCO_VLDO1_STEP); + status = micco_write(MICCO_LDO1_MDTV1, val); + break; + case LDO2: + if ((mv < MICCO_VSRAM_BASE) || mv > (MICCO_VSRAM_MAX)) + return -EINVAL; + status = micco_read(MICCO_SRAM_DVC2, &val); + if (status) + return status; + + val &= 0xe0; + val = val | ((mv - MICCO_VSRAM_BASE) / MICCO_VSRAM_STEP); + status = micco_write(MICCO_SRAM_DVC2, val); + /* Update the voltage output. Otherwise, voltage won't change */ + status = micco_write(MICCO_VCC1, 0x33); + break; + case LDO3: + if ((mv < MICCO_VLDO3_BASE) || (mv > MICCO_VLDO3_MAX)) + return -EINVAL; + + status = micco_read(MICCO_LDO643, &val); + if (status) + return status; + + val &= 0xf0; + val = val | ((mv - MICCO_VLDO3_BASE) / MICCO_VLDO3_STEP); + status = micco_write(MICCO_LDO643, val); + break; + case LDO4: + if ((mv != 2900) && (mv != 1800)) + return -EINVAL; + status = micco_read(MICCO_LDO643, &val); + if (2900 == mv) + val |= 0x10; + else + val &= ~0x10; + status = micco_write(MICCO_LDO643, val); + break; + case LDO5: /* TODO */ + return 0; + case LDO6: + if ((mv < MICCO_VLDO6_BASE) || (mv > MICCO_VLDO6_MAX)) + return -EINVAL; + + status = micco_read(MICCO_LDO643, &val); + if (status) + return status; + + val &= 0x1f; + val |= ((mv - MICCO_VLDO6_BASE) / MICCO_VLDO6_STEP) << 5; + status = micco_write(MICCO_LDO643, val); + break; + case LDO7: + printk(KERN_WARNING "power supply module TODO: %d\n", cmd); + return -EINVAL; + case LDO8: + printk(KERN_WARNING "power supply module TODO: %d\n", cmd); + return -EINVAL; + case LDO9: + if ((mv < MICCO_VLDO9_BASE) || (mv > MICCO_VLDO9_MAX)) + return -EINVAL; + status = micco_read(MICCO_LDO987, &val); + if (status) + return status; + + val &= 0x1f; + val |= ((mv - MICCO_VLDO9_BASE) / MICCO_VLDO9_STEP) << 5; + status = micco_write(MICCO_LDO987, val); + break; + case LDO10: + if ((mv < MICCO_VLDO10_BASE) || (mv > MICCO_VLDO10_MAX)) + return -EINVAL; + + status = micco_read(MICCO_LDO1110, &val); + if (status) + return status; + + val &= 0xf8; + val = val | ((mv - MICCO_VLDO10_BASE) / MICCO_VLDO10_STEP); + status = micco_write(MICCO_LDO1110, val); + break; + case LDO11: + if ((mv < MICCO_VLDO11_BASE) || (mv > MICCO_VLDO11_MAX)) + return -EINVAL; + + status = micco_read(MICCO_LDO1110, &val); + if (status) + return status; + + val &= 0x0f; + val |= ((mv - MICCO_VLDO11_BASE) / MICCO_VLDO11_STEP) << 4; + status = micco_write(MICCO_LDO1110, val); + break; + case LDO12: + if ((mv < MICCO_VLDO12_BASE) || (mv > MICCO_VLDO12_MAX)) + return -EINVAL; + + + status = micco_read(MICCO_LDO1312, &val); + if (status) + return status; + + val &= 0xf8; + if (mv > MICCO_VLDO12_BASE1) { + val = val | ((mv - MICCO_VLDO12_BASE1) / \ + MICCO_VLDO12_STEP); + val |= 0x08; + } else { + val = val | ((mv - MICCO_VLDO12_BASE) / \ + MICCO_VLDO12_STEP); + val &= ~0x08; + } + status = micco_write(MICCO_LDO1312, val); + break; + case LDO13: + if ((mv < MICCO_VLDO13_BASE) || (mv > MICCO_VLDO13_MAX)) + return -EINVAL; + + status = micco_read(MICCO_LDO1312, &val); + if (status) + return status; + + val &= 0x0f; + val |= ((mv - MICCO_VLDO13_BASE) / MICCO_VLDO13_STEP) << 4; + status = micco_write(MICCO_LDO1312, val); + break; + case LDO14: + if ((mv < MICCO_VLDO14_BASE) || (mv > MICCO_VLDO14_MAX)) + return -EINVAL; + status = micco_read(MICCO_LDO1514, &val); + if (status) + return status; + + val &= 0xf0; + val = val | ((mv - MICCO_VLDO14_BASE) / MICCO_VLDO14_STEP); + status = micco_write(MICCO_LDO1514, val); + break; + case LDO15: + if ((mv < MICCO_VLDO15_BASE) || (mv > MICCO_VLDO15_MAX)) + return -EINVAL; + + status = micco_read(MICCO_LDO1514, &val); + if (status) + return status; + + val &= 0x0f; + val |= ((mv - MICCO_VLDO15_BASE) / MICCO_VLDO15_STEP) << 4; + status = micco_write(MICCO_LDO1514, val); + break; + case USB_OTG: /* TODO */ + case LDO_GPADC: /* TODO */ + case LDO_AUDIO: /* TODO */ + case LDO_PMCORE: /* TODO */ + case LDO_BBAT: /* TODO */ + printk(KERN_WARNING "power supply module TODO: %d\n", cmd); + return -EINVAL; + default: + printk(KERN_INFO "error command\n"); + return -EINVAL; + } + + if (status != 0) + return status; + + end_calc_time(); + return status; +} + +static void micco_worker(struct work_struct *work) +{ + u8 val; + + if(!cpu_is_pxa910()) + event = micco_event_change(); + pmic_event_handle(event); + + /* We don't put these codes to USB specific code because + * we need handle it even when no USB callback registered. + */ + if (event & PMIC_EVENT_OTGCP_IOVER) { + /* According to Micco spec, when OTGCP_IOVER happen, + * Need clean the USBPCP_EN in MISC. and then set + * it again. + */ + micco_read(MICCO_MISC, &val); + val &= ~MICCO_MISC_USBCP_EN; + micco_write(MICCO_MISC, val); + val |= MICCO_MISC_USBCP_EN; + micco_write(MICCO_MISC, val); + } +} + +/* + * Micco interrupt service routine. + * In the ISR we need to check the Status bits in Micco and according + * to those bits to check which kind of IRQ had happened. + */ +static irqreturn_t micco_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct micco_platform_data *pdata = client->dev.platform_data; + + /* clear the irq */ + pdata->ack_irq(); + /* pxa910: clear the level triggered interrupt in the handler */ + if(cpu_is_pxa910()) + event = micco_event_change(); + + schedule_work(&pdata->work); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM +/* + * Suspend the micco interface. + * Add register save/restore here. + */ +static int micco_suspend(struct i2c_client *client, pm_message_t state) +{ + unsigned char val; + + disable_irq(client->irq); + if ((get_pm_state() == PM_SUSPEND_MEM)) { + micco_read(MICCO_OVER1, &val); + micco_regs[MICCO_OVER1].data = val; + micco_read(MICCO_APP_OVER2, &val); + micco_regs[MICCO_APP_OVER2].data = val; + micco_read(MICCO_APP_OVER3, &val); + micco_regs[MICCO_APP_OVER3].data = val; + micco_read(MICCO_BUCK_SLEEP, &val); + micco_regs[MICCO_BUCK_SLEEP].data = val; + micco_read(MICCO_SYSCTRL_A, &val); + micco_regs[MICCO_SYSCTRL_A].data = val; + + micco_write(MICCO_BUCK_SLEEP, 0x6D); + micco_write(MICCO_SYSCTRL_A, 0xff); + +/* micco_write(MICCO_OVER1, 0x04); + micco_write(MICCO_APP_OVER2, 0x30); + micco_write(MICCO_APP_OVER3, 0x7c); +*/ + } + return 0; +} + +/* + * Resume the Micco interface. + */ +static int micco_resume(struct i2c_client *client) +{ + int i; + + for (i = 0; i < MICCO_REG_NUM; i++) + micco_regs[i].hit = 0; + + enable_irq(client->irq); + micco_irq_handler(client->irq, client); + if ((get_pm_state() == PM_SUSPEND_MEM)) { + micco_write(MICCO_OVER1, micco_regs[MICCO_OVER1].data); + micco_write(MICCO_APP_OVER2, micco_regs[MICCO_APP_OVER2].data); + micco_write(MICCO_APP_OVER3, micco_regs[MICCO_APP_OVER3].data); + micco_write(MICCO_BUCK_SLEEP, micco_regs[MICCO_BUCK_SLEEP].data); + micco_write(MICCO_SYSCTRL_A, micco_regs[MICCO_SYSCTRL_A].data); + } + return 0; +} + +#else /* */ +#define micco_suspend NULL +#define micco_resume NULL +#endif /* */ + +static struct pmic_ops micco_pmic_ops = { + .get_voltage = get_micco_voltage, + .set_voltage = set_micco_voltage, + + .is_vbus_assert = is_micco_vbus_assert, + .is_avbusvld = is_micco_avbusvld, + .is_asessvld = is_micco_asessvld, + .is_bsessvld = is_micco_bsessvld, + .is_srp_ready = is_micco_srp_ready, + + .set_pump = micco_set_pump, + .set_vbus_supply = micco_set_vbus_supply, + .set_usbotg_a_mask = micco_set_usbotg_a_mask, + .set_usbotg_b_mask = micco_set_usbotg_b_mask, +}; + +#ifdef CONFIG_PROC_FS +#define MICCO_PROC_FILE "driver/micco" +static struct proc_dir_entry *micco_proc_file; +static int index; + +static ssize_t micco_proc_read(struct file *filp, + char *buffer, size_t length, loff_t *offset) +{ + u8 reg_val; + + if ((index < 0) || (index > MICCO_REG_NUM)) + return 0; + + micco_read(index, ®_val); + printk(KERN_INFO "register 0x%x: 0x%x\n", index, reg_val); + return 0; +} + +#define VIBRA_OFF_VALUE 0 +#define VIBRA_ON_VALUE 0x30 /* you can choose the value of bit6 ~ bit1 + to change the freq of the vibrator */ +static ssize_t micco_proc_write(struct file *filp, + const char *buff, size_t len, loff_t *off) +{ + u8 reg_val; + char messages[256], vol[256]; + + if (len > 256) + len = 256; + + if (copy_from_user(messages, buff, len)) + return -EFAULT; + + if (strncmp(messages, "viboff", 6) == 0) + micco_set_vibrator(VIBRA_OFF_VALUE); + else if (strncmp(messages, "vibon", 5) == 0) + micco_set_vibrator(VIBRA_ON_VALUE); + else if (strncmp(messages, "vib", 3) == 0){ + unsigned int val = 0; + sscanf(messages, "vib%x", &val); + micco_set_vibrator(val); + }else if ('-' == messages[0]) { + /* set the register index */ + memcpy(vol, messages+1, len-1); + index = (int) simple_strtoul(vol, NULL, 16); + } else { + /* set the register value */ + reg_val = (int)simple_strtoul(messages, NULL, 16); + micco_write(index, reg_val & 0xFF); + } + + return len; +} + +static struct file_operations micco_proc_ops = { + .read = micco_proc_read, + .write = micco_proc_write, +}; + +static void create_micco_proc_file(void) +{ + micco_proc_file = create_proc_entry(MICCO_PROC_FILE, 0644, NULL); + if (micco_proc_file) { + micco_proc_file->owner = THIS_MODULE; + micco_proc_file->proc_fops = &micco_proc_ops; + } else + printk(KERN_INFO "proc file create failed!\n"); +} + +static void remove_micco_proc_file(void) +{ + extern struct proc_dir_entry proc_root; + remove_proc_entry(MICCO_PROC_FILE, &proc_root); +} + +#endif + +static int __devinit micco_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct micco_platform_data *pdata; + int ret; + int chip_id; + int i; + + chip_id = i2c_smbus_read_byte_data(client, MICCO_CHIP_ID); + if (chip_id < 0) { + printk(KERN_WARNING "micco unavailable!\n"); + g_client = NULL; + return -ENXIO; + } else { + printk(KERN_INFO "micco(chip id:0x%02x) detected.\n", chip_id); + } + + g_client = client; + + ret = micco_initchip(); + if (ret != 0) + printk(KERN_WARNING "Initialize Micco failed with ret 0x%x\n", + ret); + + pdata = client->dev.platform_data; + /* init spinlock */ + spin_lock_init(&pdata->lock); + /* init workqueue */ + INIT_WORK(&pdata->work, micco_worker); + /* init irq */ + pdata->init_irq(); + /* FIXME: define trigger type in platform data later */ + if (cpu_is_pxa910()) { + ret = request_irq(client->irq, micco_irq_handler, IRQF_DISABLED, + "Micco", client); + } else { + ret = request_irq(client->irq, micco_irq_handler, IRQF_TRIGGER_FALLING, + "Micco", client); + } + if (ret) { + printk(KERN_WARNING "Request IRQ for Micco failed, return:%d\n", + ret); + return ret; + } + + pdata->platform_init(); + + for (i = 0; pdata->power_chips[i].power_supply_modules != NULL; i++) { + if (pdata->power_chips[i].chip_id == chip_id) { + micco_power_module = \ + pdata->power_chips[i].power_supply_modules; + break; + } + } + if (pdata->power_chips[i].power_supply_modules == NULL) + panic("This Micco chip is not supported"); + +#ifdef CONFIG_PROC_FS + create_micco_proc_file(); +#endif + + pmic_set_ops(&micco_pmic_ops); + +#ifdef CONFIG_TOUCHSCREEN_MICCO + { + struct platform_device *pdev; + pdev = platform_device_alloc("micco-touch", (int)id->driver_data); +// pdev->dev.platform_data = subdev->platform_data; + platform_device_add(pdev); + } +#endif + + return 0; +} + +static int micco_remove(struct i2c_client *client) +{ + pmic_set_ops(NULL); + +#ifdef CONFIG_PROC_FS + remove_micco_proc_file(); +#endif + + free_irq(client->irq, NULL); + + return 0; +} + +static const struct i2c_device_id micco_id[] = { + { "micco", 0 }, + { } +}; + +static struct i2c_driver micco_driver = { + .driver = { + .name = "micco", + }, + .id_table = micco_id, + .probe = micco_probe, + .remove = micco_remove, + .suspend = micco_suspend, + .resume = micco_resume, +}; + +static int __init micco_init(void) +{ + return i2c_add_driver(&micco_driver); +} + +static void __exit micco_exit(void) +{ + flush_scheduled_work(); + i2c_del_driver(&micco_driver); +} + +subsys_initcall(micco_init); +module_exit(micco_exit); + +MODULE_DESCRIPTION("Micco Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/i2c/chips/pca963x.c b/drivers/i2c/chips/pca963x.c new file mode 100644 index 00000000000000..3246e57d8f7aa6 --- /dev/null +++ b/drivers/i2c/chips/pca963x.c @@ -0,0 +1,431 @@ +/* pca963x.c - 4-bit I2C-bus LED driver + * + * Copyright (C) 2008 HTC Corporation. + * Author: Shan-Fu Chiou + * + * 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 +#include +#include +#include + +static uint8_t address[] = { 0x02, 0x03, 0x04 }; +static DEFINE_SPINLOCK(pca963x_lock); + +enum op_t { + OP_SET_BLINK, + OP_SET_GRPPWM, + OP_SET_GRPFREQ, + OP_SET_BLUE_BRIGHTNESS, + OP_SET_GREEN_BRIGHTNESS, + OP_SET_RED_BRIGHTNESS, +}; + +enum power_mode { + MODE_SLEEP, + MODE_NORMAL, +}; + +struct pca963x_t { + uint8_t colors[3]; + uint8_t blink; + uint8_t grppwm; + uint8_t grpfreq; +}; + +struct pca963x_data { + struct pca963x_t data; + uint8_t dirty; + uint8_t status; + enum power_mode mode; + struct work_struct work; + struct i2c_client *client; + struct led_classdev leds[3]; /* blue, green, red */ +}; + +static ssize_t pca963x_blink_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + if (((pca963x->dirty >> OP_SET_BLINK) & 0x01)) + flush_scheduled_work(); + return sprintf(buf, "%u\n", pca963x->data.blink); +} + +static ssize_t pca963x_blink_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + int val = -1; + + sscanf(buf, "%u", &val); + if (val < 0 || val > 1) + return -EINVAL; + + spin_lock(&pca963x_lock); + pca963x->dirty |= 1 << OP_SET_BLINK; + pca963x->data.blink = val; + spin_unlock(&pca963x_lock); + schedule_work(&pca963x->work); + + return count; +} + +static DEVICE_ATTR(blink, 0644, pca963x_blink_show, pca963x_blink_store); + +static ssize_t pca963x_grpfreq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + if (((pca963x->dirty >> OP_SET_GRPFREQ) & 0x01)) + flush_scheduled_work(); + return sprintf(buf, "%u\n", pca963x->data.grpfreq); +} + +static ssize_t pca963x_grpfreq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + unsigned long val = simple_strtoul(buf, NULL, 10); + + if (val > 0xff) + return -EINVAL; + + spin_lock(&pca963x_lock); + pca963x->dirty |= 1 << OP_SET_GRPFREQ; + pca963x->data.grpfreq = val; + spin_unlock(&pca963x_lock); + schedule_work(&pca963x->work); + + return count; +} + +static DEVICE_ATTR(grpfreq, 0644, pca963x_grpfreq_show, pca963x_grpfreq_store); + +static ssize_t pca963x_grppwm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + if (((pca963x->dirty >> OP_SET_GRPPWM) & 0x01)) + flush_scheduled_work(); + return sprintf(buf, "%u\n", pca963x->data.grppwm); +} + +static ssize_t pca963x_grppwm_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + unsigned long val = simple_strtoul(buf, NULL, 10); + + if (val > 0xff) + return -EINVAL; + + spin_lock(&pca963x_lock); + pca963x->dirty |= 1 << OP_SET_GRPPWM; + pca963x->data.grppwm = val; + spin_unlock(&pca963x_lock); + schedule_work(&pca963x->work); + + return count; +} + +static DEVICE_ATTR(grppwm, 0644, pca963x_grppwm_show, pca963x_grppwm_store); + +static void led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct pca963x_data *pca963x; + int idx = 2; + + spin_lock(&pca963x_lock); + if (!strcmp(led_cdev->name, "blue")) { + idx = 0; + } else if (!strcmp(led_cdev->name, "green")) { + idx = 1; + } else { + idx = 2; + } + pca963x = container_of(led_cdev, struct pca963x_data, leds[idx]); + pca963x->data.colors[idx] = brightness; + pca963x->dirty |= (1 << (OP_SET_BLUE_BRIGHTNESS + idx)); + spin_unlock(&pca963x_lock); + + schedule_work(&pca963x->work); + +} + +static void pca963x_update_brightness(struct pca963x_data *pca963x, int idx, + int brightness) +{ + if (brightness > LED_OFF) { + if (brightness == LED_FULL) { + pca963x->status &= ~(1 << idx); + pca963x->status |= (1 << (idx + 4)); + } else { + pca963x->status |= (1 << idx); + pca963x->status &= ~(1 << (idx + 4)); + } + } else { + pca963x->status &= ~(1 << idx); + pca963x->status &= ~(1 << (idx + 4)); + } + i2c_smbus_write_byte_data(pca963x->client, address[idx], brightness); +} + +static void pca963x_work_func(struct work_struct *work) +{ + int ret; + uint8_t dirty = 0; + struct pca963x_t work_data; + struct pca963x_data *pca963x = + container_of(work, struct pca963x_data, work); + + spin_lock(&pca963x_lock); + work_data = pca963x->data; + dirty = pca963x->dirty; + pca963x->dirty = 0; + spin_unlock(&pca963x_lock); + + ret = i2c_smbus_read_byte_data(pca963x->client, 0x00); + /* check if should switch to normal mode */ + if (!pca963x->mode) { + i2c_smbus_write_byte_data(pca963x->client, 0x00, 0x01); + pca963x->mode = MODE_NORMAL; + i2c_smbus_write_byte_data(pca963x->client, 0x08, 0xFF); + } + + if ((dirty >> OP_SET_BLINK) & 0x01) { + ret = i2c_smbus_read_byte_data(pca963x->client, 0x01); + if (work_data.blink) /* enable blinking */ + i2c_smbus_write_byte_data(pca963x->client, 0x01, + ret | 0x20); + else { + /* set group duty cycle control to default */ + i2c_smbus_write_byte_data(pca963x->client, 0x06, 0xFF); + /* set group frequency to default */ + i2c_smbus_write_byte_data(pca963x->client, 0x07, 0x00); + /* enable dimming */ + i2c_smbus_write_byte_data(pca963x->client, 0x01, + ret & 0xDF); + } + } + + if ((dirty >> OP_SET_GRPPWM) & 0x01) { + i2c_smbus_write_byte_data(pca963x->client, 0x06, + work_data.grppwm); + } + + if ((dirty >> OP_SET_GRPFREQ) & 0x01) { + i2c_smbus_write_byte_data(pca963x->client, 0x07, + work_data.grpfreq); + } + + if ((dirty >> OP_SET_BLUE_BRIGHTNESS) & 0x01) + pca963x_update_brightness(pca963x, 0, work_data.colors[0]); + + if ((dirty >> OP_SET_GREEN_BRIGHTNESS) & 0x01) + pca963x_update_brightness(pca963x, 1, work_data.colors[1]); + + if ((dirty >> OP_SET_RED_BRIGHTNESS) & 0x01) + pca963x_update_brightness(pca963x, 2, work_data.colors[2]); + + /* check if could go to low power mode */ + if (((pca963x->status & 0x0F) == 0) && (!work_data.blink)) { + i2c_smbus_write_byte_data(pca963x->client, 0x08, 0xAA); + i2c_smbus_write_byte_data(pca963x->client, 0x00, 0x11); + pca963x->mode = MODE_SLEEP; + } +} + +static void set_pca963x_default(struct i2c_client *client) +{ + i2c_smbus_write_byte_data(client, 0x00, 0x01); + i2c_smbus_write_byte_data(client, 0x01, 0x00); + /* set all LEDx brightness off */ + i2c_smbus_write_byte_data(client, address[0], LED_OFF); + i2c_smbus_write_byte_data(client, address[1], LED_OFF); + i2c_smbus_write_byte_data(client, address[2], LED_OFF); + /* set group duty cycle control to default */ + i2c_smbus_write_byte_data(client, 0x06, 0xFF); + /* set group frequency to default */ + i2c_smbus_write_byte_data(client, 0x07, 0x00); + /* + * set LEDx individual brightness and group dimming/blinking + * can be controlled by * its PWMx register and GRPPWM registers. + */ + i2c_smbus_write_byte_data(client, 0x08, 0xFF); + /* low power mode. oscillator off */ + i2c_smbus_write_byte_data(client, 0x00, 0x11); +} + +static int pca963x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + + struct pca963x_data *pca963x; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + ret = -ENODEV; + goto exit; + } + + pca963x = kzalloc(sizeof(struct pca963x_data), GFP_KERNEL); + if (pca963x == NULL) { + ret = -ENOMEM; + goto err_alloc_failed; + } + + INIT_WORK(&pca963x->work, pca963x_work_func); + + pca963x->client = client; + + pca963x->leds[0].name = "blue"; + pca963x->leds[0].brightness = LED_OFF; + pca963x->leds[0].brightness_set = led_brightness_set; + + pca963x->leds[1].name = "green"; + pca963x->leds[1].brightness = LED_OFF; + pca963x->leds[1].brightness_set = led_brightness_set; + + pca963x->leds[2].name = "red"; + pca963x->leds[2].brightness = LED_OFF; + pca963x->leds[2].brightness_set = led_brightness_set; + + pca963x->dirty = 0; + pca963x->status = 0; + + pca963x->data.colors[0] = LED_OFF; + pca963x->data.colors[1] = LED_OFF; + pca963x->data.colors[2] = LED_OFF; + pca963x->data.blink = 0; + pca963x->data.grppwm = 0; + pca963x->data.grpfreq = 0; + i2c_set_clientdata(client, pca963x); + + set_pca963x_default(client); + pca963x->mode = MODE_SLEEP; + + /* blue */ + ret = led_classdev_register(&client->dev, &pca963x->leds[0]); + if (ret < 0) { + printk(KERN_ERR "pca963x: led_classdev_register failed\n"); + goto err_led0_classdev_register_failed; + } + /* green */ + ret = led_classdev_register(&client->dev, &pca963x->leds[1]); + if (ret < 0) { + printk(KERN_ERR "pca963x: led_classdev_register failed\n"); + goto err_led1_classdev_register_failed; + } + /* red */ + ret = led_classdev_register(&client->dev, &pca963x->leds[2]); + if (ret < 0) { + printk(KERN_ERR "pca963x: led_classdev_register failed\n"); + goto err_led2_classdev_register_failed; + } + + ret = device_create_file(&client->dev, &dev_attr_blink); + ret = device_create_file(&client->dev, &dev_attr_grppwm); + ret = device_create_file(&client->dev, &dev_attr_grpfreq); + + return 0; + +err_led2_classdev_register_failed: + led_classdev_unregister(&pca963x->leds[2]); +err_led1_classdev_register_failed: + led_classdev_unregister(&pca963x->leds[1]); +err_led0_classdev_register_failed: + led_classdev_unregister(&pca963x->leds[0]); +err_alloc_failed: + kfree(pca963x); +exit: + return ret; +} + +static int pca963x_suspend(struct i2c_client *client, pm_message_t mesg) +{ + flush_scheduled_work(); + return 0; +} + +static int pca963x_remove(struct i2c_client *client) +{ + struct pca963x_data *pca963x = i2c_get_clientdata(client); + + cancel_work_sync(&pca963x->work); + device_remove_file(&client->dev, &dev_attr_blink); + device_remove_file(&client->dev, &dev_attr_grppwm); + device_remove_file(&client->dev, &dev_attr_grpfreq); + set_pca963x_default(client); + led_classdev_unregister(&pca963x->leds[0]); + led_classdev_unregister(&pca963x->leds[1]); + led_classdev_unregister(&pca963x->leds[2]); + i2c_detach_client(client); + + kfree(pca963x); + return 0; +} + +static const struct i2c_device_id pca963x_id[] = { + { "pca963x", 0 }, + { } +}; + +static struct i2c_driver pca963x_driver = { + .driver = { + .name = "pca963x", + }, + .probe = pca963x_probe, + .suspend = pca963x_suspend, + .remove = pca963x_remove, + .id_table = pca963x_id, +}; + +static int __init pca963x_init(void) +{ + return i2c_add_driver(&pca963x_driver); +} + +static void __exit pca963x_exit(void) +{ + i2c_del_driver(&pca963x_driver); +} + +MODULE_AUTHOR("Shan-Fu Chiou "); +MODULE_DESCRIPTION("pca963x driver"); +MODULE_LICENSE("GPL"); + +module_init(pca963x_init); +module_exit(pca963x_exit); diff --git a/drivers/i2c/chips/portofino.c b/drivers/i2c/chips/portofino.c new file mode 100644 index 00000000000000..a87fac7a520548 --- /dev/null +++ b/drivers/i2c/chips/portofino.c @@ -0,0 +1,236 @@ +/* + * * Monahans Portofino PMIC Management Routines + * * + * * Copyright (C) 2009, Marvell Corporation(bin.yang@marvell.com). + * * + * * Author: Bin Yang + * * Yael Sheli Chemla + * * + * * This software program is licensed subject to the GNU General Public License + * * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORTOFINO_REG_NUM 0x1C + +static struct i2c_client *g_client; + +int portofino_read(u8 reg, u8 *pval) +{ + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + ret = i2c_smbus_read_byte_data(g_client, reg); + if (ret >= 0) { + *pval = ret; + status = 0; + } else { + status = -EIO; + } + + return status; +} +EXPORT_SYMBOL(portofino_read); + +int portofino_write(u8 reg, u8 val) +{ + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + ret = i2c_smbus_write_byte_data(g_client, reg, val); + if (ret == 0) + status = 0; + else + status = -EIO; + + return status; +} +EXPORT_SYMBOL(portofino_write); + +static int portofino_initchip(void) +{ + return 0; +} + +int portofino_set_vibrator(unsigned char value) +{ + return 0; +} +EXPORT_SYMBOL(portofino_set_vibrator); + +#ifdef CONFIG_PM +static int portofino_suspend(struct i2c_client *client, pm_message_t state) +{ + return 0; +} + +static int portofino_resume(struct i2c_client *client) +{ + return 0; +} +#else +#define portofino_suspend NULL +#define portofino_resume NULL +#endif + +#ifdef CONFIG_PROC_FS +#define PORTOFINO_PROC_FILE "driver/portofino" +static struct proc_dir_entry *portofino_proc_file; +static int index; + +static ssize_t portofino_proc_read(struct file *filp, + char *buffer, size_t length, loff_t *offset) +{ + u8 reg_val; + + if ((index < 0) || (index > PORTOFINO_REG_NUM)) + return 0; + + portofino_read(index, ®_val); + printk(KERN_INFO "register 0x%x: 0x%x\n", index, reg_val); + return 0; +} + +static ssize_t portofino_proc_write(struct file *filp, + const char *buff, size_t len, loff_t *off) +{ + u8 reg_val; + char messages[256], vol[256]; + u32 l; + static int init_led; + + if (len > 256) + len = 256; + + if (copy_from_user(messages, buff, len)) + return -EFAULT; + + if ('-' == messages[0]) { + /* set the register index */ + memcpy(vol, messages+1, len-1); + index = (int) simple_strtoul(vol, NULL, 16); + }else if ('l' == messages[0]) { + if(!init_led) { + init_led = 1; + portofino_read(0x7, ®_val); + portofino_write(0x7, reg_val|0x2); + portofino_write(0xc, 0xff); + } + /* set the register index */ + memcpy(vol, messages+1, len-1); + l = (int) simple_strtoul(vol, NULL, 16); + printk(KERN_INFO "Set LED-%d to 0x%x\n", + (l&0xf00)>>8, (l&0x1f)); + if(l&0xff) + portofino_write(0xd+((l&0xf00)>>8), (l&0x1f)+0x20); + else + portofino_write(0xd+((l&0xf00)>>8), 0x0); + } else { + /* set the register value */ + reg_val = (int)simple_strtoul(messages, NULL, 16); + portofino_write(index, reg_val & 0xFF); + } + + return len; +} + +static struct file_operations portofino_proc_ops = { + .read = portofino_proc_read, + .write = portofino_proc_write, +}; + +static void create_portofino_proc_file(void) +{ + portofino_proc_file = create_proc_entry(PORTOFINO_PROC_FILE, 0644, NULL); + if (portofino_proc_file) { + portofino_proc_file->owner = THIS_MODULE; + portofino_proc_file->proc_fops = &portofino_proc_ops; + } else + printk(KERN_INFO "proc file create failed!\n"); +} + +static void remove_portofino_proc_file(void) +{ + extern struct proc_dir_entry proc_root; + remove_proc_entry(PORTOFINO_PROC_FILE, &proc_root); +} + +#endif +static int __devinit portofino_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + int chip_id; + + chip_id = i2c_smbus_read_byte_data(client, PORTOFINO_ID); + if (chip_id < 0) { + printk(KERN_WARNING "portofino unavailable!\n"); + return -ENXIO; + } else { + printk(KERN_INFO "portofino(chip id:0x%02x) detected.\n", chip_id); + } + + g_client = client; + + ret = portofino_initchip(); + if (ret != 0) + printk(KERN_WARNING "Initialize Portofino failed with ret 0x%x\n", + ret); + +#ifdef CONFIG_PROC_FS + create_portofino_proc_file(); +#endif + return 0; +} + +static int portofino_remove(struct i2c_client *client) +{ +#ifdef CONFIG_PROC_FS + remove_portofino_proc_file(); +#endif + return 0; +} + +static const struct i2c_device_id portofino_id[] = { + { "portofino", 0 }, + { } +}; + +static struct i2c_driver portofino_driver = { + .driver = { + .name = "portofino", + }, + .id_table = portofino_id, + .probe = portofino_probe, + .remove = portofino_remove, + .suspend = portofino_suspend, + .resume = portofino_resume, +}; + +static int __init portofino_init(void) +{ + return i2c_add_driver(&portofino_driver); +} + +static void __exit portofino_exit(void) +{ + i2c_del_driver(&portofino_driver); +} + +subsys_initcall(portofino_init); +module_exit(portofino_exit); + +MODULE_DESCRIPTION("Portofino Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/i2c/chips/sanremo.c b/drivers/i2c/chips/sanremo.c new file mode 100644 index 00000000000000..465276e2ee1311 --- /dev/null +++ b/drivers/i2c/chips/sanremo.c @@ -0,0 +1,1414 @@ +/* + * * Monahans Sanremo PMIC Management Routines + * * + * * + * * Copyright (C) 2009, Marvell Corporation + * * Author: Bin Yang + * * Yael Sheli Chemla + * * + * * This software program is licensed subject to the GNU General Public License + * * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SANREMO_REG_NUM 0xef +static struct input_dev *onkey_input_dev; + +extern int get_pm_state(void); +static struct power_supply_module *sanremo_power_module; +static unsigned int event; +static struct i2c_client *g_client; +static int charging_status; +static int usb_connect; +DECLARE_WAIT_QUEUE_HEAD(charging_display_wait); + +static int sanremo_version; +struct timer_list on_key_timer; +static int sanremo_on_key(int enable); +#define ONKEY_TIMER_TIMEOUT (8*HZ) + +int sanremo_get_headset_state(void) +{ + u8 tmp; + + if (sanremo_version < 0x48) { + sanremo_read(SANREMO_STATUS, &tmp); + return (tmp & SANREMO_MICIN_STATUS) ? 1 : 0; + } else { + sanremo_read(SANREMO_STATUS_B0, &tmp); + return (tmp & SANREMO_HEADSET_STATUS) ? 0 : 1; + } +} + +int sanremo_read(u8 reg, u8 *pval) +{ + struct sanremo_platform_data *pdata = g_client->dev.platform_data; + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -ENODEV; + + spin_lock(&pdata->lock); + disable_irq(g_client->irq); + ret = i2c_smbus_read_byte_data(g_client, reg); + enable_irq(g_client->irq); + if (ret >= 0) { + *pval = ret; + status = 0; + } else + status = -EIO; + spin_unlock(&pdata->lock); + + return status; +} +EXPORT_SYMBOL(sanremo_read); + +int sanremo_write(u8 reg, u8 val) +{ + struct sanremo_platform_data *pdata = g_client->dev.platform_data; + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -ENODEV; + + spin_lock(&pdata->lock); + disable_irq(g_client->irq); + ret = i2c_smbus_write_byte_data(g_client, reg, val); + enable_irq(g_client->irq); + if (ret == 0) + status = 0; + else + status = -EIO; + spin_unlock(&pdata->lock); + + return status; +} +EXPORT_SYMBOL(sanremo_write); + +int sanremo_enable_pen_down_irq(int enable) +{ + u8 tmp; + if(enable) { + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + if(!(tmp & SANREMO_PEN_MASK)) { + tmp |= SANREMO_PEN_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE3, tmp); + } + } else { + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + if(tmp & SANREMO_PEN_MASK) { + tmp &= ~SANREMO_PEN_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE3, tmp); + } + } + return 0; +} + +int sanremo_enable_mic_irq(int enable) +{ + u8 tmp; + if (enable) { + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + if (!(tmp & SANREMO_MICIN_MASK)) { + tmp |= SANREMO_MICIN_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE3, tmp); + } + } else { + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + if (tmp & SANREMO_MICIN_MASK) { + tmp &= ~SANREMO_MICIN_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE3, tmp); + } + } + return 0; +} + +int sanremo_enable_headset_irq(int enable) +{ + u8 tmp; + if (enable) { + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + if (!(tmp & SANREMO_HEADSET_MASK)) { + tmp |= SANREMO_HEADSET_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE3, tmp); + } + } else { + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + if (tmp & SANREMO_HEADSET_MASK) { + tmp &= ~SANREMO_HEADSET_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE3, tmp); + } + } + return 0; +} + +int sanremo_tsi_poweron(void) +{ + /*done in platform init*/ + return 0; +} + +int sanremo_tsi_poweroff(void) +{ + /*don't disable GPADC because CP will use it*/ + return 0; +} + +int sanremo_tsi_readxy(u16 *x, u16 *y, int *pen_state) +{ + u8 meas1, meas2; + + sanremo_read(SANREMO_TSIX_MEAS1, &meas1); + sanremo_read(SANREMO_TSIX_MEAS2, &meas2); + *pen_state = (meas2 & (1<<6))? 1 : 0; + if(*pen_state) { + *x = (meas1 << 4) + (meas2 & 0xf); + sanremo_read(SANREMO_TSIY_MEAS1, &meas1); + sanremo_read(SANREMO_TSIY_MEAS2, &meas2); + *y = (meas1 << 4) + (meas2 & 0xf); + } + return 0; +} + +int sanremo_tsi_enable_pen(int pen_en) +{ + u8 tmp; + if(pen_en) { + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + tmp |= 0x08; + sanremo_write(SANREMO_MEAS_ENABLE3, tmp); + } else { + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + tmp &= ~0x08; + sanremo_write(SANREMO_MEAS_ENABLE3, tmp); + } + return 0; +} + +int sanremo_tsi_enable_mic(int mic_en) +{ + u8 tmp; + if (sanremo_version < 0x48) { + if (mic_en) { + sanremo_read(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_MIC_BUTTON_DETECTION, &tmp); + tmp |= 0x27; + sanremo_write(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_MIC_BUTTON_DETECTION, tmp); + } else { + sanremo_read(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_MIC_BUTTON_DETECTION, &tmp); + tmp &= ~0x27; + sanremo_write(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_MIC_BUTTON_DETECTION, tmp); + } + } else { + if (mic_en) { + sanremo_read(SANREMO_AUDIO_MIC_BUTTON_DETECTION_B0, &tmp); + tmp |= 0x27; + sanremo_write(SANREMO_AUDIO_MIC_BUTTON_DETECTION_B0, tmp); + } else { + sanremo_read(SANREMO_AUDIO_MIC_BUTTON_DETECTION_B0, &tmp); + tmp &= ~0x27; + sanremo_write(SANREMO_AUDIO_MIC_BUTTON_DETECTION_B0, tmp); + } + } + return 0; +} + +int sanremo_tsi_enable_headset(int headset_en) +{ + u8 tmp; + if (sanremo_version < 0x48) { + if (headset_en) { + sanremo_read(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_HEADSET_DETECTION, &tmp); + tmp |= 0x01; + sanremo_write(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_HEADSET_DETECTION, tmp); + } else { + sanremo_read(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_HEADSET_DETECTION, &tmp); + tmp &= ~0x01; + sanremo_write(SANREMO_AUDIO_REG_BASE + SANREMO_AUDIO_HEADSET_DETECTION, tmp); + } + } else { + if (headset_en) { + sanremo_read(SANREMO_AUDIO_HEADSET_DETECTION_BO, &tmp); + tmp |= 0x01; + sanremo_write(SANREMO_AUDIO_HEADSET_DETECTION_BO, tmp); + } else { + sanremo_read(SANREMO_AUDIO_HEADSET_DETECTION_BO, &tmp); + tmp &= ~0x01; + sanremo_write(SANREMO_AUDIO_HEADSET_DETECTION_BO, tmp); + } + } + return 0; +} + +int sanremo_tsi_enable_tsi(int tsi_en) +{ + u8 tmp; + if(tsi_en) { + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + tmp |= 0x30; + sanremo_write(SANREMO_MEAS_ENABLE3, tmp); + } else { + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + tmp &= ~0x30; + sanremo_write(SANREMO_MEAS_ENABLE3, tmp); + } + return 0; +} + +#define VIBRA_OFF_VALUE 0 +#define VIBRA_ON_VALUE 0x30 /* you can choose the value of bit6 ~ bit1 */ + /* to change the freq of the vibrator */ +int sanremo_set_vibrator(unsigned char value) +{ + if(value == 0) { + sanremo_write(SANREMO_VIBRA_SET, 0x00); + sanremo_write(SANREMO_VIBRA_PWM, 0x00); + } else { + sanremo_write(SANREMO_VIBRA_PWM, 0xc0); + sanremo_write(SANREMO_VIBRA_SET, 0x01|((value&0x7)<<1)); + } + return 0; +} +EXPORT_SYMBOL(sanremo_set_vibrator); + +int sanremo_usb_connect(void) +{ + return usb_connect; +} +EXPORT_SYMBOL(sanremo_usb_connect); + +int sanremo_get_vbat(void) +{ + u8 tmp; + unsigned int val; + + sanremo_read(SANREMO_VBAT_MEAS2, &tmp); + val = tmp&0xf; + sanremo_read(SANREMO_VBAT_MEAS1, &tmp); + val += tmp<<4; + + return val*131836/100000; +} + +static int sanremo_charging_display(void *data) +{ + u8 tmp; + u8 save; + + while(1) { + wait_event(charging_display_wait, charging_status); + tmp = 0; + portofino_read(0xd, &save); + while(charging_status) { + portofino_write(0xd, tmp?save:0); + tmp = !tmp; + msleep(500); + } + portofino_write(0xd, save); + } + return 0; +} + +static void sanremo_start_charging(void) +{ + u8 tmp; + unsigned int val; + + /*check for battery*/ + sanremo_read(SANREMO_STATUS, &tmp); + if(!(tmp & SANREMO_BAT_STATUS)) { + printk(KERN_INFO "Battery is NOT detected, ignore charging\n"); + return; + }else { + printk(KERN_INFO "Battery is detected\n"); + } + + /*check charging*/ + sanremo_read(SANREMO_STATUS, &tmp); + if(!(tmp & SANREMO_CHG_STATUS)) { + printk(KERN_INFO "Charger cable is NOT detected\n"); + return; + }else { + + printk(KERN_INFO "Charger cable is detected\n"); + } + + val = sanremo_get_vbat(); + + if(val < 3850) { + printk(KERN_INFO "Voltage is about %dmv, start charging\n", val); + + /*enable TIT and IBAT measurement*/ + sanremo_read(SANREMO_MEAS_ENABLE1, &tmp); + sanremo_write(SANREMO_MEAS_ENABLE1, tmp|SANREMO_MEAS_EN1_TINT); + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + sanremo_write(SANREMO_MEAS_ENABLE3, tmp|SANREMO_MEAS_EN3_IBAT); + /*set a off time*/ + if (sanremo_version < 0x48) { + sanremo_write(SANREMO_MEAS_OFF_TIME1, 0x41); + sanremo_read(SANREMO_MEAS_OFF_TIME2, &tmp); + sanremo_write(SANREMO_MEAS_OFF_TIME2, (tmp&0x3f)|0x6c); + } else { + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + sanremo_write(SANREMO_MEAS_ENABLE3, tmp|SANREMO_MEASOFFTIME1_BAT_DET_EN_B0); + sanremo_write(SANREMO_MEAS_OFF_TIME1, 0x80); + sanremo_write(SANREMO_MEAS_OFF_TIME2, (tmp&0x7f)|0xd8); + } + /* enable GPADC FSM */ + sanremo_read(SANREMO_GPADC_MISC1, &tmp); + sanremo_write(SANREMO_GPADC_MISC1, tmp|SANREMO_GPADC_MISC1_GPFSM_EN); + /*enable interrupts*/ + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + sanremo_write(SANREMO_INTERRUPT_ENABLE3, + tmp|SANREMO_CHG_FAIL_MASK|SANREMO_CHG_DONE_MASK| + SANREMO_CHG_IOVER_MASK); + + /* set pre-regulator to 1000mA & Vsys to 4.5v*/ + portofino_write(PORTOFINO_PREREG1,0x09); + /*set the end of charge value and enable s ILIM FSM*/ + sanremo_write(SANREMO_CHG_CTRL1, 0x30); + /*set current limit at 550 mA*/ + sanremo_write(SANREMO_CHG_CTRL2, 0x2a); + /*set the max charging time to 90 min*/ + sanremo_write(SANREMO_CHG_CTRL3, 0x30); + /*enable monitoring*/ + sanremo_write(SANREMO_CHG_CTRL4, 0x40); + + sanremo_write(SANREMO_GPADC0_MEAS1, 0x82); + + charging_status = 1; + wake_up(&charging_display_wait); + + /*enable fast charing*/ + sanremo_write(SANREMO_CHG_CTRL1, 0x32); + + /*set the temperature threshold */ + sanremo_write(SANREMO_TINT_MEAS1, 0xc0); + sanremo_write(SANREMO_TINT_MEAS2, 0x82); + sanremo_write(SANREMO_GPADC0_MEAS1, 0x7d); + } +} + +static void sanremo_stop_charging(void) +{ + u8 tmp; + + printk("sanremo stop charging\n"); + /*disable TIT and IBAT measurement*/ + sanremo_read(SANREMO_MEAS_ENABLE1, &tmp); + sanremo_write(SANREMO_MEAS_ENABLE1, tmp&(~SANREMO_MEAS_EN1_TINT)); + sanremo_read(SANREMO_MEAS_ENABLE3, &tmp); + sanremo_write(SANREMO_MEAS_ENABLE3, tmp&(~SANREMO_MEAS_EN3_IBAT)); + /*disable GPADC FSM */ + sanremo_read(SANREMO_GPADC_MISC1, &tmp); + sanremo_write(SANREMO_GPADC_MISC1, tmp&(~SANREMO_GPADC_MISC1_GPFSM_EN)); + /*enable interrupts*/ + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &tmp); + sanremo_write(SANREMO_INTERRUPT_ENABLE3, + tmp&(~(SANREMO_CHG_FAIL_MASK|SANREMO_CHG_DONE_MASK| + SANREMO_CHG_IOVER_MASK))); + charging_status = 0; +} + +static void sanremo_charger_init(void) +{ + u8 tmp; + int val; + + /*enable interrupt*/ + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + sanremo_write(SANREMO_INTERRUPT_ENABLE1, + tmp|SANREMO_CHG_MASK); + + + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + + /*detection*/ + sanremo_read(SANREMO_MEAS_OFF_TIME1, &tmp); + sanremo_write(SANREMO_MEAS_OFF_TIME1, tmp|0x1); + if (sanremo_version < 0x48) { + sanremo_read(SANREMO_GPADC_MISC2, &tmp); + sanremo_write(SANREMO_GPADC_MISC2, tmp|0x40); + } else { + sanremo_read(SANREMO_CHG_CTRL6, &tmp); + sanremo_write(SANREMO_CHG_CTRL6, tmp|0x01); + } + + sanremo_write(SANREMO_VBAT_LOW_TH, 0xaa); + sanremo_read(SANREMO_MEAS_ENABLE1, &tmp); + sanremo_write(SANREMO_MEAS_ENABLE1, tmp|SANREMO_MEAS_EN1_VBAT); +/* + sanremo_read(SANREMO_INTERRUPT_ENABLE2, &tmp); + sanremo_write(SANREMO_INTERRUPT_ENABLE2, + tmp|SANREMO_VBAT_MASK); +*/ + + kthread_run(sanremo_charging_display, NULL, "cdisplay"); + +// sanremo_start_charging(); +} + +static void sanremo_turn_off_booster(void) +{ + /* turn off booster by writing 0 to duty cycle string1/2/3 */ + portofino_write(0x03,0x0); + + return; +} + +void sanremo_turn_off_power(void) +{ + u8 tmp; + + printk("turning off power....\n"); + sanremo_read(SANREMO_RESET_OUT, &tmp); + sanremo_write(SANREMO_RESET_OUT, tmp | 0x80); + + return; +} + +static int onkey_report(struct input_dev *idev) +{ + printk("------report key--------\n"); + input_report_key(idev, KEY_POWER, 1); + input_report_key(idev, KEY_POWER, 0); + input_sync(idev); + return 0; +} + +static void sanremo_on_key_event(void) +{ + u8 tmp; + unsigned int val; + + /*check for battery*/ + sanremo_read(SANREMO_STATUS, &tmp); + if(tmp & SANREMO_ONKEY_STATUS) { + printk(KERN_INFO "On key pressed\n"); + sanremo_turn_off_booster(); + sanremo_turn_off_power(); + return; + }else { + printk(KERN_INFO "On key released\n"); + } +} + +static unsigned int sanremo_event_change(void) +{ + unsigned int ret = 0; + u8 status1, status2, status3; + u8 e1, e2, e3; + u8 tmp; + + sanremo_read(SANREMO_INTERRUPT_STATUS1, &status1); + sanremo_read(SANREMO_INTERRUPT_STATUS2, &status2); + sanremo_read(SANREMO_INTERRUPT_STATUS3, &status3); + sanremo_write(SANREMO_INTERRUPT_STATUS1, status1); + sanremo_write(SANREMO_INTERRUPT_STATUS2, status2); + sanremo_write(SANREMO_INTERRUPT_STATUS3, status3); + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &e1); + sanremo_read(SANREMO_INTERRUPT_ENABLE2, &e2); + sanremo_read(SANREMO_INTERRUPT_ENABLE3, &e3); + + if (status1 & SANREMO_EXTON_INT) + ret |= PMIC_EVENT_EXTON; + + if (status1 & SANREMO_CHG_INT) { + ret |= PMIC_EVENT_CHARGER | PMIC_EVENT_VBUS; + sanremo_read(SANREMO_STATUS, &tmp); + usb_connect = !!(tmp & SANREMO_CHG_STATUS); + sanremo_start_charging(); + } + + if (status1 & SANREMO_ONKEY_INT) { + ret |= PMIC_EVENT_ON_KEY; + if (mod_timer(&on_key_timer, jiffies + ONKEY_TIMER_TIMEOUT)) { + /* timer is active already */ + } + } + + /* + if (status2 & SANREMO_VBAT_MASK) { + printk(KERN_INFO "Battery is low.\n"); + sanremo_start_charging(); + } + */ + + if (status2 & SANREMO_VCHG_INT) { + ret |= PMIC_EVENT_VBUS; + } + + if (status2 & SANREMO_GPADC1_INT) + ret |= PMIC_EVENT_TBAT; + + if(status3 & SANREMO_PEN_INT) + ret |= PMIC_EVENT_TOUCH; + + if (status3 & SANREMO_HEADSET_INT) + ret |= PMIC_EVENT_HSDETECT; + + if (status3 & SANREMO_MICIN_INT) + ret |= PMIC_EVENT_MICDETECT; + + if (status3 & SANREMO_HOOK_INT) + ret |= PMIC_EVENT_HOOKSWITCH; + + if (status3 & SANREMO_CHG_FAIL_INT) { + ret |= PMIC_EVENT_CHARGER; + if(charging_status) { + printk("charging failed.\n"); + sanremo_stop_charging(); + } + } + if (status3 & SANREMO_CHG_DONE_INT) { + ret |= PMIC_EVENT_CHARGER; + if(charging_status) { + printk("charging done\n"); + sanremo_stop_charging(); + } + } + if (status3 & SANREMO_CHG_IOVER_INT) { + ret |= PMIC_EVENT_REV_IOVER; + if(charging_status) { + printk("over charging.\n"); + sanremo_stop_charging(); + } + } + + return ret; +} + +static void sanremo_worker(struct work_struct *work) +{ + unsigned long flags; + struct sanremo_platform_data *pdata = g_client->dev.platform_data; + + /* clear the irq */ + pdata->ack_irq(); + event |= sanremo_event_change(); + pmic_event_handle(event); + event = 0; + enable_irq(g_client->irq); +} + +static irqreturn_t sanremo_irq_handler(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct sanremo_platform_data *pdata = client->dev.platform_data; + + disable_irq(client->irq); + schedule_work(&pdata->work); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM +static int sanremo_suspend(struct i2c_client *client, pm_message_t state) +{ + return 0; +} + +static int sanremo_resume(struct i2c_client *client) +{ + return 0; +} +#else +#define sanremo_suspend NULL +#define sanremo_resume NULL +#endif + +/* Get voltage */ +static int get_power_supply_module(int cmd) +{ + int power_module, i; + + if (cmd < VCC_CORE || cmd >= MAX_CMD) { + printk(KERN_WARNING "input wrong command: %d\n", cmd); + return -EINVAL; + } + + for (i = 0; sanremo_power_module[i].command != 0; i++) { + if (sanremo_power_module[i].command == cmd) { + power_module = sanremo_power_module[i].power_module; + break; + } + } + + return power_module; +} + +static int get_sanremo_voltage(int cmd, int *pmv) +{ + int power_module, status = 0, data; + u8 val; + + *pmv = -1; + + power_module = get_power_supply_module(cmd); + + start_calc_time(); + switch (power_module) { + case SAN_BUCK1: + case SAN_BUCK1_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x3f; + *pmv = SANREMO_VBUCK1_MV(data); + } + break; + case SAN_BUCK2: + case SAN_BUCK2_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x3f; + *pmv = SANREMO_VBUCK2_MV(data); + } + break; + case SAN_BUCK3: + case SAN_BUCK3_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x3f; + *pmv = SANREMO_VBUCK3_MV(data); + } + break; + case SAN_LDO1: + status = sanremo_read(SANREMO_LDO1, &val); + if (!status) { + data = val & 0x3f; + *pmv = SANREMO_LDO1_MV(data); + } + break; + case SAN_LDO1_SLEEP: + status = sanremo_read(SANREMO_LDO1, &val); + if (!status) { + data = (val >> 2) & 0x03; + *pmv = SANREMO_LDO1_SLEEP_MV(data); + } + break; + case SAN_LDO2: + case SAN_LDO3: + case SAN_LDO4: + case SAN_LDO7: + case SAN_LDO8: + case SAN_LDO9: + case SAN_LDO14: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x07; + *pmv = SANREMO_LDO2_MV(data); + } + break; + case SAN_LDO2_SLEEP: + case SAN_LDO3_SLEEP: + case SAN_LDO4_SLEEP: + case SAN_LDO7_SLEEP: + case SAN_LDO8_SLEEP: + case SAN_LDO9_SLEEP: + case SAN_LDO14_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = (val >> 3) & 0x07; + *pmv = SANREMO_LDO2_MV(data); + } + break; + case SAN_LDO5: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x03; + *pmv = SANREMO_LDO5_MV(data); + } + break; + case SAN_LDO5_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = (val >> 2) & 0x03; + *pmv = SANREMO_LDO5_SLEEP_MV(data); + } + break; + case SAN_LDO6: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x03; + *pmv = SANREMO_LDO6_MV(data); + } + break; + case SAN_LDO6_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = (val >> 2) & 0x03; + *pmv = SANREMO_LDO6_MV(data); + } + break; + case SAN_LDO10: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x0f; + *pmv = SANREMO_LDO10_MV(data); + } + break; + case SAN_LDO10_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = (val >> 4) & 0x0f; + *pmv = SANREMO_LDO10_MV(data); + } + break; + case SAN_LDO12: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = val & 0x0f; + *pmv = SANREMO_LDO12_MV(data); + } + break; + case SAN_LDO12_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + data = (val >> 4) & 0x0f; + *pmv = SANREMO_LDO10_MV(data); + } + break; + default: + printk(KERN_WARNING "input wrong command: %d\n", cmd); + return -EINVAL; + } + end_calc_time(); + return status; +} + +static int set_sanremo_voltage(int cmd, int mv) +{ + int power_module, status = 0, cnt; + u8 val; + + power_module = get_power_supply_module(cmd); + + start_calc_time(); + switch (power_module) { + case SAN_BUCK1: + status = sanremo_read(SANREMO_MISC1, &val); + if (status) + break; + /* General I2C port can control V_BUCK1 domain */ + val &= ~0x01; + status = sanremo_write(SANREMO_MISC1, val); + if (status) + break; + status = sanremo_write(SANREMO_REG(power_module), + SANREMO_VBUCK1_CNT(mv)); + if (status) + break; + status = sanremo_read(SANREMO_GO, &val); + if (status) + break; + /* enable V_BUCK1 change */ + val |= 0x01; + status = sanremo_write(SANREMO_GO, val); + break; + case SAN_BUCK1_SLEEP: + status = sanremo_write(SANREMO_REG(power_module), + SANREMO_VBUCK1_CNT(mv)); + break; + case SAN_BUCK2: + case SAN_BUCK2_SLEEP: + status = sanremo_write(SANREMO_REG(power_module), + SANREMO_VBUCK2_CNT(mv)); + break; + case SAN_BUCK3: + case SAN_BUCK3_SLEEP: + status = sanremo_write(SANREMO_REG(power_module), + SANREMO_VBUCK3_CNT(mv)); + break; + case SAN_LDO1: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val &= ~0x03; + cnt = SANREMO_LDO1_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO1_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = (val >> 2) & ~0x03; + cnt = SANREMO_LDO1_SLEEP_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO2: + case SAN_LDO3: + case SAN_LDO4: + case SAN_LDO7: + case SAN_LDO8: + case SAN_LDO9: + case SAN_LDO14: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val &= ~0x07; + cnt = SANREMO_LDO2_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO2_SLEEP: + case SAN_LDO3_SLEEP: + case SAN_LDO4_SLEEP: + case SAN_LDO7_SLEEP: + case SAN_LDO8_SLEEP: + case SAN_LDO9_SLEEP: + case SAN_LDO14_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = (val >> 3) & ~0x07; + cnt = SANREMO_LDO2_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO5: + if (mv == 0) { + /* Disable LDO5 output */ + status = sanremo_read(SANREMO_SUPPLIES_EN11, &val); + if (!status) + status = sanremo_write(SANREMO_SUPPLIES_EN11, + val & ~0x80); + return status; + } + + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = val & ~0x03; + cnt = SANREMO_LDO5_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + + /* enable LDO5 output */ + status = sanremo_read(SANREMO_SUPPLIES_EN11, &val); + if (!status) + status = sanremo_write(SANREMO_SUPPLIES_EN11, + val | 0x80); + break; + case SAN_LDO5_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = (val >> 2) & ~0x03; + cnt = SANREMO_LDO5_SLEEP_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO6: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = val & ~0x07; + cnt = SANREMO_LDO6_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO6_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = (val >> 3) & ~0x07; + cnt = SANREMO_LDO6_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO10: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = val & ~0x0f; + cnt = SANREMO_LDO10_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO10_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = (val >> 4) & ~0x0f; + cnt = SANREMO_LDO10_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO12: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = val & ~0x0f; + cnt = SANREMO_LDO12_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + case SAN_LDO12_SLEEP: + status = sanremo_read(SANREMO_REG(power_module), &val); + if (!status) { + val = (val >> 4) & ~0x0f; + cnt = SANREMO_LDO10_CNT(mv); + if (cnt == -1) { + status = -EINVAL; + break; + } + val |= cnt; + status = sanremo_write(SANREMO_REG(power_module), val); + } + break; + default: + printk(KERN_WARNING "input wrong command: %d\n", cmd); + return -EINVAL; + } + end_calc_time(); + return status; +} + +static int is_sanremo_vbus_assert(void) +{ + u8 tmp; + + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + sanremo_write(SANREMO_INTERRUPT_ENABLE1, tmp | SANREMO_CHG_MASK); + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + + sanremo_read(SANREMO_STATUS, &tmp); + if (tmp & SANREMO_CHG_STATUS) + return 1; + + return 0; +} +static int is_sanremo_avbusvld(void) +{ + return 0; +} + +static int is_sanremo_asessvld(void) +{ + return 0; +} + +static int is_sanremo_bsessvld(void) +{ + return 0; +} + +static int is_sanremo_srp_ready(void) +{ + return 0; +} + +static int sanremo_set_pump(int enable) +{ + u8 tmp; + + if(enable) { + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + tmp |= SANREMO_CHG_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE1, tmp); + } else { + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + tmp &= ~SANREMO_CHG_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE1, tmp); + } + return 0; +} + +static int sanremo_on_key(int enable) +{ + u8 tmp; + if(enable) { + init_timer(&on_key_timer); + on_key_timer.function = sanremo_on_key_event; + on_key_timer.data = (long)NULL; + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + tmp |= SANREMO_ONKEY_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE1, tmp); + } else { + sanremo_read(SANREMO_INTERRUPT_ENABLE1, &tmp); + tmp &= ~SANREMO_ONKEY_MASK; + sanremo_write(SANREMO_INTERRUPT_ENABLE1, tmp); + } + return 0; +} + +static int sanremo_set_vbus_supply(int enable, int srp) +{ + u8 tmp; + sanremo_read(SANREMO_MISC1, &tmp); + if (enable) { + tmp |= SANREMO_MISC1_GPIO2_VAL; + } else { + tmp &= ~SANREMO_MISC1_GPIO2_VAL; + } + sanremo_write(SANREMO_MISC1, tmp); + tmp |= SANREMO_MISC1_GPIO2_DIR; + sanremo_write(SANREMO_MISC1, tmp); + + sanremo_read(SANREMO_MISC1, &tmp); + + pr_debug("%s enable %d misc1 %x\n\n", __func__, enable, tmp); + + return 0; +} +static int sanremo_set_usbotg_a_mask(void) +{ + return 0; +} +static int sanremo_set_usbotg_b_mask(void) +{ + return 0; +} + +static struct pmic_ops sanremo_pmic_ops = { + .get_voltage = get_sanremo_voltage, + .set_voltage = set_sanremo_voltage, + + .is_vbus_assert = is_sanremo_vbus_assert, + .is_avbusvld = is_sanremo_avbusvld, + .is_asessvld = is_sanremo_asessvld, + .is_bsessvld = is_sanremo_bsessvld, + .is_srp_ready = is_sanremo_srp_ready, + + .set_pump = sanremo_set_pump, + .set_vbus_supply = sanremo_set_vbus_supply, + .set_usbotg_a_mask = sanremo_set_usbotg_a_mask, + .set_usbotg_b_mask = sanremo_set_usbotg_b_mask, +}; + +#ifdef CONFIG_PROC_FS +#define SANREMO_PROC_FILE "driver/sanremo" +static struct proc_dir_entry *sanremo_proc_file; +static int index; + +static ssize_t sanremo_proc_read(struct file *filp, + char *buffer, size_t length, loff_t *offset) +{ + u8 reg_val; + + if (index == 0xffff) { + int i; + for(i=0; i SANREMO_REG_NUM)) + return 0; + + sanremo_read(index, ®_val); + printk(KERN_INFO "register 0x%x: 0x%x\n", index, reg_val); + return 0; +} + +static ssize_t sanremo_proc_write(struct file *filp, + const char *buff, size_t len, loff_t *off) +{ + u8 reg_val; + char messages[256], vol[256]; + + if (len > 256) + len = 256; + + if (copy_from_user(messages, buff, len)) + return -EFAULT; + + if ('-' == messages[0]) { + /* set the register index */ + memcpy(vol, messages+1, len-1); + index = (int) simple_strtoul(vol, NULL, 16); + }else if ('v' == messages[0]) { + /* set the register index */ + memcpy(vol, messages+1, len-1); + index = (int) simple_strtoul(vol, NULL, 16); + if(index > 5) + index = 5; + sanremo_set_vibrator((unsigned char) index); + }else if ('b' == messages[0]) { + printk("vbat is about %dmv\n", sanremo_get_vbat()); + } else { + /* set the register value */ + reg_val = (int)simple_strtoul(messages, NULL, 16); + sanremo_write(index, reg_val & 0xFF); + } + + return len; +} + +static struct file_operations sanremo_proc_ops = { + .read = sanremo_proc_read, + .write = sanremo_proc_write, +}; + +static void create_sanremo_proc_file(void) +{ + sanremo_proc_file = create_proc_entry(SANREMO_PROC_FILE, 0644, NULL); + if (sanremo_proc_file) { + sanremo_proc_file->owner = THIS_MODULE; + sanremo_proc_file->proc_fops = &sanremo_proc_ops; + } else + printk(KERN_INFO "proc file create failed!\n"); +} + +static void remove_sanremo_proc_file(void) +{ + extern struct proc_dir_entry proc_root; + remove_proc_entry(SANREMO_PROC_FILE, &proc_root); +} + +#endif + +static int sanremo_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sanremo_platform_data *pdata; + int ret; + int chip_id; + int i; + u8 tmp; + + chip_id = i2c_smbus_read_byte_data(client, SANREMO_ID); + if (chip_id < 0) { + printk(KERN_WARNING "sanremo unavailable!\n"); + return -ENXIO; + } else { + printk(KERN_INFO "sanremo(chip id:0x%02x) detected.\n", chip_id); + } + + sanremo_version = chip_id; + g_client = client; + + pdata = client->dev.platform_data; + /* init spinlock */ + spin_lock_init(&pdata->lock); + +#if 1 + /* hw fix*/ + spin_lock(&pdata->lock); + disable_irq(g_client->irq); + i2c_smbus_write_byte(client, 0xfa); + i2c_smbus_write_byte(client, 0xfb); + i2c_smbus_write_byte(client, 0xff); + i2c_smbus_write_byte_data(client, 0xd6, 0x0a); + i2c_smbus_write_byte_data(client, 0xd7, 0x02); + i2c_smbus_write_byte(client, 0xfe); + enable_irq(g_client->irq); + spin_unlock(&pdata->lock); +#endif + + /*init RTC clock*/ + sanremo_write(SANREMO_RTC1, 0x40); + sanremo_read(SANREMO_RTC_MISC2, &tmp); + tmp |= 0x2; + sanremo_write(SANREMO_RTC_MISC2, tmp); + + /* init workqueue */ + INIT_WORK(&pdata->work, sanremo_worker); + + + /* init irq */ + pdata->init_irq(); + /* FIXME: define trigger type in platform data later */ + ret = request_irq(client->irq, sanremo_irq_handler, IRQF_DISABLED, + "Sanremo", client); + if (ret) { + printk(KERN_WARNING "Request IRQ for Sanremo failed, return:%d\n", + ret); + return ret; + } + + pdata->platform_init(); + + for (i = 0; pdata->power_chips[i].power_supply_modules != NULL; i++) { + if (pdata->power_chips[i].chip_id == chip_id) { + sanremo_power_module = \ + pdata->power_chips[i].power_supply_modules; + break; + } + } + if (pdata->power_chips[i].power_supply_modules == NULL) + panic("This Sanremo chip is not supported"); + +#ifdef CONFIG_PROC_FS + create_sanremo_proc_file(); +#endif + + pmic_set_ops(&sanremo_pmic_ops); + +#ifdef CONFIG_TOUCHSCREEN_SANREMO + { + struct platform_device *pdev; + if(pdata->tsi) { + pdev = platform_device_alloc("sanremo_touch", + (int)id->driver_data); + pdev->dev.platform_data = pdata->tsi; + platform_device_add(pdev); + } + } +#endif + + /* enable on key interrupts */ + sanremo_on_key(1); + + sanremo_read(SANREMO_STATUS, &tmp); + usb_connect = !!(tmp & SANREMO_CHG_STATUS); + + sanremo_charger_init(); + + onkey_input_dev = input_allocate_device(); + if (onkey_input_dev == NULL) { + printk(KERN_ERR "%s: failed to allocate input dev\n", + __FUNCTION__); + return -ENOMEM; + } + onkey_input_dev->name = "on-key"; + __set_bit(EV_KEY, onkey_input_dev->evbit); + __set_bit(KEY_POWER, onkey_input_dev->keybit); + __set_bit(EV_REP, onkey_input_dev->evbit); + __set_bit(EV_SYN, onkey_input_dev->evbit); + + ret = input_register_device(onkey_input_dev); + if (ret) { + printk(KERN_ERR + "%s: unabled to register input device, ret = %d\n", + __FUNCTION__, ret); + return ret; + } + + return 0; +} + +int sanremo_enable_headset_detect(void (*func)(unsigned long ), int enable) +{ + int ret = 0; + printk(KERN_INFO "enable headset detect!\n"); + if (sanremo_version < 0x48) { + sanremo_enable_mic_irq(enable); + sanremo_tsi_enable_mic(enable); + if (enable) + ret = pmic_callback_register(PMIC_EVENT_MICDETECT, func); + else + pmic_callback_unregister(PMIC_EVENT_MICDETECT, func); + if (enable && ret < 0) + printk(KERN_INFO "pmic_callback_register PMIC_EVENT_MICDETECT failed\n"); + } else { + sanremo_enable_headset_irq(enable); + sanremo_tsi_enable_headset(enable); + if (enable) + ret = pmic_callback_register(PMIC_EVENT_HSDETECT, func); + else + pmic_callback_unregister(PMIC_EVENT_HSDETECT, func); + if (enable && ret < 0) + printk(KERN_INFO "pmic_callback_register PMIC_EVENT_HSDETECT failed\n"); + } + func(PMIC_EVENT_HSDETECT); +} + +static int sanremo_remove(struct i2c_client *client) +{ + pmic_set_ops(NULL); + +#ifdef CONFIG_PROC_FS + remove_sanremo_proc_file(); +#endif + + free_irq(client->irq, NULL); + + return 0; +} + +static const struct i2c_device_id sanremo_id[] = { + { "sanremo", 0 }, + { } +}; + +static struct i2c_driver sanremo_driver = { + .driver = { + .name = "sanremo", + }, + .id_table = sanremo_id, + .probe = sanremo_probe, + .remove = sanremo_remove, + .suspend = sanremo_suspend, + .resume = sanremo_resume, +}; + +static int __init sanremo_init(void) +{ + return i2c_add_driver(&sanremo_driver); +} + +static void __exit sanremo_exit(void) +{ + flush_scheduled_work(); + i2c_del_driver(&sanremo_driver); +} + +subsys_initcall(sanremo_init); +module_exit(sanremo_exit); + +MODULE_DESCRIPTION("Sanremo Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 07c2cd43109c1e..82e305b05a4adc 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -135,6 +135,13 @@ config INPUT_EVDEV To compile this driver as a module, choose M here: the module will be called evdev. +config CIR + tristate "CIR device" + ---help--- + Say Y here if you want your CIR or Nec remote control. + + If unsure, say Y. + config INPUT_EVBUG tristate "Event debugging" help @@ -161,6 +168,15 @@ config INPUT_APMPOWER To compile this driver as a module, choose M here: the module will be called apm-power. +config INPUT_KEYRESET + tristate "Reset key" + depends on INPUT + ---help--- + Say Y here if you want to reboot when some keys are pressed; + + To compile this driver as a module, choose M here: the + module will be called keyreset. + config XEN_KBDDEV_FRONTEND tristate "Xen virtual keyboard and mouse support" depends on XEN_FBDEV_FRONTEND diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 7ad212d31f99b6..d4d382022e2b7b 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -21,8 +21,10 @@ obj-$(CONFIG_INPUT_MOUSE) += mouse/ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ +obj-$(CONFIG_CIR) += gpio_ir.o obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index f7c5c14ec12ad9..e5bef5d5b52013 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -38,8 +38,8 @@ MODULE_LICENSE("GPL"); static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) { - printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", - dev_name(&handle->dev->dev), type, code, value); + /* printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", */ + /* dev_name(&handle->dev->dev), type, code, value); */ } static int evbug_connect(struct input_handler *handler, struct input_dev *dev, @@ -64,10 +64,10 @@ static int evbug_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_unregister_handle; - printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n", + /* printk(KERN_DEBUG "evbug.c: Connected device: %s (%s at %s)\n", dev_name(&dev->dev), dev->name ?: "unknown", - dev->phys ?: "unknown"); + dev->phys ?: "unknown"); */ return 0; diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 2ee6c7a68bdccf..1fcd6a2e3dc81e 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "input-compat.h" struct evdev { @@ -43,6 +44,8 @@ struct evdev_client { struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + struct wake_lock wake_lock; + char name[28]; }; static struct evdev *evdev_table[EVDEV_MINORS]; @@ -55,6 +58,7 @@ static void evdev_pass_event(struct evdev_client *client, * Interrupts are disabled, just acquire the lock */ spin_lock(&client->buffer_lock); + wake_lock_timeout(&client->wake_lock, 5 * HZ); client->buffer[client->head++] = *event; client->head &= EVDEV_BUFFER_SIZE - 1; spin_unlock(&client->buffer_lock); @@ -72,8 +76,11 @@ static void evdev_event(struct input_handle *handle, struct evdev *evdev = handle->private; struct evdev_client *client; struct input_event event; + struct timespec ts; - do_gettimeofday(&event.time); + ktime_get_ts(&ts); + event.time.tv_sec = ts.tv_sec; + event.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC; event.type = type; event.code = code; event.value = value; @@ -234,6 +241,7 @@ static int evdev_release(struct inode *inode, struct file *file) mutex_unlock(&evdev->mutex); evdev_detach_client(evdev, client); + wake_lock_destroy(&client->wake_lock); kfree(client); evdev_close_device(evdev); @@ -270,6 +278,9 @@ static int evdev_open(struct inode *inode, struct file *file) } spin_lock_init(&client->buffer_lock); + snprintf(client->name, sizeof(client->name), "%s-%d", + dev_name(&evdev->dev), task_tgid_vnr(current)); + wake_lock_init(&client->wake_lock, WAKE_LOCK_SUSPEND, client->name); client->evdev = evdev; evdev_attach_client(evdev, client); @@ -335,6 +346,8 @@ static int evdev_fetch_next_event(struct evdev_client *client, if (have_event) { *event = client->buffer[client->tail++]; client->tail &= EVDEV_BUFFER_SIZE - 1; + if (client->head == client->tail) + wake_unlock(&client->wake_lock); } spin_unlock_irq(&client->buffer_lock); diff --git a/drivers/input/gpio_ir.c b/drivers/input/gpio_ir.c new file mode 100644 index 00000000000000..d5d5ab188ff5ee --- /dev/null +++ b/drivers/input/gpio_ir.c @@ -0,0 +1,497 @@ +/* + * linux/drivers/input/gpio_ir.c + * + * Support for the Aspenite /Zyloniteii /Teton development Platforms + * + * Copyright (C) 2008 Marvell International Ltd. + * + * 2008-010-12: Ofer Zaarur + * + * 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 + + +/* Only and only one XXX_READOUT_IRCBUFFER can be defined */ +#define MARVELL_READOUT_IRCBUFFER + +/* + * Nec SC33 code: + * ___ _-_-_-_-_-_-_-_~~ + * Pre 0 1 1 1 0 1 1 0 1 0 0 0 1 0 0 1 + */ + + +/* + * for Nex protocol analysis + */ +#define ENDED 100000 /*uSec*/ +#define END_MIN 35000 /*uSec*/ +#define END_MAX 42000 /*uSec*/ +#define PREAM_MIN 12000 /*uSec*/ +#define PREAM_MAX 21000 /*uSec*/ +#define ONE_MIN 2100 /*uSec*/ +#define ONE_MAX 4100 /*uSec*/ +#define ZERO_MIN 800 /*uSec*/ +#define ZERO_MAX 2100 /*uSec*/ +#define word_size 32 /*word size according to the protocol */ +#define calibration_n 2167 /* timer resolution eq=1500 OS timer HZ */ + +struct input_dev *cir_input_dev; /* Representation of an input device */ +static struct platform_device *cir_dev_in; /* Device structure */ + +/* + * Circular buffer + */ +#define CIRC_BUFF_MASK 0x3ff +#define CIRC_BUFF_LENGTH 0x400 +unsigned int cbuffer[CIRC_BUFF_LENGTH]; +unsigned int cb_start=0, cb_end=0; +int ir_pin; + + +unsigned int ir_code_bit = 0; + +/* + * Table of IR-signal-code and key + */ +ir_key_table_t marvell_nec_ir_key_table[] = { + + {0x00, MV_IR_KEY_DIGIT_1}, + {0x01, MV_IR_KEY_DIGIT_2}, + {0x02, MV_IR_KEY_DIGIT_3}, + {0x03, MV_IR_KEY_DIGIT_4}, + {0x04, MV_IR_KEY_DIGIT_5}, + {0x05, MV_IR_KEY_DIGIT_6}, + {0x06, MV_IR_KEY_DIGIT_7}, + {0x07, MV_IR_KEY_DIGIT_8}, + {0x08, MV_IR_KEY_DIGIT_9}, + {0x09, MV_IR_KEY_DIGIT_10}, + {0x0A, MV_IR_KEY_DIGIT_11}, + {0x0B, MV_IR_KEY_DIGIT_12}, + {0x0C, MV_IR_KEY_DIGIT_13}, + {0x0D, MV_IR_KEY_DIGIT_14}, + {0x0E, MV_IR_KEY_DIGIT_15}, + {0x0F, MV_IR_KEY_DIGIT_16}, + {0x10, MV_IR_KEY_DIGIT_17}, + {0x11, MV_IR_KEY_DIGIT_18}, + {0x12, MV_IR_KEY_DIGIT_19}, + {0x13, MV_IR_KEY_DIGIT_20}, + {0x14, MV_IR_KEY_DIGIT_21}, + {0x15, MV_IR_KEY_DIGIT_22}, + {0x16, MV_IR_KEY_DIGIT_23}, + {0x17, MV_IR_KEY_DIGIT_24}, + {0x18, MV_IR_KEY_DIGIT_25}, + {0x19, MV_IR_KEY_DIGIT_26}, + {0x1A, MV_IR_KEY_DIGIT_27}, + {0x1B, MV_IR_KEY_DIGIT_28}, + {0x1C, MV_IR_KEY_DIGIT_29}, + {0x1D, MV_IR_KEY_DIGIT_30}, + {0x1E, MV_IR_KEY_DIGIT_31}, + {0x1F, MV_IR_KEY_DIGIT_32}, + {0x20, MV_IR_KEY_DIGIT_33}, + {0, MV_IR_KEY_NULL}, + +}; + +ir_key_table_t *ir_key_table = marvell_nec_ir_key_table; + +unsigned int ir_key = MV_IR_KEY_NULL; +unsigned int ir_key_recvflag = 0; + +/* + * structure to store the time when interrupt comes + */ +typedef struct system_time { + unsigned int jiff; + unsigned int base; +} system_time_t; +system_time_t len_time = {0, 0}; + +/* + * analyse the level length based on NEC protocol. + * NEC ir transmitter timing: + * Preamble: 9ms/4.5ms/0.56ms, Bit1: 1.69ms/0.56ms, Bit0: 0.56ms/0.56ms. + * Hold: 9ms/2.25ms/0.56ms + */ +static unsigned int word = 0, dont_send_more=0 , count_word = 0 , preamble = 0; +static unsigned int end = 0, i=0; + + +/* + *Analyse the address and key + * + */ +static void analyseword (int word) +{ +/* printk(KERN_DEBUG "%x\n", word); */ +if ((word << CODE_SIZE) == (CUSTOM_CODE << CODE_SIZE) && !dont_send_more) { + if ((word & 0xff000000)==(~(word << 8) & 0xff000000)){ + ir_key = MV_IR_KEY_NULL; + i = 0; + /* check for the key */ + while (ir_key_table[i].ir_key != MV_IR_KEY_NULL) { + if ((ir_key_table[i].ir_encode) == ((word & 0xff0000) + >> CODE_SIZE)) { + ir_key = ir_key_table[i].ir_key; +/* printk(KERN_INFO "Key %d was found\n",ir_key);*/ + input_report_key(cir_input_dev, ir_key , 1); + input_report_key(cir_input_dev, ir_key , 0); + cb_start = cb_end; + dont_send_more = 1; + break; + } + + i++; + } + /* key not found, just noises */ + } +} +else { uart_putc('N'); +} +} +static void clear_ircbuffer (void) +{ + unsigned int event_len; + + while ((cb_start != cb_end) && (((cb_start+1) & CIRC_BUFF_MASK) != cb_end)) { + event_len = cbuffer[cb_start]*1000; + /*printk(KERN_INFO "%u\n",event_len); */ + cb_start++; + cb_start &= CIRC_BUFF_MASK; + if ((event_len > ZERO_MIN) && (event_len < ZERO_MAX)) { + uart_putc('0'); + if (preamble == 1) { + count_word++; + } + if (count_word == word_size) { + analyseword (word); + count_word = 0; + preamble = 0; + word = 0; + } + continue; + } + if ((event_len >= ONE_MIN) && (event_len < ONE_MAX)) { + uart_putc('1'); + if (preamble == 1) { + word |= 1 << count_word; + count_word++; + } + if (count_word == word_size) { + analyseword (word); + count_word = 0; + preamble = 0; + word = 0; + } + continue; + } + if ((event_len > PREAM_MIN) && (event_len < PREAM_MAX)) { + uart_putc('P'); + preamble = 1; + count_word = 0; + end = 0; + continue; + } + if ((event_len > END_MIN) && (event_len < END_MAX)){ + uart_putc('S'); + continue; + } + if (event_len > ENDED){ + uart_putc('E'); + dont_send_more = 0; + continue; + } + } + +} +/* + * This function should be called in an independent thread. + * It reads out bit-wised code. + */ +void readout_ircbuffer(unsigned long nodata) +{ + clear_ircbuffer(); + return; +} + +/* + * Store the event length + */ +static void store_ircbuffer(unsigned int event_len) +{ + cbuffer[cb_end] = event_len; + cb_end++; + cb_end &= CIRC_BUFF_MASK; + + /* + * if cb_start is equal to cb_end, there's no new data in the cbuffer. + * Therefore, we have to avoid that, because we're writing a new data. + * + */ + if (cb_end == cb_start) { + cb_start++; + cb_start &= CIRC_BUFF_MASK; + } +} + +void cir_free(struct cir_device *cir) +{ +/*free IRQ */ +} +EXPORT_SYMBOL(cir_free); + +DECLARE_TASKLET(clearbuf, readout_ircbuffer, 0); + +static irqreturn_t cir_interrupt(int irq, void *dev_id) +{ + unsigned int event_len; + + /* + * + * Calculate the length of the event + * + */ + len_time.base = len_time.jiff; + len_time.jiff = timer_services_counter_read(COUNTER_0) / + calibration_n; + event_len = (len_time.jiff > len_time.base) ? + (len_time.jiff - len_time.base) : (len_time.jiff + + 0xffffffff - len_time.base); + store_ircbuffer(event_len); + if (event_len > (END_MIN/1000)) { + tasklet_schedule(&clearbuf); + } + return 0; +} + +/** + * cir_enable - enable the cir -unmask + * + * Turn on the cir. + */ +void cir_enable(struct cir_dev *dev) +{ + struct cir_device *cir = dev->cir; + __raw_writel(GPIO_BIT(IR_SHIFT), cir->mmio_base + GAPMASK0); +} + +EXPORT_SYMBOL(cir_enable); + +/** + * cir_disable - shut down the cir - mask + * + * Turn off the cir port. + */ +void cir_disable(struct cir_dev *dev) +{ + struct cir_device *cir = dev->cir; + + __raw_writel(GPIO_BIT(IR_SHIFT), cir->mmio_base + GCPMASK0); +} + +EXPORT_SYMBOL(cir_disable); + +#ifdef CONFIG_PM +/* + * Basic power management. + */ + +static int cir_suspend(struct platform_device *pdev, pm_message_t state) +{ + +/* require timer services can be disabled */ + return 0; +} + +static int cir_resume(struct platform_device *pdev) +{ + /* require timer services resume to be enabled */ + + return 0; +} +#else +#define cir_suspend NULL +#define cir_resume NULL +#endif + +static int __devinit cir_probe(struct platform_device *pdev) +{ + + struct cir_device *cir; + struct resource *res; + int ret = 0; + cir = kzalloc(sizeof(struct cir_device), GFP_KERNEL); + if (cir == NULL) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + /* Allocate an input device data structure */ + cir_input_dev = input_allocate_device(); + if (!cir_input_dev) { + return -ENOMEM; + } + + cir->input_dev = cir_input_dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev,"no memory resource defined\n"); + ret = -ENODEV; + } + cir->mmio_base = ioremap_nocache(res->start, SZ_256); + printk(KERN_DEBUG "cir->address 0x%p", cir->mmio_base); + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "no memory resource defined\n"); + ret = -ENODEV; + } + + cir->irq = platform_get_irq(pdev, 0); + printk(KERN_DEBUG "cir->irq : %d \n", cir->irq); + if (cir->irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + ret = -ENODEV; + goto err_free_io; + } + + ir_pin = IRQ_TO_GPIO(cir->irq); + ret = request_irq(cir->irq ,cir_interrupt ,IRQF_TRIGGER_FALLING ,"CIR" + ,cir); + printk(KERN_DEBUG "ret from request irq. %d\n", ret); + printk(KERN_DEBUG "the is set to irq. %d\n", cir->irq); + if (ret)goto + err_free_io; + platform_set_drvdata(pdev, cir); + printk(KERN_DEBUG " Initialize CIR driver completed \n"); + /* setup input device */ + cir_input_dev->name = "aspenite_cir"; + cir_input_dev->phys = "aspenite_cir/input2"; + cir_input_dev->dev.parent = &pdev->dev; + cir->pdev = pdev->dev.platform_data; + cir_input_dev->evbit[0] = BIT(EV_KEY); + + /* Announce that the CIR will generate key map */ + ir_key = MV_IR_KEY_NULL; + for (i=0; i< 33;) { + ir_key = ir_key_table[i].ir_key; + set_bit(ir_key, cir_input_dev->keybit); + i++; + } + printk(KERN_DEBUG " key map was set \n"); + /* Register with the input subsystem */ + if (input_register_device(cir_input_dev)) + printk(KERN_DEBUG "can't register CIR_input Driver.\n"); + printk(KERN_DEBUG "CIR_input Driver Initialized.\n"); + + return 0; + +err_free_io: + return ret; +} + +static int __devexit cir_remove(struct platform_device *pdev) +{ + struct cir_device *cir; + + cir = platform_get_drvdata(pdev); + if (cir == NULL) + return -ENODEV; + kfree(cir); + return 0; +} + +static int __devinit aspenite_cir_probe(struct platform_device *pdev) +{ + return cir_probe(pdev); +} + +static struct platform_driver aspenite_cir_driver = { + .driver = { + .name = "aspenite-cir", + }, + .probe = aspenite_cir_probe, + .remove = __devexit_p(cir_remove), + .suspend = cir_suspend, + .resume = cir_resume, +}; + +/** + * cir_init - setup the cir port + * + * initialise cir. + * + */ + +int __init cir_init(struct cir_dev *dev) +{ + if (gpio_request(IR_PIN, "IR_PIN")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", IR_PIN); + return -EIO; + } + +/* Direction is input */ + gpio_direction_input(IR_PIN); + return 0; +} + + +/** + * cir_exit + * + * exit cir driver. + * + */ + +void cir_exit(struct cir_dev *dev) +{ + + cir_disable(dev); + +/* Unregister from the input subsystem */ + input_unregister_device(cir_input_dev); + +/* Unregister driver */ + platform_device_unregister(cir_dev_in); +return; +} + +static int __init aspenite_cir_init(void) +{ + int ret = 0; + ret = platform_driver_register(&aspenite_cir_driver); + if (ret) { + printk(KERN_DEBUG KERN_ERR "failed to register \ + aspenite_cir_driver"); + return ret; + } + return ret; +} + +static void __exit aspenite_cir_exit(void) +{ + platform_driver_unregister(&aspenite_cir_driver); +} + +late_initcall(aspenite_cir_init); +module_exit(aspenite_cir_exit); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 64c102355f5321..e9efca8993b0fe 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -323,7 +323,7 @@ config KEYBOARD_OPENCORES config KEYBOARD_PXA27x tristate "PXA27x/PXA3xx keypad support" - depends on PXA27x || PXA3xx + depends on PXA27x || PXA3xx || ARCH_MMP help Enable support for PXA27x/PXA3xx keypad controller. diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index 0e53b3bc39afe4..cfd46318482b94 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -25,14 +25,18 @@ #include #include #include -#include +#include #include #include #include +#include #include -#include +#include +#include + +#undef CONFIG_PM /* * Keypad Controller registers */ @@ -108,82 +112,73 @@ struct pxa27x_keypad { int irq; - unsigned short keycodes[MAX_KEYPAD_KEYS]; - int rotary_rel_code[2]; + /* matrix key code map */ + unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM]; /* state row bits of each column scan */ uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS]; uint32_t direct_key_state; unsigned int direct_key_mask; + + int rotary_rel_code[2]; + int rotary_up_key[2]; + int rotary_down_key[2]; }; static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; struct input_dev *input_dev = keypad->input_dev; - unsigned short keycode; + unsigned int *key; int i; - for (i = 0; i < pdata->matrix_key_map_size; i++) { - unsigned int key = pdata->matrix_key_map[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned int scancode = MATRIX_SCAN_CODE(row, col, - MATRIX_ROW_SHIFT); + key = &pdata->matrix_key_map[0]; + for (i = 0; i < pdata->matrix_key_map_size; i++, key++) { + int row = ((*key) >> 28) & 0xf; + int col = ((*key) >> 24) & 0xf; + int code = (*key) & 0xffffff; - keycode = KEY_VAL(key); - keypad->keycodes[scancode] = keycode; - __set_bit(keycode, input_dev->keybit); + keypad->matrix_keycodes[(row << 3) + col] = code; + set_bit(code, input_dev->keybit); } - for (i = 0; i < pdata->direct_key_num; i++) { - keycode = pdata->direct_key_map[i]; - keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode; - __set_bit(keycode, input_dev->keybit); - } + for (i = 0; i < pdata->direct_key_num; i++) + set_bit(pdata->direct_key_map[i], input_dev->keybit); + + keypad->rotary_up_key[0] = pdata->rotary0_up_key; + keypad->rotary_up_key[1] = pdata->rotary1_up_key; + keypad->rotary_down_key[0] = pdata->rotary0_down_key; + keypad->rotary_down_key[1] = pdata->rotary1_down_key; + keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; + keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; if (pdata->enable_rotary0) { if (pdata->rotary0_up_key && pdata->rotary0_down_key) { - keycode = pdata->rotary0_up_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = pdata->rotary0_down_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode; - __set_bit(keycode, input_dev->keybit); - - keypad->rotary_rel_code[0] = -1; - } else { - keypad->rotary_rel_code[0] = pdata->rotary0_rel_code; - __set_bit(pdata->rotary0_rel_code, input_dev->relbit); - } + set_bit(pdata->rotary0_up_key, input_dev->keybit); + set_bit(pdata->rotary0_down_key, input_dev->keybit); + } else + set_bit(pdata->rotary0_rel_code, input_dev->relbit); } if (pdata->enable_rotary1) { if (pdata->rotary1_up_key && pdata->rotary1_down_key) { - keycode = pdata->rotary1_up_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode; - __set_bit(keycode, input_dev->keybit); - - keycode = pdata->rotary1_down_key; - keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode; - __set_bit(keycode, input_dev->keybit); - - keypad->rotary_rel_code[1] = -1; - } else { - keypad->rotary_rel_code[1] = pdata->rotary1_rel_code; - __set_bit(pdata->rotary1_rel_code, input_dev->relbit); - } + set_bit(pdata->rotary1_up_key, input_dev->keybit); + set_bit(pdata->rotary1_down_key, input_dev->keybit); + } else + set_bit(pdata->rotary1_rel_code, input_dev->relbit); } +} - __clear_bit(KEY_RESERVED, input_dev->keybit); +static inline unsigned int lookup_matrix_keycode( + struct pxa27x_keypad *keypad, int row, int col) +{ + return keypad->matrix_keycodes[(row << 3) + col]; } static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - struct input_dev *input_dev = keypad->input_dev; int row, col, num_keys_pressed = 0; uint32_t new_state[MAX_MATRIX_KEY_COLS]; uint32_t kpas = keypad_readl(KPAS); @@ -226,7 +221,6 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) scan: for (col = 0; col < pdata->matrix_key_cols; col++) { uint32_t bits_changed; - int code; bits_changed = keypad->matrix_key_state[col] ^ new_state[col]; if (bits_changed == 0) @@ -236,13 +230,12 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad) if ((bits_changed & (1 << row)) == 0) continue; - code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, keypad->keycodes[code], - new_state[col] & (1 << row)); + input_report_key(keypad->input_dev, + lookup_matrix_keycode(keypad, row, col), + new_state[col] & (1 << row)); } } - input_sync(input_dev); + input_sync(keypad->input_dev); memcpy(keypad->matrix_key_state, new_state, sizeof(new_state)); } @@ -265,15 +258,13 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta) if (delta == 0) return; - if (keypad->rotary_rel_code[r] == -1) { - int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1); - unsigned char keycode = keypad->keycodes[code]; + if (keypad->rotary_up_key[r] && keypad->rotary_down_key[r]) { + int keycode = (delta > 0) ? keypad->rotary_up_key[r] : + keypad->rotary_down_key[r]; /* simulate a press-n-release */ - input_event(dev, EV_MSC, MSC_SCAN, code); input_report_key(dev, keycode, 1); input_sync(dev); - input_event(dev, EV_MSC, MSC_SCAN, code); input_report_key(dev, keycode, 0); input_sync(dev); } else { @@ -301,7 +292,6 @@ static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad) static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) { struct pxa27x_keypad_platform_data *pdata = keypad->pdata; - struct input_dev *input_dev = keypad->input_dev; unsigned int new_state; uint32_t kpdk, bits_changed; int i; @@ -311,6 +301,9 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) if (pdata->enable_rotary0 || pdata->enable_rotary1) pxa27x_keypad_scan_rotary(keypad); + if (pdata->direct_key_map == NULL) + return; + new_state = KPDK_DK(kpdk) & keypad->direct_key_mask; bits_changed = keypad->direct_key_state ^ new_state; @@ -318,23 +311,59 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad) return; for (i = 0; i < pdata->direct_key_num; i++) { - if (bits_changed & (1 << i)) { - int code = MAX_MATRIX_KEY_NUM + i; - - input_event(input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(input_dev, keypad->keycodes[code], - new_state & (1 << i)); + if (bits_changed & (1 << i)) + { + if (machine_is_edge()) + { + input_event(keypad->input_dev, EV_KEY, + pdata->direct_key_map[i], + !((new_state & (1 << i))>>i)); + } + else + { + input_report_key(keypad->input_dev, + pdata->direct_key_map[i], + ((new_state & (1 << i)))); + } } } - input_sync(input_dev); + input_sync(keypad->input_dev); keypad->direct_key_state = new_state; } +#ifdef CONFIG_ARCH_MMP + +#include +#include + +/* clear the wakeup event - undocumented */ +static inline void clear_wakeup(void) +{ + uint32_t val, mask = 0; + + if (cpu_is_pxa168()) + mask = 1 << 7; + + if (cpu_is_pxa910()) + mask = 1 << 3; + + if (mask) { + val = __raw_readl(APMU_REG(0x07c)); + __raw_writel(val | mask, APMU_REG(0x07c)); + __raw_writel(val & ~mask, APMU_REG(0x07c)); + } +} +#else +static inline void clear_wakeup(void) {} +#endif + static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id) { struct pxa27x_keypad *keypad = dev_id; unsigned long kpc = keypad_readl(KPC); + clear_wakeup(); + if (kpc & KPC_DI) pxa27x_keypad_scan_direct(keypad); @@ -406,13 +435,15 @@ static void pxa27x_keypad_close(struct input_dev *dev) #ifdef CONFIG_PM static int pxa27x_keypad_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; - clk_disable(keypad->clk); - - if (device_may_wakeup(&pdev->dev)) - enable_irq_wake(keypad->irq); + if (machine_is_edge()) { + return 0; + } else { + if (input_dev->users) + clk_disable(keypad->clk); + } return 0; } @@ -423,19 +454,11 @@ static int pxa27x_keypad_resume(struct device *dev) struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct input_dev *input_dev = keypad->input_dev; - if (device_may_wakeup(&pdev->dev)) - disable_irq_wake(keypad->irq); - - mutex_lock(&input_dev->mutex); - if (input_dev->users) { - /* Enable unit clock */ clk_enable(keypad->clk); pxa27x_keypad_config(keypad); } - mutex_unlock(&input_dev->mutex); - return 0; } @@ -445,43 +468,142 @@ static const struct dev_pm_ops pxa27x_keypad_pm_ops = { }; #endif +#ifdef CONFIG_PROC_FS +static int pxa27x_keypad_write_proc(struct file *file, + const char __user *buffer, unsigned long count, void *data) +{ + struct input_dev *dev = data; + char input[PAGE_SIZE]; + int key; + int i; + + if (copy_from_user(input, buffer, PAGE_SIZE)) + return -EFAULT; + input[PAGE_SIZE-1] = 0; + i = sscanf(input, "%d", &key); + printk("report key: %d\n", key); + + set_bit(key, dev->keybit); + input_report_key(dev, key, 1); + input_sync(dev); + input_report_key(dev, key, 0); + input_sync(dev); + + return count; +} + +static void pxa27x_keypad_create_proc_file(void *data) +{ + struct proc_dir_entry *proc_file = + create_proc_entry("driver/pxa27x-keypad", 0644, NULL); + + if (proc_file) { + proc_file->write_proc = pxa27x_keypad_write_proc; + proc_file->data = data; + }else { + printk(KERN_INFO "proc file create failed!\n"); + } +} + +extern struct proc_dir_entry proc_root; +static void pxa27x_keypad_remove_proc_file(void) +{ + remove_proc_entry("driver/pxa27x-keypad", &proc_root); +} +#endif + +/* Sysfs stuff */ +static ssize_t pxa27x_keypad_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + len += sprintf(buf+len, "old keycode\tnew keycode\n"); + + return len; +} + +static ssize_t pxa27x_keypad_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) +{ + int keycode_old, keycode_new; + struct pxa27x_keypad *keypad = dev_get_drvdata(dev); + struct pxa27x_keypad_platform_data *pdata = keypad->pdata; + unsigned int *key = &pdata->matrix_key_map[0]; + int ret, i; + + ret = sscanf(buf, "%d %d", &keycode_old, &keycode_new); + if(ret == 2) { + for (i = 0; i < pdata->matrix_key_map_size; i++, key++) { + int row = ((*key) >> 28) & 0xf; + int col = ((*key) >> 24) & 0xf; + int code = (*key) & 0xffffff; + + if (code == keycode_old) { + printk("\nold keycode :%d\t new keycode: %d\n",code, keycode_new); + keypad->matrix_keycodes[(row << 3) + col] = keycode_new; + *key = KEY(row, col, keycode_new); + break; + } else { + printk("*"); + } + } + } + + return count; +} + +struct device_attribute dev_attr_pxa27x_keypad = { + .attr = { + .name = "pxa27x-keypad", + .mode = 0664, + }, + .show = pxa27x_keypad_show, + .store = pxa27x_keypad_store, +}; + +static struct attribute *pxa27x_keypad_attributes[] = { + &dev_attr_pxa27x_keypad.attr, + NULL +}; + +static struct attribute_group pxa27x_keypad_attribute_group = { + .attrs = pxa27x_keypad_attributes +}; + static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) { - struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data; struct pxa27x_keypad *keypad; struct input_dev *input_dev; struct resource *res; int irq, error; - if (pdata == NULL) { + keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL); + if (keypad == NULL) { + dev_err(&pdev->dev, "failed to allocate driver data\n"); + return -ENOMEM; + } + + keypad->pdata = pdev->dev.platform_data; + if (keypad->pdata == NULL) { dev_err(&pdev->dev, "no platform data defined\n"); - return -EINVAL; + error = -EINVAL; + goto failed_free; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "failed to get keypad irq\n"); - return -ENXIO; + error = -ENXIO; + goto failed_free; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "failed to get I/O memory\n"); - return -ENXIO; - } - - keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!keypad || !input_dev) { - dev_err(&pdev->dev, "failed to allocate memory\n"); - error = -ENOMEM; + error = -ENXIO; goto failed_free; } - keypad->pdata = pdata; - keypad->input_dev = input_dev; - keypad->irq = irq; - res = request_mem_region(res->start, resource_size(res), pdev->name); if (res == NULL) { dev_err(&pdev->dev, "failed to request I/O memory\n"); @@ -503,35 +625,43 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) goto failed_free_io; } + /* Create and register the input driver. */ + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + error = -ENOMEM; + goto failed_put_clk; + } + input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->open = pxa27x_keypad_open; input_dev->close = pxa27x_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - + keypad->input_dev = input_dev; input_set_drvdata(input_dev, keypad); input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - - pxa27x_keypad_build_keycode(keypad); - - if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) || - (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) { + if ((keypad->pdata->enable_rotary0 && + keypad->pdata->rotary0_rel_code) || + (keypad->pdata->enable_rotary1 && + keypad->pdata->rotary1_rel_code)) { input_dev->evbit[0] |= BIT_MASK(EV_REL); } + pxa27x_keypad_build_keycode(keypad); + platform_set_drvdata(pdev, keypad); + error = request_irq(irq, pxa27x_keypad_irq_handler, IRQF_DISABLED, pdev->name, keypad); if (error) { dev_err(&pdev->dev, "failed to request IRQ\n"); - goto failed_put_clk; + goto failed_free_dev; } + keypad->irq = irq; + /* Register the input device */ error = input_register_device(input_dev); if (error) { @@ -539,13 +669,22 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) goto failed_free_irq; } - platform_set_drvdata(pdev, keypad); + error = sysfs_create_group(&pdev->dev.kobj, &pxa27x_keypad_attribute_group); + if (error != 0){ + printk(KERN_ERR "register sysfs error\n"); + return error; + } + device_init_wakeup(&pdev->dev, 1); + pxa27x_keypad_create_proc_file(input_dev); return 0; failed_free_irq: free_irq(irq, pdev); + platform_set_drvdata(pdev, NULL); +failed_free_dev: + input_free_device(input_dev); failed_put_clk: clk_put(keypad->clk); failed_free_io: @@ -553,7 +692,6 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev) failed_free_mem: release_mem_region(res->start, resource_size(res)); failed_free: - input_free_device(input_dev); kfree(keypad); return error; } @@ -563,7 +701,10 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) struct pxa27x_keypad *keypad = platform_get_drvdata(pdev); struct resource *res; + pxa27x_keypad_remove_proc_file(); free_irq(keypad->irq, pdev); + + clk_disable(keypad->clk); clk_put(keypad->clk); input_unregister_device(keypad->input_dev); @@ -576,7 +717,6 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); kfree(keypad); - return 0; } diff --git a/drivers/input/keyreset.c b/drivers/input/keyreset.c new file mode 100644 index 00000000000000..4905692a54f773 --- /dev/null +++ b/drivers/input/keyreset.c @@ -0,0 +1,229 @@ +/* drivers/input/keyreset.c + * + * Copyright (C) 2008 Google, Inc. + * + * 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 +#include + + +struct keyreset_state { + struct input_handler input_handler; + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long upbit[BITS_TO_LONGS(KEY_CNT)]; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; + spinlock_t lock; + int key_down_target; + int key_down; + int key_up; + int restart_disabled; +}; + +int restart_requested; +static void deferred_restart(struct work_struct *dummy) +{ + restart_requested = 2; + sys_sync(); + restart_requested = 3; + kernel_restart(NULL); +} +static DECLARE_WORK(restart_work, deferred_restart); + +static void keyreset_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + unsigned long flags; + struct keyreset_state *state = handle->private; + + if (type != EV_KEY) + return; + + if (code >= KEY_MAX) + return; + + if (!test_bit(code, state->keybit)) + return; + + spin_lock_irqsave(&state->lock, flags); + if (!test_bit(code, state->key) == !value) + goto done; + __change_bit(code, state->key); + if (test_bit(code, state->upbit)) { + if (value) { + state->restart_disabled = 1; + state->key_up++; + } else + state->key_up--; + } else { + if (value) + state->key_down++; + else + state->key_down--; + } + if (state->key_down == 0 && state->key_up == 0) + state->restart_disabled = 0; + + pr_debug("reset key changed %d %d new state %d-%d-%d\n", code, value, + state->key_down, state->key_up, state->restart_disabled); + + if (value && !state->restart_disabled && + state->key_down == state->key_down_target) { + state->restart_disabled = 1; + if (restart_requested) + panic("keyboard reset failed, %d", restart_requested); + pr_info("keyboard reset\n"); + schedule_work(&restart_work); + restart_requested = 1; + } +done: + spin_unlock_irqrestore(&state->lock, flags); +} + +static int keyreset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i; + int ret; + struct input_handle *handle; + struct keyreset_state *state = + container_of(handler, struct keyreset_state, input_handler); + + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, state->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = "keyreset"; + handle->private = state; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("using input dev %s for key reset\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keyreset_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +static const struct input_device_id keyreset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { }, +}; +MODULE_DEVICE_TABLE(input, keyreset_ids); + +static int keyreset_probe(struct platform_device *pdev) +{ + int ret; + int key, *keyp; + struct keyreset_state *state; + struct keyreset_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + spin_lock_init(&state->lock); + keyp = pdata->keys_down; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + state->key_down_target++; + __set_bit(key, state->keybit); + } + if (pdata->keys_up) { + keyp = pdata->keys_up; + while ((key = *keyp++)) { + if (key >= KEY_MAX) + continue; + __set_bit(key, state->keybit); + __set_bit(key, state->upbit); + } + } + state->input_handler.event = keyreset_event; + state->input_handler.connect = keyreset_connect; + state->input_handler.disconnect = keyreset_disconnect; + state->input_handler.name = KEYRESET_NAME; + state->input_handler.id_table = keyreset_ids; + ret = input_register_handler(&state->input_handler); + if (ret) { + kfree(state); + return ret; + } + platform_set_drvdata(pdev, state); + return 0; +} + +int keyreset_remove(struct platform_device *pdev) +{ + struct keyreset_state *state = platform_get_drvdata(pdev); + input_unregister_handler(&state->input_handler); + kfree(state); + return 0; +} + + +struct platform_driver keyreset_driver = { + .driver.name = KEYRESET_NAME, + .probe = keyreset_probe, + .remove = keyreset_remove, +}; + +static int __init keyreset_init(void) +{ + return platform_driver_register(&keyreset_driver); +} + +static void __exit keyreset_exit(void) +{ + return platform_driver_unregister(&keyreset_driver); +} + +module_init(keyreset_init); +module_exit(keyreset_exit); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 23140a3bb8e0b1..d9f7e4ac7adc9a 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -244,6 +244,11 @@ config INPUT_SGI_BTNS To compile this driver as a module, choose M here: the module will be called sgi_btns. +config INPUT_GPIO + tristate "GPIO driver support" + help + Say Y here if you want to support gpio based keys, wheels etc... + config INPUT_WINBOND_CIR tristate "Winbond IR remote control" depends on X86 && PNP @@ -277,6 +282,27 @@ config INPUT_PCF50633_PMU Say Y to include support for delivering PMU events via input layer on NXP PCF50633. +config INPUT_KEYCHORD + tristate "Key chord input driver support" + help + Say Y here if you want to enable the key chord driver + accessible at /dev/keychord. This driver can be used + for receiving notifications when client specified key + combinations are pressed. + + To compile this driver as a module, choose M here: the + module will be called keychord. + +config INPUT_SENSOR + bool "Sensor input driver support" + help + Say Y here if you want to enable the sensor input driver. + +config AVENGERS_LITE_POWER_BUTTON + tristate "avengers lite power button" + help + Say Y here if you want to support the built-in power button based on avengers lite. + config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" depends on GPIOLIB && GENERIC_GPIO diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 7e95a5d474dcb6..1ebc94e1ea5908 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -19,6 +19,10 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o +obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o +obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o +obj-$(CONFIG_INPUT_SENSOR) += sensors.o +obj-$(CONFIG_AVENGERS_LITE_POWER_BUTTON) += power_button.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c new file mode 100644 index 00000000000000..c801172aa9eecf --- /dev/null +++ b/drivers/input/misc/gpio_axis.c @@ -0,0 +1,180 @@ +/* drivers/input/misc/gpio_axis.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 + +struct gpio_axis_state { + struct input_dev *input_dev; + struct gpio_event_axis_info *info; + uint32_t pos; +}; + +uint16_t gpio_axis_4bit_gray_map_table[] = { + [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */ + [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */ + [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */ + [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */ + [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */ + [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */ + [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */ + [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */ +}; +uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_4bit_gray_map_table[in]; +} + +uint16_t gpio_axis_5bit_singletrack_map_table[] = { + [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */ + [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */ + [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */ + [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */ + [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */ + [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */ + [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */ + [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */ + [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */ + [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */ +}; +uint16_t gpio_axis_5bit_singletrack_map( + struct gpio_event_axis_info *info, uint16_t in) +{ + return gpio_axis_5bit_singletrack_map_table[in]; +} + +static void gpio_event_update_axis(struct gpio_axis_state *as, int report) +{ + struct gpio_event_axis_info *ai = as->info; + int i; + int change; + uint16_t state = 0; + uint16_t pos; + uint16_t old_pos = as->pos; + for (i = ai->count - 1; i >= 0; i--) + state = (state << 1) | gpio_get_value(ai->gpio[i]); + pos = ai->map(ai, state); + if (ai->flags & GPIOEAF_PRINT_RAW) + pr_info("axis %d-%d raw %x, pos %d -> %d\n", + ai->type, ai->code, state, old_pos, pos); + if (report && pos != old_pos) { + if (ai->type == EV_REL) { + change = (ai->decoded_size + pos - old_pos) % + ai->decoded_size; + if (change > ai->decoded_size / 2) + change -= ai->decoded_size; + if (change == ai->decoded_size / 2) { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d unknown direction, " + "pos %d -> %d\n", ai->type, + ai->code, old_pos, pos); + change = 0; /* no closest direction */ + } + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d change %d\n", + ai->type, ai->code, change); + input_report_rel(as->input_dev, ai->code, change); + } else { + if (ai->flags & GPIOEAF_PRINT_EVENT) + pr_info("axis %d-%d now %d\n", + ai->type, ai->code, pos); + input_event(as->input_dev, ai->type, ai->code, pos); + } + input_sync(as->input_dev); + } + as->pos = pos; +} + +static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id) +{ + struct gpio_axis_state *as = dev_id; + gpio_event_update_axis(as, 1); + return IRQ_HANDLED; +} + +int gpio_event_axis_func(struct input_dev *input_dev, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + int irq; + struct gpio_event_axis_info *ai; + struct gpio_axis_state *as; + + ai = container_of(info, struct gpio_event_axis_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND) { + for (i = 0; i < ai->count; i++) + disable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + for (i = 0; i < ai->count; i++) + enable_irq(gpio_to_irq(ai->gpio[i])); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + *data = as = kmalloc(sizeof(*as), GFP_KERNEL); + if (as == NULL) { + ret = -ENOMEM; + goto err_alloc_axis_state_failed; + } + as->input_dev = input_dev; + as->info = ai; + + input_set_capability(input_dev, ai->type, ai->code); + if (ai->type == EV_ABS) { + input_set_abs_params(input_dev, ai->code, 0, + ai->decoded_size - 1, 0, 0); + } + for (i = 0; i < ai->count; i++) { + ret = gpio_request(ai->gpio[i], "gpio_event_axis"); + if (ret < 0) + goto err_request_gpio_failed; + ret = gpio_direction_input(ai->gpio[i]); + if (ret < 0) + goto err_gpio_direction_input_failed; + ret = irq = gpio_to_irq(ai->gpio[i]); + if (ret < 0) + goto err_get_irq_num_failed; + ret = request_irq(irq, gpio_axis_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "gpio_event_axis", as); + if (ret < 0) + goto err_request_irq_failed; + } + gpio_event_update_axis(as, 0); + return 0; + } + + ret = 0; + as = *data; + for (i = ai->count - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ai->gpio[i]), as); +err_request_irq_failed: +err_get_irq_num_failed: +err_gpio_direction_input_failed: + gpio_free(ai->gpio[i]); +err_request_gpio_failed: + ; + } + kfree(as); + *data = NULL; +err_alloc_axis_state_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c new file mode 100644 index 00000000000000..8b64c1e579ba10 --- /dev/null +++ b/drivers/input/misc/gpio_event.c @@ -0,0 +1,224 @@ +/* drivers/input/misc/gpio_event.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 + +struct gpio_event { + struct input_dev *input_dev; + const struct gpio_event_platform_data *info; + struct early_suspend early_suspend; + void *state[0]; +}; + +static int gpio_input_event( + struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + int i; + int ret = 0; + int tmp_ret; + struct gpio_event_info **ii; + struct gpio_event *ip = input_get_drvdata(dev); + + for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) { + if ((*ii)->event) { + tmp_ret = (*ii)->event(ip->input_dev, *ii, + &ip->state[i], type, code, value); + if (tmp_ret) + ret = tmp_ret; + } + } + return ret; +} + +static int gpio_event_call_all_func(struct gpio_event *ip, int func) +{ + int i; + int ret; + struct gpio_event_info **ii; + + if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) { + ii = ip->info->info; + for (i = 0; i < ip->info->info_count; i++, ii++) { + if ((*ii)->func == NULL) { + ret = -ENODEV; + pr_err("gpio_event_probe: Incomplete pdata, " + "no function\n"); + goto err_no_func; + } + ret = (*ii)->func(ip->input_dev, *ii, &ip->state[i], + func); + if (ret) { + pr_err("gpio_event_probe: function failed\n"); + goto err_func_failed; + } + } + return 0; + } + + ret = 0; + i = ip->info->info_count; + ii = ip->info->info + i; + while (i > 0) { + i--; + ii--; + (*ii)->func(ip->input_dev, *ii, &ip->state[i], func & ~1); +err_func_failed: +err_no_func: + ; + } + return ret; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +void gpio_event_suspend(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND); + ip->info->power(ip->info, 0); +} + +void gpio_event_resume(struct early_suspend *h) +{ + struct gpio_event *ip; + ip = container_of(h, struct gpio_event, early_suspend); + ip->info->power(ip->info, 1); + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME); +} +#endif + +static int __init gpio_event_probe(struct platform_device *pdev) +{ + int err; + struct gpio_event *ip; + struct input_dev *input_dev; + struct gpio_event_platform_data *event_info; + + event_info = pdev->dev.platform_data; + if (event_info == NULL) { + pr_err("gpio_event_probe: No pdata\n"); + return -ENODEV; + } + if (event_info->name == NULL || + event_info->info == NULL || + event_info->info_count == 0) { + pr_err("gpio_event_probe: Incomplete pdata\n"); + return -ENODEV; + } + ip = kzalloc(sizeof(*ip) + + sizeof(ip->state[0]) * event_info->info_count, GFP_KERNEL); + if (ip == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + platform_set_drvdata(pdev, ip); + + input_dev = input_allocate_device(); + if (input_dev == NULL) { + err = -ENOMEM; + pr_err("gpio_event_probe: Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + input_set_drvdata(input_dev, ip); + ip->input_dev = input_dev; + ip->info = event_info; + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + ip->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ip->early_suspend.suspend = gpio_event_suspend; + ip->early_suspend.resume = gpio_event_resume; + register_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 1); + } + + input_dev->name = ip->info->name; + input_dev->event = gpio_input_event; + + err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT); + if (err) + goto err_call_all_func_failed; + + err = input_register_device(input_dev); + if (err) { + pr_err("gpio_event_probe: Unable to register %s input device\n", + input_dev->name); + goto err_input_register_device_failed; + } + + return 0; + +err_input_register_device_failed: + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); +err_call_all_func_failed: + if (event_info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + input_free_device(input_dev); +err_input_dev_alloc_failed: + kfree(ip); +err_kp_alloc_failed: + return err; +} + +static int gpio_event_remove(struct platform_device *pdev) +{ + struct gpio_event *ip = platform_get_drvdata(pdev); + + gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT); + if (ip->info->power) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ip->early_suspend); +#endif + ip->info->power(ip->info, 0); + } + input_unregister_device(ip->input_dev); + kfree(ip); + return 0; +} + +static struct platform_driver gpio_event_driver = { + .probe = gpio_event_probe, + .remove = gpio_event_remove, + .driver = { + .name = GPIO_EVENT_DEV_NAME, + }, +}; + +static int __devinit gpio_event_init(void) +{ + return platform_driver_register(&gpio_event_driver); +} + +static void __exit gpio_event_exit(void) +{ + platform_driver_unregister(&gpio_event_driver); +} + +module_init(gpio_event_init); +module_exit(gpio_event_exit); + +MODULE_DESCRIPTION("GPIO Event Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c new file mode 100644 index 00000000000000..7e307f267a2a05 --- /dev/null +++ b/drivers/input/misc/gpio_input.c @@ -0,0 +1,343 @@ +/* drivers/input/misc/gpio_input.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 +#include + +enum { + DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ + DEBOUNCE_PRESSED = BIT(1), + DEBOUNCE_NOTPRESSED = BIT(2), + DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ + DEBOUNCE_POLL = BIT(4), /* Stable polling state */ + + DEBOUNCE_UNKNOWN = + DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, +}; + +struct gpio_key_state { + struct gpio_input_state *ds; + uint8_t debounce; +}; + +struct gpio_input_state { + struct input_dev *input_dev; + const struct gpio_event_input_info *info; + struct hrtimer timer; + int use_irq; + int debounce_count; + spinlock_t irq_lock; + struct wake_lock wake_lock; + struct gpio_key_state key_state[0]; +}; + +static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) +{ + int i; + int pressed; + struct gpio_input_state *ds = + container_of(timer, struct gpio_input_state, timer); + unsigned gpio_flags = ds->info->flags; + unsigned npolarity; + int nkeys = ds->info->keymap_size; + const struct gpio_event_direct_entry *key_entry; + struct gpio_key_state *key_state; + unsigned long irqflags; + uint8_t debounce; + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); +#endif + key_entry = ds->info->keymap; + key_state = ds->key_state; + spin_lock_irqsave(&ds->irq_lock, irqflags); + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + debounce = key_state->debounce; + if (debounce & DEBOUNCE_WAIT_IRQ) + continue; + if (key_state->debounce & DEBOUNCE_UNSTABLE) { + debounce = key_state->debounce = DEBOUNCE_UNKNOWN; + enable_irq(gpio_to_irq(key_entry->gpio)); + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) continue debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); + pressed = gpio_get_value(key_entry->gpio) ^ npolarity; + if (debounce & DEBOUNCE_POLL) { + if (pressed == !(debounce & DEBOUNCE_PRESSED)) { + ds->debounce_count++; + key_state->debounce = DEBOUNCE_UNKNOWN; + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-" + "%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + } + continue; + } + if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 1\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_PRESSED; + continue; + } + if (!pressed && (debounce & DEBOUNCE_PRESSED)) { + if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_keys_scan_keys: key %x-%x, %d " + "(%d) debounce pressed 0\n", + ds->info->type, key_entry->code, + i, key_entry->gpio); + key_state->debounce = DEBOUNCE_NOTPRESSED; + continue; + } + /* key is stable */ + ds->debounce_count--; + if (ds->use_irq) + key_state->debounce |= DEBOUNCE_WAIT_IRQ; + else + key_state->debounce |= DEBOUNCE_POLL; + if (gpio_flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " + "changed to %d\n", ds->info->type, + key_entry->code, i, key_entry->gpio, pressed); + input_event(ds->input_dev, ds->info->type, + key_entry->code, pressed); + } + +#if 0 + key_entry = kp->keys_info->keymap; + key_state = kp->key_state; + for (i = 0; i < nkeys; i++, key_entry++, key_state++) { + pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, + gpio_read_detect_status(key_entry->gpio)); + } +#endif + + if (ds->debounce_count) + hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); + else if (!ds->use_irq) + hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); + else + wake_unlock(&ds->wake_lock); + + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) +{ + struct gpio_key_state *ks = dev_id; + struct gpio_input_state *ds = ks->ds; + int keymap_index = ks - ds->key_state; + const struct gpio_event_direct_entry *key_entry; + unsigned long irqflags; + int pressed; + + if (!ds->use_irq) + return IRQ_HANDLED; + + key_entry = &ds->info->keymap[keymap_index]; + + if (ds->info->debounce_time.tv64) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ks->debounce & DEBOUNCE_WAIT_IRQ) { + ks->debounce = DEBOUNCE_UNKNOWN; + if (ds->debounce_count++ == 0) { + wake_lock(&ds->wake_lock); + hrtimer_start( + &ds->timer, ds->info->debounce_time, + HRTIMER_MODE_REL); + } + if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) + pr_info("gpio_event_input_irq_handler: " + "key %x-%x, %d (%d) start debounce\n", + ds->info->type, key_entry->code, + keymap_index, key_entry->gpio); + } else { + disable_irq(irq); + ks->debounce = DEBOUNCE_UNSTABLE; + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + } else { + pressed = gpio_get_value(key_entry->gpio) ^ + !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); + if (ds->info->flags & GPIOEDF_PRINT_KEYS) + pr_info("gpio_event_input_irq_handler: key %x-%x, %d " + "(%d) changed to %d\n", + ds->info->type, key_entry->code, keymap_index, + key_entry->gpio, pressed); + input_event(ds->input_dev, ds->info->type, + key_entry->code, pressed); + } + return IRQ_HANDLED; +} + +static int gpio_event_input_request_irqs(struct gpio_input_state *ds) +{ + int i; + int err; + unsigned int irq; + unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; + + for (i = 0; i < ds->info->keymap_size; i++) { + err = irq = gpio_to_irq(ds->info->keymap[i].gpio); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_event_input_irq_handler, + req_flags, "gpio_keys", &ds->key_state[i]); + if (err) { + pr_err("gpio_event_input_request_irqs: request_irq " + "failed for input %d, irq %d\n", + ds->info->keymap[i].gpio, irq); + goto err_request_irq_failed; + } + enable_irq_wake(irq); + } + return 0; + + for (i = ds->info->keymap_size - 1; i >= 0; i--) { + free_irq(gpio_to_irq(ds->info->keymap[i].gpio), + &ds->key_state[i]); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_input_func(struct input_dev *input_dev, + struct gpio_event_info *info, void **data, int func) +{ + int ret; + int i; + unsigned long irqflags; + struct gpio_event_input_info *di; + struct gpio_input_state *ds = *data; + + di = container_of(info, struct gpio_event_input_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + disable_irq(gpio_to_irq(di->keymap[i].gpio)); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + return 0; + } + if (func == GPIO_EVENT_FUNC_RESUME) { + spin_lock_irqsave(&ds->irq_lock, irqflags); + if (ds->use_irq) + for (i = 0; i < di->keymap_size; i++) + enable_irq(gpio_to_irq(di->keymap[i].gpio)); + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (ktime_to_ns(di->poll_time) <= 0) + di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); + + *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * + di->keymap_size, GFP_KERNEL); + if (ds == NULL) { + ret = -ENOMEM; + pr_err("gpio_event_input_func: " + "Failed to allocate private data\n"); + goto err_ds_alloc_failed; + } + ds->debounce_count = di->keymap_size; + ds->input_dev = input_dev; + ds->info = di; + wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); + spin_lock_init(&ds->irq_lock); + + for (i = 0; i < di->keymap_size; i++) { + input_set_capability(input_dev, di->type, + di->keymap[i].code); + ds->key_state[i].ds = ds; + ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; + } + + for (i = 0; i < di->keymap_size; i++) { + ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); + if (ret) { + pr_err("gpio_event_input_func: gpio_request " + "failed for %d\n", di->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_input(di->keymap[i].gpio); + if (ret) { + pr_err("gpio_event_input_func: " + "gpio_direction_input failed for %d\n", + di->keymap[i].gpio); + goto err_gpio_configure_failed; + } + } + + ret = gpio_event_input_request_irqs(ds); + + spin_lock_irqsave(&ds->irq_lock, irqflags); + ds->use_irq = ret == 0; + + pr_info("GPIO Input Driver: Start gpio inputs for %s in %s " + "mode\n", + input_dev->name, ret == 0 ? "interrupt" : "polling"); + + hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ds->timer.function = gpio_event_input_timer_func; + hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + return 0; + } + + ret = 0; + spin_lock_irqsave(&ds->irq_lock, irqflags); + hrtimer_cancel(&ds->timer); + if (ds->use_irq) { + for (i = di->keymap_size - 1; i >= 0; i--) { + free_irq(gpio_to_irq(di->keymap[i].gpio), + &ds->key_state[i]); + } + } + spin_unlock_irqrestore(&ds->irq_lock, irqflags); + + for (i = di->keymap_size - 1; i >= 0; i--) { +err_gpio_configure_failed: + gpio_free(di->keymap[i].gpio); +err_gpio_request_failed: + ; + } + wake_lock_destroy(&ds->wake_lock); + kfree(ds); +err_ds_alloc_failed: + return ret; +} diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c new file mode 100644 index 00000000000000..c1f47651a4937d --- /dev/null +++ b/drivers/input/misc/gpio_matrix.c @@ -0,0 +1,406 @@ +/* drivers/input/misc/gpio_matrix.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 + +struct gpio_kp { + struct input_dev *input_dev; + struct gpio_event_matrix_info *keypad_info; + struct hrtimer timer; + struct wake_lock wake_lock; + int current_output; + unsigned int use_irq:1; + unsigned int key_state_changed:1; + unsigned int last_key_state_changed:1; + unsigned int some_keys_pressed:2; + unsigned long keys_pressed[0]; +}; + +static void clear_phantom_key(struct gpio_kp *kp, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int key_index = out * mi->ninputs + in; + unsigned short keycode = mi->keymap[key_index];; + + if (!test_bit(keycode, kp->input_dev->key)) { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + __clear_bit(key_index, kp->keys_pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS) + pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) " + "not cleared\n", keycode, out, in, + mi->output_gpios[out], mi->input_gpios[in]); + } +} + +static int restore_keys_for_input(struct gpio_kp *kp, int out, int in) +{ + int rv = 0; + int key_index; + + key_index = out * kp->keypad_info->ninputs + in; + while (out < kp->keypad_info->noutputs) { + if (test_bit(key_index, kp->keys_pressed)) { + rv = 1; + clear_phantom_key(kp, out, in); + } + key_index += kp->keypad_info->ninputs; + out++; + } + return rv; +} + +static void remove_phantom_keys(struct gpio_kp *kp) +{ + int out, in, inp; + int key_index; + + if (kp->some_keys_pressed < 3) + return; + + for (out = 0; out < kp->keypad_info->noutputs; out++) { + inp = -1; + key_index = out * kp->keypad_info->ninputs; + for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) { + if (test_bit(key_index, kp->keys_pressed)) { + if (inp == -1) { + inp = in; + continue; + } + if (inp >= 0) { + if (!restore_keys_for_input(kp, out + 1, + inp)) + break; + clear_phantom_key(kp, out, inp); + inp = -2; + } + restore_keys_for_input(kp, out, in); + } + } + } +} + +static void report_key(struct gpio_kp *kp, int key_index, int out, int in) +{ + struct gpio_event_matrix_info *mi = kp->keypad_info; + int pressed = test_bit(key_index, kp->keys_pressed); + unsigned short keycode = mi->keymap[key_index]; + if (pressed != test_bit(keycode, kp->input_dev->key)) { + if (keycode == KEY_RESERVED) { + if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS) + pr_info("gpiomatrix: unmapped key, %d-%d " + "(%d-%d) changed to %d\n", + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + } else { + if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS) + pr_info("gpiomatrix: key %x, %d-%d (%d-%d) " + "changed to %d\n", keycode, + out, in, mi->output_gpios[out], + mi->input_gpios[in], pressed); + input_report_key(kp->input_dev, keycode, pressed); + } + } +} + +static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer) +{ + int out, in; + int key_index; + int gpio; + struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer); + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH); + + out = kp->current_output; + if (out == mi->noutputs) { + out = 0; + kp->last_key_state_changed = kp->key_state_changed; + kp->key_state_changed = 0; + kp->some_keys_pressed = 0; + } else { + key_index = out * mi->ninputs; + for (in = 0; in < mi->ninputs; in++, key_index++) { + gpio = mi->input_gpios[in]; + if (gpio_get_value(gpio) ^ !polarity) { + if (kp->some_keys_pressed < 3) + kp->some_keys_pressed++; + kp->key_state_changed |= !__test_and_set_bit( + key_index, kp->keys_pressed); + } else + kp->key_state_changed |= __test_and_clear_bit( + key_index, kp->keys_pressed); + } + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, !polarity); + else + gpio_direction_input(gpio); + out++; + } + kp->current_output = out; + if (out < mi->noutputs) { + gpio = mi->output_gpios[out]; + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(gpio, polarity); + else + gpio_direction_output(gpio, polarity); + hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) { + if (kp->key_state_changed) { + hrtimer_start(&kp->timer, mi->debounce_delay, + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + kp->key_state_changed = kp->last_key_state_changed; + } + if (kp->key_state_changed) { + if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS) + remove_phantom_keys(kp); + key_index = 0; + for (out = 0; out < mi->noutputs; out++) + for (in = 0; in < mi->ninputs; in++, key_index++) + report_key(kp, key_index, out, in); + } + if (!kp->use_irq || kp->some_keys_pressed) { + hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL); + return HRTIMER_NORESTART; + } + + /* No keys are pressed, reenable interrupt */ + for (out = 0; out < mi->noutputs; out++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[out], polarity); + else + gpio_direction_output(mi->output_gpios[out], polarity); + } + for (in = 0; in < mi->ninputs; in++) + enable_irq(gpio_to_irq(mi->input_gpios[in])); + wake_unlock(&kp->wake_lock); + return HRTIMER_NORESTART; +} + +static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id) +{ + int i; + struct gpio_kp *kp = dev_id; + struct gpio_event_matrix_info *mi = kp->keypad_info; + unsigned gpio_keypad_flags = mi->flags; + + if (!kp->use_irq) /* ignore interrupt while registering the handler */ + return IRQ_HANDLED; + + for (i = 0; i < mi->ninputs; i++) + disable_irq(gpio_to_irq(mi->input_gpios[i])); + for (i = 0; i < mi->noutputs; i++) { + if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE) + gpio_set_value(mi->output_gpios[i], + !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH)); + else + gpio_direction_input(mi->output_gpios[i]); + } + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + return IRQ_HANDLED; +} + +static int gpio_keypad_request_irqs(struct gpio_kp *kp) +{ + int i; + int err; + unsigned int irq; + unsigned long request_flags; + struct gpio_event_matrix_info *mi = kp->keypad_info; + + switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) { + default: + request_flags = IRQF_TRIGGER_FALLING; + break; + case GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_RISING; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ: + request_flags = IRQF_TRIGGER_LOW; + break; + case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH: + request_flags = IRQF_TRIGGER_HIGH; + break; + } + + for (i = 0; i < mi->ninputs; i++) { + err = irq = gpio_to_irq(mi->input_gpios[i]); + if (err < 0) + goto err_gpio_get_irq_num_failed; + err = request_irq(irq, gpio_keypad_irq_handler, request_flags, + "gpio_kp", kp); + if (err) { + pr_err("gpiomatrix: request_irq failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + goto err_request_irq_failed; + } + err = set_irq_wake(irq, 1); + if (err) { + pr_err("gpiomatrix: set_irq_wake failed for input %d, " + "irq %d\n", mi->input_gpios[i], irq); + } + disable_irq(irq); + } + return 0; + + for (i = mi->noutputs - 1; i >= 0; i--) { + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); +err_request_irq_failed: +err_gpio_get_irq_num_failed: + ; + } + return err; +} + +int gpio_event_matrix_func(struct input_dev *input_dev, + struct gpio_event_info *info, void **data, int func) +{ + int i; + int err; + int key_count; + struct gpio_kp *kp; + struct gpio_event_matrix_info *mi; + + mi = container_of(info, struct gpio_event_matrix_info, info); + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) { + /* TODO: disable scanning */ + return 0; + } + + if (func == GPIO_EVENT_FUNC_INIT) { + if (mi->keymap == NULL || + mi->input_gpios == NULL || + mi->output_gpios == NULL) { + err = -ENODEV; + pr_err("gpiomatrix: Incomplete pdata\n"); + goto err_invalid_platform_data; + } + key_count = mi->ninputs * mi->noutputs; + + *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) * + BITS_TO_LONGS(key_count), GFP_KERNEL); + if (kp == NULL) { + err = -ENOMEM; + pr_err("gpiomatrix: Failed to allocate private data\n"); + goto err_kp_alloc_failed; + } + kp->input_dev = input_dev; + kp->keypad_info = mi; + set_bit(EV_KEY, input_dev->evbit); + for (i = 0; i < key_count; i++) { + if (mi->keymap[i]) + set_bit(mi->keymap[i] & KEY_MAX, + input_dev->keybit); + } + + for (i = 0; i < mi->noutputs; i++) { + if (gpio_cansleep(mi->output_gpios[i])) { + pr_err("gpiomatrix: unsupported output gpio %d," + " can sleep\n", mi->output_gpios[i]); + err = -EINVAL; + goto err_request_output_gpio_failed; + } + err = gpio_request(mi->output_gpios[i], "gpio_kp_out"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "output %d\n", mi->output_gpios[i]); + goto err_request_output_gpio_failed; + } + if (mi->flags & GPIOKPF_DRIVE_INACTIVE) + err = gpio_direction_output(mi->output_gpios[i], + !(mi->flags & GPIOKPF_ACTIVE_HIGH)); + else + err = gpio_direction_input(mi->output_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_configure failed for " + "output %d\n", mi->output_gpios[i]); + goto err_output_gpio_configure_failed; + } + } + for (i = 0; i < mi->ninputs; i++) { + err = gpio_request(mi->input_gpios[i], "gpio_kp_in"); + if (err) { + pr_err("gpiomatrix: gpio_request failed for " + "input %d\n", mi->input_gpios[i]); + goto err_request_input_gpio_failed; + } + err = gpio_direction_input(mi->input_gpios[i]); + if (err) { + pr_err("gpiomatrix: gpio_direction_input failed" + " for input %d\n", mi->input_gpios[i]); + goto err_gpio_direction_input_failed; + } + } + kp->current_output = mi->noutputs; + kp->key_state_changed = 1; + + hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kp->timer.function = gpio_keypad_timer_func; + wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp"); + err = gpio_keypad_request_irqs(kp); + kp->use_irq = err == 0; + + pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for %s " + "in %s mode\n", input_dev->name, + kp->use_irq ? "interrupt" : "polling"); + + if (kp->use_irq) + wake_lock(&kp->wake_lock); + hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + + return 0; + } + + err = 0; + kp = *data; + + if (kp->use_irq) + for (i = mi->noutputs - 1; i >= 0; i--) + free_irq(gpio_to_irq(mi->input_gpios[i]), kp); + + hrtimer_cancel(&kp->timer); + wake_lock_destroy(&kp->wake_lock); + for (i = mi->noutputs - 1; i >= 0; i--) { +err_gpio_direction_input_failed: + gpio_free(mi->input_gpios[i]); +err_request_input_gpio_failed: + ; + } + for (i = mi->noutputs - 1; i >= 0; i--) { +err_output_gpio_configure_failed: + gpio_free(mi->output_gpios[i]); +err_request_output_gpio_failed: + ; + } + kfree(kp); +err_kp_alloc_failed: +err_invalid_platform_data: + return err; +} diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c new file mode 100644 index 00000000000000..6f8453c97bd2f4 --- /dev/null +++ b/drivers/input/misc/gpio_output.c @@ -0,0 +1,84 @@ +/* drivers/input/misc/gpio_output.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 + +int gpio_event_output_event( + struct input_dev *input_dev, struct gpio_event_info *info, void **data, + unsigned int type, unsigned int code, int value) +{ + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + if (type != oi->type) + return 0; + if (!(oi->flags & GPIOEDF_ACTIVE_HIGH)) + value = !value; + for (i = 0; i < oi->keymap_size; i++) + if (code == oi->keymap[i].code) + gpio_set_value(oi->keymap[i].gpio, value); + return 0; +} + +int gpio_event_output_func( + struct input_dev *input_dev, struct gpio_event_info *info, void **data, + int func) +{ + int ret; + int i; + struct gpio_event_output_info *oi; + oi = container_of(info, struct gpio_event_output_info, info); + + if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) + return 0; + + if (func == GPIO_EVENT_FUNC_INIT) { + int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH); + for (i = 0; i < oi->keymap_size; i++) + input_set_capability(input_dev, oi->type, + oi->keymap[i].code); + + for (i = 0; i < oi->keymap_size; i++) { + ret = gpio_request(oi->keymap[i].gpio, + "gpio_event_output"); + if (ret) { + pr_err("gpio_event_output_func: gpio_request " + "failed for %d\n", oi->keymap[i].gpio); + goto err_gpio_request_failed; + } + ret = gpio_direction_output(oi->keymap[i].gpio, + output_level); + if (ret) { + pr_err("gpio_event_output_func: " + "gpio_direction_output failed for %d\n", + oi->keymap[i].gpio); + goto err_gpio_direction_output_failed; + } + } + return 0; + } + + ret = 0; + for (i = oi->keymap_size - 1; i >= 0; i--) { +err_gpio_direction_output_failed: + gpio_free(oi->keymap[i].gpio); +err_gpio_request_failed: + ; + } + return ret; +} + diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c new file mode 100644 index 00000000000000..ea94f26dadf26f --- /dev/null +++ b/drivers/input/misc/keychord.c @@ -0,0 +1,386 @@ +/* + * drivers/input/misc/keychord.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include + +#define KEYCHORD_NAME "keychord" +#define BUFFER_SIZE 16 + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Key chord input driver"); +MODULE_SUPPORTED_DEVICE("keychord"); +MODULE_LICENSE("GPL"); + +#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \ + ((char *)kc + sizeof(struct input_keychord) + \ + kc->count * sizeof(kc->keycodes[0]))) + +struct keychord_device { + struct input_handler input_handler; + int registered; + + /* list of keychords to monitor */ + struct input_keychord *keychords; + int keychord_count; + + /* bitmask of keys contained in our keychords */ + unsigned long keybit[BITS_TO_LONGS(KEY_CNT)]; + /* current state of the keys */ + unsigned long keystate[BITS_TO_LONGS(KEY_CNT)]; + /* number of keys that are currently pressed */ + int key_down; + + /* second input_device_id is needed for null termination */ + struct input_device_id device_ids[2]; + + spinlock_t lock; + wait_queue_head_t waitq; + unsigned char head; + unsigned char tail; + __u16 buff[BUFFER_SIZE]; +}; + +static int check_keychord(struct keychord_device *kdev, + struct input_keychord *keychord) +{ + int i; + + if (keychord->count != kdev->key_down) + return 0; + + for (i = 0; i < keychord->count; i++) { + if (!test_bit(keychord->keycodes[i], kdev->keystate)) + return 0; + } + + /* we have a match */ + return 1; +} + +static void keychord_event(struct input_handle *handle, unsigned int type, + unsigned int code, int value) +{ + struct keychord_device *kdev = handle->private; + struct input_keychord *keychord; + unsigned long flags; + int i, got_chord = 0; + + if (type != EV_KEY || code >= KEY_MAX) + return; + + spin_lock_irqsave(&kdev->lock, flags); + /* do nothing if key state did not change */ + if (!test_bit(code, kdev->keystate) == !value) + goto done; + __change_bit(code, kdev->keystate); + if (value) + kdev->key_down++; + else + kdev->key_down--; + + /* don't notify on key up */ + if (!value) + goto done; + /* ignore this event if it is not one of the keys we are monitoring */ + if (!test_bit(code, kdev->keybit)) + goto done; + + keychord = kdev->keychords; + if (!keychord) + goto done; + + /* check to see if the keyboard state matches any keychords */ + for (i = 0; i < kdev->keychord_count; i++) { + if (check_keychord(kdev, keychord)) { + kdev->buff[kdev->head] = keychord->id; + kdev->head = (kdev->head + 1) % BUFFER_SIZE; + got_chord = 1; + break; + } + /* skip to next keychord */ + keychord = NEXT_KEYCHORD(keychord); + } + +done: + spin_unlock_irqrestore(&kdev->lock, flags); + + if (got_chord) + wake_up_interruptible(&kdev->waitq); +} + +static int keychord_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + int i, ret; + struct input_handle *handle; + struct keychord_device *kdev = + container_of(handler, struct keychord_device, input_handler); + + /* + * ignore this input device if it does not contain any keycodes + * that we are monitoring + */ + for (i = 0; i < KEY_MAX; i++) { + if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit)) + break; + } + if (i == KEY_MAX) + return -ENODEV; + + handle = kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev = dev; + handle->handler = handler; + handle->name = KEYCHORD_NAME; + handle->private = kdev; + + ret = input_register_handle(handle); + if (ret) + goto err_input_register_handle; + + ret = input_open_device(handle); + if (ret) + goto err_input_open_device; + + pr_info("keychord: using input dev %s for fevent\n", dev->name); + + return 0; + +err_input_open_device: + input_unregister_handle(handle); +err_input_register_handle: + kfree(handle); + return ret; +} + +static void keychord_disconnect(struct input_handle *handle) +{ + input_close_device(handle); + input_unregister_handle(handle); + kfree(handle); +} + +/* + * keychord_read is used to read keychord events from the driver + */ +static ssize_t keychord_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + __u16 id; + int retval; + unsigned long flags; + + if (count < sizeof(id)) + return -EINVAL; + count = sizeof(id); + + if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + retval = wait_event_interruptible(kdev->waitq, + kdev->head != kdev->tail); + if (retval) + return retval; + + spin_lock_irqsave(&kdev->lock, flags); + /* pop a keychord ID off the queue */ + id = kdev->buff[kdev->tail]; + kdev->tail = (kdev->tail + 1) % BUFFER_SIZE; + spin_unlock_irqrestore(&kdev->lock, flags); + + if (copy_to_user(buffer, &id, count)) + return -EFAULT; + + return count; +} + +/* + * keychord_write is used to configure the driver + */ +static ssize_t keychord_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct keychord_device *kdev = file->private_data; + struct input_keychord *keychords = 0; + struct input_keychord *keychord, *next, *end; + int ret, i, key; + unsigned long flags; + + if (count < sizeof(struct input_keychord)) + return -EINVAL; + keychords = kzalloc(count, GFP_KERNEL); + if (!keychords) + return -ENOMEM; + + /* read list of keychords from userspace */ + if (copy_from_user(keychords, buffer, count)) { + kfree(keychords); + return -EFAULT; + } + + /* unregister handler before changing configuration */ + if (kdev->registered) { + input_unregister_handler(&kdev->input_handler); + kdev->registered = 0; + } + + spin_lock_irqsave(&kdev->lock, flags); + /* clear any existing configuration */ + kfree(kdev->keychords); + kdev->keychords = 0; + kdev->keychord_count = 0; + kdev->key_down = 0; + memset(kdev->keybit, 0, sizeof(kdev->keybit)); + memset(kdev->keystate, 0, sizeof(kdev->keystate)); + kdev->head = kdev->tail = 0; + + keychord = keychords; + end = (struct input_keychord *)((char *)keychord + count); + + while (keychord < end) { + next = NEXT_KEYCHORD(keychord); + if (keychord->count <= 0 || next > end) { + pr_err("keychord: invalid keycode count %d\n", + keychord->count); + goto err_unlock_return; + } + if (keychord->version != KEYCHORD_VERSION) { + pr_err("keychord: unsupported version %d\n", + keychord->version); + goto err_unlock_return; + } + + /* keep track of the keys we are monitoring in keybit */ + for (i = 0; i < keychord->count; i++) { + key = keychord->keycodes[i]; + if (key < 0 || key >= KEY_CNT) { + pr_err("keychord: keycode %d out of range\n", + key); + goto err_unlock_return; + } + __set_bit(key, kdev->keybit); + } + + kdev->keychord_count++; + keychord = next; + } + + kdev->keychords = keychords; + spin_unlock_irqrestore(&kdev->lock, flags); + + ret = input_register_handler(&kdev->input_handler); + if (ret) { + kfree(keychords); + kdev->keychords = 0; + return ret; + } + kdev->registered = 1; + + return count; + +err_unlock_return: + spin_unlock_irqrestore(&kdev->lock, flags); + kfree(keychords); + return -EINVAL; +} + +static unsigned int keychord_poll(struct file *file, poll_table *wait) +{ + struct keychord_device *kdev = file->private_data; + + poll_wait(file, &kdev->waitq, wait); + + if (kdev->head != kdev->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int keychord_open(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev; + + kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL); + if (!kdev) + return -ENOMEM; + + spin_lock_init(&kdev->lock); + init_waitqueue_head(&kdev->waitq); + + kdev->input_handler.event = keychord_event; + kdev->input_handler.connect = keychord_connect; + kdev->input_handler.disconnect = keychord_disconnect; + kdev->input_handler.name = KEYCHORD_NAME; + kdev->input_handler.id_table = kdev->device_ids; + + kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT; + __set_bit(EV_KEY, kdev->device_ids[0].evbit); + + file->private_data = kdev; + + return 0; +} + +static int keychord_release(struct inode *inode, struct file *file) +{ + struct keychord_device *kdev = file->private_data; + + if (kdev->registered) + input_unregister_handler(&kdev->input_handler); + kfree(kdev); + + return 0; +} + +static const struct file_operations keychord_fops = { + .owner = THIS_MODULE, + .open = keychord_open, + .release = keychord_release, + .read = keychord_read, + .write = keychord_write, + .poll = keychord_poll, +}; + +static struct miscdevice keychord_misc = { + .fops = &keychord_fops, + .name = KEYCHORD_NAME, + .minor = MISC_DYNAMIC_MINOR, +}; + +static int __init keychord_init(void) +{ + return misc_register(&keychord_misc); +} + +static void __exit keychord_exit(void) +{ + misc_deregister(&keychord_misc); +} + +module_init(keychord_init); +module_exit(keychord_exit); diff --git a/drivers/input/misc/power_button.c b/drivers/input/misc/power_button.c new file mode 100644 index 00000000000000..a76bf8d5a6137a --- /dev/null +++ b/drivers/input/misc/power_button.c @@ -0,0 +1,260 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +enum PM_EVENT{ + PM_STANDBY , + PM_SHUTDOWN, + PM_STANDBY_NOW , + PM_SHUTDOWN_NOW, + PM_NORMAL, + }; + +struct work_struct irq_work; +static struct power_button_platform_data *ops; +struct input_dev *input; +static struct wake_lock power_button_wakeup; + +atomic_t event; + +enum PM_STATUS{ + PM_INIT, + PM_SHUTDOWN_WAITING, +}; + +enum PM_STATUS status_pm; + +static struct class *power_button_class; + +#define SLEEP_CODE 0x58 +#define SHUTDOWN_CODE 0x57 + +void shutdown_ok(void) +{ + printk("shutdown now\n"); + kernel_power_off(); +} + +void standby_ok(void) +{ + printk("standby now\n"); + ops->send_standby_ack(); +} + +static ssize_t shutdown_show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 0); +} + +static ssize_t standby_show_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 0); +} + +static ssize_t shutdown_store_status(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + printk("OS shutdown the system\n"); + atomic_set(&event, PM_SHUTDOWN_NOW); + schedule_work(&irq_work); + return 0; +} + +static ssize_t standby_store_status(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + printk("OS make the systemto be standby\n"); + atomic_set(&event , PM_STANDBY_NOW); + schedule_work(&irq_work); + return 0; +} + +static struct device_attribute bl_device_attributes[] = { + __ATTR(pm_shutdown, 0644, shutdown_show_status, shutdown_store_status), + __ATTR(pm_standby, 0644, standby_show_status, + standby_store_status), + __ATTR_NULL, +}; + +static void power_button_irq_work(struct work_struct *work) +{ + int status; + + status = atomic_read(&event) ; + switch (status) { + case PM_SHUTDOWN_NOW: + shutdown_ok(); + return; + case PM_STANDBY_NOW: + standby_ok(); + return; + case PM_SHUTDOWN: + /* ACK */ + printk("long press is detected\n"); + ops->send_powerdwn_ack(); + input_event(input, EV_KEY, SHUTDOWN_CODE, 1); + input_sync(input); + input_event(input, EV_KEY, SHUTDOWN_CODE, 0); + input_sync(input); + + return; + case PM_STANDBY: + printk("short press is detected\n"); + input_event(input, EV_KEY, SLEEP_CODE, 1); + input_sync(input); + input_event(input, EV_KEY, SLEEP_CODE, 0); + input_sync(input); + + return; + default: + return; + } + +} + +static irqreturn_t standby_handler(int irq, void *dev_id) +{ + atomic_set(&event, PM_STANDBY); + schedule_work(&irq_work); + + return IRQ_HANDLED; +} + +static irqreturn_t shutdown_handler(int irq, void *dev_id) +{ + atomic_set(&event, PM_SHUTDOWN); + schedule_work(&irq_work); + + return IRQ_HANDLED; +} + +static int power_button_probe(struct platform_device *pdev) +{ + int ret = 0; + + ops = pdev->dev.platform_data; + ops->init(shutdown_handler, standby_handler); + + INIT_WORK(&irq_work , power_button_irq_work); + + atomic_set(&event , PM_NORMAL); + + + input = input_allocate_device(); + if (!input) + goto out; + + + input->name = "power-button"; + input->phys = "power-button/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + input_set_capability(input, EV_KEY , SLEEP_CODE); + + input_set_capability(input, EV_KEY , SHUTDOWN_CODE); + + ret = input_register_device(input); + if (ret) { + pr_err("power button: Unable to register input device, " + "error: %d\n", ret); + goto out; + } + + status_pm = PM_INIT; + + printk("power button probe finished\n"); + + return 0; +out: + return ret; +} + +static int power_button_suspend(struct platform_device *pdev) +{ + if (wake_lock_active(&power_button_wakeup)) + return -EBUSY; + + return 0; +} + +static int power_button_resume(struct platform_device *pdev) +{ + printk(KERN_ERR "%s\n", __func__); + input_event(input, EV_KEY, SLEEP_CODE, 1); + input_sync(input); + input_event(input, EV_KEY, SLEEP_CODE, 0); + input_sync(input); + wake_lock_timeout(&power_button_wakeup, HZ * 5); + + return 0; +} + +static struct platform_driver power_button_driver = { + .probe = power_button_probe, + .driver = { + .name = "power-button", + .owner = THIS_MODULE, + }, + .suspend = power_button_suspend, + .resume = power_button_resume, +}; + +static int __init power_button_init(void) +{ + int ret; + + power_button_class = class_create(THIS_MODULE, "power-button"); + if (IS_ERR(power_button_class)) { + printk(KERN_WARNING "Unable to create power_button class; errno = %ld\n", + PTR_ERR(power_button_class)); + return PTR_ERR(power_button_class); + } + + power_button_class->dev_attrs = bl_device_attributes; + + ret = platform_driver_register(&power_button_driver); + if (ret) { + printk(KERN_ERR "power_button_driver register failure\n"); + return ret; + } + + wake_lock_init(&power_button_wakeup, WAKE_LOCK_SUSPEND, "power_button"); + + return 0; +} + +static void __exit power_button_exit(void) +{ + input_unregister_device(input); + platform_driver_unregister(&power_button_driver); + class_destroy(power_button_class); + wake_lock_destroy(&power_button_wakeup); +} + + +module_init(power_button_init); +module_exit(power_button_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/drivers/input/misc/sensors.c b/drivers/input/misc/sensors.c new file mode 100644 index 00000000000000..ccce5947a2587b --- /dev/null +++ b/drivers/input/misc/sensors.c @@ -0,0 +1,624 @@ +/* + * sensors.c - sensor work as input device + * + * Copyright (C) 2008 Jack Ren + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_POLLING_DELAY 0xffffffff +/* #define PEERFORMANCE */ + +DEFINE_MUTEX(sensor_input_lock); +static struct input_dev *sensor_input_idev; /* input device */ +static int sensor_input_usage; +static LIST_HEAD(sensors); + +static int sensor_input_kthread(void *data) +{ + struct sensor_input_dev *sensor = (struct sensor_input_dev *)data; + int delay = sensor->delay; +#ifdef PEERFORMANCE +#define TIMEOUT 4 + int count, last_count; + unsigned long timeout = jiffies + HZ*4; +#endif + + //daemonize(sensor->name); +#ifdef PEERFORMANCE + timeout = jiffies + HZ*TIMEOUT; +#endif + while(!sensor->thread_exit) { + wait_event_timeout(sensor->wait, (delay!=sensor->delay) || sensor->thread_exit, msecs_to_jiffies(delay)); +#ifdef PEERFORMANCE + count++; + if (time_after(jiffies, timeout)) { + printk("%s: %dms/packet\n", sensor->name, TIMEOUT*1000/(count - last_count)); + last_count = count; + timeout = jiffies + HZ*TIMEOUT; + } +#endif + if(sensor->delay==0) + sensor->delay = DEFAULT_POLLING_DELAY; + delay = sensor->delay; + if (mutex_trylock(&sensor_input_lock)) { + if (sensor->on) { + if (sensor->dev) + sensor->report(sensor->dev); + } + mutex_unlock(&sensor_input_lock); + } + } + if (sensor->exit) { + sensor->exit(sensor->dev); + } + //complete_and_exit(&sensor->thread_exit_complete, 0); + return 0; +} + +static int sensor_input_open(struct input_dev *input) +{ + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + mutex_lock(&sensor_input_lock); + + sensor = input_get_drvdata(input); + + if (sensor == NULL) { + if (sensor_input_usage == 0) { +#ifdef CONFIG_INPUT_MERGED_SENSORS + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + sensor->poweron(); + sensor->on = 1; + sensor->thread_exit = 0; + sensor->count++; + sensor->thread_task = kthread_create(sensor_input_kthread, sensor, sensor->name); + wake_up_process(sensor->thread_task); + } +#endif + } + sensor_input_usage++; + } else { + if (sensor->count == 0) { + sensor->poweron(); + sensor->on = 1; + sensor->thread_exit = 0; + sensor->thread_task = kthread_create(sensor_input_kthread, sensor, sensor->name); + wake_up_process(sensor->thread_task); + } + sensor->count++; + } + + mutex_unlock(&sensor_input_lock); + return 0; +} + +static void sensor_input_close(struct input_dev *input) +{ + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + mutex_lock(&sensor_input_lock); + + sensor = (struct sensor_input_dev *)input_get_drvdata(input); + + if (sensor == NULL) { + sensor_input_usage--; + if (sensor_input_usage == 0) { +#ifdef CONFIG_INPUT_MERGED_SENSORS + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + sensor->count--; + if (sensor->count == 0) { + sensor->poweroff(); + sensor->on = 0; + //init_completion(&sensor->thread_exit_complete); + sensor->thread_exit = 1; + wake_up(&sensor->wait); + //wait_for_completion_timeout(&sensor->thread_exit_complete); + kthread_stop(sensor->thread_task); + } + } +#endif + } + } else { + sensor->count--; + if (sensor->count == 0) { + sensor->poweroff(); + sensor->on = 0; + //init_completion(&sensor->thread_exit_complete); + sensor->thread_exit = 1; + wake_up(&sensor->wait); + //wait_for_completion(&sensor->thread_exit_complete); + kthread_stop(sensor->thread_task); + } + } + mutex_unlock(&sensor_input_lock); +} + +/* Sysfs stuff */ +static ssize_t sensor_input_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + mutex_lock(&sensor_input_lock); + list_for_each_entry_safe(sensor, tmp, &sensors, list) + len += sprintf(buf+len, "%s\t%d\n", sensor->name, sensor->delay); + mutex_unlock(&sensor_input_lock); + + return len; +} + +static ssize_t sensor_input_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) +{ + char name[PAGE_SIZE]; + int delay; + int ret; + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + ret = sscanf(buf, "%s %d", name, &delay); + if(ret == 2) { + mutex_lock(&sensor_input_lock); + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + if(!strcmp(name, sensor->name)) { + sensor->delay = delay; + wake_up(&sensor->wait); + printk(KERN_INFO "set %s delay to %dms\n", name, delay); + } + } + mutex_unlock(&sensor_input_lock); + } + + return count; +} + +#ifdef CONFIG_PM +static int sensor_input_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + if(sensor_input_usage == 0) + return 0; + + if(!mutex_trylock(&sensor_input_lock)) + return -EBUSY; + + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + sensor->poweroff(); + sensor->on = 0; + } + + return 0; +} + +static int sensor_input_resume(struct platform_device *pdev) +{ + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + if(sensor_input_usage == 0) + return 0; + + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + sensor->poweron(); + sensor->on = 1; + } + mutex_unlock(&sensor_input_lock); + + return 0; +} +#else +#define sensor_input_suspend NULL +#define sensor_input_resume NULL +#endif + +#ifdef CONFIG_PROC_FS +static ssize_t sensor_input_read_proc(struct file *file, + char *buffer, size_t length, loff_t *offset) +{ + int len = 0; + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + printk("Sensor Input List: \n"); + mutex_lock(&sensor_input_lock); + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + printk("\tname: %s\tpower:%d\n", + sensor->name, sensor->on?1:0); + } + mutex_unlock(&sensor_input_lock); + + return 0; +} + +static ssize_t sensor_input_write_proc(struct file *file, + const char __user *buffer, size_t count, loff_t *offset) +{ + char input[PAGE_SIZE]; + int power, i=0; + char name[40]; + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + if (copy_from_user(input, buffer, PAGE_SIZE)) + return -EFAULT; + + input[PAGE_SIZE-1] = 0; + i = sscanf(input, "%s %d", name, &power); + + if(i == 2) { + mutex_lock(&sensor_input_lock); + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + if (!strcmp(name, sensor->name)) { + sensor->on = power; + if (power) { + sensor->thread_exit = 0; + kernel_thread(sensor_input_kthread, sensor, 0); + printk(KERN_INFO "name %s\tpower: %d\n", sensor->name, power); + } else { + //init_completion(&sensor->thread_exit_complete); + sensor->thread_exit = 1; + wake_up(&sensor->wait); + //wait_for_completion(&sensor->thread_exit_complete); + kthread_stop(sensor->thread_task); + } + } + } + mutex_unlock(&sensor_input_lock); + } + + return count; +} + +static struct file_operations sensor_input_proc_ops = { + .read = sensor_input_read_proc, + .write = sensor_input_write_proc, +}; + +static void create_sensor_input_proc_file(void) +{ + struct proc_dir_entry *sensor_input_proc_file = + create_proc_entry("driver/sensor-input", 0644, NULL); + + if (sensor_input_proc_file){ + sensor_input_proc_file->owner = THIS_MODULE; + sensor_input_proc_file->proc_fops = &sensor_input_proc_ops; + } + else + printk(KERN_INFO "proc file create failed!\n"); +} + +extern struct proc_dir_entry proc_root; +static void remove_sensor_input_proc_file(void) +{ + remove_proc_entry("driver/sensor-input", &proc_root); +} +#endif + +struct device_attribute dev_attr_sensor_input = { + .attr = { + .name = "sensors", + .mode = 0664, + }, + .show = sensor_input_show, + .store = sensor_input_store, +}; + +static struct attribute *sensor_input_attributes[] = { + &dev_attr_sensor_input.attr, + NULL +}; + +static struct attribute_group sensor_input_attribute_group = { + .attrs = sensor_input_attributes +}; + +static int __devinit sensor_input_probe(struct platform_device *pdev) +{ + int err; + + if (sensor_input_idev) + return -EINVAL; + + sensor_input_idev = input_allocate_device(); + if (!sensor_input_idev) + return -ENOMEM; + + sensor_input_idev->name = "sensor-input"; + sensor_input_idev->phys = "sensor-input/input0"; + sensor_input_idev->open = sensor_input_open; + sensor_input_idev->close = sensor_input_close; + +#ifdef CONFIG_INPUT_MERGED_SENSORS + /* used as orientation sensor */ + __set_bit(EV_ABS, sensor_input_idev->evbit); + __set_bit(ABS_RX, sensor_input_idev->absbit); + __set_bit(ABS_RY, sensor_input_idev->absbit); + __set_bit(ABS_RZ, sensor_input_idev->absbit); + + /* used as acceleration sensor */ + __set_bit(ABS_X, sensor_input_idev->absbit); + __set_bit(ABS_Y, sensor_input_idev->absbit); + __set_bit(ABS_Z, sensor_input_idev->absbit); + + /* used as raw data */ + __set_bit(ABS_HAT1X, sensor_input_idev->absbit); + __set_bit(ABS_HAT2X, sensor_input_idev->absbit); + __set_bit(ABS_HAT3X, sensor_input_idev->absbit); + __set_bit(ABS_MISC, sensor_input_idev->absbit); + + __set_bit(ABS_PRESSURE, sensor_input_idev->absbit); + __set_bit(ABS_DISTANCE, sensor_input_idev->absbit); + + /* used as keyboard */ + __set_bit(EV_KEY, sensor_input_idev->evbit); + __set_bit(EV_REP, sensor_input_idev->evbit); + __set_bit(KEY_SEND, sensor_input_idev->keybit); + __set_bit(KEY_3, sensor_input_idev->keybit); + __set_bit(KEY_RIGHTCTRL, sensor_input_idev->keybit); + __set_bit(KEY_HOME, sensor_input_idev->keybit); + + __set_bit(EV_SYN, sensor_input_idev->evbit); +#endif + err = input_register_device(sensor_input_idev); + if (err) { + printk(KERN_ERR "register input driver error\n"); + input_free_device(sensor_input_idev); + sensor_input_idev = NULL; + return err; + } + + input_set_drvdata(sensor_input_idev, NULL); + err = sysfs_create_group(&pdev->dev.kobj, &sensor_input_attribute_group); + if (err != 0){ + printk(KERN_ERR "register sysfs error\n"); + return err; + } + +#ifdef CONFIG_PROC_FS + create_sensor_input_proc_file(); +#endif + return 0; +} + +static int sensor_input_remove(struct platform_device *pdev) +{ + if(!list_empty(&sensors)) + return -EBUSY; + input_unregister_device(sensor_input_idev); + sensor_input_idev = NULL; + sysfs_remove_group(&pdev->dev.kobj, &sensor_input_attribute_group); + +#ifdef CONFIG_PROC_FS + remove_sensor_input_proc_file(); +#endif + return 0; +} + +static struct platform_driver sensor_input_driver = { + .probe = sensor_input_probe, + .remove = sensor_input_remove, + .suspend = sensor_input_suspend, + .resume = sensor_input_resume, + .driver = { + .name = "sensor_input", + .owner = THIS_MODULE, + }, +}; + +int __init sensor_input_init(void) +{ + return platform_driver_register(&sensor_input_driver); +} + +void __exit sensor_input_exit(void) +{ + platform_driver_unregister(&sensor_input_driver); +} + +int sensor_input_add(int type, char *name, + void (*report)(struct input_dev *), + void (*exit)(struct input_dev *), + void (*poweron)(void), + void (*poweroff)(void)) +{ + struct sensor_input_dev *sensor = + kzalloc(sizeof(struct sensor_input_dev), GFP_KERNEL); + + struct input_dev *sensor_input_idev_ext; + + sensor->dev = NULL; + sensor->name = name; + sensor->report = report; + sensor->exit = exit; + sensor->poweron = poweron; + sensor->poweroff = poweroff; + sensor->delay = DEFAULT_POLLING_DELAY; + init_waitqueue_head(&sensor->wait); + mutex_lock(&sensor_input_lock); + list_add(&sensor->list, &sensors); +#ifdef CONFIG_INPUT_MERGED_SENSORS + if (sensor_input_usage != 0) { + sensor->poweron(); + sensor->on = 1; + sensor->thread_exit = 0; + kernel_thread(sensor_input_kthread, sensor, 0); + } +#endif + mutex_unlock(&sensor_input_lock); + +#ifdef CONFIG_INPUT_MERGED_SENSORS + sensor->dev = sensor_input_idev; +#else + sensor_input_idev_ext = input_allocate_device(); + if (!sensor_input_idev) + return -ENOMEM; + + sensor_input_idev_ext->phys = "sensor-input/input0"; + sensor_input_idev_ext->open = sensor_input_open; + sensor_input_idev_ext->close = sensor_input_close; + sensor_input_idev_ext->name = name; + + if (type == INPUT_G_SENSOR) { + /* used as acceleration sensor */ + __set_bit(EV_ABS, sensor_input_idev_ext->evbit); + __set_bit(ABS_X, sensor_input_idev_ext->absbit); + __set_bit(ABS_Y, sensor_input_idev_ext->absbit); + __set_bit(ABS_Z, sensor_input_idev_ext->absbit); + + input_set_abs_params(sensor_input_idev_ext, ABS_X, -100000, 100000, 0, 0); + input_set_abs_params(sensor_input_idev_ext, ABS_Y, -100000, 100000, 0, 0); + input_set_abs_params(sensor_input_idev_ext, ABS_Z, -100000, 100000, 0, 0); + input_set_abs_params(sensor_input_idev_ext, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(sensor_input_idev_ext, ABS_TOOL_WIDTH, 0, 15, 0, 0); + + input_set_drvdata(sensor_input_idev_ext, sensor); + int err = input_register_device(sensor_input_idev_ext); + if (err) { + printk(KERN_ERR "register input driver error\n"); + input_free_device(sensor_input_idev_ext); + sensor_input_idev_ext = NULL; + return err; + } + + } else if (type == INPUT_GYRO_SENSOR) { + /* used as orientation sensor */ + __set_bit(EV_ABS, sensor_input_idev_ext->evbit); + + __set_bit(ABS_RX, sensor_input_idev_ext->absbit); + __set_bit(ABS_RY, sensor_input_idev_ext->absbit); + __set_bit(ABS_RZ, sensor_input_idev_ext->absbit); + + /* used as keyboard */ + __set_bit(EV_KEY, sensor_input_idev_ext->evbit); + __set_bit(EV_REP, sensor_input_idev_ext->evbit); + __set_bit(KEY_SEND, sensor_input_idev_ext->keybit); + __set_bit(KEY_3, sensor_input_idev_ext->keybit); + __set_bit(KEY_RIGHTCTRL, sensor_input_idev_ext->keybit); + __set_bit(KEY_HOME, sensor_input_idev_ext->keybit); + + __set_bit(EV_SYN, sensor_input_idev_ext->evbit); + + /* used as raw data */ + __set_bit(ABS_HAT1X, sensor_input_idev_ext->absbit); + __set_bit(ABS_HAT2X, sensor_input_idev_ext->absbit); + __set_bit(ABS_HAT3X, sensor_input_idev_ext->absbit); + __set_bit(ABS_MISC, sensor_input_idev_ext->absbit); + + input_set_abs_params(sensor_input_idev_ext, ABS_RX, -100000, 100000, 0, 0); + input_set_abs_params(sensor_input_idev_ext, ABS_RY, -100000, 100000, 0, 0); + input_set_abs_params(sensor_input_idev_ext, ABS_RZ, -100000, 100000, 0, 0); + + input_set_drvdata(sensor_input_idev_ext, sensor); + int err = input_register_device(sensor_input_idev_ext); + if (err) { + printk(KERN_ERR "register input driver error\n"); + input_free_device(sensor_input_idev_ext); + sensor_input_idev_ext = NULL; + return err; + } + + } else if (type == INPUT_AMBIENT_SENSOR) { + /*used as light sensor*/ + __set_bit(EV_ABS, sensor_input_idev_ext->evbit); + __set_bit(ABS_PRESSURE, sensor_input_idev_ext->absbit); + + input_set_abs_params(sensor_input_idev_ext, ABS_PRESSURE, -100000, 100000000, 0, 0); + + input_set_drvdata(sensor_input_idev_ext, sensor); + int err = input_register_device(sensor_input_idev_ext); + if (err) { + printk(KERN_ERR "register input driver error\n"); + input_free_device(sensor_input_idev_ext); + sensor_input_idev_ext = NULL; + return err; + } + + } else if (type == INPUT_PROXIMITY_SENSOR) { + /*used as light sensor*/ + __set_bit(EV_ABS, sensor_input_idev_ext->evbit); + __set_bit(ABS_DISTANCE, sensor_input_idev_ext->absbit); + + input_set_abs_params(sensor_input_idev_ext, ABS_DISTANCE, 0, 1, 0, 0); + + input_set_drvdata(sensor_input_idev_ext, sensor); + int err = input_register_device(sensor_input_idev_ext); + if (err) { + printk(KERN_ERR "register input driver error\n"); + input_free_device(sensor_input_idev_ext); + sensor_input_idev_ext = NULL; + return err; + } + + } else { + input_free_device(sensor_input_idev_ext); + sensor_input_idev_ext = NULL; + return -1; + } + sensor->dev = sensor_input_idev_ext; +#endif + return 0; +} + +void sensor_input_del(char *name) +{ + struct sensor_input_dev *sensor; + struct sensor_input_dev *tmp; + + mutex_lock(&sensor_input_lock); + list_for_each_entry_safe(sensor, tmp, &sensors, list) { + if(!strcmp(name, sensor->name)) { + if (sensor->count != 0) + return; + input_unregister_device(sensor->dev); + list_del(&sensor->list); + kfree(sensor); + return; + } + } + mutex_unlock(&sensor_input_lock); + printk(KERN_ERR "Try to remove a unknow sensor: %s\n", name); +} + +MODULE_DESCRIPTION("Sensor Input driver"); +MODULE_AUTHOR("Bin Yang "); +MODULE_LICENSE("GPL"); + +module_init(sensor_input_init); +module_exit(sensor_input_exit); + diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 8a8fa4d2d6a842..5d49355d75facd 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -307,6 +307,12 @@ config TOUCHSCREEN_MIGOR To compile this driver as a module, choose M here: the module will be called migor_ts. +config TOUCHSCREEN_SYNAPTICS_I2C_RMI + tristate "Synaptics i2c touchscreen" + depends on I2C + help + This enables support for Synaptics RMI over I2C based touchscreens. + config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO @@ -565,16 +571,43 @@ config TOUCHSCREEN_TOUCHIT213 To compile this driver as a module, choose M here: the module will be called touchit213. -config TOUCHSCREEN_TSC2007 - tristate "TSC2007 based touchscreens" - depends on I2C +config TSC2007 + boolean "TSC2007 chip" + depends on I2C && I2C_PXA + default n help - Say Y here if you have a TSC2007 based touchscreen. + TSC2007 touch screen support. + If you say yes here, you support for it. - If unsure, say N. + This driver can NOT be built as a module. - To compile this driver as a module, choose M here: the - module will be called tsc2007. +config TOUCHSCREEN_SANREMO + bool "SANREMO Touchscreen interface support" + depends on I2C && I2C_PXA && SANREMO + default y + help + Say Y here to enable support for the Sanremo touchscreen controller. + +config TOUCHSCREEN_TPO + bool "TPO Touchscreen interface support" + depends on I2C && I2C_PXA + default y + help + Say Y here to enable support for the TPO touchscreen controller. + +config TOUCHSCREEN_MICCO + tristate "MICCO based touchscreen" + depends on I2C && I2C_PXA && MICCO + default y + help + Say Y here if you want to enable micco touch driver + +config TOUCHSCREEN_MICCO_TIMER + tristate "optimize performane using timer" + depends on TOUCHSCREEN_MICCO + default y + help + Say Y here if you want to optimize micco touch performance config TOUCHSCREEN_W90X900 tristate "W90P910 touchscreen driver" diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 7fef7d5cca234b..1ff72a7456a329 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI) += synaptics_i2c_rmi.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o @@ -44,5 +45,9 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o +obj-$(CONFIG_TSC2007) += tsc2007.o ts_linear.o +obj-$(CONFIG_SANREMO) += sanremo_touch.o +obj-$(CONFIG_TOUCHSCREEN_TPO) += tpo_touch.o +obj-$(CONFIG_TOUCHSCREEN_MICCO) += micco_touch.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o diff --git a/drivers/input/touchscreen/micco_touch.c b/drivers/input/touchscreen/micco_touch.c new file mode 100644 index 00000000000000..08df429800a052 --- /dev/null +++ b/drivers/input/touchscreen/micco_touch.c @@ -0,0 +1,599 @@ +/* + * linux/drivers/input/touchscreen/micco_touch.c + * + * touch screen driver for Littleton Platform + * + * Copyright (C) 2006, Marvell Corporation (fengwei.yin@Marvell.com) + * + * 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 + +/* micco_ts_opt: + * =0, 1, 2, 3: + * sampling rate is higher while the opt value is higher. + * Touch timer block time is higher while the opt value is higher. + */ +static int micco_ts_opt = 3; +enum { + TSI_EVENT_NONE = 0, + TSI_EVENT_TOUCH = 1, + TSI_EVENT_LIFT = 2 +}; + +enum { + TSI_PEN_UNKNOW = 0, + TSI_PEN_DOWN = 1, + TSI_PEN_UP = 2 +}; + +struct micco_ts_data { + spinlock_t ts_lock; + struct task_struct *thread; + int suspended; + int pen_state; + int use_count; +#ifdef CONFIG_TOUCHSCREEN_MICCO_TIMER + struct timer_list *ts_timer; + int step; +#else + wait_queue_head_t ts_wait_queue; + struct completion thread_init; + struct completion thread_exit; +#endif +}; + +static struct micco_ts_data *micco_td; +static struct input_dev *micco_ts_input_dev; + +static int micco_ts_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + static char kbuf[1024]; + int arg; + + if (count >= 1024) + return -EINVAL; + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + + sscanf(kbuf, "%d", &arg); + if(arg <= 3) + micco_ts_opt = arg; + printk("micco ts opt = %d\n", micco_ts_opt); + + return count; +} + +int micco_ts_read(u16 *x, u16 *y, int *pen_state) +{ + int ret; + + ret = micco_tsi_readxy(x, y, *pen_state); + return ret; +} + +/* + * The callback function for micco touch. + * Just turn on the LCD backligtht when PEN DOWN on touch. + * Leave all the touch X,Y read to a kernel thread. + * + * TODO: + * 1. Move the backlight out of the touch driver. + * 2. Make backlight align with kernel LCD backlight driver. + * 3. Make user application handle the touch activity + * turn on the LCD backlight. + */ +void micco_ts_interrupt(unsigned long event) +{ + if (micco_td->use_count > 0) { +#ifdef CONFIG_TOUCHSCREEN_MICCO_TIMER + if (TSI_PEN_UP == micco_td->pen_state) { + micco_td->pen_state = TSI_PEN_DOWN; + micco_td->ts_timer->expires = jiffies; + micco_td->step = 0; + add_timer(micco_td->ts_timer); + } +#else + wake_up_interruptible(&micco_td->ts_wait_queue); +#endif + } + + return; +} +EXPORT_SYMBOL(micco_ts_interrupt); + +extern int is_android(void); +#ifdef CONFIG_TOUCHSCREEN_MICCO_TIMER +static void micco_ts_timer_handler(unsigned long d) +{ + struct micco_ts_data *ts_data = (struct micco_ts_data *)d; + static u16 tem_x = 0xffff; + static u16 tem_y = 0xffff; + int ret = 0, state; + u8 val; + unsigned long next; + int cur_step; + + cur_step = ts_data->step; +_start: + switch (ts_data->step) { + case 0: + tem_x = 0xffff; + tem_y = 0xffff; + if (0 == micco_read(MICCO_STATUS_A, &val)) { + /* pen down */ + if (val & 0x40) { + if (TSI_PEN_UP == ts_data->pen_state) { + ts_data->pen_state = TSI_PEN_DOWN; + pr_debug("%s: touch pen down!", __func__); + } + + /* Enable the auto measure of the TSI. + * This will disable pen down interupt automatically. + */ + micco_tsi_enable_tsi(1); + ts_data->step = 1; + if (micco_ts_opt >= 3) { + mdelay(1); + }else { + next = 1; + break; + } + } else { + ts_data->step = 3; + } + }else { + next = 1; + break; + } + case 1: + micco_read(MICCO_EVENT_C, &val); + if(val & 0x20) { + ts_data->step = 2; + }else { + pr_debug("%s: micco c polling val = 0x%x", + __func__, val); + } + if (micco_ts_opt >= 1) { + if(ts_data->step == 1) { + mdelay(1); + goto _start; + } + }else{ + next = 1; + break; + } + case 2: + pr_debug("%s: micco c val = 0x%x when TSI ready", + __func__, val); + + micco_ts_read(&tem_x, &tem_y, &state); + pr_debug("%s: tem_x:0x%x; tem_y:0x%x; pen_state:0x%x", + __func__, tem_x, tem_y, state); + case 3: + micco_tsi_enable_tsi(0); + micco_td->step = 4; + if (micco_ts_opt >= 2) { + mdelay(1); + }else { + next = 1; + break; + } + case 4: + cur_step = 4; + ret = micco_read(MICCO_STATUS_A, &val); + pr_debug("%s: after tsi disable, before pen_down" \ + "enabled status a = 0x%x", __func__, val); + if (val & 0x40) { + /* Pen still down. The X, Y data is valid. + * Report it. + */ + if (tem_x > 1000) tem_x = 1000; + if (tem_y > 1000) tem_y = 1000; + + if (machine_is_littleton()) + tem_x = 1000 - (tem_x & 0xfff); + if (machine_is_tavorevb()) + tem_y = 1000 - (tem_y & 0xfff); + input_report_abs(micco_ts_input_dev, + ABS_X, tem_x & 0xfff); + input_report_abs(micco_ts_input_dev, + ABS_Y, tem_y & 0xfff); + if (!is_android()) + input_report_abs(micco_ts_input_dev, + ABS_PRESSURE, 255); + if (is_android()) { + input_report_key(micco_ts_input_dev, + BTN_TOUCH, 1); + input_sync(micco_ts_input_dev); + } + + next = 1; + } else { /* Pen is up now */ + /* Report a pen up event */ + + if (!is_android()) { + input_report_abs(micco_ts_input_dev, + ABS_PRESSURE, 0); + input_report_abs(micco_ts_input_dev, + ABS_TOOL_WIDTH, 1); + } + if (is_android()) { + input_report_key(micco_ts_input_dev, + BTN_TOUCH, 0); + input_sync(micco_ts_input_dev); + } + pr_debug("%s: report pen up", __func__); + micco_tsi_enable_pen(1); + ts_data->pen_state = TSI_PEN_UP; + next = 0xffffffff; + } + micco_td->step = 0; + break; + default: + break; + } + + if(next != 0xffffffff) { + ts_data->ts_timer->expires = jiffies+msecs_to_jiffies(next); + ts_data->ts_timer->data = ts_data; + add_timer(ts_data->ts_timer); + } + + return; +} +#else +/* + * The touchscreen sample reader thread + */ +static int micco_ts_thread(void *d) +{ + struct micco_ts_data *ts_data = d; + u16 tem_x = 0xffff; + u16 tem_y = 0xffff; + int ret = 0, state; + u8 val; + + DEFINE_WAIT(ts_wait); + /* set up thread context */ + + ts_data->thread = current; + daemonize("micco_ts_thread"); + + /* init is complete */ + complete(&ts_data->thread_init); + + /* touch reader loop */ + while (1) { + /* if the pen state is up, sleep */ + if (TSI_PEN_UP == ts_data->pen_state) { + prepare_to_wait(&ts_data->ts_wait_queue, + &ts_wait, TASK_INTERRUPTIBLE); + + if (TSI_PEN_UP == ts_data->pen_state) + schedule(); + + finish_wait(&ts_data->ts_wait_queue, &ts_wait); + } + + try_to_freeze(); + + /* Now the pen state is down */ + ret = micco_read(MICCO_STATUS_A, &val); + + /* pen down */ + if (val & 0x40) { + if (TSI_PEN_UP == ts_data->pen_state) { + ts_data->pen_state = TSI_PEN_DOWN; + pr_debug("%s: touch pen down!", __func__); + } + + /* Enable the auto measure of the TSI. + * This will disable pen down interupt automatically. + */ + micco_tsi_enable_tsi(1); + mdelay(1); + micco_read(MICCO_EVENT_C, &val); + while (!(val & 0x20)) { + pr_debug("%s: micco c val = 0x%x", + __func__, val); + micco_read(MICCO_EVENT_C, &val); + mdelay(1); + } + pr_debug("%s: micco c val = 0x%x when TSI ready", + __func__, val); + + micco_ts_read(&tem_x, &tem_y, &state); + pr_debug("%s: tem_x:0x%x; tem_y:0x%x; pen_state:0x%x", + __func__, tem_x, tem_y, state); + } + + micco_tsi_enable_tsi(0); + /* After disable the auto tsi, need wait a while to read the + * correct pen_down state. Currently, just wait for 1 ms. + */ + + mdelay(1); + ret = micco_read(MICCO_STATUS_A, &val); + pr_debug("%s: after tsi disable, before pen_down" \ + "enabled status a = 0x%x", __func__, val); + + if (val & 0x40) { + /* Pen still down. The X, Y data is valid. + * Report it. + */ + if (tem_x > 1000) tem_x = 1000; + if (tem_y > 1000) tem_y = 1000; + if (tem_x < 0 ) tem_x = 0; + if (tem_y < 0 ) tem_y = 0; + + if (machine_is_littleton()) + tem_x = 1000 - (tem_x & 0xfff); + if (machine_is_tavorevb()) + tem_y = 1000 - (tem_y & 0xfff); + input_report_abs(micco_ts_input_dev, + ABS_X, tem_x & 0xfff); + input_report_abs(micco_ts_input_dev, + ABS_Y, tem_y & 0xfff); + input_report_abs(micco_ts_input_dev, + ABS_PRESSURE, 255); + if (is_android()) + input_report_key(micco_ts_input_dev, + BTN_TOUCH, 1); + msleep(8); + + /* don't enable the pen down interrupt because + * the pen down interrupt enabling for Micco will + * trigger an pen down interrupt. polling to get + * the next pen X, Y. + */ + continue; + } else { /* Pen is up now */ + /* Report a pen up event */ + input_report_abs(micco_ts_input_dev, + ABS_PRESSURE, 0); + input_report_abs(micco_ts_input_dev, + ABS_TOOL_WIDTH, 1); + if (is_android()) { + input_report_key(micco_ts_input_dev, + BTN_TOUCH, 0); + input_sync(micco_ts_input_dev); + } + pr_debug("%s: report pen up", __func__); + ts_data->pen_state = TSI_PEN_UP; + micco_tsi_enable_pen(1); + } + + if (!ts_data->thread) + break; + } + + /* Always enable the pen down detection before exit from thread */ + micco_tsi_enable_pen(1); + complete_and_exit(&ts_data->thread_exit, 0); + return 0; +} +#endif + +static int micco_ts_open(struct input_dev *idev) +{ + int ret = 0; + unsigned long flags; + + pr_debug("%s: enter", __func__); + + if (micco_td->suspended) { + printk(KERN_INFO "touch has been suspended!\n"); + return -1; + } + + spin_lock_irqsave(&micco_td->ts_lock, flags); + if (micco_td->use_count++ == 0) { + spin_unlock_irqrestore(&micco_td->ts_lock, flags); + +#ifdef CONFIG_TOUCHSCREEN_MICCO_TIMER + micco_td->ts_timer = + (struct timer_list *)kmalloc(sizeof(struct timer_list), GFP_KERNEL); + init_timer(micco_td->ts_timer); + micco_td->ts_timer->function = micco_ts_timer_handler; + micco_td->ts_timer->data = micco_td; +#else + init_completion(&micco_td->thread_init); + ret = kernel_thread(micco_ts_thread, micco_td, 0); + if (ret < 0) + return ret; + + wait_for_completion(&micco_td->thread_init); +#endif + } else { + spin_unlock_irqrestore(&micco_td->ts_lock, flags); + } + + return 0; +} + +/* Kill the touchscreen thread and stop the touch digitiser. */ +static void micco_ts_close(struct input_dev *idev) +{ + unsigned long flags; + + pr_debug("%s: enter with use count = %d", __func__, + micco_td->use_count); + + spin_lock_irqsave(&micco_td->ts_lock, flags); + if (--micco_td->use_count == 0) { + spin_unlock_irqrestore(&micco_td->ts_lock, flags); + +#ifdef CONFIG_TOUCHSCREEN_MICCO_TIMER + kfree(micco_td->ts_timer); +#else + pr_debug("%s: kill thread", __func__); + /* kill thread */ + if (micco_td->thread) { + init_completion(&micco_td->thread_exit); + micco_td->thread = NULL; + wake_up_interruptible(&micco_td->ts_wait_queue); + wait_for_completion(&micco_td->thread_exit); + } +#endif + } else { + spin_unlock_irqrestore(&micco_td->ts_lock, flags); + } +} + +static int micco_ts_probe(struct platform_device *pdev) +{ + int ret; + struct proc_dir_entry *micco_ts_proc_entry; + + micco_td = kzalloc(sizeof(struct micco_ts_data), GFP_KERNEL); + if (!micco_td) { + ret = -ENOMEM; + goto micco_ts_out; + } + spin_lock_init(&micco_td->ts_lock); +#ifndef CONFIG_TOUCHSCREEN_MICCO_TIMER + init_waitqueue_head(&micco_td->ts_wait_queue); +#endif + micco_td->pen_state = TSI_PEN_UP; + micco_td->suspended = 0; + micco_td->use_count = 0; + + /* register input device */ + micco_ts_input_dev = input_allocate_device(); + if (micco_ts_input_dev == NULL) { + printk(KERN_ERR "%s: failed to allocate input dev\n", + __FUNCTION__); + return -ENOMEM; + } + + micco_ts_input_dev->name = "micco-ts"; + + micco_ts_input_dev->phys = "micco-ts/input0"; + micco_ts_input_dev->dev.parent = &pdev->dev; + + micco_ts_input_dev->open = micco_ts_open; + micco_ts_input_dev->close = micco_ts_close; + + __set_bit(EV_ABS, micco_ts_input_dev->evbit); + __set_bit(ABS_X, micco_ts_input_dev->absbit); + __set_bit(ABS_Y, micco_ts_input_dev->absbit); + __set_bit(ABS_PRESSURE, micco_ts_input_dev->absbit); + + __set_bit(EV_SYN, micco_ts_input_dev->evbit); + __set_bit(EV_KEY, micco_ts_input_dev->evbit); + __set_bit(BTN_TOUCH, micco_ts_input_dev->keybit); + + input_set_abs_params(micco_ts_input_dev, ABS_X, 0, 1000, 0, 0); + input_set_abs_params(micco_ts_input_dev, ABS_Y, 0, 1000, 0, 0); + input_set_abs_params(micco_ts_input_dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(micco_ts_input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); + + ret = input_register_device(micco_ts_input_dev); + if (ret) { + printk(KERN_ERR + "%s: unabled to register input device, ret = %d\n", + __FUNCTION__, ret); + return ret; + } + + /* The Littleton IRQ come from Micco. So we just implemate + * a callback IRQ function for Micco. + */ + ret = pmic_callback_register(PMIC_EVENT_TOUCH, micco_ts_interrupt); + if (ret < 0) + goto pmic_cb_out; + + /* Enable the touch IRQ here for lcd backlight */ + micco_enable_pen_down_irq(1); + micco_tsi_poweron(); + micco_ts_proc_entry = create_proc_entry("driver/micco_touch", 0, NULL); + if (micco_ts_proc_entry) { + micco_ts_proc_entry->write_proc = micco_ts_proc_write; + } + return 0; + +pmic_cb_out: + input_unregister_device(micco_ts_input_dev); +micco_ts_out: + kfree(micco_td); + + return ret; +} + +static int micco_ts_remove(struct platform_device *pdev) +{ + input_unregister_device(micco_ts_input_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int micco_ts_resume(struct platform_device *pdev) +{ + micco_enable_pen_down_irq(1); + micco_tsi_poweron(); + micco_td->suspended = 0; + + return 0; +} + +static int micco_ts_suspend(struct platform_device *pdev, pm_message_t state) +{ + micco_td->suspended = 1; + micco_tsi_poweroff(); + micco_enable_pen_down_irq(0); + + return 0; +} +#else +#define micco_ts_resume NULL +#define micco_ts_suspend NULL +#endif + +static struct platform_driver micco_ts_driver = { + .driver = { + .name = "micco-touch", + }, + .probe = micco_ts_probe, + .remove = micco_ts_remove, + .resume = micco_ts_resume, + .suspend = micco_ts_suspend, +}; + +static int __init micco_ts_init(void) +{ + return platform_driver_register(&micco_ts_driver); +} + +static void __exit micco_ts_exit(void) +{ + /* We move these codes here because we want to detect the + * pen down event even when touch driver is not opened. + */ + micco_tsi_poweroff(); + micco_enable_pen_down_irq(0); + pmic_callback_unregister(PMIC_EVENT_TOUCH, micco_ts_interrupt); + + platform_driver_unregister(&micco_ts_driver); +} + +module_init(micco_ts_init); +module_exit(micco_ts_exit); + +MODULE_AUTHOR("Yin, Fengwei "); +MODULE_DESCRIPTION("Littleton Platfrom touch screen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/sanremo_touch.c b/drivers/input/touchscreen/sanremo_touch.c new file mode 100644 index 00000000000000..e4e059d048ab59 --- /dev/null +++ b/drivers/input/touchscreen/sanremo_touch.c @@ -0,0 +1,251 @@ +/* + * linux/drivers/input/touchscreen/sanremo_touch.c + * + * touch screen driver for TTC DKB Platform + * + * Copyright (C) 2009, Marvell Corporation (bin.yang@Marvell.com) + * Author: Bin Yang + * Yael Sheli Chemla + * + * 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 + +struct sanremo_ts_data { + int use_count; + struct timer_list *ts_timer; +}; + +static struct sanremo_ts_data *sanremo_td; +static struct input_dev *sanremo_ts_input_dev; +static unsigned *ts; +static int *ts_revert; + +void sanremo_ts_interrupt(unsigned long event) +{ + if (sanremo_td->use_count > 0) { + sanremo_td->ts_timer->expires = jiffies+msecs_to_jiffies(100); + add_timer(sanremo_td->ts_timer); + sanremo_enable_pen_down_irq(0); + } + + return; +} + +extern int is_android(void); +static void sanremo_ts_timer_handler(unsigned long d) +{ + static u16 tem_x = 0xffff; + static u16 tem_y = 0xffff; + int pen_state; + + sanremo_tsi_readxy(&tem_x, &tem_y, &pen_state); + if (pen_state) { + if(ts_revert[0] == -1) + tem_x = ts[1] - tem_x + ts[0]; + if(ts_revert[1] == -1) + tem_y = ts[3] - tem_y + ts[2]; + + input_report_abs(sanremo_ts_input_dev, + ABS_X, tem_x); + input_report_abs(sanremo_ts_input_dev, + ABS_Y, tem_y); + if (!is_android()) + input_report_abs(sanremo_ts_input_dev, + ABS_PRESSURE, 255); + if (is_android()) { + input_report_key(sanremo_ts_input_dev, + BTN_TOUCH, 1); + input_sync(sanremo_ts_input_dev); + } + sanremo_td->ts_timer->expires = jiffies+msecs_to_jiffies(100); + add_timer(sanremo_td->ts_timer); + } else { + /* Report a pen up event */ + if (!is_android()) { + input_report_abs(sanremo_ts_input_dev, + ABS_PRESSURE, 0); + input_report_abs(sanremo_ts_input_dev, + ABS_TOOL_WIDTH, 1); + } + if (is_android()) { + input_report_key(sanremo_ts_input_dev, + BTN_TOUCH, 0); + input_sync(sanremo_ts_input_dev); + } + sanremo_enable_pen_down_irq(1); + } + + return; +} + +static int sanremo_ts_open(struct input_dev *idev) +{ + if (sanremo_td->use_count++ == 0) { + sanremo_enable_pen_down_irq(1); + sanremo_tsi_poweron(); + sanremo_tsi_enable_pen(1); + sanremo_tsi_enable_tsi(1); + } + return 0; +} + +static void sanremo_ts_close(struct input_dev *idev) +{ + if(--sanremo_td->use_count == 0) { + sanremo_enable_pen_down_irq(0); + sanremo_tsi_poweroff(); + sanremo_tsi_enable_pen(0); + sanremo_tsi_enable_tsi(0); + } +} + +static int sanremo_ts_probe(struct platform_device *pdev) +{ + int ret; + u8 tmp; + struct sanremo_touch_platform_data *pdata = pdev->dev.platform_data; + + if(sanremo_read(SANREMO_ID, &tmp) < 0) + return -ENODEV; + + sanremo_td = kzalloc(sizeof(struct sanremo_ts_data), GFP_KERNEL); + if (!sanremo_td) { + ret = -ENOMEM; + goto sanremo_ts_out; + } + + ts = pdata->ts; + ts_revert = pdata->ts_revert; + + sanremo_td->use_count = 0; + sanremo_td->ts_timer = + (struct timer_list *)kmalloc(sizeof(struct timer_list), GFP_KERNEL); + init_timer(sanremo_td->ts_timer); + sanremo_td->ts_timer->function = sanremo_ts_timer_handler; +// sanremo_td->ts_timer->data = sanremo_td; + + /* register input device */ + sanremo_ts_input_dev = input_allocate_device(); + if (sanremo_ts_input_dev == NULL) { + printk(KERN_ERR "%s: failed to allocate input dev\n", + __FUNCTION__); + return -ENOMEM; + } + + sanremo_ts_input_dev->name = "sanremo-ts"; + + sanremo_ts_input_dev->phys = "sanremo-ts/input0"; + sanremo_ts_input_dev->dev.parent = &pdev->dev; + + sanremo_ts_input_dev->open = sanremo_ts_open; + sanremo_ts_input_dev->close = sanremo_ts_close; + + __set_bit(EV_ABS, sanremo_ts_input_dev->evbit); + __set_bit(ABS_X, sanremo_ts_input_dev->absbit); + __set_bit(ABS_Y, sanremo_ts_input_dev->absbit); + __set_bit(ABS_PRESSURE, sanremo_ts_input_dev->absbit); + + __set_bit(EV_SYN, sanremo_ts_input_dev->evbit); + __set_bit(EV_KEY, sanremo_ts_input_dev->evbit); + __set_bit(BTN_TOUCH, sanremo_ts_input_dev->keybit); + + input_set_abs_params(sanremo_ts_input_dev, ABS_X, ts[0], ts[1], 0, 0); + input_set_abs_params(sanremo_ts_input_dev, ABS_Y, ts[2], ts[3], 0, 0); + input_set_abs_params(sanremo_ts_input_dev, ABS_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(sanremo_ts_input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); + + ret = input_register_device(sanremo_ts_input_dev); + if (ret) { + printk(KERN_ERR + "%s: unabled to register input device, ret = %d\n", + __FUNCTION__, ret); + return ret; + } + + /* The Littleton IRQ come from Sanremo. So we just implemate + * a callback IRQ function for Sanremo. + */ + ret = pmic_callback_register(PMIC_EVENT_TOUCH, sanremo_ts_interrupt); + if (ret < 0) + goto pmic_cb_out; + + return 0; + +pmic_cb_out: + input_unregister_device(sanremo_ts_input_dev); +sanremo_ts_out: + kfree(sanremo_td); + + return ret; +} + +static int sanremo_ts_remove(struct platform_device *pdev) +{ + pmic_callback_unregister(PMIC_EVENT_TOUCH, sanremo_ts_interrupt); + input_unregister_device(sanremo_ts_input_dev); + kfree(sanremo_td->ts_timer); + kfree(sanremo_td); + + return 0; +} + +#ifdef CONFIG_PM +static int sanremo_ts_resume(struct platform_device *pdev) +{ + sanremo_enable_pen_down_irq(1); + sanremo_tsi_poweron(); + + return 0; +} + +static int sanremo_ts_suspend(struct platform_device *pdev, pm_message_t state) +{ + sanremo_tsi_poweroff(); + sanremo_enable_pen_down_irq(0); + + return 0; +} +#else +#define sanremo_ts_resume NULL +#define sanremo_ts_suspend NULL +#endif + +static struct platform_driver sanremo_ts_driver = { + .driver = { + .name = "sanremo_touch", + }, + .probe = sanremo_ts_probe, + .remove = sanremo_ts_remove, + .resume = sanremo_ts_resume, + .suspend = sanremo_ts_suspend, +}; + +static int __init sanremo_ts_init(void) +{ + return platform_driver_register(&sanremo_ts_driver); +} + +static void __exit sanremo_ts_exit(void) +{ + platform_driver_unregister(&sanremo_ts_driver); +} + +module_init(sanremo_ts_init); +module_exit(sanremo_ts_exit); + +MODULE_AUTHOR("Bin Yang "); +MODULE_DESCRIPTION("Sanremo touch screen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c new file mode 100644 index 00000000000000..a45634b74fe419 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c @@ -0,0 +1,632 @@ +/* drivers/input/keyboard/synaptics_i2c_rmi.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 +#include +#include +#include +#include + +#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0) + +static struct workqueue_struct *synaptics_wq; + +struct synaptics_ts_data { + uint16_t addr; + struct i2c_client *client; + struct input_dev *input_dev; + int use_irq; + struct hrtimer timer; + struct work_struct work; + uint16_t max[2]; + int snap_state[2][2]; + int snap_down_on[2]; + int snap_down_off[2]; + int snap_up_on[2]; + int snap_up_off[2]; + int snap_down[2]; + int snap_up[2]; + uint32_t flags; + int (*power)(int on); + struct early_suspend early_suspend; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h); +static void synaptics_ts_late_resume(struct early_suspend *h); +#endif + +static int synaptics_init_panel(struct synaptics_ts_data *ts) +{ + int ret; + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_page_select_failed; + } + ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n"); + +err_page_select_failed: + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n"); + return ret; +} + +static void synaptics_ts_work_func(struct work_struct *work) +{ + int i; + int ret; + int bad_data = 0; + struct i2c_msg msg[2]; + uint8_t start_reg; + uint8_t buf[15]; + struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work); + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x00; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = sizeof(buf); + msg[1].buf = buf; + + /* printk("synaptics_ts_work_func\n"); */ + for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n"); + bad_data = 1; + } else { + /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */ + /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */ + /* buf[0], buf[1], buf[2], buf[3], */ + /* buf[4], buf[5], buf[6], buf[7], */ + /* buf[8], buf[9], buf[10], buf[11], */ + /* buf[12], buf[13], buf[14], ret); */ + if ((buf[14] & 0xc0) != 0x40) { + printk(KERN_WARNING "synaptics_ts_work_func:" + " bad read %x %x %x %x %x %x %x %x %x" + " %x %x %x %x %x %x, ret %d\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], ret); + if (bad_data) + synaptics_init_panel(ts); + bad_data = 1; + continue; + } + bad_data = 0; + if ((buf[14] & 1) == 0) { + /* printk("read %d coordinates\n", i); */ + break; + } else { + int pos[2][2]; + int f, a; + int base; + /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */ + /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */ + int z = buf[1]; + int w = buf[0] >> 4; + int finger = buf[0] & 7; + + /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */ + /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */ + /* int z2 = buf[1+6]; */ + /* int w2 = buf[0+6] >> 4; */ + /* int finger2 = buf[0+6] & 7; */ + + /* int dx = (int8_t)buf[12]; */ + /* int dy = (int8_t)buf[13]; */ + int finger2_pressed; + + /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */ + /* x, y, z, w, finger, */ + /* x2, y2, z2, w2, finger2, */ + /* dx, dy); */ + + base = 2; + for (f = 0; f < 2; f++) { + uint32_t flip_flag = SYNAPTICS_FLIP_X; + for (a = 0; a < 2; a++) { + int p = buf[base + 1]; + p |= (uint16_t)(buf[base] & 0x1f) << 8; + if (ts->flags & flip_flag) + p = ts->max[a] - p; + if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) { + if (ts->snap_state[f][a]) { + if (p <= ts->snap_down_off[a]) + p = ts->snap_down[a]; + else if (p >= ts->snap_up_off[a]) + p = ts->snap_up[a]; + else + ts->snap_state[f][a] = 0; + } else { + if (p <= ts->snap_down_on[a]) { + p = ts->snap_down[a]; + ts->snap_state[f][a] = 1; + } else if (p >= ts->snap_up_on[a]) { + p = ts->snap_up[a]; + ts->snap_state[f][a] = 1; + } + } + } + pos[f][a] = p; + base += 2; + flip_flag <<= 1; + } + base += 2; + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(pos[f][0], pos[f][1]); + } + if (z) { + input_report_abs(ts->input_dev, ABS_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_Y, pos[0][1]); + } + input_report_abs(ts->input_dev, ABS_PRESSURE, z); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w); + input_report_key(ts->input_dev, BTN_TOUCH, finger); + finger2_pressed = finger > 1 && finger != 7; + input_report_key(ts->input_dev, BTN_2, finger2_pressed); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]); + } + input_sync(ts->input_dev); + } + } + } + if (ts->use_irq) + enable_irq(ts->client->irq); +} + +static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) +{ + struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer); + /* printk("synaptics_ts_timer_func\n"); */ + + queue_work(synaptics_wq, &ts->work); + + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id) +{ + struct synaptics_ts_data *ts = dev_id; + + /* printk("synaptics_ts_irq_handler\n"); */ + disable_irq(ts->client->irq); + queue_work(synaptics_wq, &ts->work); + return IRQ_HANDLED; +} + +static int synaptics_ts_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct synaptics_ts_data *ts; + uint8_t buf0[4]; + uint8_t buf1[8]; + struct i2c_msg msg[2]; + int ret = 0; + uint16_t max_x, max_y; + int fuzz_x, fuzz_y, fuzz_p, fuzz_w; + struct synaptics_i2c_rmi_platform_data *pdata; + int inactive_area_left; + int inactive_area_right; + int inactive_area_top; + int inactive_area_bottom; + int snap_left_on; + int snap_left_off; + int snap_right_on; + int snap_right_off; + int snap_top_on; + int snap_top_off; + int snap_bottom_on; + int snap_bottom_off; + uint32_t panel_version; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + INIT_WORK(&ts->work, synaptics_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + pdata = client->dev.platform_data; + if (pdata) + ts->power = pdata->power; + if (ts->power) { + ret = ts->power(1); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_probe power on failed\n"); + goto err_power_failed; + } + } + + ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + /* fail? */ + } + { + int retry = 10; + while (retry-- > 0) { + ret = i2c_smbus_read_byte_data(ts->client, 0xe4); + if (ret >= 0) + break; + msleep(100); + } + } + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret); + panel_version = ret << 8; + ret = i2c_smbus_read_byte_data(ts->client, 0xe5); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret); + panel_version |= ret; + + ret = i2c_smbus_read_byte_data(ts->client, 0xe3); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret); + + if (pdata) { + while (pdata->version > panel_version) + pdata++; + ts->flags = pdata->flags; + inactive_area_left = pdata->inactive_left; + inactive_area_right = pdata->inactive_right; + inactive_area_top = pdata->inactive_top; + inactive_area_bottom = pdata->inactive_bottom; + snap_left_on = pdata->snap_left_on; + snap_left_off = pdata->snap_left_off; + snap_right_on = pdata->snap_right_on; + snap_right_off = pdata->snap_right_off; + snap_top_on = pdata->snap_top_on; + snap_top_off = pdata->snap_top_off; + snap_bottom_on = pdata->snap_bottom_on; + snap_bottom_off = pdata->snap_bottom_off; + fuzz_x = pdata->fuzz_x; + fuzz_y = pdata->fuzz_y; + fuzz_p = pdata->fuzz_p; + fuzz_w = pdata->fuzz_w; + } else { + inactive_area_left = 0; + inactive_area_right = 0; + inactive_area_top = 0; + inactive_area_bottom = 0; + snap_left_on = 0; + snap_left_off = 0; + snap_right_on = 0; + snap_right_off = 0; + snap_top_on = 0; + snap_top_off = 0; + snap_bottom_on = 0; + snap_bottom_off = 0; + fuzz_x = 0; + fuzz_y = 0; + fuzz_p = 0; + fuzz_w = 0; + } + + ret = i2c_smbus_read_byte_data(ts->client, 0xf0); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret); + + ret = i2c_smbus_read_byte_data(ts->client, 0xf1); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret); + + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + goto err_detect_failed; + } + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf0; + buf0[0] = 0xe0; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 8; + msg[1].buf = buf1; + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "i2c_transfer failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n", + buf1[0], buf1[1], buf1[2], buf1[3], + buf1[4], buf1[5], buf1[6], buf1[7]); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_detect_failed; + } + ret = i2c_smbus_read_word_data(ts->client, 0x04); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + ret = i2c_smbus_read_word_data(ts->client, 0x06); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(max_x, max_y); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) { + printk(KERN_ERR "synaptics_init_panel failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "synaptics-rmi-touchscreen"; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + inactive_area_left = inactive_area_left * max_x / 0x10000; + inactive_area_right = inactive_area_right * max_x / 0x10000; + inactive_area_top = inactive_area_top * max_y / 0x10000; + inactive_area_bottom = inactive_area_bottom * max_y / 0x10000; + snap_left_on = snap_left_on * max_x / 0x10000; + snap_left_off = snap_left_off * max_x / 0x10000; + snap_right_on = snap_right_on * max_x / 0x10000; + snap_right_off = snap_right_off * max_x / 0x10000; + snap_top_on = snap_top_on * max_y / 0x10000; + snap_top_off = snap_top_off * max_y / 0x10000; + snap_bottom_on = snap_bottom_on * max_y / 0x10000; + snap_bottom_off = snap_bottom_off * max_y / 0x10000; + fuzz_x = fuzz_x * max_x / 0x10000; + fuzz_y = fuzz_y * max_y / 0x10000; + ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left; + ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right; + ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top; + ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom; + ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on; + ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off; + ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on; + ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off; + ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on; + ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off; + ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on; + ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off; + printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y); + printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n", + inactive_area_left, inactive_area_right, + inactive_area_top, inactive_area_bottom); + printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n", + snap_left_on, snap_left_off, snap_right_on, snap_right_off, + snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off); + input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + /* ts->input_dev->name = ts->keypad_info->name; */ + ret = input_register_device(ts->input_dev); + if (ret) { + printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name); + goto err_input_register_device_failed; + } + if (client->irq) { + ret = request_irq(client->irq, synaptics_ts_irq_handler, 0, client->name, ts); + if (ret == 0) { + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + if (ret) + free_irq(client->irq, ts); + } + if (ret == 0) + ts->use_irq = 1; + else + dev_err(&client->dev, "request_irq failed\n"); + } + if (!ts->use_irq) { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = synaptics_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = synaptics_ts_early_suspend; + ts->early_suspend.resume = synaptics_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling"); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: +err_detect_failed: +err_power_failed: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + +static int synaptics_ts_remove(struct i2c_client *client) +{ + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + unregister_early_suspend(&ts->early_suspend); + if (ts->use_irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input_dev); + kfree(ts); + return 0; +} + +static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->use_irq) + disable_irq(client->irq); + else + hrtimer_cancel(&ts->timer); + ret = cancel_work_sync(&ts->work); + if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */ + enable_irq(client->irq); + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + if (ts->power) { + ret = ts->power(0); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power off failed\n"); + } + return 0; +} + +static int synaptics_ts_resume(struct i2c_client *client) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(1); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power on failed\n"); + } + + synaptics_init_panel(ts); + + if (ts->use_irq) + enable_irq(client->irq); + + if (!ts->use_irq) + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + else + i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void synaptics_ts_late_resume(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id synaptics_ts_id[] = { + { SYNAPTICS_I2C_RMI_NAME, 0 }, + { } +}; + +static struct i2c_driver synaptics_ts_driver = { + .probe = synaptics_ts_probe, + .remove = synaptics_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = synaptics_ts_suspend, + .resume = synaptics_ts_resume, +#endif + .id_table = synaptics_ts_id, + .driver = { + .name = SYNAPTICS_I2C_RMI_NAME, + }, +}; + +static int __devinit synaptics_ts_init(void) +{ + synaptics_wq = create_singlethread_workqueue("synaptics_wq"); + if (!synaptics_wq) + return -ENOMEM; + return i2c_add_driver(&synaptics_ts_driver); +} + +static void __exit synaptics_ts_exit(void) +{ + i2c_del_driver(&synaptics_ts_driver); + if (synaptics_wq) + destroy_workqueue(synaptics_wq); +} + +module_init(synaptics_ts_init); +module_exit(synaptics_ts_exit); + +MODULE_DESCRIPTION("Synaptics Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tpo_touch.c b/drivers/input/touchscreen/tpo_touch.c new file mode 100644 index 00000000000000..642a563667d3e0 --- /dev/null +++ b/drivers/input/touchscreen/tpo_touch.c @@ -0,0 +1,335 @@ +/* + * * Copyright (C) 2009, Marvell Corporation(bin.yang@marvell.com). + * * + * * Author: Bin Yang + * * + * * This software program is licensed subject to the GNU General Public License + * * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html + * */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct i2c_client *g_client; +static struct input_dev *tpo_ts_input_dev; +struct work_struct tpo_ts_work; +static struct timer_list tpo_timer; +#define TPO_LEN 12 +static u8 tpo_buf[TPO_LEN]; +#define TPO_PEN_UP 0 +#define TPO_PEN_DOWN 1 +static int pen_status = TPO_PEN_UP; + +int tpo_touch_read_reg(u8 reg, u8 *pval) +{ + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + ret = i2c_smbus_read_byte_data(g_client, reg); + if (ret >= 0) { + *pval = ret; + status = 0; + } else { + status = -EIO; + } + + return status; +} + +int tpo_touch_write_reg(u8 reg, u8 val) +{ + int ret; + int status; + + if (g_client == NULL) /* No global client pointer? */ + return -1; + ret = i2c_smbus_write_byte_data(g_client, reg, val); + if (ret == 0) + status = 0; + else + status = -EIO; + + return status; +} + +static int tpo_touch_read(char *buf, int count) +{ + int ret; + + ret = i2c_master_recv(g_client, (char *)buf, count); + + return ret; +} + +static int tpo_touch_recieve_data(void) +{ + return tpo_touch_read(tpo_buf, TPO_LEN); +} + +#define tpo_16(x) ((((u16)tpo_buf[x] & 0xff) << 8) | (u16)tpo_buf[x+1]) +static void tpo_touch_work(struct work_struct *work) +{ + u16 tem_x1 = 0xffff; + u16 tem_y1 = 0xffff; + u8 tmp; + + int ret = tpo_touch_recieve_data(); + if (ret < 0) { + printk(KERN_ERR "%s: failed to receive data", __func__); + return; + } + + tmp = tpo_buf[10]; + + if(tmp == 1) { + if (pen_status == TPO_PEN_DOWN) { + tem_x1 = tpo_16(2); + tem_y1 = tpo_16(4); + tem_y1 = 480 - tem_y1; + input_report_abs(tpo_ts_input_dev, ABS_PRESSURE, 255); + input_report_abs(tpo_ts_input_dev, ABS_X, tem_x1); + input_report_abs(tpo_ts_input_dev, ABS_Y, tem_y1); + input_report_key(tpo_ts_input_dev, BTN_TOUCH, 1); + input_sync(tpo_ts_input_dev); + } + pen_status = TPO_PEN_DOWN; + mod_timer(&tpo_timer, jiffies + msecs_to_jiffies(50)); + } else if (tmp == 0) { + if(pen_status == TPO_PEN_DOWN) { + pen_status = TPO_PEN_UP; + input_report_abs(tpo_ts_input_dev, ABS_PRESSURE, 0); + input_report_key(tpo_ts_input_dev, BTN_TOUCH, 0); + input_sync(tpo_ts_input_dev); + } + } +} + +static irqreturn_t tpo_touch_irq_handler(int irq, void *dev_id) +{ + schedule_work(&tpo_ts_work); + return IRQ_HANDLED; +} + +#ifdef CONFIG_PM +static int tpo_touch_suspend(struct i2c_client *client, pm_message_t state) +{ + tpo_touch_write_reg(0, 0x10); //sleep mode + return 0; +} + +static int tpo_touch_resume(struct i2c_client *client) +{ + tpo_touch_write_reg(0, 0); //normal mode + return 0; +} +#else +#define tpo_touch_suspend NULL +#define tpo_touch_resume NULL +#endif + +#ifdef CONFIG_PROC_FS +#define TPO_TOUCH_PROC_FILE "driver/tpo_touch" +static struct proc_dir_entry *tpo_touch_proc_file; +static int index; + +static ssize_t tpo_touch_proc_read(struct file *filp, + char *buffer, size_t length, loff_t *offset) +{ + u8 reg_val; + + if ((index < 0) || (index > TPO_LEN)) + return 0; + + tpo_touch_read_reg(index, ®_val); + printk(KERN_INFO "register 0x%x: 0x%x\n", index, reg_val); + return 0; +} + +static ssize_t tpo_touch_proc_write(struct file *filp, + const char *buff, size_t len, loff_t *off) +{ + u8 reg_val; + char messages[256], vol[256]; + + if (len > 256) + len = 256; + + if (copy_from_user(messages, buff, len)) + return -EFAULT; + + if ('-' == messages[0]) { + /* set the register index */ + memcpy(vol, messages+1, len-1); + index = (int) simple_strtoul(vol, NULL, 16); + } else { + /* set the register value */ + reg_val = (int)simple_strtoul(messages, NULL, 16); + tpo_touch_write_reg(index, reg_val & 0xFF); + } + + return len; +} + +static struct file_operations tpo_touch_proc_ops = { + .read = tpo_touch_proc_read, + .write = tpo_touch_proc_write, +}; + +static void create_tpo_touch_proc_file(void) +{ + tpo_touch_proc_file = create_proc_entry(TPO_TOUCH_PROC_FILE, 0644, NULL); + if (tpo_touch_proc_file) { + tpo_touch_proc_file->proc_fops = &tpo_touch_proc_ops; + } else + printk(KERN_INFO "proc file create failed!\n"); +} + +static void remove_tpo_touch_proc_file(void) +{ + extern struct proc_dir_entry proc_root; + remove_proc_entry(TPO_TOUCH_PROC_FILE, &proc_root); +} +#endif + +static int tpo_touch_open(struct input_dev *idev) +{ + return 0; +} + +static void tpo_touch_close(struct input_dev *idev) +{ + return; +} + +static void tpo_send_event_workaround(unsigned long data) +{ + schedule_work(&tpo_ts_work); +} + +static int __devinit tpo_touch_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + + g_client = client; + + ret = tpo_touch_recieve_data(); + if (ret < 0) { + printk(KERN_WARNING "tpo_touch unavailable!\n"); + g_client = NULL; + return -ENXIO; + } else { + printk(KERN_INFO "tpo_touch(chip id:0x%02x) detected.\n", tpo_buf[0]); + } + + /* register input device */ + tpo_ts_input_dev = input_allocate_device(); + if (tpo_ts_input_dev == NULL) { + printk(KERN_ERR "%s: failed to allocate input dev\n", + __FUNCTION__); + return -ENOMEM; + } + + tpo_ts_input_dev->name = "tpo-ts"; + tpo_ts_input_dev->phys = "tpo-ts/input0"; + tpo_ts_input_dev->open = tpo_touch_open; + tpo_ts_input_dev->close = tpo_touch_close; + + __set_bit(EV_ABS, tpo_ts_input_dev->evbit); + __set_bit(ABS_X, tpo_ts_input_dev->absbit); + __set_bit(ABS_Y, tpo_ts_input_dev->absbit); + __set_bit(ABS_PRESSURE, tpo_ts_input_dev->absbit); + + __set_bit(EV_SYN, tpo_ts_input_dev->evbit); + __set_bit(EV_KEY, tpo_ts_input_dev->evbit); + __set_bit(BTN_TOUCH, tpo_ts_input_dev->keybit); + + input_set_abs_params(tpo_ts_input_dev, ABS_X, 0, 320, 0, 0); + input_set_abs_params(tpo_ts_input_dev, ABS_Y, 0, 480, 0, 0); + input_set_abs_params(tpo_ts_input_dev, ABS_PRESSURE, 0, 255, 0, 0); + + ret = input_register_device(tpo_ts_input_dev); + if (ret) { + printk(KERN_ERR + "%s: unabled to register input device, ret = %d\n", + __FUNCTION__, ret); + return ret; + } + + INIT_WORK(&tpo_ts_work, tpo_touch_work); + + init_timer(&tpo_timer); + tpo_timer.function = tpo_send_event_workaround; + tpo_timer.data = (long)NULL; + + ret = request_irq(client->irq, tpo_touch_irq_handler, IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "tpo touch", client); + + if (ret) { + printk(KERN_WARNING "Request IRQ for Bigstream touch failed, return:%d\n", + ret); + return ret; + } + +#ifdef CONFIG_PROC_FS + create_tpo_touch_proc_file(); +#endif + return 0; +} + +static int tpo_touch_remove(struct i2c_client *client) +{ +#ifdef CONFIG_PROC_FS + remove_tpo_touch_proc_file(); +#endif + input_unregister_device(tpo_ts_input_dev); + return 0; +} + +static const struct i2c_device_id tpo_touch_id[] = { + { "tpo_touch", 0 }, + { } +}; + +static struct i2c_driver tpo_touch_driver = { + .driver = { + .name = "tpo_touch", + }, + .id_table = tpo_touch_id, + .probe = tpo_touch_probe, + .remove = tpo_touch_remove, + .suspend = tpo_touch_suspend, + .resume = tpo_touch_resume, +}; + +static int __init tpo_touch_init(void) +{ + return i2c_add_driver(&tpo_touch_driver); +} + +static void __exit tpo_touch_exit(void) +{ + i2c_del_driver(&tpo_touch_driver); +} + +module_init(tpo_touch_init); +module_exit(tpo_touch_exit); + +MODULE_DESCRIPTION("TPO touch Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/ts_linear.c b/drivers/input/touchscreen/ts_linear.c new file mode 100644 index 00000000000000..1771836b6c98df --- /dev/null +++ b/drivers/input/touchscreen/ts_linear.c @@ -0,0 +1,153 @@ +/* + * Touchscreen Linear Scale Adaptor + * + * Copyright (C) 2009 Marvell Corporation + * + * Author: Mark F. Brown + * Based on tslib 1.0 plugin linear.c by Russel King + * + * This library is licensed under GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * sysctl-tuning infrastructure. + */ +static struct ts_calibration { +/* Linear scaling and offset parameters for x,y (can include rotation) */ + int a[7]; +} cal; + +static ctl_table ts_proc_calibration_table[] = { + { + .procname = "a0", + .data = &cal.a[0], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "a1", + .data = &cal.a[1], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "a2", + .data = &cal.a[2], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "a3", + .data = &cal.a[3], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "a4", + .data = &cal.a[4], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "a5", + .data = &cal.a[5], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "a6", + .data = &cal.a[6], + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + + { } +}; + +static ctl_table ts_proc_root[] = { + { + .procname = "ts_device", + .mode = 0555, + .child = ts_proc_calibration_table, + }, + { } +}; + +static ctl_table ts_dev_root[] = { + { + .procname = "dev", + .mode = 0555, + .child = ts_proc_root, + }, + { } +}; + +static struct ctl_table_header *ts_sysctl_header; + +int ts_linear_scale(int *x, int *y, int swap_xy) +{ + int xtemp, ytemp; + + xtemp = *x; + ytemp = *y; + + if (cal.a[6] == 0) + return -EINVAL; + + *x = (cal.a[2] + cal.a[0] * xtemp + cal.a[1] * ytemp) / cal.a[6]; + *y = (cal.a[5] + cal.a[3] * xtemp + cal.a[4] * ytemp) / cal.a[6]; + + if (swap_xy) { + int tmp = *x; + *x = *y; + *y = tmp; + } + return 0; +} + +EXPORT_SYMBOL(ts_linear_scale); + +static int __init ts_linear_init(void) +{ + ts_sysctl_header = register_sysctl_table(ts_dev_root); + /* Use default values that leave ts numbers unchanged after transform */ + cal.a[0] = 1; + cal.a[1] = 0; + cal.a[2] = 0; + cal.a[3] = 0; + cal.a[4] = 1; + cal.a[5] = 0; + cal.a[6] = 1; + return 0; +} + +static void __exit ts_linear_cleanup(void) +{ + unregister_sysctl_table(ts_sysctl_header); +} + +module_init(ts_linear_init); +module_exit(ts_linear_cleanup); + +MODULE_DESCRIPTION("touch screen linear scaling driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c index be23780e8a3e3b..556de244e9f257 100644 --- a/drivers/input/touchscreen/tsc2007.c +++ b/drivers/input/touchscreen/tsc2007.c @@ -1,19 +1,9 @@ /* - * drivers/input/touchscreen/tsc2007.c + * linux/drivers/input/touchscreen/tsc2007.c * - * Copyright (c) 2008 MtekVision Co., Ltd. - * Kwangwoo Lee + * touch screen driver for tsc2007 * - * Using code from: - * - ads7846.c - * Copyright (c) 2005 David Brownell - * Copyright (c) 2006 Nokia Corporation - * - corgi_ts.c - * Copyright (C) 2004-2005 Richard Purdie - * - omap_ts.[hc], ads7846.h, ts_osk.c - * Copyright (C) 2002 MontaVista Software - * Copyright (C) 2004 Texas Instruments - * Copyright (C) 2005 Dirk Behme + * Copyright (C) 2006, Marvell Corporation * * 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 @@ -21,373 +11,401 @@ */ #include -#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include -#include - -#define TS_POLL_DELAY 1 /* ms delay between samples */ -#define TS_POLL_PERIOD 1 /* ms delay between samples */ - -#define TSC2007_MEASURE_TEMP0 (0x0 << 4) -#define TSC2007_MEASURE_AUX (0x2 << 4) -#define TSC2007_MEASURE_TEMP1 (0x4 << 4) -#define TSC2007_ACTIVATE_XN (0x8 << 4) -#define TSC2007_ACTIVATE_YN (0x9 << 4) -#define TSC2007_ACTIVATE_YP_XN (0xa << 4) -#define TSC2007_SETUP (0xb << 4) -#define TSC2007_MEASURE_X (0xc << 4) -#define TSC2007_MEASURE_Y (0xd << 4) -#define TSC2007_MEASURE_Z1 (0xe << 4) -#define TSC2007_MEASURE_Z2 (0xf << 4) - -#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) -#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) -#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) -#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) - -#define TSC2007_12BIT (0x0 << 1) -#define TSC2007_8BIT (0x1 << 1) - -#define MAX_12BIT ((1 << 12) - 1) - -#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) - -#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) -#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) -#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) -#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) -#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) - -struct ts_event { - u16 x; - u16 y; - u16 z1, z2; +#include + +#include +#include + +extern int ts_linear_scale(int *x, int *y, int swap_xy); + +/* Use MAV filter */ +#define TSC_CMD_SETUP 0xb0 + +/* Use 12-bit */ +#define TSC_CMD_X 0xc0 +#define TSC_CMD_PLATEX 0x80 +#define TSC_CMD_Y 0xd0 +#define TSC_CMD_PLATEY 0x90 + +#define TSC_X_MAX 4096 +#define TSC_Y_MAX 4096 +#define TSC_X_MIN 0 +#define TSC_Y_MIN 0 + +/* delay time for compute x, y, computed as us */ + +#define DEBUG +#ifdef DEBUG +#define TS_DEBUG(fmt,args...) printk(KERN_DEBUG fmt, ##args ) +#else +#define TS_DEBUG(fmt,args...) +#endif +static int x_min=TSC_X_MIN; +static int y_min=TSC_Y_MIN; +static int x_max=TSC_X_MAX; +static int y_max=TSC_Y_MAX; +static int invert = 0; +static int debounce_time = 150; +static int init_debounce = true; +static int delay_time = 1; + +enum tsc2007_status { + PEN_UP, + PEN_DOWN, }; -struct tsc2007 { - struct input_dev *input; - char phys[32]; - struct delayed_work work; +struct _tsc2007 { + struct input_dev *dev; + int x; /* X sample values */ + int y; /* Y sample values */ - struct i2c_client *client; - - u16 model; - u16 x_plate_ohms; - - bool pendown; - int irq; - - int (*get_pendown_state)(void); - void (*clear_penirq)(void); + int status; + struct work_struct irq_work; + struct i2c_client *client; + unsigned long last_touch; }; +struct _tsc2007 *g_tsc2007; -static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) +/* update abs params when min and max coordinate values are set */ +int tsc2007_proc_minmax(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) { - s32 data; - u16 val; - - data = i2c_smbus_read_word_data(tsc->client, cmd); - if (data < 0) { - dev_err(&tsc->client->dev, "i2c io error: %d\n", data); - return data; + struct _tsc2007 *tsc2007= g_tsc2007; + struct input_dev *input = tsc2007->dev; + + /* update value */ + int ret = proc_dointvec(table, write, buffer, lenp, ppos); + + /* updated abs params */ + if (input) { + TS_DEBUG(KERN_DEBUG "update x_min %d x_max %d" + " y_min %d y_max %d\n", x_min, x_max, + y_min, y_max); + input_set_abs_params(input, ABS_X, x_min, x_max, 0, 0); + input_set_abs_params(input, ABS_Y, y_min, y_max, 0, 0); } - - /* The protocol and raw data format from i2c interface: - * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P - * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. - */ - val = swab16(data) >> 4; - - dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); - - return val; + return ret; } -static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) -{ - /* y- still on; turn on only y+ (and ADC) */ - tc->y = tsc2007_xfer(tsc, READ_Y); +static ctl_table tsc2007_proc_table[] = { + { + .procname = "x-max", + .data = &x_max, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &tsc2007_proc_minmax, + }, + { + .procname = "y-max", + .data = &y_max, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &tsc2007_proc_minmax, + }, + { + .procname = "x-min", + .data = &x_min, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &tsc2007_proc_minmax, + }, + { + .procname = "y-min", + .data = &y_min, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &tsc2007_proc_minmax, + }, + { + .procname = "invert_xy", + .data = &invert, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "debounce_time", + .data = &debounce_time, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { + .procname = "delay_time", + .data = &delay_time, + .maxlen = sizeof(int), + .mode = 0666, + .proc_handler = &proc_dointvec, + }, + { } +}; - /* turn y- off, x+ on, then leave in lowpower */ - tc->x = tsc2007_xfer(tsc, READ_X); +static ctl_table tsc2007_proc_root[] = { + { + .procname = "ts_device", + .mode = 0555, + .child = tsc2007_proc_table, + }, + { } +}; - /* turn y+ off, x- on; we'll use formula #1 */ - tc->z1 = tsc2007_xfer(tsc, READ_Z1); - tc->z2 = tsc2007_xfer(tsc, READ_Z2); +static ctl_table tsc2007_proc_dev_root[] = { + { + .procname = "dev", + .mode = 0555, + .child = tsc2007_proc_root, + }, + { } +}; - /* Prepare for next touch reading - power down ADC, enable PENIRQ */ - tsc2007_xfer(tsc, PWRDOWN); -} +static struct ctl_table_header *sysctl_header; -static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) +static int __init init_sysctl(void) { - u32 rt = 0; - - /* range filtering */ - if (tc->x == MAX_12BIT) - tc->x = 0; - - if (likely(tc->x && tc->z1)) { - /* compute touch pressure resistance using equation #1 */ - rt = tc->z2 - tc->z1; - rt *= tc->x; - rt *= tsc->x_plate_ohms; - rt /= tc->z1; - rt = (rt + 2047) >> 12; - } - - return rt; + sysctl_header = register_sysctl_table(tsc2007_proc_dev_root); + return 0; } -static void tsc2007_send_up_event(struct tsc2007 *tsc) +static void __exit cleanup_sysctl(void) { - struct input_dev *input = tsc->input; - - dev_dbg(&tsc->client->dev, "UP\n"); - - input_report_key(input, BTN_TOUCH, 0); - input_report_abs(input, ABS_PRESSURE, 0); - input_sync(input); + unregister_sysctl_table(sysctl_header); } -static void tsc2007_work(struct work_struct *work) +static int tsc2007_measure(struct i2c_client *client, int *x, int * y) { - struct tsc2007 *ts = - container_of(to_delayed_work(work), struct tsc2007, work); - struct ts_event tc; - u32 rt; - - /* - * NOTE: We can't rely on the pressure to determine the pen down - * state, even though this controller has a pressure sensor. - * The pressure value can fluctuate for quite a while after - * lifting the pen and in some cases may not even settle at the - * expected value. - * - * The only safe way to check for the pen up condition is in the - * work function by reading the pen signal state (it's a GPIO - * and IRQ). Unfortunately such callback is not always available, - * in that case we have rely on the pressure anyway. - */ - if (ts->get_pendown_state) { - if (unlikely(!ts->get_pendown_state())) { - tsc2007_send_up_event(ts); - ts->pendown = false; - goto out; - } + u8 x_buf[2] = {0, 0}; + u8 y_buf[2] = {0, 0}; - dev_dbg(&ts->client->dev, "pen is still down\n"); - } - - tsc2007_read_values(ts, &tc); + i2c_smbus_write_byte(client, TSC_CMD_PLATEX); + msleep_interruptible(delay_time); - rt = tsc2007_calculate_pressure(ts, &tc); - if (rt > MAX_12BIT) { - /* - * Sample found inconsistent by debouncing or pressure is - * beyond the maximum. Don't report it to user space, - * repeat at least once more the measurement. - */ - dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); - goto out; + i2c_smbus_write_byte(client, TSC_CMD_X); + i2c_master_recv(client, x_buf, 2); + *x = (x_buf[0]<<4) | (x_buf[1] >>4); - } + i2c_smbus_write_byte(client, TSC_CMD_PLATEY); + msleep_interruptible(delay_time); - if (rt) { - struct input_dev *input = ts->input; + i2c_smbus_write_byte(client, TSC_CMD_Y); + i2c_master_recv(client, y_buf, 2); + *y = (y_buf[0]<<4) | (y_buf[1] >>4); - if (!ts->pendown) { - dev_dbg(&ts->client->dev, "DOWN\n"); + return 0; +} - input_report_key(input, BTN_TOUCH, 1); - ts->pendown = true; - } +static void tsc2007_irq_work(struct work_struct *work) +{ + struct _tsc2007 *tsc2007= g_tsc2007; + struct i2c_client *client = tsc2007-> client; + struct input_dev *input = tsc2007->dev; - input_report_abs(input, ABS_X, tc.x); - input_report_abs(input, ABS_Y, tc.y); - input_report_abs(input, ABS_PRESSURE, rt); + int x = -1, y = -1, is_valid = 0; + int tmp_x = 0, tmp_y = 0; - input_sync(input); + int gpio = irq_to_gpio(client->irq); - dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", - tc.x, tc.y, rt); - } else if (!ts->get_pendown_state && ts->pendown) { - /* - * We don't have callback to check pendown state, so we - * have to assume that since pressure reported is 0 the - * pen was lifted up. - */ - tsc2007_send_up_event(ts); - ts->pendown = false; - } + /* Ignore if PEN_DOWN */ + if(PEN_UP == tsc2007->status){ - out: - if (ts->pendown) - schedule_delayed_work(&ts->work, - msecs_to_jiffies(TS_POLL_PERIOD)); - else - enable_irq(ts->irq); -} + if (gpio_request(gpio, "tsc2007 touch detect")) { + printk(KERN_ERR "Request GPIO failed, gpio: %X\n", gpio); + return; + } + gpio_direction_input(gpio); + + while(0 == gpio_get_value(gpio)){ + + if ((jiffies_to_msecs( + ((long)jiffies - (long)tsc2007->last_touch)) < + debounce_time && + tsc2007->status == PEN_DOWN) || + init_debounce) + { + init_debounce = false; + tsc2007_measure(client, &tmp_x, &tmp_y); + TS_DEBUG(KERN_DEBUG + "dropping pen touch %lu %lu (%u)\n", + jiffies, tsc2007->last_touch, + jiffies_to_msecs( + (long)jiffies - (long)tsc2007->last_touch)); + schedule(); + continue; + } + + + /* continue report x, y */ + if (x > 0 && y > 0) + { + int ox=x, oy=y; + ts_linear_scale(&x, &y, invert); + TS_DEBUG(KERN_DEBUG "ox=%d oy=%d x=%d y=%d!\n", ox, oy, x, y); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, 255); + input_report_abs(input, ABS_TOOL_WIDTH, 1); + input_report_key(input, BTN_TOUCH, 1); + input_sync(input); + } + + tsc2007->status = PEN_DOWN; + tsc2007_measure(client, &x, &y); + is_valid = 1; + schedule(); + } -static irqreturn_t tsc2007_irq(int irq, void *handle) -{ - struct tsc2007 *ts = handle; + if (is_valid) + { + /*consider PEN_UP */ + tsc2007->status = PEN_UP; + input_report_abs(input, ABS_PRESSURE, 0); + input_report_abs(input, ABS_TOOL_WIDTH, 1); + input_report_key(input, BTN_TOUCH, 0); + input_sync(input); + tsc2007->last_touch = jiffies; + TS_DEBUG(KERN_DEBUG "pen up!\n"); + } - if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { - disable_irq_nosync(ts->irq); - schedule_delayed_work(&ts->work, - msecs_to_jiffies(TS_POLL_DELAY)); + gpio_free(gpio); } - - if (ts->clear_penirq) - ts->clear_penirq(); - - return IRQ_HANDLED; } -static void tsc2007_free_irq(struct tsc2007 *ts) -{ - free_irq(ts->irq, ts); - if (cancel_delayed_work_sync(&ts->work)) { - /* - * Work was pending, therefore we need to enable - * IRQ here to balance the disable_irq() done in the - * interrupt handler. - */ - enable_irq(ts->irq); - } +static irqreturn_t tsc2007_interrupt(int irq, void *dev_id) +{ + schedule_work(&g_tsc2007->irq_work); + + return IRQ_HANDLED; } -static int __devinit tsc2007_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int __devinit tsc2007_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct tsc2007 *ts; - struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; + struct _tsc2007 *tsc2007; struct input_dev *input_dev; - int err; + int ret; - if (!pdata) { - dev_err(&client->dev, "platform data is required!\n"); - return -EINVAL; - } + tsc2007 = kzalloc(sizeof(struct _tsc2007), GFP_KERNEL); + input_dev = input_allocate_device(); - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_WORD_DATA)) - return -EIO; + g_tsc2007 = tsc2007; - ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - err = -ENOMEM; - goto err_free_mem; + if (!tsc2007 || !input_dev) { + ret = -ENOMEM; + goto fail1; } - ts->client = client; - ts->irq = client->irq; - ts->input = input_dev; - INIT_DELAYED_WORK(&ts->work, tsc2007_work); + i2c_set_clientdata(client, tsc2007); + + tsc2007->dev = input_dev; - ts->model = pdata->model; - ts->x_plate_ohms = pdata->x_plate_ohms; - ts->get_pendown_state = pdata->get_pendown_state; - ts->clear_penirq = pdata->clear_penirq; + input_dev->name = "tsc2007"; + input_dev->phys = "tsc2007/input0"; - snprintf(ts->phys, sizeof(ts->phys), - "%s/input0", dev_name(&client->dev)); + //input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &client->dev; - input_dev->name = "TSC2007 Touchscreen"; - input_dev->phys = ts->phys; - input_dev->id.bustype = BUS_I2C; + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(ABS_PRESSURE, input_dev->evbit); + __set_bit(ABS_X, input_dev->evbit); + __set_bit(ABS_Y, input_dev->evbit); - input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_X, x_min, x_max, 0, 0); + input_set_abs_params(input_dev, ABS_Y, y_min, y_max, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); - if (pdata->init_platform_hw) - pdata->init_platform_hw(); + ret = request_irq(client->irq, tsc2007_interrupt, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "tsc2007 irq", NULL); + if (ret){ + printk(KERN_ERR "tsc2007 request irq failed\n"); + goto fail2; + } - err = request_irq(ts->irq, tsc2007_irq, 0, - client->dev.driver->name, ts); - if (err < 0) { - dev_err(&client->dev, "irq %d busy?\n", ts->irq); - goto err_free_mem; + ret = input_register_device(tsc2007->dev); + if (ret){ + printk(KERN_ERR "tsc2007 register device fail\n"); + goto fail2; } - /* Prepare for touch readings - power down ADC and enable PENIRQ */ - err = tsc2007_xfer(ts, PWRDOWN); - if (err < 0) - goto err_free_irq; + /*init */ + tsc2007->status = PEN_UP; + tsc2007->client = client; + tsc2007->last_touch = jiffies; - err = input_register_device(input_dev); - if (err) - goto err_free_irq; + INIT_WORK(&tsc2007->irq_work, tsc2007_irq_work); - i2c_set_clientdata(client, ts); + /* init tsc2007 */ + i2c_smbus_write_byte(client, TSC_CMD_SETUP); return 0; - err_free_irq: - tsc2007_free_irq(ts); - if (pdata->exit_platform_hw) - pdata->exit_platform_hw(); - err_free_mem: + fail2: + free_irq(client->irq, client); + fail1: + i2c_set_clientdata(client, NULL); input_free_device(input_dev); - kfree(ts); - return err; + kfree(tsc2007); + return ret; } static int __devexit tsc2007_remove(struct i2c_client *client) { - struct tsc2007 *ts = i2c_get_clientdata(client); - struct tsc2007_platform_data *pdata = client->dev.platform_data; - - tsc2007_free_irq(ts); + struct _tsc2007 *tsc2007 = i2c_get_clientdata(client); - if (pdata->exit_platform_hw) - pdata->exit_platform_hw(); - - input_unregister_device(ts->input); - kfree(ts); + if(client->irq) + free_irq(client->irq, client); + + i2c_set_clientdata(client, NULL); + input_unregister_device(tsc2007->dev); + kfree(tsc2007); return 0; } -static const struct i2c_device_id tsc2007_idtable[] = { - { "tsc2007", 0 }, - { } -}; +static struct i2c_device_id tsc2007_idtable[] = { + { "tsc2007", 0 }, + { } +}; MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); static struct i2c_driver tsc2007_driver = { .driver = { - .owner = THIS_MODULE, - .name = "tsc2007" + .name = "tsc2007", }, - .id_table = tsc2007_idtable, + .id_table = tsc2007_idtable, .probe = tsc2007_probe, .remove = __devexit_p(tsc2007_remove), }; -static int __init tsc2007_init(void) +static int __init tsc2007_ts_init(void) { - return i2c_add_driver(&tsc2007_driver); + init_sysctl(); + return i2c_add_driver(&tsc2007_driver); } -static void __exit tsc2007_exit(void) +static void __exit tsc2007_ts_exit(void) { + cleanup_sysctl(); i2c_del_driver(&tsc2007_driver); } -module_init(tsc2007_init); -module_exit(tsc2007_exit); +module_init(tsc2007_ts_init); +module_exit(tsc2007_ts_exit); -MODULE_AUTHOR("Kwangwoo Lee "); -MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); +MODULE_DESCRIPTION("tsc2007 touch screen driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 505eb64c329cb6..4b0388507afa09 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -347,6 +347,12 @@ config LEDS_TRIGGER_DEFAULT_ON This allows LEDs to be initialised in the ON state. If unsure, say Y. +config LEDS_TRIGGER_SLEEP + tristate "LED Sleep Mode Trigger" + depends on LEDS_TRIGGERS && HAS_EARLYSUSPEND + help + This turns LEDs on when the screen is off but the cpu still running. + comment "iptables trigger is under Netfilter config (LED target)" depends on LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 0cd8b9957380d0..2341c0812ef433 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -46,3 +46,4 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o +obj-$(CONFIG_LEDS_TRIGGER_SLEEP) += ledtrig-sleep.o diff --git a/drivers/leds/ledtrig-sleep.c b/drivers/leds/ledtrig-sleep.c new file mode 100644 index 00000000000000..f16404212152c8 --- /dev/null +++ b/drivers/leds/ledtrig-sleep.c @@ -0,0 +1,80 @@ +/* drivers/leds/ledtrig-sleep.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 + +static int ledtrig_sleep_pm_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored); + +DEFINE_LED_TRIGGER(ledtrig_sleep) +static struct notifier_block ledtrig_sleep_pm_notifier = { + .notifier_call = ledtrig_sleep_pm_callback, + .priority = 0, +}; + +static void ledtrig_sleep_early_suspend(struct early_suspend *h) +{ + led_trigger_event(ledtrig_sleep, LED_FULL); +} + +static void ledtrig_sleep_early_resume(struct early_suspend *h) +{ + led_trigger_event(ledtrig_sleep, LED_OFF); +} + +static struct early_suspend ledtrig_sleep_early_suspend_handler = { + .suspend = ledtrig_sleep_early_suspend, + .resume = ledtrig_sleep_early_resume, +}; + +static int ledtrig_sleep_pm_callback(struct notifier_block *nfb, + unsigned long action, + void *ignored) +{ + switch (action) { + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + led_trigger_event(ledtrig_sleep, LED_OFF); + return NOTIFY_OK; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + led_trigger_event(ledtrig_sleep, LED_FULL); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + +static int __init ledtrig_sleep_init(void) +{ + led_trigger_register_simple("sleep", &ledtrig_sleep); + register_pm_notifier(&ledtrig_sleep_pm_notifier); + register_early_suspend(&ledtrig_sleep_early_suspend_handler); + return 0; +} + +static void __exit ledtrig_sleep_exit(void) +{ + unregister_early_suspend(&ledtrig_sleep_early_suspend_handler); + unregister_pm_notifier(&ledtrig_sleep_pm_notifier); + led_trigger_unregister_simple(ledtrig_sleep); +} + +module_init(ledtrig_sleep_init); +module_exit(ledtrig_sleep_exit); + diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 83567b898d0975..19c2abb19004cb 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -417,6 +417,37 @@ config RADIO_TEA5764_XTAL Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N here if TEA5764 reference frequency is connected in FREQIN. +config RADIO_SI4703 + tristate "si4703 FM radio support" + depends on VIDEO_V4L2 + ---help--- + Say Y here if you want to enable fm radio. + +choice + prompt "si4703 FM band select" + ---help--- + Select the FM band + +config RADIO_FM_EURO + boolean "USA/Europe FM band" + depends on RADIO_SI4703 + ---help--- + Say Y here if you want to USA/Europe FM band + +config RADIO_FM_JAPAN_WIDEBAND + boolean "Japan FM wide band" + depends on RADIO_SI4703 + ---help--- + Say Y here if you want to Japan FM wide band + +config RADIO_FM_JAPAN + boolean "Japan FM band" + depends on RADIO_SI4703 + ---help--- + Say Y here if you want to Japan FM band + +endchoice + config RADIO_SAA7706H tristate "SAA7706H Car Radio DSP" depends on I2C && VIDEO_V4L2 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index f615583b4837c6..a2eb69e976f298 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -20,7 +20,8 @@ obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o obj-$(CONFIG_USB_DSBR) += dsbr100.o -obj-$(CONFIG_RADIO_SI470X) += si470x/ +obj-$(CONFIG_USB_SI470X) += radio-si470x.o +obj-$(CONFIG_RADIO_SI4703) += radio-si4703.o obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o diff --git a/drivers/media/radio/radio-si4703.c b/drivers/media/radio/radio-si4703.c new file mode 100644 index 00000000000000..6c61453175ea10 --- /dev/null +++ b/drivers/media/radio/radio-si4703.c @@ -0,0 +1,778 @@ +/* + * chip-si4703 - main file for si4703 FM chip driver + * + * Copyright (C) 2008, Marvell Corporation. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PXA3xx_DVFM +#include +#include +#include +#endif + +/* + * Version Information + */ +#include /* for KERNEL_VERSION MACRO */ + +#define DRIVER_VERSION "v0.01" +#define RADIO_VERSION KERNEL_VERSION(0, 0, 1) + +static struct v4l2_queryctrl chip_qctrl[] = { + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 16, + .step = 1, + .default_value = 0xf, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +/* Frequency limits in MHz -- these are European values. For Japanese + * devices, that would be 76MHz and 91MHz. + */ +#define REG_DEVICEID 0x00 +#define REG_CHIPID 0x01 +#define REG_POWERCFG 0x02 +#define REG_CHANNEL 0x03 +#define REG_SYSCONFIG1 0x04 +#define REG_SYSCONFIG2 0x05 +#define REG_SYSCONFIG3 0x06 +#define REG_TEST1 0x07 +#define REG_TEST2 0x08 +#define REG_BOOTCFG 0x09 +#define REG_RSSI 0x0a +#define REG_READCHAN 0x0b +#define REG_RDSA 0x0c +#define REG_RDSB 0x0d +#define REG_RDSC 0x0e +#define REG_RDSD 0x0f + +/* REG_POWERCFG */ +#define DMUTE 0x4000 +#define MUTE 0x0000 +#define MONO 0x2000 +#define SKMODE 0x0400 +#define SEEKUP 0x0200 +#define SEEKDOWN 0x0000 +#define SEEK 0x0100 +#define DISABLE 0x0040 +#define ENABLE 0x0001 +#define DSOFTMUTE 0x8000 + +/* REG_CHANNEL */ +#define TUNE 0x8000 + +/* REG_SYSCONFIG1 */ +#define RDSIEN 0x8000 +#define STCIEN 0x4000 +#define RDS 0x1000 +#define GPIO2_INTERRUPT 0x0004 + +/* REG_SYSCONFIG2 */ +#define RECOMMENDED_SEEKTH 0x0c00 +#define BAND_EURO 0x0000 /* 87.5-108MHz */ +#define WIDEBAND_JAPAN 0x0040 /* 76-108MHZ */ +#define BAND_JAPAN 0x0080 /* 76-90MHz */ +#define SPACE_200KHZ 0x0000 +#define SPACE_100KHZ 0x0010 +#define SPACE_50KHZ 0x0020 +#define VOLUME 0x000F +#define SPACINGSELECT 0x0010 +#define VOLUMECTRL 0x000f + +/* REG_SYSCONFIG3 */ +#define SEN_THRESHOLD 0x0040 +#define DETECTION_THRESHOLD 0x0008 + +/* REG_RSSI */ +#define STC 0x4000 +#define RSSI 0x00FF +#define AFCRL 0x1000 + +#define FREQ_MUL 1600 + +#ifdef CONFIG_RADIO_FM_EURO +#define BAND BAND_EURO +#define SPACE SPACE_100KHZ +#define CHANNEL_SPACE 100 /* in kHZ */ +#define FREQ_MIN 87.5 +#define FREQ_MAX 108 +#endif + +#ifdef CONFIG_RADIO_FM_JAPAN_WIDEBAND +#define BAND WIDEBAND_JAPAN +#define SPACE SPACE_100KHZ +#define CHANNEL_SPACE 100 /* in kHZ */ +#define FREQ_MIN 76 +#define FREQ_MAX 108 +#endif + +#ifdef CONFIG_RADIO_FM_JAPAN +#define BAND BAND_JAPAN +#define SPACE SPACE_100KHZ +#define CHANNEL_SPACE 100 /* in kHZ */ +#define FREQ_MIN 76 +#define FREQ_MAX 90 +#endif + +#define REG_READ_START REG_RSSI +#define REG_WRITE_START REG_POWERCFG +#define NR_SI4703_REGS 16 +struct si4703_device { + struct i2c_client *client; + int irq; + struct video_device *videodev; + uint16_t regs[NR_SI4703_REGS]; + int curfreq; + int stereo; + int users; + int removed; + int curvol; + int muted; + wait_queue_head_t wait; +}; + +extern void enable_oscc_tout_s0(void) +{ +} +extern void disable_oscc_tout_s0(void) +{ +} + +#ifdef CONFIG_PXA3xx_DVFM +static int dvfm_radio_idx; +static void set_radio_dvfm_constraint(void) +{ + /* Disable Lowpower mode */ + dvfm_disable_op_name("D1", dvfm_radio_idx); + dvfm_disable_op_name("D2", dvfm_radio_idx); + if (cpu_is_pxa935()) + dvfm_disable_op_name("CG", dvfm_radio_idx); +} + +static void unset_radio_dvfm_constraint(void) +{ + /* Enable Lowpower mode */ + dvfm_enable_op_name("D1", dvfm_radio_idx); + dvfm_enable_op_name("D2", dvfm_radio_idx); + if (cpu_is_pxa935()) + dvfm_enable_op_name("CG",dvfm_radio_idx); +} +#else +static void set_radio_dvfm_constraint(void) {} +static void unset_radio_dvfm_constraint(void) {} +#endif +int si4703_write(struct si4703_device *chip, uint8_t reg, uint16_t val) +{ + uint16_t count; + uint8_t buf[NR_SI4703_REGS*2]; + int i; + int ret; + int index; + + chip->regs[reg] = val; + + for (i = 0; i < NR_SI4703_REGS; i++) { + index = (REG_WRITE_START + i)%NR_SI4703_REGS; + buf[2*i] = chip->regs[index] >> 8; + buf[2*i + 1] = chip->regs[index] & 0xff; + } + count = ((reg + NR_SI4703_REGS - REG_WRITE_START)%NR_SI4703_REGS + 1)*2; + + ret = i2c_master_send(chip->client, buf, count); + if (ret < 0) { + printk(KERN_ERR "si4703_write: write failed!\n"); + return -EIO; + } + + return 0; +} + +int si4703_read(struct si4703_device *chip, uint8_t reg, uint16_t *val) +{ + int ret; + uint16_t count; + uint8_t buf[NR_SI4703_REGS*2]; + int i; + + count = ((reg + NR_SI4703_REGS - REG_READ_START)%NR_SI4703_REGS + 1)*2; + ret = i2c_master_recv(chip->client, buf, count); + if (ret < 0) { + printk(KERN_ERR "si4703_read: read failed!\n"); + return -EIO; + } else { + for (i = 0; i < count/2; i++) + chip->regs[(REG_READ_START + i)%NR_SI4703_REGS] = + (buf[2*i] << 8) + (buf[2*i + 1] & 0xff); + *val = chip->regs[reg]; + } + + return 0; +} + +/* Low-level device interface begins here */ +uint16_t si4703_get_rssi(struct si4703_device *chip) +{ + uint16_t val; + + si4703_read(chip, REG_RSSI, &val); + + return val; +} + +uint16_t si4703_get_chan(struct si4703_device *chip) +{ + uint16_t val; + + si4703_read(chip, REG_READCHAN, &val); + + return val; +} + +int si4703_power_up(struct si4703_device *chip) +{ + si4703_write(chip, REG_POWERCFG, DMUTE | ENABLE); + si4703_write(chip, REG_CHANNEL, 0x0000); + si4703_write(chip, REG_SYSCONFIG1, STCIEN | + RDSIEN | RDS | GPIO2_INTERRUPT); + si4703_write(chip, REG_SYSCONFIG2, RECOMMENDED_SEEKTH | + SPACINGSELECT | VOLUMECTRL | SPACE | BAND); + si4703_write(chip, REG_SYSCONFIG3, SEN_THRESHOLD | + DETECTION_THRESHOLD); + mdelay(50); + + return 0; +} + +int si4703_power_down(struct si4703_device *chip) +{ + /* Set DISABLE bit as 1, perform internal power-down sequence and + * set ENABLE bit=0 later by device itself + */ + return si4703_write(chip, REG_POWERCFG, ENABLE | DISABLE); +} + +/* switch on chip */ +static int si4703_unmute(struct si4703_device *chip) +{ + uint16_t val; + + if (si4703_read(chip, REG_SYSCONFIG2, &val)) + return -EIO; + + val &= ~0xf; + val |= chip->curvol; + if (si4703_write(chip, REG_SYSCONFIG2, val)) + return -EIO; + + chip->muted = 0; + return 0; +} + +/* switch off chip */ +static int si4703_mute(struct si4703_device *chip) +{ + uint16_t val; + + if (si4703_read(chip, REG_SYSCONFIG2, &val)) + return -EIO; + + chip->curvol = val & 0xf; + val &= ~0xf; + if (si4703_write(chip, REG_SYSCONFIG2, val)) + return -EIO; + + chip->muted = 1; + return 0; +} + +static int si4703_setvol_generic(struct si4703_device *chip, int vol) +{ + uint16_t val; + + if (si4703_read(chip, REG_SYSCONFIG2, &val)) + return -EIO; + + val &= ~0xf; + if (si4703_write(chip, REG_SYSCONFIG2, val | vol)) + return -EIO; + + return 0; +} + +static int si4703_setvol(struct si4703_device *chip, int vol) +{ + /* user is unmuting the card */ + if (chip->muted && vol != 0) { + chip->curvol = vol; + si4703_unmute(chip); + return 0; + } + + /* requested volume == current */ + if (vol == chip->curvol) + return 0; + + /* volume == 0 means mute the card */ + if (vol == 0) { + si4703_mute(chip); + chip->curvol = vol; + return 0; + } + si4703_setvol_generic(chip, vol); + chip->curvol = vol; + + return 0; +} + +/* set a frequency, freq unit: 1/16th kHz */ +int si4703_setfreq(struct si4703_device *chip, unsigned int freq) +{ + long timeout; + uint16_t val; + uint16_t channel; + unsigned int freq_base; + unsigned int freq_step; + + freq_base = FREQ_MIN*FREQ_MUL; + freq_step = CHANNEL_SPACE*FREQ_MUL/1000; + channel = (freq - freq_base)/freq_step; + pr_debug("%s:channel %d(freq: %d)\n", __FUNCTION__, channel, freq); + if (si4703_write(chip, REG_CHANNEL, channel | TUNE)) + return -EIO; + + /* wait for STC to be set */ + timeout = wait_event_timeout(chip->wait, si4703_get_rssi(chip) & STC, + 60*HZ/1000); + pr_debug("get channel: %d\n", si4703_get_chan(chip) & 0x3ff); + + if (timeout == 0) + return -EIO; + + if (si4703_get_rssi(chip) & AFCRL) + return -EIO; + + /* clear tune bit */ + timeout = 6; + if (si4703_read(chip, REG_CHANNEL, &val)) + return -EIO; + + if (si4703_write(chip, REG_CHANNEL, ~TUNE & val)) + return -EIO; + + /* wait for STC to be clear */ + do { + mdelay(10); + } while ((si4703_get_rssi(chip) & STC) && timeout--); + + if (timeout == 0) + return -EIO; + + return 0; +} + + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "si4703", sizeof(v->driver)); + strlcpy(v->card, "Si4703 FM Radio", sizeof(v->card)); + sprintf(v->bus_info, "I2C"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + + if (v->index > 0) + return -EINVAL; + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_MIN*FREQ_MUL; + v->rangehigh = FREQ_MAX*FREQ_MUL; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = si4703_get_rssi(chip) & RSSI; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + chip->curfreq = f->frequency; + if (si4703_setfreq(chip, chip->curfreq)) { + printk(KERN_ERR "Set frequency failed"); + return -EIO; + } + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + + f->type = V4L2_TUNER_RADIO; + f->frequency = chip->curfreq; + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(chip_qctrl); i++) { + if (qc->id && qc->id == chip_qctrl[i].id) { + memcpy(qc, &(chip_qctrl[i]), + sizeof(*qc)); + return 0; + } + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = chip->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = chip->curvol; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + if (si4703_mute(chip)) + printk(KERN_ERR "si4703: no reponse"); + } else { + if (si4703_unmute(chip)) + printk(KERN_ERR "si4703: no reponse"); + } + break; + case V4L2_CID_AUDIO_VOLUME: + si4703_setvol(chip, ctrl->value); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; +} + +static int si4703_open(struct file *file) +{ + + + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + set_radio_dvfm_constraint(); + + /* enable clock */ + enable_oscc_tout_s0(); + + chip->users = 1; + chip->muted = 1; + + if (si4703_power_up(chip)) { + printk(KERN_ERR "Radio did not start up properly"); + chip->users = 0; + disable_oscc_tout_s0(); + return -EIO; + } + si4703_setfreq(chip, chip->curfreq); + + return 0; +} + + + +static int si4703_close(struct file *file) +{ + struct si4703_device *chip = video_get_drvdata(video_devdata(file)); + + unset_radio_dvfm_constraint(); + + if (!chip) { + disable_oscc_tout_s0(); + return -ENODEV; + } + + if (si4703_power_down(chip)) + printk(KERN_ERR "Radio did not shutdown properly"); + + chip->users = 0; + if (chip->removed) + kfree(chip); + + /* disable clock */ + disable_oscc_tout_s0(); + + return 0; +} + +/* File system interface */ +static const struct v4l2_file_operations si4703_fops = { + .owner = THIS_MODULE, + .open = si4703_open, + .release = si4703_close, + .ioctl = video_ioctl2, +}; +/* upgrade changes from .25 to .28 */ +struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, +}; + + +/* V4L2 interface */ +static struct video_device si4703_videodev_template = { + .name = "si4703", + .fops = &si4703_fops, + .release = video_device_release, + .ioctl_ops = &radio_ioctl_ops, +}; + +static irqreturn_t si4703_irq_handler(int irq, void *dev_id) +{ + struct si4703_device *chip = dev_id; + + wake_up(&chip->wait); + pr_debug("si4703: interrupt\n"); + return IRQ_HANDLED; +} + +static int si4703_probe(struct i2c_client *client) +{ + uint16_t id; + int ret; + struct si4703_device *chip; + struct si4703_platform_data *pdata = client->dev.platform_data; + chip = kmalloc(sizeof(struct si4703_device), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->videodev = video_device_alloc(); + if (!chip->videodev) { + kfree(chip); + return -ENOMEM; + } + chip->client = client; + chip->irq = client->irq; + init_waitqueue_head(&chip->wait); + i2c_set_clientdata(client, chip); + + enable_oscc_tout_s0(); + pdata->setup(client, pdata->context); + if (chip->irq >= 0) { + ret = request_irq(chip->irq, si4703_irq_handler, + IRQF_TRIGGER_FALLING, "si4703", chip); + if (ret) { + kfree(chip->videodev); + kfree(chip); + printk(KERN_ERR "request IRQ for si4703 failed!\n"); + return ret; + } + } + + /* init shadow registers */ + if (si4703_read(chip, REG_READ_START - 1, &id)) { + free_irq(chip->irq, chip); + kfree(chip->videodev); + kfree(chip); + printk(KERN_ERR "%s: failed to init shadow registers\n", + __FUNCTION__); + disable_oscc_tout_s0(); + return -EIO; + } + + si4703_power_up(chip); + ret = si4703_read(chip, REG_CHIPID, &id); + if (ret) + printk(KERN_ERR "%s: failed to detect si4703\n", + __FUNCTION__); + else + printk(KERN_INFO "%s: si4703(0x%04x) detected\n", + __FUNCTION__, id); + + si4703_power_down(chip); + + /* init volume to maxium */ + chip->curvol = 0xf; + + memcpy(chip->videodev, &si4703_videodev_template, + sizeof(si4703_videodev_template)); + chip->removed = 0; + chip->users = 0; + chip->curfreq = FREQ_MIN*FREQ_MUL; + video_set_drvdata(chip->videodev, chip); + if (video_register_device(chip->videodev, VFL_TYPE_RADIO, 0)) { + printk(KERN_ERR "Could not register video device"); + free_irq(chip->irq, chip); + video_device_release(chip->videodev); + kfree(chip->videodev); + kfree(chip); + disable_oscc_tout_s0(); + return -EIO; + } + + disable_oscc_tout_s0(); + return 0; +} + +static int si4703_remove(struct i2c_client *client) +{ + struct si4703_device *chip = i2c_get_clientdata(client); + + if (chip->irq >= 0) + free_irq(chip->irq, chip); + video_unregister_device(chip->videodev); + kfree(chip->videodev); + kfree(chip); + + return 0; +} + +static struct i2c_device_id si4703_idtable[] = { + { "si4703", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si4703_idtable); + +/* This is the driver that will be inserted */ +static struct i2c_driver si4703_driver = { + .driver = { + .name = "si4703", + }, + .id_table = si4703_idtable, + .probe = si4703_probe, + .remove = si4703_remove, +}; + +static int __init si4703_init(void) +{ +#ifdef CONFIG_PXA3xx_DVFM + dvfm_register("FM radio", &dvfm_radio_idx); +#endif + + return i2c_add_driver(&si4703_driver); +} + +static void __exit si4703_exit(void) +{ +#ifdef CONFIG_PXA3xx_DVFM + dvfm_unregister("FM radio", &dvfm_radio_idx); +#endif + i2c_del_driver(&si4703_driver); +} + + +MODULE_AUTHOR("Walter Shao "); +MODULE_DESCRIPTION("si4703 FM Radio driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9644cf760aaa62..4185e4c64133da 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -309,6 +309,12 @@ config VIDEO_KS0127 To compile this driver as a module, choose M here: the module will be called ks0127. +config VIDEO_OV3640 + tristate "OmniVision OV3640 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + Support for the OmniVision OV3640 QXGA camera. + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 @@ -317,6 +323,30 @@ config VIDEO_OV7670 OV7670 VGA camera. It currently only works with the M88ALP01 controller. +config VIDEO_OV7660 + tristate "OmniVision OV7660 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7660 VGA camera. It currently only works with the M88ALP01 + controller. + +config VIDEO_SIV120A + tristate "SETI SIV120A sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the Nice Seti + SIV120A VGA camera. It currently only works with the PXA168 camera + controller. + +config VIDEO_OV7740 + tristate "OmniVision OV7740 sensor support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a Video4Linux2 sensor-level driver for the OmniVision + OV7740 VGA camera. It currently only works with the M88ALP01 + controller. + config VIDEO_MT9V011 tristate "Micron mt9v011 sensor support" depends on I2C && VIDEO_V4L2 @@ -1106,4 +1136,29 @@ config USB_S2255 This driver can be compiled as a module, called s2255drv. endif # V4L_USB_DRIVERS + +config PXA168_CAMERA + tristate "Marvell PXA168 Camera Controller support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a video4linux2 driver for the Marvell PXA168 camera + controller. + +config CLI5001_ISP + tristate "CLI5001 Camera ISP support" + depends on I2C && VIDEO_V4L2 + ---help--- + This is a video4linux2 driver for the CLI5001 camera ISP + controller. + +config VIDEO_OV529 + tristate "OmniVision OV529 Encoder support" + depends on MACH_IPCAM && VIDEO_OV7740 + ---help--- + This is a driver for the OmniVision OV529 encoder with Video4Linux2 + interface. It currently only works with IPCAM platform and ov7740 + sensor. + +source "drivers/media/video/cmmb/Kconfig" + endif # VIDEO_CAPTURE_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index c51c386559f2ea..e9e8d7622eb586 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -68,7 +68,7 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o -obj-$(CONFIG_VIDEO_OV7670) += ov7670.o +#obj-$(CONFIG_VIDEO_OV7670) += ov7670.o obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o @@ -122,6 +122,11 @@ obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o +obj-$(CONFIG_VIDEO_OV7660) += ov7660.o +obj-$(CONFIG_VIDEO_OV7670) += ov7670_pxa.o +obj-$(CONFIG_VIDEO_OV3640) += ov3640.o +obj-$(CONFIG_VIDEO_OV7740) += ov7740.o +obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o @@ -163,6 +168,11 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ +obj-$(CONFIG_PXA168_CAMERA) += pxa168_camera.o +obj-$(CONFIG_VIDEO_OV529) += ov529_drv.o ov529_smc.o +obj-$(CONFIG_VIDEO_SIV120A) += siv120a.o +obj-$(CONFIG_CLI5001_ISP) += cli5001.o +obj-$(CONFIG_CMMB_CA) += cmmb/ obj-$(CONFIG_VIDEO_SAA7164) += saa7164/ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o diff --git a/drivers/media/video/cli5001.c b/drivers/media/video/cli5001.c new file mode 100644 index 00000000000000..882b102d5d3ef6 --- /dev/null +++ b/drivers/media/video/cli5001.c @@ -0,0 +1,1036 @@ +/* + * A V4L2 driver for OmniVision CLI5001 camera ISP. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pxa168_camera.h" +#include "cli5001.h" +#include + + +MODULE_AUTHOR("Jun Nie "); +MODULE_DESCRIPTION("A driver for cli5001 camera ISP"); +MODULE_LICENSE("GPL"); + +static struct i2c_client *c; + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QXGA_WIDTH 2048 +#define QXGA_HEIGHT 1536 + +/* + * Our nominal (default) frame rate. + */ +#define CLI5001_FRAME_RATE 30 + +/* + * The cli5001 sits on i2c with ID 0x42 + */ +#define CLI5001_I2C_ADDR 0x42 + +/* Registers */ + + +/* + * Information we maintain about a known sensor. + */ +struct cli5001_format_struct; /* coming later */ +struct cli5001_info { + struct cli5001_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +/* + * Low-level register I/O. + */ + +static int cli5001_set_reg_bank(struct i2c_client *c, char txData) +{ + int ret; + unsigned char buf[2]; + static char bank = 0; + + if(bank == txData) + return 0; + + buf[0] = 0x01; + buf[1] = bank = txData; + struct i2c_msg msg[] = { + { + .addr = 0x41, /* cli5001 address high byte access I2C address */ + .flags = 0, + .len = 2, + .buf = &buf, + }, + }; + + ret = i2c_transfer(c->adapter, msg, 1); + if (ret < 0) { + printk(KERN_ERR "cli5001: set reg bank error %d\n", ret); + return ret; + } + return 0; +} + +static int cli5001_read(struct i2c_client *c, + u16 reg) +{ + int ret; + + ret = cli5001_set_reg_bank(c, (u8)(reg >> 8)); + if(ret < 0) + return ret; + ret = i2c_smbus_write_byte(c, (u8)(reg & 0xff)); + if(ret < 0) + return ret; + return i2c_smbus_read_byte(c); +} + +static int cli5001_write(struct i2c_client *c, + u16 reg, char value) +{ + int ret; + + ret = cli5001_set_reg_bank(c, (u8)(reg >> 8)); + if(ret < 0){ + printk("set cli5001 bank fail!\n"); + return ret; + } + ret = i2c_smbus_write_byte_data(c, (u8)(reg & 0xff), value); + if (ret < 0) { + printk(KERN_ERR "cli5001_write error %d reg 0x%x val 0x%x\n", + ret, reg, value); + return ret; + } + return 0; +} + +/* ISP vender macro */ +#define CoreISP2_IIC_Read(v) cli5001_read(c, v) +#define CoreISP2_IIC_Write(addr, v) cli5001_write(c, addr, v) +#define DebugMessage printk +#define WaitTime_us(a) msleep((a)/1000) +typedef unsigned long long ClUint_64; +typedef unsigned int ClUint_32; +typedef unsigned short ClUint_16; +typedef unsigned char ClUint_8; +typedef long ClSint_64; +typedef int ClSint_32; +typedef short ClSint_16; +typedef char ClSint_8; +typedef char Cl_Char; +typedef ClUint_32 Cl_Handle; +typedef ClUint_8 Cl_Bool; + +#define _ROTATION_NORMAL_ 1 +#define _ROTATION_HORIZONTAL_ 2 +#define _ROTATION_VERTICAL_ 3 +#define _ROTATION_HORIZONTAL_VERTICAL_ 4 + +#define _QVGA_RES_ 1 +#define _HVGA_RES_ 2 +#define _3M_RES_ 3 +#define _XGA_RES_ 4 + +/* + * Stuff that knows about the sensor. + */ +static void cli5001_reset(struct i2c_client *client) +{ +} + +static void CoreISP2_Send_Command( ClUint_8 Cmd_Type ) +{ + int count = 100; + + CoreISP2_IIC_Write(0xebb0, Cmd_Type); + + while(CoreISP2_IIC_Read(0xebb0) != 0x00) + { + if(count < 0) + { + DebugMessage("isp2 command time out 0x%x [0x%x]\n", Cmd_Type, CoreISP2_IIC_Read(0xebb0)); + return; + } + count--; + WaitTime_us(100000); // 100ms + } + DebugMessage("isp2 command finish: 0x%x \n", Cmd_Type); +} + +void CoreISP2_Mode_Change(ClUint_8 res) +{ + CoreISP2_IIC_Write(0xe64a, _ROTATION_HORIZONTAL_VERTICAL_); + + switch(res){ + case _3M_RES_: + CoreISP2_IIC_Write(0xe6a8, 0); // 3M + //CoreISP2_Send_Command(0xb0); //init + CoreISP2_Send_Command(0xdb); // capture command for 3M + break; + case _HVGA_RES_: //480x320 + + //CoreISP2_Send_Command(0xb0); //init + CoreISP2_Send_Command(0xd0); // preview command for HVGA + break; + + case _QVGA_RES_: //320x240 + //CoreISP2_Send_Command(0xb0); // init cmd + CoreISP2_Send_Command(0xd1); // preview command for QVGA + break; + case _XGA_RES_: //1024x768 + //CoreISP2_Send_Command(0xb0); // init cmd + CoreISP2_Send_Command(0xd3); // preview command for 1024x768 + break; + default: + break; + } +} + +static void CoreISP2_FullAF(void) +{ + int count = 50; + + CoreISP2_Send_Command(0xF3); + + while(CoreISP2_IIC_Read(0xebb1) != 0x24) // 0x24 means finish + { + if(count < 0) + return; + count--; + WaitTime_us(50000); // 50ms + } +} + +static void CoreISP2_WBmode(ClUint_8 wbmode) +{ + switch (wbmode) + { + case 0 : //auto + CoreISP2_IIC_Write(0xeb98, 0x00); + break; + case 1 : //cloudy + CoreISP2_IIC_Write(0xeb98, 0x01); + break; + case 2 : //sunnyl + CoreISP2_IIC_Write(0xeb98, 0x02); + break; + case 3 : //fluorescent + CoreISP2_IIC_Write(0xeb98, 0x03); + break; + case 4 : //incandescent + CoreISP2_IIC_Write(0xeb98, 0x06); + break; + default : + CoreISP2_IIC_Write(0xeb98, 0x00); + break; + + } +} + +static void CoreISP2_Set_Exposure_mode(u8 Exmode) +{ + switch (Exmode) + { + case 0 : // -2 level + CoreISP2_IIC_Write(0xebb6, 0x01); + CoreISP2_IIC_Write(0xebb6, 0xc6); + break; + case 1 : // -1 level + CoreISP2_IIC_Write(0xebb6, 0x02); + CoreISP2_IIC_Write(0xebb6, 0xc6); + break; + case 2 : // 0 level + CoreISP2_IIC_Write(0xebb6, 0x03); + CoreISP2_IIC_Write(0xebb6, 0xc6); + break; + case 3 : // 1 level + CoreISP2_IIC_Write(0xebb6, 0x04); + CoreISP2_IIC_Write(0xebb6, 0xc6); + break; + case 4 : // 2 level + CoreISP2_IIC_Write(0xebb6, 0x05); + CoreISP2_IIC_Write(0xebb6, 0xc6); + break; + default : + CoreISP2_IIC_Write(0xebb6, 0x03); + CoreISP2_IIC_Write(0xebb6, 0xc6); + break; + } +} + +static void CoreISP2_Set_Effect_mode(u8 Effectmode) +{ + switch (Effectmode) + { + case 0: //Normal : ImgEffect + CoreISP2_IIC_Write(0xee20, 0x00); + break; + + case 1: //Warm : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x01); + break; + + case 2: //Cool : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x02); + break; + + case 3: //Fog : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x03); + break; + + case 4: //Opp_Neg : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x04); + break; + + case 5: //Emboss : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x40); + break; + + case 6: //Sketh1 : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x80); + break; + + case 7: //Sketh2 : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0xc0); + break; + + case 8: //Opp_Neg : ImgEffectB + CoreISP2_IIC_Write(0xee20, 0x04); + break; + // YUV effect + case 9: //Sol : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x01); + break; + + case 10: //Aqua : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x02); + break; + + case 11: //Pos : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x03); + break; + + case 12: //Green : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x04); + break; + + case 13: //Vio : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x05); + break; + + case 14: //Soft : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x06); + break; + + case 15: //Bin : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x07); + break; + + case 16: //Orange : ImgEffectC + CoreISP2_IIC_Write(0xee22, 0x08); + break; + + } +} + +//float +/* +static ClUint_32 CoreISP2_Get_Version(void) +{ + ClUint_32 SW_Ver; + ClUint_16 majorVer; + ClUint_16 minorVer; + ClUint_16 data1; + ClUint_16 data0; + + CoreISP2_Send_Command(0x14); // Jacky change this + + data1 = CoreISP2_IIC_Read(0xE628); + data0 = CoreISP2_IIC_Read(0xE629); + majorVer = data1<<8 | data0; + + data1 = CoreISP2_IIC_Read(0xE62A); + data0 = CoreISP2_IIC_Read(0xE62B); + minorVer = data1<<8 | data0; + + SW_Ver = majorVer <<16 | minorVer; + + DebugMessage(" ====Get ISP vision %d [0x%x]====\n", SW_Ver, SW_Ver); + return SW_Ver; + +} + +*/ + +static int cli5001_init(struct i2c_client *client) +{ + int ret, data_cnt, i; + const struct firmware *fw; + size_t Size; + u8* pInitialData; + u32 tempdiv,temprem, wData; + static int firmware_in = 1; + + c = client; + + /* download MCU binary */ + if(firmware_in){ + firmware_in = 0; + ret = request_firmware(&fw, "cli5001.bin", &c->dev); + if (ret){ + printk("request ISP firmware fail\n"); + goto init; + } + + pInitialData = fw->data; + Size = fw->size; + + // -LSC Size Setting + CoreISP2_IIC_Write(0xE2D2, LSC_ISP2_SIZE/256); + CoreISP2_IIC_Write(0xE2D3, LSC_ISP2_SIZE%256); + + // -Download LSC file + for(data_cnt=0; data_cntindex >= N_CLI5001_FMTS) + return -EINVAL; + + ofmt = cli5001_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + + +static int cli5001_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct cli5001_format_struct **ret_fmt, + struct cli5001_win_size **ret_wsize) +{ + int index; + struct cli5001_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + for (index = 0; index < N_CLI5001_FMTS; index++) + if (cli5001_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_CLI5001_FMTS) { + /* default to first format */ + index = 0; + pix->pixelformat = cli5001_formats[0].pixelformat; + } + if (ret_fmt != NULL) + *ret_fmt = cli5001_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + pix->field = V4L2_FIELD_NONE; +#if 0 + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = cli5001_win_sizes; wsize < cli5001_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width >= wsize->width && pix->height >= wsize->height) + break; + if (wsize >= cli5001_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + pix->width = wsize->width; + pix->height = wsize->height; +#endif + pix->bytesperline = pix->width*cli5001_formats[index].bpp >> 3; + pix->sizeimage = pix->height*pix->bytesperline; + printk("cli5001 w %d h %d bpl %d sizeimage %d\n", + pix->width, pix->height, pix->bytesperline, pix->sizeimage); + return 0; +} + +/* + * Set a format. + */ +static int cli5001_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct cli5001_format_struct *ovfmt; + struct cli5001_win_size *wsize; + struct cli5001_info *info = i2c_get_clientdata(c); + unsigned char v; + + ret = cli5001_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + switch(fmt->fmt.pix.width){ + case 320: + ret = _QVGA_RES_; + break; + case 480: + ret = _HVGA_RES_; + break; + case 1024: + ret = _XGA_RES_; + break; + default: + ret = _3M_RES_; + } + CoreISP2_Mode_Change (ret); + + if(fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420){ + //CoreISP2_IIC_Write(0xe059, 0x80); // Enabled YUV420 format + printk("cli5001 fmt YUV420\n"); + }else{ + CoreISP2_IIC_Write(0xe059, 0x83); // Enabled JPEG format + printk("cli5001 fmt JPEG\n"); + } + return 0; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int cli5001_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + unsigned char clkrc; + int ret; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = CLI5001_FRAME_RATE; + return 0; +} + +static int cli5001_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + unsigned char clkrc; + int ret, div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator*CLI5001_FRAME_RATE)/tpf->denominator; + tpf->numerator = 1; + tpf->denominator = CLI5001_FRAME_RATE/div; + return 0; +} + + + +/* + * Code for dealing with controls. + */ + +static int cli5001_t_contrast(int value) +{ + return 0; +} + +static int cli5001_t_vflip(int value) +{ + return 0; +} + +static struct cli5001_control { + struct v4l2_queryctrl qc; + void (*tweak)(int value); +} cli5001_controls[] = +{ + { + .qc = { + .id = V4L2_CID_FOCUS_AUTO, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Focus", + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = CoreISP2_FullAF, + }, + { + .qc = { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Do White Balance", + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = CoreISP2_WBmode, + }, + { + .qc = { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 4, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = cli5001_t_contrast, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "ColorEffect", + .minimum = 0, + .maximum = 16, + .step = 1, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = cli5001_t_contrast, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX cli5001 spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = cli5001_t_contrast, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = cli5001_t_vflip, + }, +}; +#define N_CONTROLS (ARRAY_SIZE(cli5001_controls)) + +static struct cli5001_control *cli5001_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (cli5001_controls[i].qc.id == id) + return cli5001_controls + i; + return NULL; +} + + +static int cli5001_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct cli5001_control *ctrl = cli5001_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int cli5001_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + /* we do not support get control, just set it */ + return 0; +} + +static int cli5001_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct cli5001_control *octrl = cli5001_find_control(ctrl->id); + int ret = 0; + if (octrl == NULL) + return -EINVAL; + octrl->tweak((u8)(ctrl->value)); + return ret; +} + +/* + * Basic i2c stuff. + */ + +static int cli5001_s_input(struct i2c_client *c, int *id) +{ + return 0; +} + +static int cli5001_streamon(struct i2c_client *c) +{ + //CoreISP2_IIC_Write(0xe060, 0x00); // pll on + return 0; +} + +static int cli5001_streamoff(struct i2c_client *c) +{ + DebugMessage("cli5001_streamoff\n"); + //CoreISP2_IIC_Write(0xE060, 0x3); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cli5001_g_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + reg->val = cli5001_read(client, (u16)reg->reg); + return 0; +} + +static int cli5001_s_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return cli5001_write(client, (u16)reg->reg, (unsigned char)reg->val); +} +#endif + +static int cli5001_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + int ret = -EINVAL; + + switch (cmd) { + case VIDIOC_DBG_G_CHIP_IDENT: + ret = v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CLI5001, 0); + + case VIDIOC_INT_RESET: + cli5001_reset(client); + ret = 0; + break; + + case VIDIOC_INT_INIT: + ret = cli5001_init(client); + break; + + case VIDIOC_ENUM_FMT: + ret = cli5001_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + break; + case VIDIOC_TRY_FMT: + ret = cli5001_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + break; + case VIDIOC_S_FMT: + ret = cli5001_s_fmt(client, (struct v4l2_format *) arg); + break; + case VIDIOC_QUERYCTRL: + ret = cli5001_queryctrl(client, (struct v4l2_queryctrl *) arg); + break; + case VIDIOC_S_CTRL: + ret = cli5001_s_ctrl(client, (struct v4l2_control *) arg); + break; + case VIDIOC_G_CTRL: + ret = cli5001_g_ctrl(client, (struct v4l2_control *) arg); + break; + case VIDIOC_S_PARM: + ret = cli5001_s_parm(client, (struct v4l2_streamparm *) arg); + break; + case VIDIOC_G_PARM: + ret = cli5001_g_parm(client, (struct v4l2_streamparm *) arg); + break; + case VIDIOC_S_INPUT: + ret = cli5001_s_input(client, (int *) arg); + break; + case VIDIOC_STREAMON: + ret = cli5001_streamon(client); + break; + case VIDIOC_STREAMOFF: + ret = cli5001_streamoff(client); + break; +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + ret = cli5001_g_register(client, (struct v4l2_dbg_register *) arg); + break; + case VIDIOC_DBG_S_REGISTER: + ret = cli5001_s_register(client, (struct v4l2_dbg_register *) arg); + break; +#endif + default: + break; + } + return ret; +} + +extern struct clk *pxa168_ccic_gate_clk; +int ccic_sensor_attach(struct i2c_client *client); +static int __devinit cli5001_probe(struct i2c_client *client) +{ + int ret; + struct cli5001_info *info; + struct sensor_platform_data *pdata; + pdata = client->dev.platform_data; + + clk_enable(pxa168_ccic_gate_clk); + ccic_set_clock_parallel(); //clock must be enabled before power on. + + pdata->power_on(1, 1); + /* + * Set up our info structure. + */ + info = kzalloc(sizeof (struct cli5001_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &cli5001_formats[1]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + /* + * Make sure it's an cli5001 + */ + ret = cli5001_detect(client); + if (ret) + goto out_free_info; + + ccic_sensor_attach(client); + //pdata->power_on(0, 1); + ccic_disable_clock(); + printk("cli5001 detected\n"); + return 0; + +out_free_info: + kfree(info); +out_free: + return ret; +} + +static int cli5001_remove(struct i2c_client *client) +{ + return 0; //TODO +} + +static struct i2c_device_id cli5001_idtable[] = { + { "cli5001", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cli5001_idtable); + +static struct i2c_driver cli5001_driver = { + .driver = { + .name = "cli5001", + }, + .id_table = cli5001_idtable, + .command = cli5001_command, + .probe = cli5001_probe, + .remove = cli5001_remove, +}; + + +/* + * Module initialization + */ +static int __init cli5001_mod_init(void) +{ + printk(KERN_NOTICE "cli5001 camera ISP driver, at your service\n"); + return i2c_add_driver(&cli5001_driver); +} + +static void __exit cli5001_mod_exit(void) +{ + i2c_del_driver(&cli5001_driver); +} + +late_initcall(cli5001_mod_init); +module_exit(cli5001_mod_exit); diff --git a/drivers/media/video/cli5001.h b/drivers/media/video/cli5001.h new file mode 100644 index 00000000000000..dc503dffdf02b9 --- /dev/null +++ b/drivers/media/video/cli5001.h @@ -0,0 +1,3078 @@ +#define LSC_ISP2_SIZE 3072 + +unsigned char LSC_INIT_TABLE[3072]={ +0x89, +0x82, +0x7A, +0x74, +0x6F, +0x6A, +0x66, +0x63, +0x5F, +0x5C, +0x59, +0x57, +0x55, +0x53, +0x52, +0x51, +0x50, +0x50, +0x50, +0x51, +0x52, +0x53, +0x54, +0x56, +0x58, +0x5A, +0x5C, +0x5F, +0x62, +0x66, +0x6C, +0x71, +0x85, +0x7F, +0x78, +0x72, +0x6D, +0x68, +0x64, +0x61, +0x5D, +0x5A, +0x57, +0x55, +0x53, +0x51, +0x50, +0x4F, +0x4E, +0x4E, +0x4E, +0x4F, +0x50, +0x51, +0x52, +0x54, +0x56, +0x58, +0x5A, +0x5D, +0x60, +0x64, +0x69, +0x6D, +0x80, +0x7B, +0x74, +0x6F, +0x6A, +0x66, +0x62, +0x5E, +0x5A, +0x57, +0x54, +0x52, +0x50, +0x4E, +0x4D, +0x4C, +0x4C, +0x4B, +0x4C, +0x4C, +0x4D, +0x4E, +0x4F, +0x51, +0x53, +0x55, +0x58, +0x5B, +0x5D, +0x61, +0x65, +0x69, +0x7C, +0x78, +0x72, +0x6D, +0x68, +0x64, +0x5F, +0x5C, +0x58, +0x55, +0x52, +0x4F, +0x4D, +0x4C, +0x4B, +0x4A, +0x49, +0x49, +0x49, +0x4A, +0x4B, +0x4C, +0x4D, +0x4F, +0x51, +0x53, +0x56, +0x58, +0x5B, +0x5E, +0x62, +0x65, +0x7A, +0x76, +0x70, +0x6B, +0x66, +0x62, +0x5D, +0x59, +0x56, +0x52, +0x50, +0x4D, +0x4B, +0x4A, +0x49, +0x48, +0x47, +0x47, +0x47, +0x48, +0x49, +0x4A, +0x4B, +0x4D, +0x4F, +0x51, +0x54, +0x57, +0x5A, +0x5D, +0x60, +0x63, +0x78, +0x74, +0x6F, +0x6A, +0x64, +0x60, +0x5B, +0x57, +0x53, +0x50, +0x4E, +0x4B, +0x49, +0x48, +0x47, +0x46, +0x45, +0x45, +0x45, +0x46, +0x47, +0x48, +0x49, +0x4B, +0x4D, +0x4F, +0x52, +0x55, +0x58, +0x5B, +0x5E, +0x61, +0x76, +0x72, +0x6D, +0x68, +0x63, +0x5E, +0x59, +0x55, +0x52, +0x4E, +0x4C, +0x4A, +0x48, +0x46, +0x45, +0x44, +0x44, +0x44, +0x44, +0x44, +0x45, +0x46, +0x48, +0x49, +0x4B, +0x4E, +0x50, +0x53, +0x57, +0x5A, +0x5D, +0x60, +0x75, +0x71, +0x6C, +0x66, +0x61, +0x5C, +0x58, +0x54, +0x50, +0x4D, +0x4A, +0x48, +0x46, +0x45, +0x44, +0x43, +0x42, +0x42, +0x42, +0x43, +0x44, +0x45, +0x46, +0x48, +0x4A, +0x4D, +0x4F, +0x52, +0x55, +0x58, +0x5C, +0x5E, +0x73, +0x70, +0x6A, +0x65, +0x60, +0x5B, +0x56, +0x52, +0x4F, +0x4C, +0x49, +0x47, +0x45, +0x44, +0x43, +0x42, +0x41, +0x41, +0x41, +0x42, +0x42, +0x44, +0x45, +0x47, +0x49, +0x4B, +0x4E, +0x51, +0x54, +0x57, +0x5B, +0x5D, +0x73, +0x6F, +0x69, +0x64, +0x5E, +0x5A, +0x55, +0x51, +0x4E, +0x4B, +0x48, +0x46, +0x44, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4B, +0x4D, +0x50, +0x54, +0x57, +0x5A, +0x5D, +0x72, +0x6E, +0x69, +0x63, +0x5E, +0x59, +0x54, +0x50, +0x4D, +0x4A, +0x48, +0x45, +0x44, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x44, +0x45, +0x47, +0x4A, +0x4D, +0x50, +0x53, +0x56, +0x5A, +0x5C, +0x72, +0x6E, +0x68, +0x63, +0x5D, +0x58, +0x54, +0x50, +0x4D, +0x4A, +0x47, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x3F, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x4A, +0x4C, +0x50, +0x53, +0x56, +0x5A, +0x5C, +0x72, +0x6E, +0x68, +0x63, +0x5D, +0x59, +0x54, +0x50, +0x4D, +0x4A, +0x47, +0x45, +0x43, +0x42, +0x41, +0x40, +0x3F, +0x3F, +0x3F, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x53, +0x56, +0x5A, +0x5C, +0x72, +0x6E, +0x69, +0x63, +0x5E, +0x59, +0x54, +0x50, +0x4D, +0x4A, +0x47, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x3F, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x4A, +0x4D, +0x50, +0x53, +0x56, +0x5A, +0x5C, +0x73, +0x6F, +0x69, +0x64, +0x5F, +0x59, +0x55, +0x51, +0x4D, +0x4B, +0x48, +0x46, +0x44, +0x42, +0x41, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x44, +0x46, +0x48, +0x4A, +0x4D, +0x50, +0x53, +0x57, +0x5A, +0x5D, +0x74, +0x70, +0x6A, +0x65, +0x60, +0x5A, +0x56, +0x52, +0x4E, +0x4B, +0x49, +0x46, +0x45, +0x43, +0x42, +0x41, +0x41, +0x41, +0x41, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4B, +0x4E, +0x51, +0x54, +0x57, +0x5B, +0x5D, +0x75, +0x71, +0x6C, +0x66, +0x61, +0x5C, +0x57, +0x53, +0x50, +0x4C, +0x4A, +0x47, +0x46, +0x44, +0x43, +0x42, +0x42, +0x41, +0x42, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4A, +0x4C, +0x4F, +0x52, +0x55, +0x58, +0x5B, +0x5E, +0x77, +0x73, +0x6D, +0x67, +0x62, +0x5D, +0x59, +0x55, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x45, +0x44, +0x43, +0x43, +0x43, +0x43, +0x43, +0x44, +0x45, +0x47, +0x49, +0x4B, +0x4D, +0x50, +0x53, +0x56, +0x59, +0x5C, +0x5F, +0x79, +0x74, +0x6E, +0x69, +0x64, +0x5F, +0x5A, +0x56, +0x53, +0x4F, +0x4D, +0x4A, +0x48, +0x47, +0x46, +0x45, +0x44, +0x44, +0x45, +0x45, +0x46, +0x47, +0x49, +0x4A, +0x4D, +0x4F, +0x51, +0x54, +0x57, +0x5A, +0x5D, +0x60, +0x7C, +0x77, +0x70, +0x6B, +0x66, +0x61, +0x5D, +0x58, +0x55, +0x51, +0x4F, +0x4C, +0x4A, +0x49, +0x48, +0x47, +0x46, +0x46, +0x46, +0x47, +0x48, +0x49, +0x4A, +0x4C, +0x4E, +0x51, +0x53, +0x56, +0x58, +0x5B, +0x5F, +0x62, +0x7F, +0x7A, +0x73, +0x6D, +0x68, +0x63, +0x5F, +0x5B, +0x57, +0x54, +0x51, +0x4E, +0x4D, +0x4B, +0x4A, +0x49, +0x48, +0x48, +0x48, +0x49, +0x4A, +0x4B, +0x4C, +0x4E, +0x50, +0x52, +0x55, +0x57, +0x5A, +0x5D, +0x61, +0x64, +0x83, +0x7D, +0x76, +0x6F, +0x6A, +0x66, +0x61, +0x5D, +0x59, +0x56, +0x53, +0x51, +0x4F, +0x4D, +0x4C, +0x4B, +0x4B, +0x4A, +0x4B, +0x4B, +0x4C, +0x4D, +0x4E, +0x50, +0x52, +0x54, +0x57, +0x59, +0x5C, +0x5F, +0x64, +0x68, +0x89, +0x82, +0x79, +0x72, +0x6D, +0x68, +0x64, +0x60, +0x5C, +0x59, +0x56, +0x54, +0x52, +0x50, +0x4F, +0x4E, +0x4D, +0x4D, +0x4D, +0x4E, +0x4E, +0x4F, +0x51, +0x52, +0x54, +0x57, +0x59, +0x5C, +0x5F, +0x62, +0x68, +0x6C, +0x90, +0x86, +0x7C, +0x75, +0x6F, +0x6A, +0x6D, +0x61, +0x5E, +0x5A, +0x58, +0x55, +0x53, +0x51, +0x50, +0x4F, +0x4E, +0x4E, +0x4F, +0x4F, +0x4F, +0x51, +0x52, +0x54, +0x55, +0x58, +0x5A, +0x5D, +0x61, +0x65, +0x6B, +0x70, +0x77, +0x71, +0x6A, +0x65, +0x61, +0x5E, +0x5B, +0x59, +0x57, +0x55, +0x53, +0x52, +0x51, +0x50, +0x50, +0x50, +0x50, +0x51, +0x52, +0x54, +0x55, +0x57, +0x5A, +0x5C, +0x5F, +0x62, +0x65, +0x69, +0x6D, +0x72, +0x79, +0x7F, +0x73, +0x6E, +0x68, +0x63, +0x60, +0x5D, +0x5A, +0x57, +0x55, +0x53, +0x51, +0x50, +0x4F, +0x4E, +0x4E, +0x4E, +0x4E, +0x4F, +0x50, +0x51, +0x53, +0x55, +0x58, +0x5A, +0x5D, +0x60, +0x64, +0x67, +0x6B, +0x6F, +0x75, +0x7A, +0x6E, +0x6A, +0x65, +0x61, +0x5D, +0x5A, +0x57, +0x54, +0x52, +0x50, +0x4E, +0x4D, +0x4C, +0x4B, +0x4B, +0x4B, +0x4B, +0x4C, +0x4D, +0x4F, +0x50, +0x52, +0x55, +0x57, +0x5A, +0x5E, +0x61, +0x65, +0x68, +0x6C, +0x72, +0x76, +0x6B, +0x67, +0x63, +0x5F, +0x5C, +0x58, +0x55, +0x52, +0x50, +0x4E, +0x4C, +0x4B, +0x4A, +0x49, +0x49, +0x49, +0x49, +0x4A, +0x4B, +0x4C, +0x4E, +0x50, +0x52, +0x55, +0x58, +0x5B, +0x5F, +0x63, +0x66, +0x6A, +0x6F, +0x73, +0x68, +0x65, +0x61, +0x5D, +0x5A, +0x56, +0x53, +0x50, +0x4E, +0x4C, +0x4A, +0x49, +0x48, +0x47, +0x47, +0x47, +0x47, +0x48, +0x49, +0x4A, +0x4C, +0x4E, +0x50, +0x53, +0x56, +0x59, +0x5D, +0x61, +0x65, +0x69, +0x6D, +0x70, +0x65, +0x63, +0x5F, +0x5B, +0x58, +0x54, +0x51, +0x4F, +0x4C, +0x4A, +0x48, +0x47, +0x46, +0x46, +0x45, +0x45, +0x46, +0x46, +0x47, +0x49, +0x4A, +0x4C, +0x4F, +0x51, +0x54, +0x58, +0x5B, +0x5F, +0x63, +0x67, +0x6B, +0x6F, +0x64, +0x61, +0x5D, +0x5A, +0x56, +0x53, +0x4F, +0x4D, +0x4A, +0x48, +0x47, +0x46, +0x45, +0x44, +0x44, +0x44, +0x44, +0x45, +0x46, +0x47, +0x49, +0x4B, +0x4D, +0x50, +0x53, +0x56, +0x5A, +0x5E, +0x62, +0x66, +0x6A, +0x6D, +0x62, +0x60, +0x5C, +0x58, +0x54, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x45, +0x44, +0x43, +0x43, +0x42, +0x42, +0x43, +0x44, +0x44, +0x46, +0x47, +0x49, +0x4C, +0x4E, +0x51, +0x55, +0x59, +0x5D, +0x61, +0x65, +0x69, +0x6C, +0x61, +0x5E, +0x5B, +0x57, +0x53, +0x50, +0x4D, +0x4A, +0x48, +0x46, +0x44, +0x43, +0x42, +0x42, +0x41, +0x41, +0x42, +0x42, +0x43, +0x45, +0x46, +0x48, +0x4B, +0x4D, +0x50, +0x54, +0x57, +0x5C, +0x60, +0x64, +0x68, +0x6B, +0x60, +0x5E, +0x5A, +0x56, +0x52, +0x4F, +0x4C, +0x49, +0x47, +0x45, +0x44, +0x42, +0x41, +0x41, +0x41, +0x41, +0x41, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4A, +0x4C, +0x4F, +0x53, +0x57, +0x5B, +0x5F, +0x63, +0x68, +0x6B, +0x5F, +0x5D, +0x59, +0x55, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x41, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x52, +0x56, +0x5A, +0x5F, +0x63, +0x67, +0x6A, +0x5F, +0x5C, +0x59, +0x55, +0x51, +0x4E, +0x4B, +0x48, +0x46, +0x44, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x52, +0x56, +0x5A, +0x5E, +0x63, +0x67, +0x6A, +0x5F, +0x5C, +0x58, +0x55, +0x51, +0x4E, +0x4B, +0x48, +0x46, +0x44, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x52, +0x56, +0x5A, +0x5E, +0x63, +0x67, +0x6A, +0x5F, +0x5C, +0x58, +0x55, +0x51, +0x4E, +0x4B, +0x49, +0x46, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x41, +0x41, +0x42, +0x44, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x52, +0x56, +0x5A, +0x5F, +0x63, +0x67, +0x6A, +0x5F, +0x5D, +0x59, +0x55, +0x52, +0x4F, +0x4C, +0x49, +0x47, +0x45, +0x44, +0x42, +0x42, +0x41, +0x41, +0x41, +0x41, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4A, +0x4D, +0x50, +0x53, +0x57, +0x5B, +0x5F, +0x63, +0x68, +0x6B, +0x60, +0x5D, +0x59, +0x56, +0x52, +0x4F, +0x4C, +0x4A, +0x48, +0x46, +0x44, +0x43, +0x42, +0x42, +0x42, +0x42, +0x42, +0x43, +0x44, +0x45, +0x47, +0x49, +0x4B, +0x4E, +0x51, +0x54, +0x58, +0x5C, +0x60, +0x64, +0x68, +0x6B, +0x61, +0x5E, +0x5A, +0x57, +0x53, +0x50, +0x4D, +0x4B, +0x49, +0x47, +0x45, +0x44, +0x44, +0x43, +0x43, +0x43, +0x43, +0x44, +0x45, +0x46, +0x48, +0x4A, +0x4C, +0x4F, +0x52, +0x55, +0x59, +0x5D, +0x61, +0x65, +0x69, +0x6C, +0x62, +0x5F, +0x5B, +0x58, +0x54, +0x51, +0x4F, +0x4C, +0x4A, +0x48, +0x47, +0x46, +0x45, +0x44, +0x44, +0x44, +0x45, +0x45, +0x46, +0x48, +0x49, +0x4B, +0x4E, +0x50, +0x53, +0x57, +0x5A, +0x5E, +0x62, +0x66, +0x6A, +0x6D, +0x64, +0x61, +0x5C, +0x59, +0x56, +0x53, +0x50, +0x4E, +0x4B, +0x4A, +0x48, +0x47, +0x46, +0x46, +0x46, +0x46, +0x46, +0x47, +0x48, +0x49, +0x4B, +0x4D, +0x4F, +0x52, +0x55, +0x58, +0x5C, +0x60, +0x63, +0x67, +0x6B, +0x6F, +0x66, +0x63, +0x5E, +0x5B, +0x57, +0x54, +0x52, +0x4F, +0x4D, +0x4B, +0x4A, +0x49, +0x48, +0x48, +0x48, +0x48, +0x48, +0x49, +0x4A, +0x4B, +0x4D, +0x4F, +0x51, +0x54, +0x57, +0x5A, +0x5E, +0x61, +0x65, +0x68, +0x6D, +0x71, +0x69, +0x65, +0x60, +0x5C, +0x59, +0x56, +0x54, +0x51, +0x4F, +0x4D, +0x4C, +0x4B, +0x4A, +0x4A, +0x4A, +0x4A, +0x4A, +0x4B, +0x4C, +0x4D, +0x4F, +0x51, +0x53, +0x56, +0x59, +0x5C, +0x60, +0x63, +0x66, +0x6A, +0x6F, +0x73, +0x6D, +0x69, +0x63, +0x5F, +0x5B, +0x58, +0x56, +0x53, +0x51, +0x50, +0x4E, +0x4D, +0x4D, +0x4C, +0x4C, +0x4C, +0x4D, +0x4D, +0x4E, +0x50, +0x51, +0x53, +0x56, +0x58, +0x5B, +0x5F, +0x62, +0x65, +0x68, +0x6C, +0x72, +0x76, +0x72, +0x6C, +0x66, +0x61, +0x5D, +0x5A, +0x58, +0x56, +0x54, +0x52, +0x51, +0x50, +0x4F, +0x4F, +0x4E, +0x4F, +0x4F, +0x50, +0x51, +0x52, +0x54, +0x56, +0x58, +0x5B, +0x5E, +0x61, +0x64, +0x67, +0x6B, +0x70, +0x76, +0x7B, +0x78, +0x70, +0x68, +0x63, +0x5F, +0x5B, +0x5F, +0x56, +0x55, +0x53, +0x52, +0x51, +0x50, +0x50, +0x50, +0x50, +0x50, +0x51, +0x52, +0x54, +0x55, +0x57, +0x5A, +0x5C, +0x5F, +0x62, +0x65, +0x68, +0x6D, +0x72, +0x79, +0x7F, +0x82, +0x7A, +0x73, +0x6D, +0x69, +0x66, +0x62, +0x60, +0x5D, +0x5B, +0x59, +0x57, +0x56, +0x55, +0x55, +0x55, +0x55, +0x56, +0x57, +0x59, +0x5B, +0x5D, +0x60, +0x64, +0x67, +0x6B, +0x6E, +0x72, +0x77, +0x7C, +0x84, +0x8C, +0x7C, +0x76, +0x70, +0x6B, +0x67, +0x63, +0x60, +0x5D, +0x5A, +0x58, +0x56, +0x54, +0x53, +0x52, +0x52, +0x52, +0x52, +0x53, +0x54, +0x56, +0x58, +0x5A, +0x5D, +0x61, +0x64, +0x68, +0x6C, +0x70, +0x74, +0x79, +0x80, +0x86, +0x77, +0x72, +0x6C, +0x68, +0x64, +0x61, +0x5D, +0x5A, +0x57, +0x55, +0x53, +0x51, +0x50, +0x4F, +0x4E, +0x4E, +0x4F, +0x50, +0x51, +0x53, +0x54, +0x57, +0x5A, +0x5D, +0x61, +0x65, +0x69, +0x6D, +0x71, +0x76, +0x7C, +0x81, +0x73, +0x6E, +0x69, +0x65, +0x62, +0x5E, +0x5A, +0x57, +0x54, +0x52, +0x50, +0x4E, +0x4D, +0x4C, +0x4B, +0x4C, +0x4C, +0x4D, +0x4E, +0x4F, +0x51, +0x54, +0x57, +0x5A, +0x5E, +0x62, +0x66, +0x6A, +0x6F, +0x73, +0x78, +0x7D, +0x6F, +0x6B, +0x67, +0x63, +0x5F, +0x5B, +0x58, +0x54, +0x52, +0x4F, +0x4D, +0x4B, +0x4A, +0x49, +0x49, +0x49, +0x49, +0x4A, +0x4B, +0x4D, +0x4F, +0x51, +0x54, +0x57, +0x5B, +0x5F, +0x63, +0x68, +0x6C, +0x71, +0x76, +0x7A, +0x6D, +0x69, +0x65, +0x61, +0x5D, +0x59, +0x55, +0x52, +0x4F, +0x4D, +0x4B, +0x49, +0x48, +0x47, +0x47, +0x47, +0x47, +0x48, +0x49, +0x4A, +0x4C, +0x4E, +0x51, +0x54, +0x58, +0x5C, +0x61, +0x65, +0x6A, +0x6E, +0x73, +0x78, +0x6A, +0x67, +0x63, +0x5F, +0x5B, +0x57, +0x53, +0x50, +0x4D, +0x4B, +0x49, +0x47, +0x46, +0x45, +0x45, +0x45, +0x45, +0x46, +0x47, +0x48, +0x4A, +0x4C, +0x4F, +0x52, +0x56, +0x5A, +0x5E, +0x63, +0x68, +0x6D, +0x72, +0x76, +0x69, +0x66, +0x62, +0x5D, +0x59, +0x55, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x46, +0x44, +0x44, +0x43, +0x43, +0x43, +0x44, +0x45, +0x47, +0x48, +0x4B, +0x4D, +0x50, +0x54, +0x58, +0x5C, +0x61, +0x66, +0x6B, +0x70, +0x74, +0x67, +0x64, +0x60, +0x5C, +0x58, +0x54, +0x50, +0x4D, +0x4A, +0x48, +0x46, +0x44, +0x43, +0x42, +0x42, +0x42, +0x42, +0x43, +0x44, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x52, +0x56, +0x5B, +0x60, +0x65, +0x6A, +0x6F, +0x73, +0x66, +0x63, +0x5F, +0x5B, +0x56, +0x52, +0x4F, +0x4C, +0x49, +0x47, +0x45, +0x43, +0x42, +0x41, +0x41, +0x41, +0x41, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4A, +0x4D, +0x51, +0x55, +0x59, +0x5E, +0x63, +0x69, +0x6E, +0x72, +0x66, +0x63, +0x5E, +0x5A, +0x56, +0x52, +0x4E, +0x4B, +0x48, +0x46, +0x44, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x4A, +0x4D, +0x50, +0x54, +0x58, +0x5D, +0x63, +0x68, +0x6D, +0x71, +0x65, +0x62, +0x5E, +0x59, +0x55, +0x51, +0x4E, +0x4A, +0x48, +0x45, +0x44, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x53, +0x58, +0x5D, +0x62, +0x67, +0x6D, +0x71, +0x65, +0x62, +0x5E, +0x59, +0x55, +0x51, +0x4E, +0x4A, +0x48, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x53, +0x58, +0x5D, +0x62, +0x67, +0x6C, +0x70, +0x66, +0x62, +0x5E, +0x5A, +0x55, +0x51, +0x4E, +0x4B, +0x48, +0x46, +0x44, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x53, +0x58, +0x5D, +0x62, +0x67, +0x6C, +0x70, +0x66, +0x63, +0x5E, +0x5A, +0x56, +0x52, +0x4E, +0x4B, +0x48, +0x46, +0x44, +0x43, +0x41, +0x41, +0x40, +0x40, +0x41, +0x41, +0x42, +0x43, +0x45, +0x47, +0x4A, +0x4D, +0x50, +0x54, +0x58, +0x5D, +0x63, +0x67, +0x6D, +0x71, +0x67, +0x64, +0x5F, +0x5B, +0x57, +0x53, +0x4F, +0x4C, +0x49, +0x47, +0x45, +0x44, +0x42, +0x42, +0x41, +0x41, +0x41, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4B, +0x4D, +0x51, +0x55, +0x59, +0x5E, +0x63, +0x68, +0x6D, +0x71, +0x68, +0x65, +0x60, +0x5C, +0x58, +0x54, +0x50, +0x4D, +0x4B, +0x48, +0x46, +0x45, +0x43, +0x43, +0x42, +0x42, +0x42, +0x43, +0x44, +0x45, +0x47, +0x49, +0x4C, +0x4F, +0x52, +0x56, +0x5A, +0x5F, +0x64, +0x69, +0x6E, +0x72, +0x6A, +0x66, +0x62, +0x5D, +0x59, +0x55, +0x52, +0x4F, +0x4C, +0x4A, +0x48, +0x46, +0x45, +0x44, +0x44, +0x43, +0x44, +0x44, +0x45, +0x47, +0x49, +0x4B, +0x4D, +0x50, +0x54, +0x58, +0x5C, +0x61, +0x65, +0x6A, +0x6E, +0x73, +0x6C, +0x68, +0x63, +0x5F, +0x5B, +0x57, +0x54, +0x51, +0x4E, +0x4B, +0x4A, +0x48, +0x47, +0x46, +0x45, +0x45, +0x46, +0x46, +0x47, +0x49, +0x4A, +0x4D, +0x4F, +0x52, +0x56, +0x5A, +0x5E, +0x62, +0x67, +0x6B, +0x70, +0x74, +0x6F, +0x6A, +0x65, +0x61, +0x5D, +0x59, +0x56, +0x53, +0x50, +0x4E, +0x4C, +0x4A, +0x49, +0x48, +0x47, +0x47, +0x47, +0x48, +0x49, +0x4B, +0x4C, +0x4F, +0x51, +0x54, +0x58, +0x5C, +0x60, +0x64, +0x68, +0x6C, +0x71, +0x76, +0x72, +0x6D, +0x68, +0x63, +0x5F, +0x5C, +0x58, +0x55, +0x52, +0x50, +0x4E, +0x4C, +0x4B, +0x4A, +0x4A, +0x4A, +0x4A, +0x4B, +0x4C, +0x4D, +0x4F, +0x51, +0x54, +0x57, +0x5A, +0x5E, +0x62, +0x66, +0x6A, +0x6E, +0x73, +0x79, +0x76, +0x71, +0x6A, +0x65, +0x62, +0x5E, +0x5B, +0x58, +0x55, +0x53, +0x51, +0x4F, +0x4E, +0x4D, +0x4C, +0x4C, +0x4D, +0x4D, +0x4E, +0x50, +0x52, +0x54, +0x56, +0x59, +0x5D, +0x60, +0x64, +0x68, +0x6C, +0x70, +0x76, +0x7C, +0x7D, +0x76, +0x6E, +0x69, +0x64, +0x61, +0x5E, +0x5A, +0x58, +0x56, +0x53, +0x52, +0x51, +0x50, +0x4F, +0x4F, +0x50, +0x50, +0x51, +0x53, +0x55, +0x57, +0x59, +0x5C, +0x60, +0x63, +0x66, +0x6A, +0x6F, +0x74, +0x7B, +0x81, +0x83, +0x7A, +0x72, +0x6B, +0x66, +0x62, +0x66, +0x5C, +0x5A, +0x58, +0x55, +0x54, +0x53, +0x52, +0x51, +0x51, +0x52, +0x52, +0x53, +0x55, +0x57, +0x59, +0x5B, +0x5E, +0x61, +0x65, +0x68, +0x6C, +0x71, +0x77, +0x7E, +0x86, +0x7F, +0x78, +0x70, +0x6A, +0x66, +0x62, +0x5E, +0x5B, +0x59, +0x56, +0x54, +0x52, +0x50, +0x4F, +0x4E, +0x4E, +0x4D, +0x4D, +0x4D, +0x4E, +0x4E, +0x4F, +0x51, +0x52, +0x54, +0x56, +0x58, +0x5B, +0x5E, +0x62, +0x68, +0x6D, +0x7A, +0x74, +0x6D, +0x68, +0x64, +0x60, +0x5D, +0x59, +0x57, +0x54, +0x52, +0x50, +0x4E, +0x4D, +0x4C, +0x4C, +0x4B, +0x4B, +0x4B, +0x4C, +0x4D, +0x4E, +0x4F, +0x50, +0x52, +0x54, +0x56, +0x59, +0x5C, +0x5F, +0x64, +0x69, +0x76, +0x70, +0x6A, +0x66, +0x62, +0x5E, +0x5B, +0x57, +0x54, +0x52, +0x50, +0x4E, +0x4C, +0x4B, +0x4A, +0x49, +0x49, +0x49, +0x49, +0x4A, +0x4A, +0x4B, +0x4D, +0x4E, +0x50, +0x52, +0x54, +0x57, +0x59, +0x5C, +0x61, +0x65, +0x72, +0x6D, +0x68, +0x64, +0x60, +0x5C, +0x59, +0x55, +0x52, +0x50, +0x4E, +0x4C, +0x4A, +0x49, +0x48, +0x48, +0x47, +0x47, +0x47, +0x48, +0x48, +0x49, +0x4B, +0x4C, +0x4E, +0x50, +0x52, +0x55, +0x57, +0x5A, +0x5E, +0x62, +0x6F, +0x6B, +0x67, +0x62, +0x5E, +0x5B, +0x57, +0x54, +0x51, +0x4E, +0x4C, +0x4A, +0x49, +0x47, +0x47, +0x46, +0x45, +0x45, +0x46, +0x46, +0x47, +0x48, +0x49, +0x4B, +0x4C, +0x4E, +0x51, +0x53, +0x56, +0x59, +0x5C, +0x60, +0x6E, +0x6A, +0x65, +0x61, +0x5D, +0x59, +0x55, +0x52, +0x4F, +0x4D, +0x4B, +0x49, +0x47, +0x46, +0x45, +0x44, +0x44, +0x44, +0x44, +0x44, +0x45, +0x46, +0x47, +0x49, +0x4B, +0x4D, +0x4F, +0x52, +0x55, +0x57, +0x5B, +0x5E, +0x6C, +0x68, +0x64, +0x60, +0x5C, +0x58, +0x54, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x46, +0x45, +0x44, +0x43, +0x43, +0x43, +0x43, +0x43, +0x44, +0x45, +0x46, +0x48, +0x4A, +0x4C, +0x4E, +0x51, +0x53, +0x56, +0x59, +0x5C, +0x6B, +0x67, +0x63, +0x5F, +0x5A, +0x57, +0x53, +0x50, +0x4D, +0x4A, +0x48, +0x46, +0x45, +0x44, +0x43, +0x42, +0x42, +0x42, +0x42, +0x42, +0x43, +0x44, +0x45, +0x47, +0x49, +0x4B, +0x4D, +0x50, +0x52, +0x55, +0x58, +0x5B, +0x6A, +0x66, +0x62, +0x5E, +0x5A, +0x56, +0x52, +0x4F, +0x4C, +0x4A, +0x47, +0x46, +0x44, +0x43, +0x42, +0x41, +0x41, +0x41, +0x41, +0x41, +0x42, +0x43, +0x44, +0x46, +0x48, +0x4A, +0x4C, +0x4F, +0x52, +0x54, +0x58, +0x5B, +0x69, +0x66, +0x61, +0x5D, +0x59, +0x55, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x45, +0x43, +0x42, +0x41, +0x41, +0x40, +0x40, +0x40, +0x41, +0x41, +0x42, +0x44, +0x45, +0x47, +0x49, +0x4C, +0x4E, +0x51, +0x54, +0x57, +0x5A, +0x69, +0x65, +0x61, +0x5C, +0x58, +0x54, +0x51, +0x4E, +0x4B, +0x49, +0x46, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4B, +0x4E, +0x50, +0x53, +0x57, +0x59, +0x69, +0x65, +0x61, +0x5C, +0x58, +0x54, +0x51, +0x4D, +0x4B, +0x48, +0x46, +0x44, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x46, +0x48, +0x4B, +0x4E, +0x50, +0x53, +0x56, +0x59, +0x69, +0x65, +0x61, +0x5C, +0x58, +0x54, +0x51, +0x4E, +0x4B, +0x48, +0x46, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x46, +0x49, +0x4B, +0x4E, +0x50, +0x53, +0x56, +0x59, +0x69, +0x65, +0x61, +0x5D, +0x58, +0x55, +0x51, +0x4E, +0x4B, +0x49, +0x47, +0x45, +0x43, +0x42, +0x41, +0x40, +0x40, +0x40, +0x40, +0x40, +0x41, +0x42, +0x43, +0x45, +0x47, +0x49, +0x4B, +0x4E, +0x51, +0x53, +0x57, +0x59, +0x6A, +0x66, +0x61, +0x5D, +0x59, +0x55, +0x52, +0x4F, +0x4C, +0x49, +0x47, +0x45, +0x44, +0x43, +0x42, +0x41, +0x41, +0x40, +0x41, +0x41, +0x42, +0x43, +0x44, +0x45, +0x47, +0x49, +0x4C, +0x4E, +0x51, +0x54, +0x57, +0x5A, +0x6A, +0x67, +0x62, +0x5E, +0x5A, +0x56, +0x53, +0x4F, +0x4D, +0x4A, +0x48, +0x46, +0x45, +0x43, +0x42, +0x42, +0x41, +0x41, +0x41, +0x42, +0x42, +0x43, +0x45, +0x46, +0x48, +0x4A, +0x4C, +0x4F, +0x51, +0x54, +0x57, +0x5A, +0x6B, +0x67, +0x63, +0x5F, +0x5B, +0x57, +0x54, +0x50, +0x4E, +0x4B, +0x49, +0x47, +0x46, +0x44, +0x43, +0x43, +0x42, +0x42, +0x42, +0x43, +0x43, +0x44, +0x46, +0x47, +0x49, +0x4B, +0x4D, +0x50, +0x52, +0x55, +0x58, +0x5B, +0x6D, +0x69, +0x64, +0x60, +0x5C, +0x58, +0x55, +0x52, +0x4F, +0x4D, +0x4A, +0x48, +0x47, +0x46, +0x45, +0x44, +0x43, +0x43, +0x43, +0x44, +0x45, +0x46, +0x47, +0x48, +0x4A, +0x4C, +0x4E, +0x51, +0x53, +0x56, +0x59, +0x5C, +0x6F, +0x6A, +0x65, +0x62, +0x5E, +0x5A, +0x56, +0x53, +0x51, +0x4E, +0x4C, +0x4A, +0x48, +0x47, +0x46, +0x45, +0x45, +0x45, +0x45, +0x45, +0x46, +0x47, +0x48, +0x4A, +0x4B, +0x4D, +0x4F, +0x52, +0x54, +0x57, +0x5A, +0x5D, +0x72, +0x6D, +0x67, +0x63, +0x5F, +0x5C, +0x58, +0x55, +0x52, +0x50, +0x4E, +0x4C, +0x4A, +0x49, +0x48, +0x47, +0x47, +0x47, +0x47, +0x47, +0x48, +0x49, +0x4A, +0x4B, +0x4D, +0x4F, +0x51, +0x53, +0x56, +0x58, +0x5C, +0x5F, +0x75, +0x70, +0x6A, +0x65, +0x61, +0x5E, +0x5A, +0x57, +0x54, +0x52, +0x4F, +0x4E, +0x4C, +0x4B, +0x4A, +0x49, +0x49, +0x48, +0x49, +0x49, +0x4A, +0x4B, +0x4C, +0x4D, +0x4F, +0x51, +0x53, +0x55, +0x57, +0x5A, +0x5E, +0x62, +0x79, +0x73, +0x6C, +0x67, +0x63, +0x60, +0x5C, +0x59, +0x56, +0x54, +0x52, +0x50, +0x4E, +0x4D, +0x4C, +0x4B, +0x4B, +0x4B, +0x4B, +0x4B, +0x4C, +0x4D, +0x4E, +0x4F, +0x51, +0x53, +0x55, +0x57, +0x59, +0x5C, +0x60, +0x65, +0x7F, +0x78, +0x70, +0x6A, +0x66, +0x62, +0x5F, +0x5C, +0x59, +0x56, +0x54, +0x52, +0x50, +0x4F, +0x4E, +0x4E, +0x4D, +0x4D, +0x4D, +0x4D, +0x4E, +0x4F, +0x50, +0x51, +0x53, +0x55, +0x57, +0x59, +0x5C, +0x5F, +0x65, +0x6A, +0x87, +0x7C, +0x74, +0x6D, +0x68, +0x64, +0x68, +0x5D, +0x5B, +0x58, +0x56, +0x54, +0x52, +0x51, +0x50, +0x4F, +0x4F, +0x4F, +0x4F, +0x4F, +0x50, +0x51, +0x52, +0x53, +0x55, +0x56, +0x58, +0x5B, +0x5E, +0x62, +0x68, +0x6E +}; + + diff --git a/drivers/media/video/cmmb/Kconfig b/drivers/media/video/cmmb/Kconfig new file mode 100644 index 00000000000000..b7aa7f06c88b31 --- /dev/null +++ b/drivers/media/video/cmmb/Kconfig @@ -0,0 +1,24 @@ +menuconfig CMMB + tristate "China Mobile Multimedia Broadcasting" + ---help--- + CMMB is a mobile television and multimedia standard developed + and specified in China by the State Administration of Radio, + Film, and Television (SARFT). + +if CMMB + +config CMMB_IF101 + tristate "CMMB Demodulator IF101 Controller support" + depends on VIDEO_V4L2 + select SPI + select SPI_PXA2XX + ---help--- + This is a video4linux2 driver for the CMMB Demodulator IF101 + controller. + +config CMMB_CA + tristate "CA drvier for CMMB module" + ---help--- + This is the ca driver for cmmb module + +endif #CMMB diff --git a/drivers/media/video/cmmb/Makefile b/drivers/media/video/cmmb/Makefile new file mode 100644 index 00000000000000..af187529489acb --- /dev/null +++ b/drivers/media/video/cmmb/Makefile @@ -0,0 +1,5 @@ +cmmb_if101-objs := if101.o +cmmb_ca-objs := ca.o + +obj-$(CONFIG_CMMB_IF101) += cmmb_if101.o +obj-$(CONFIG_CMMB_CA) += cmmb_ca.o diff --git a/drivers/media/video/cmmb/ca.c b/drivers/media/video/cmmb/ca.c new file mode 100644 index 00000000000000..0503e864bfe080 --- /dev/null +++ b/drivers/media/video/cmmb/ca.c @@ -0,0 +1,409 @@ +#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 + +#include "ca.h" + +static u8 *writebuf; +static u8 *readbuf; + +static volatile unsigned long last = 0; +static volatile u16 readnum; +static long timeout; +static long timeout_end; +static struct clk *uartclk = NULL; + + +static inline void MFP_Set_UART_Rx() +{ + UART3TRX = 0xc3 ; /* RX */ +} + +static inline void MFP_Set_UART_Tx() +{ + UART3TRX = 0xc2 ; /* TX */ +} + +static int uart_init(int divisor) +{ + int temp; + /* switch receiver and transmitter off */ + SerialLCR = 0; + SerialIER = 0; + SerialFIFO = 0; + + /* Gain access to divisor latch */ + SerialLCR = LCR_WLS0 | LCR_WLS1 | LCR_DLAB; + + /* Load baud rate divisor in two steps, lsb, then msb of value */ + + SerialDATA = divisor & 0xff; + SerialIER = (divisor >> 8) & 0xff; + + + /* set the port to sensible defaults (no break, no interrupts, + * no parity, 8 databits, 1 stopbit, transmitter and receiver + * enabled), reset dlab bit: + */ + SerialLCR = 0x1f ; + /* SerialLCR = 0x13; */ + SerialFIFO = 0x1 | 0x2 | 0x4 | 0x80; + + /* SerialLCR = 0x4; */ + /* turn the receiver and transmitter back on */ + SerialIER = IER_UUE | IER_RAVIE | 0x4 | 0x10; + /* SerialIER = 0x55; */ + + SerialMCR = 0x8; + return 0; +} + +int uart_read(int num) +{ + u16 i; + int rev; + long interval; + + readnum = 0; + + MFP_Set_UART_Rx(); + + last = jiffies; + + while (1) { + msleep(5); + + if (readnum >= num) + break; + interval = jiffies - last ; + if (readnum >= 2) { + if (interval >= timeout_end) { + break; + } + } else { + if (interval >= timeout) { + break; + } + } + } + return 0; + +} + +static irqreturn_t smd_irq(int irq, void *ptr) +{ + while (SerialLSR & LSR_DR) { + last = jiffies; + readbuf[readnum] = SerialDATA & 0xff ; + readnum++; + if (readnum >= CA_BUFFER_SIZE) + readnum = 0; + } + return IRQ_HANDLED; +} + +int uart_write(u8 *buf, u16 length, u16 num) +{ + u16 i; + MFP_Set_UART_Tx(); + for (i = 0; i < length; i++) { + SerialDATA = buf[i]; + while (!(SerialLSR & LSR_TEMT)) + ; + } + uart_read(num); + return i; +} + + +static void smd_hw_reset() +{ + GPIO_RST_PDR |= 0x8; + + GPIO_RST_PSR &= 0xFFFFFFF7;/* low */ + + PWM2_APBC = 0x3; + PWM2_CR = 0x0; + PWM2_DCR = 0x0; + PWM2_PCR = 0x0; + msleep(30); + + PWM2_APBC = 0x3; + PWM2_CR = 0x0; + PWM2_DCR = 0x1; + PWM2_PCR = 0x1; + + GPIO_RST_PSR |= 0x8;/* high */ +} + +static int smd_reset(u8 ATR[]) +{ + int atrlen; + u8 BitMap; + int iByteOffset; + int i; + + ATRParser gATRParser; + + smd_hw_reset(); + + uart_read(MAX_ATR_LEN); + + atrlen = readnum; + for (i = 0; i < readnum; i++) { + ATR[i] = readbuf[i]; + } + + memset((void *)&gATRParser, 0, sizeof(ATRParser)); + + gATRParser.T0 = ATR[0]; + gATRParser.TS = ATR[1]; + + BitMap = gATRParser.T0 & 0xF0; + + gATRParser.LengthOfHistoricalBytes = gATRParser.T0 & 0x0F; + gATRParser.LengthOfInterfaceBytes = 0; + + iByteOffset = 2; + + for (i = 1; BitMap && gATRParser.LengthOfInterfaceBytes <= + (ATR_Max_Interface_Bytes - gATRParser.LengthOfHistoricalBytes); i++) { + if (BitMap == 0) + break; + + if (BitMap & TA_bit_Mask) { /* TA */ + gATRParser.LengthOfTA++; + gATRParser.TA[i] = ATR[iByteOffset++]; + } + + if (BitMap & TB_bit_Mask) { /* TB */ + gATRParser.LengthOfTB++; + gATRParser.TB[i] = ATR[iByteOffset++]; + } + + if (BitMap & TC_bit_Mask) { /* TC */ + gATRParser.LengthOfTC++; + gATRParser.TC[i] = ATR[iByteOffset++]; + } + + + if (BitMap & TD_bit_Mask) { /* TD */ + gATRParser.LengthOfTD++; + gATRParser.TD[i] = ATR[iByteOffset++]; + gATRParser.ProtocolType[gATRParser.LengthOfProtocol++] = gATRParser.TD[i] & 0xF; + } + + BitMap = gATRParser.TD[i] & 0xF0; + + /* Store the correct number of interface bytes */ + gATRParser.LengthOfInterfaceBytes = gATRParser.LengthOfTA + gATRParser.LengthOfTB + + gATRParser.LengthOfTC + gATRParser.LengthOfTD; + } + + for (i = 0; i < gATRParser.LengthOfHistoricalBytes; ++i) { + gATRParser.HistoricalBytes[i] = ATR[iByteOffset++]; + } + + /* Step 2: Change the T1 to T0 Protocol using the PTS command. */ + if (((gATRParser.TA[1]&0xFF) != 0x11) && ((gATRParser.TA[1]&0xFF) != 0x01) && + ((gATRParser.TA[1]&0xFF) != 0x0)) { + u8 SentBuf[4]; + /* UINT8 RecvBuf[4]; */ + + SentBuf[0] = 0xff; + SentBuf[1] = 0x10; + SentBuf[2] = 0x13; /* 1--F 3--D */ + SentBuf[3] = SentBuf[0] ^ SentBuf[1] ^ SentBuf[2]; + + uart_write(SentBuf, 4, 4); + uart_init(52); + } + return atrlen; + +} + +static ssize_t ca_read(struct file *filp, char *buf, size_t count , loff_t *f_pos) +{ + int remained; + int nrecv; + + if (count > readnum) + nrecv = readnum; + else + nrecv = count; + remained = nrecv ; + + do { + remained = copy_to_user(buf + nrecv - remained, readbuf + nrecv - remained, remained); + } while (remained != 0); + + + return nrecv; +} + +static ssize_t ca_write(struct file *filp, char *buf, size_t count , loff_t *f_pos) +{ + int sentlen, remained; + + sentlen = GET_WRITE_NUM(count); + + remained = sentlen; + + do { + remained = copy_from_user(writebuf + sentlen - remained, buf + sentlen - remained, remained); + } while (remained != 0); + + sentlen = uart_write(writebuf, sentlen, GET_READ_NUM(count)); + + return sentlen; + +} + +static int ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int result; + int ret = 0; + int atrlen; + u8 ATR[MAX_ATR_LEN]; + + switch (cmd) { + case CMD_CA_RESET: + atrlen = smd_reset(ATR); + copy_to_user((u8 *)arg, ATR, atrlen); + ret = atrlen; + break; + case CMD_CA_TIMEOUT: + timeout = arg; + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +static int ca_open(struct inode *inode, struct file *filp) +{ + int ret; + + timeout = 30; + timeout_end = 2; + + writebuf = (u8 *)kmalloc(CA_BUFFER_SIZE , GFP_KERNEL); + if (writebuf == NULL) + { + printk("kmalloc error\n"); + } + + readbuf = (u8 *)kmalloc(CA_BUFFER_SIZE , GFP_KERNEL); + if (readbuf == NULL) { + printk("kmalloc error\n"); + } + + clk_enable(uartclk); + clk_set_rate(uartclk, 58500000); + uart_init(206); + + ret = request_irq(IRQ_PXA168_UART3, smd_irq, 0, "CAUART", NULL); + if (ret) { + printk(KERN_ERR "%s: can't request uart3 irq\n", __FUNCTION__); + return -1; + } + + MFP_Set_UART_Rx(); + + return 0; +} + +static int ca_release(struct inode *inode, struct file *filp) +{ + free_irq(IRQ_PXA168_UART3, NULL); + + kfree(writebuf); + kfree(readbuf); + + return 0; +} + +static struct file_operations ca_fops = { + .open = ca_open, + .read = ca_read, + .write = ca_write, + .release = ca_release, + .ioctl = ca_ioctl, + .owner = THIS_MODULE +}; + +static struct miscdevice ca_miscdev = { + MINOR_NUMBER, + "pxa168-ca", + &ca_fops, +}; + + + +static int __init ca_init(void) +{ + int ret = 0; + + ret = misc_register(&ca_miscdev); + if (ret) { + printk("cannot register ca driver\n"); + return ret; + } + + uartclk = clk_get(ca_miscdev.this_device, "UART3CLK"); + if (IS_ERR(uartclk)) { + dev_err(ca_miscdev.this_device, "unable to get CCICRSTCLK"); + return PTR_ERR(uartclk); + } + + return ret; +} + +static void __exit ca_exit(void) +{ + misc_deregister(&ca_miscdev); + clk_disable(uartclk); + +} + +module_init(ca_init); +module_exit(ca_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marvell"); +MODULE_DESCRIPTION("Marvell CA Driver"); diff --git a/drivers/media/video/cmmb/ca.h b/drivers/media/video/cmmb/ca.h new file mode 100644 index 00000000000000..8e589b607173c5 --- /dev/null +++ b/drivers/media/video/cmmb/ca.h @@ -0,0 +1,110 @@ +#ifndef _CA_H_ +#define _CA_H_ + +#define UART_REG_BASE 0xd4026000 +#define PWM2_REG_BASE 0xd401a400 +#define GPIO_REG_BASE 0xd4019100 + +#define LCR_DLAB (1 << 7) /* Divisor Latch Access Bit */ +#define LCR_SB (1 << 6) /* Set Break */ +#define LCR_STKYP (1 << 5) /* Sticky Parity */ +#define LCR_EPS (1 << 4) /* Even Parity Select */ +#define LCR_PEN (1 << 3) /* Parity Enable */ +#define LCR_STB (1 << 2) /* Stop Bit */ +#define LCR_WLS1 (1 << 1) /* Word Length Select */ +#define LCR_WLS0 (1 << 0) /* Word Length Select */ + +#define IER_UUE (1 << 6) /* UART Unit Enable */ +#define IER_RAVIE (1 << 0) /* UART Unit Enable */ + +#define LSR_DR (1 << 0) /* Data Ready */ +#define LSR_TEMT (1 << 6) /* Transmitter Empty */ + +#define __REG_PXA168(x) (*((volatile u32 *)((x & 0xffffff) | 0xfe000000))) + +#define PWM2_APBC __REG_PXA168(0xd4015010) +/* PWM2 in ASPEN */ +#define PWM2_CR __REG_PXA168(PWM2_REG_BASE) +#define PWM2_DCR __REG_PXA168(PWM2_REG_BASE + 0x4) +#define PWM2_PCR __REG_PXA168(PWM2_REG_BASE + 0x8) + +/* UART3 regs */ +#define SerialDATA __REG_PXA168(UART_REG_BASE) +#define SerialFIFO __REG_PXA168(UART_REG_BASE + 0x8) +#define SerialLCR __REG_PXA168(UART_REG_BASE + 0xc) +#define SerialMCR __REG_PXA168(UART_REG_BASE + 0x10) +#define SerialLSR __REG_PXA168(UART_REG_BASE + 0x14) +#define SerialIER __REG_PXA168(UART_REG_BASE + 0x4) +#define SerialMSR __REG_PXA168(UART_REG_BASE + 0x18) + +#define UART3TRX __REG_PXA168(0xd401e188) + +/* GPIO regs */ +#define GPIO_RST_PDR __REG_PXA168(GPIO_REG_BASE + 0xc) +#define GPIO_RST_PSR __REG_PXA168(GPIO_REG_BASE + 0x18) + + +#define MINOR_NUMBER 10 + +#define DIRECTION_TX 0 +#define DIRECTION_RX 1 + +/* command */ +#define CMD_CA_RESET 1 +#define CMD_CA_TIMEOUT 2 + +#define MAX_ATR_LEN 33 +#define MAX_BUFFER_SIZE (2 * 1024) + +#define STATUS_START_WAIT 0 +#define STATUS_END_WAIT 1 + +#define TIMEOUT_THRESHOLD 90000000 + +#define CA_BUFFER_SIZE 1024 + +#define GET_WRITE_NUM(x) (x>>16) +#define GET_READ_NUM(x) (x & 0xffff) + +#define TA_bit_Mask 0x10 +#define TB_bit_Mask 0x20 +#define TC_bit_Mask 0x40 +#define TD_bit_Mask 0x80 +#define ATR_Max_Interface_Bytes 30 +#define ATR_Max_Historical_Bytes 15 +#define ATR_Max_TA_Bytes 15 +#define ATR_Max_TB_Bytes 15 +#define ATR_Max_TC_Bytes 15 +#define ATR_Max_TD_Bytes 15 + +typedef struct _ATRParser { + u8 TS; /* Initial Character */ + u8 T0; /* Format character */ + u8 TA[ATR_Max_TA_Bytes]; /* Interface Bytes: TA */ + u8 TB[ATR_Max_TB_Bytes]; /* Interface Bytes: TB */ + u8 TC[ATR_Max_TC_Bytes]; /* Interface Bytes: TC */ + u8 TD[ATR_Max_TD_Bytes]; /* Interface Bytes: TD */ + u8 HistoricalBytes[ATR_Max_Historical_Bytes]; /* minimum = 0 , maximum = 15 */ + u8 TCK; /* Check character */ + + u8 LengthOfAtr; /* Maximum = 33 */ + u8 LengthOfInterfaceBytes; /* Maximum = 30 */ + u8 LengthOfHistoricalBytes; /* Maximum = 15 */ + u8 LengthOfTA; /* Maximum = 15 */ + u8 LengthOfTB; /* Maximum = 15 */ + u8 LengthOfTC; /* Maximum = 15 */ + u8 LengthOfTD; /* Maximum = 15 */ + u8 LengthOfProtocol; /* Maximum = 15 */ + u8 TCKPresent; + u8 ProtocolType[ATR_Max_TD_Bytes]; +} ATRParser; + +int smd_init(); +int uart_write(u8 *buf, u16 length, u16 num); +int uart_read(int num); + + + + + +#endif diff --git a/drivers/media/video/cmmb/if101.c b/drivers/media/video/cmmb/if101.c new file mode 100644 index 00000000000000..9cafc50ac19649 --- /dev/null +++ b/drivers/media/video/cmmb/if101.c @@ -0,0 +1,692 @@ +/* + * linux/drivers/media/video/cmmb/if101.c - cmmb if101 driver + * + * Based on linux/drivers/media/video/cafe_ccic.c + * + * Copyright: (C) Copyright 2008 Marvell International Ltd. + * Mingwei Wang + * + * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" + * multifunction chip. Currently works with the Omnivision OV7670 + * sensor. + * + * The data sheet for this device can be found at: + * http://www.marvell.com/products/pcconn/88ALP01.jsp + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * Copyright 2006-7 Jonathan Corbet + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#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 +/* #include */ +/* #include */ + +#define DRIVER_NAME "cmmb_if101" + +#define SPI_CID V4L2_CID_PRIVATE_BASE + +/* assemble an ioctl command */ +#define CMMB_IOCTL(addr, data) (((addr) << 16) | (data)) + +/* disassemble an ioctl command */ +#define CMMB_IOCTL_ADDR(val) ((val) >> 16) +#define CMMB_IOCTL_DATA(val) ((val) & 0xffff) + +/* + * Parameters. + */ +MODULE_AUTHOR("Mingwei Wang"); +MODULE_DESCRIPTION("CMMB Demodulator IF101 driver"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("Video"); + +/* + * Internal DMA buffer management. Since the controller cannot do S/G I/O, + * we must have physically contiguous buffers to bring frames into. + * These parameters control how many buffers we use, whether we + * allocate them at load time (better chance of success, but nails down + * memory) or when somebody tries to use the camera (riskier), and, + * for load-time allocation, how big they should be. + * + * The controller can cycle through three buffers. We could use + * more by flipping pointers around, but it probably makes little + * sense. + */ + +#define MAX_DMA_BUFS 3 +static int alloc_bufs_at_read = 0; +module_param(alloc_bufs_at_read, bool, 0444); +MODULE_PARM_DESC(alloc_bufs_at_read, + "Non-zero value causes DMA buffers to be allocated when the " + "video capture device is read, rather than at module load " + "time. This saves memory, but decreases the chances of " + "successfully getting those buffers."); + +static int dma_buf_size = 65536; +module_param(dma_buf_size, uint, 0444); +MODULE_PARM_DESC(dma_buf_size, + "The size of the allocated DMA buffers. If actual operating " + "parameters require larger buffers, an attempt to reallocate " + "will be made."); + +static int min_buffers = 1; +module_param(min_buffers, uint, 0644); +MODULE_PARM_DESC(min_buffers, + "The minimum number of streaming I/O buffers we are willing " + "to work with."); + +static int max_buffers = 10; +module_param(max_buffers, uint, 0644); +MODULE_PARM_DESC(max_buffers, + "The maximum number of streaming I/O buffers an application " + "will be allowed to allocate. These buffers are big and live " + "in vmalloc space."); + +/* + * A description of one of our devices. + * Locking: controlled by s_mutex. Certain fields, however, require + * the dev_lock spinlock; they are marked as such by comments. + * dev_lock is also required for access to device registers. + */ +struct cmmb_dev { + unsigned long flags; /* Buffer status, mainly (dev_lock) */ + int users; /* How many open FDs */ + struct file *owner; /* Who has data access (v4l2) */ + + /* + * Subsystem structures. + */ + int irq; + struct video_device v4ldev; + struct v4l2_buffer v4lbuf; + + struct list_head dev_list; /* link to other devices */ + + /* DMA buffers */ + int mapcount; + unsigned int dma_buf_size; /* allocated size */ + int order; /* Internal buffer addresses */ + + void *dma_bufs; /* Internal buffer addresses */ + dma_addr_t dma_handles; /* Buffer bus addresses */ + + /* Locks */ + struct mutex s_mutex; /* Access to this structure */ + spinlock_t dev_lock; /* Access to device */ + + /* Misc */ + wait_queue_head_t iowait; /* Waiting on frame data */ + struct spi_device *spi; +}; + +/* + * Low-level register I/O. + */ + +/* ---------------------------------------------------------------------*/ +/* + * We keep a simple list of known devices to search at open time. + */ +static LIST_HEAD(cmmb_dev_list); +static DEFINE_MUTEX(cmmb_dev_list_lock); + +static void cmmb_add_dev(struct cmmb_dev *cam) +{ + mutex_lock(&cmmb_dev_list_lock); + list_add_tail(&cam->dev_list, &cmmb_dev_list); + mutex_unlock(&cmmb_dev_list_lock); +} + +static void cmmb_remove_dev(struct cmmb_dev *cam) +{ + mutex_lock(&cmmb_dev_list_lock); + list_del(&cam->dev_list); + mutex_unlock(&cmmb_dev_list_lock); +} + +static struct cmmb_dev *cmmb_find_dev(int minor) +{ + struct cmmb_dev *cam; + + mutex_lock(&cmmb_dev_list_lock); + list_for_each_entry(cam, &cmmb_dev_list, dev_list) { + if (cam->v4ldev.minor == minor) + goto done; + } + cam = NULL; +done: + mutex_unlock(&cmmb_dev_list_lock); + return cam; +} + +static struct cmmb_dev *cmmb_find_by_spi(struct spi_device *spi) +{ + struct cmmb_dev *cam; + + mutex_lock(&cmmb_dev_list_lock); + list_for_each_entry(cam, &cmmb_dev_list, dev_list) { + if (cam->spi == spi) + goto done; + } + cam = NULL; +done: + mutex_unlock(&cmmb_dev_list_lock); + return cam; +} + +/* -------------------------------------------------------------------- */ +/* + * DMA buffer management. These functions need s_mutex held. + */ + +/* FIXME: this is inefficient as hell, since dma_alloc_coherent just + * does a get_free_pages() call, and we waste a good chunk of an orderN + * allocation. Should try to allocate the whole set in one chunk. + */ +static int cmmb_alloc_dma_bufs(struct cmmb_dev *cam) +{ + cam->dma_buf_size = dma_buf_size; + + cam->order = get_order(cam->dma_buf_size); + cam->dma_bufs = (unsigned long *)__get_free_pages(GFP_KERNEL, cam->order); + if (cam->dma_bufs == NULL) { + printk("Failed to allocate DMA buffer\n"); + return -ENOMEM; + } + cam->dma_handles = __pa(cam->dma_bufs); + + /* For debug, remove eventually */ + memset(cam->dma_bufs, 0xcc, cam->dma_buf_size); + + return 0; +} + +static void cmmb_free_dma_bufs(struct cmmb_dev *cam) +{ + free_pages((unsigned long)cam->dma_bufs, cam->order); + cam->dma_bufs = NULL; +} + + +/* ----------------------------------------------------------------------- */ +/* + * Here starts the V4L2 interface code. + */ + +static int cmmb_vidioc_g_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct cmmb_dev *cam = filp->private_data; + int ret; + unsigned char addr = CMMB_IOCTL_ADDR(ctrl->value); + unsigned char data = CMMB_IOCTL_DATA(ctrl->value); + + mutex_lock(&cam->s_mutex); + if (ctrl->id == SPI_CID) { + ret = spi_read(cam->spi, (u8 *)&data, sizeof(data)); + } else { + printk("This control ID doesn't support get\n"); + ret = -EINVAL; + } + ctrl->value = CMMB_IOCTL(addr, data); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int cmmb_vidioc_s_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct cmmb_dev *cam = filp->private_data; + int ret; + /* unsigned char addr = CMMB_IOCTL_ADDR(ctrl->value); */ + unsigned char data = CMMB_IOCTL_DATA(ctrl->value); + + mutex_lock(&cam->s_mutex); + if (ctrl->id == SPI_CID) { + ret = spi_write(cam->spi, (const u8 *)&data, sizeof(data)); + } else { + printk("This control ID doesn't support set\n"); + ret = -EINVAL; + } + mutex_unlock(&cam->s_mutex); + return ret; +} + + +extern int spi_finish; +static int spi_read_bytes(struct cmmb_dev *cam, unsigned char *buffer, int len) +{ + int i, j; + + spi_finish = 0; + for (i = 0; (i + 4096) < len; i += 4096) { + spi_read(cam->spi, buffer + i, 4096); + } + /* + * The last spi_read need set spi_finish = 1, + * such led GPIO_CMMB_CS to be pull up + */ + spi_finish = 1; + j = len % 4096; + if (j) + spi_read(cam->spi, buffer + i, j); + else + spi_read(cam->spi, buffer + i, 4096); + + return 0; +} + + +static int cmmb_vidioc_g_ext_ctrl(struct file *filp, void *priv, + struct v4l2_ext_controls *ctrl) +{ + struct cmmb_dev *cam = filp->private_data; + int ret = 1; + unsigned char *buffer; + int len; + + if (ctrl->count != 2) + return -EINVAL; + + buffer = (unsigned char *)ctrl->controls[0].value; + len = ctrl->controls[1].value; + + mutex_lock(&cam->s_mutex); + + dmac_inv_range(buffer, buffer + len); + + spi_read_bytes(cam, cam->dma_bufs, len); + + mutex_unlock(&cam->s_mutex); + + return ret; +} + + +static int cmmb_vidioc_try_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + return 0; +} + + +static int cmmb_vidioc_querybuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct cmmb_dev *cam = filp->private_data; + int ret = -EINVAL; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0) + goto out; + + cam->v4lbuf.length = dma_buf_size; + cam->v4lbuf.index = 0; + cam->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + cam->v4lbuf.field = V4L2_FIELD_NONE; + cam->v4lbuf.memory = V4L2_MEMORY_MMAP; + cam->v4lbuf.m.offset = 0; + + *buf = cam->v4lbuf; + + ret = 0; +out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static void cmmb_v4l_vm_open(struct vm_area_struct *vma) +{ + struct cmmb_dev *cam = vma->vm_private_data; + /* + * Locking: done under mmap_sem, so we don't need to + * go back to the camera lock here. + */ + cam->mapcount++; +} + + +static void cmmb_v4l_vm_close(struct vm_area_struct *vma) +{ + struct cmmb_dev *cam = vma->vm_private_data; + + mutex_lock(&cam->s_mutex); + cam->mapcount--; + /* Docs say we should stop I/O too... */ + if (cam->mapcount == 0) + cam->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; + mutex_unlock(&cam->s_mutex); +} + +static struct vm_operations_struct cmmb_v4l_vm_ops = { + .open = cmmb_v4l_vm_open, + .close = cmmb_v4l_vm_close +}; + + +static int cmmb_v4l_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct cmmb_dev *cam = filp->private_data; + int ret = -EINVAL; + + /* if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED)) */ + /* if (! (vma->vm_flags & VM_SHARED)) */ + /* return -EINVAL; */ + /* + * Find the buffer they are looking for. + */ + mutex_lock(&cam->s_mutex); + + ret = remap_pfn_range(vma, vma->vm_start, cam->dma_handles >> PAGE_SHIFT, dma_buf_size, pgprot_val(PAGE_SHARED)); + if (ret) + goto out; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = cam; + vma->vm_ops = &cmmb_v4l_vm_ops; + cam->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + cmmb_v4l_vm_open(vma); + ret = 0; +out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + + +static int cmmb_v4l_open(struct file *filp) +{ + struct cmmb_dev *cam; + struct cmmb_platform_data *pdata; + + cam = cmmb_find_dev(video_devdata(filp)->minor); + if (cam == NULL) + return -ENODEV; + + pdata = cam->spi->dev.platform_data; + + if (pdata->power_on) + pdata->power_on(); + + filp->private_data = cam; + + mutex_lock(&cam->s_mutex); + (cam->users)++; + mutex_unlock(&cam->s_mutex); + return 0; +} + + +static int cmmb_v4l_release(struct file *filp) +{ + struct cmmb_dev *cam = filp->private_data; + struct cmmb_platform_data *pdata; + + mutex_lock(&cam->s_mutex); + (cam->users)--; + if (cam->users == 0) { + if (alloc_bufs_at_read) + cmmb_free_dma_bufs(cam); + } + mutex_unlock(&cam->s_mutex); + + pdata = cam->spi->dev.platform_data; + + if (pdata->power_off) + pdata->power_off(); + + return 0; +} + +static unsigned int cmmb_v4l_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct cmmb_dev *cam = filp->private_data; + int frame = 0; + + clear_bit(frame, &cam->flags); + + interruptible_sleep_on(&cam->iowait); + + return POLLIN | POLLRDNORM; +} + +static void cmmb_v4l_dev_release(struct video_device *vd) +{ + struct cmmb_dev *cam = container_of(vd, struct cmmb_dev, v4ldev); + + kfree(cam); +} + + +/* + * This template device holds all of those v4l2 methods; we + * clone it for specific real devices. + */ + +static const struct v4l2_file_operations cmmb_v4l_fops = { + .owner = THIS_MODULE, + .open = cmmb_v4l_open, + .release = cmmb_v4l_release, + .poll = cmmb_v4l_poll, + .mmap = cmmb_v4l_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops cmmb_v4l_ioctl_ops = { + .vidioc_try_fmt_vid_cap = cmmb_vidioc_try_fmt_cap, + .vidioc_querybuf = cmmb_vidioc_querybuf, + .vidioc_g_ctrl = cmmb_vidioc_g_ctrl, + .vidioc_s_ctrl = cmmb_vidioc_s_ctrl, + .vidioc_g_ext_ctrls = cmmb_vidioc_g_ext_ctrl, +}; + +static struct video_device cmmb_v4l_template = { + .name = "cmmb-module", + .minor = -1, /* Get one dynamically */ + .tvnorms = V4L2_STD_NTSC_M, + /* .current_norm = V4L2_STD_NTSC_M, */ /* make mplayer happy */ + + .fops = &cmmb_v4l_fops, + .ioctl_ops = &cmmb_v4l_ioctl_ops, + .release = cmmb_v4l_dev_release, +}; + + +/* ---------------------------------------------------------------------- */ +/* + * Interrupt handler stuff + */ + +static irqreturn_t cmmb_irq(int irq, void *data) +{ + struct cmmb_dev *cam = data; + int frame = 0; + + spin_lock(&cam->dev_lock); + /* + * Basic frame housekeeping. + */ + if (test_bit(frame, &cam->flags) && printk_ratelimit()) + printk("Frame overrun on %d, frames lost\n", frame); + set_bit(frame, &cam->flags); + + wake_up_interruptible(&cam->iowait); + + spin_unlock(&cam->dev_lock); + return IRQ_HANDLED; +} + +static int __devinit cmmb_probe(struct spi_device *spi) +{ + struct cmmb_platform_data *pdata; + struct cmmb_dev *cam; + int ret; + u8 byte; + + pdata = spi->dev.platform_data; + if (!pdata || !pdata->power_on) + return -ENODEV; + pdata->power_on(); + + /* + * bits_per_word cannot be configured in platform data + */ + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + goto out; + +#if 1 + /* + * SPI test + */ + byte = 0x01; + spi_write(spi, (const u8 *)&byte, sizeof(byte)); + spi_read(spi, (u8 *)&byte, sizeof(byte)); + if (byte != 0x42) { + printk(KERN_NOTICE "CMMB Demodulator can't be detected\n"); + ret = -EINVAL; + goto out; + } +#endif + + /* + * Start putting together one of our big cmmb device structures. + */ + ret = -ENOMEM; + cam = kzalloc(sizeof(struct cmmb_dev), GFP_KERNEL); + if (cam == NULL) + goto out; + + cam->spi = spi; + + mutex_init(&cam->s_mutex); + mutex_lock(&cam->s_mutex); + + spin_lock_init(&cam->dev_lock); + INIT_LIST_HEAD(&cam->dev_list); + init_waitqueue_head(&cam->iowait); + + ret = request_irq(spi->irq, cmmb_irq, IRQF_TRIGGER_FALLING, + "CMMB Demodulator", cam); + if (ret) + goto out_free; + + /* + * Get the v4l2 setup done. + */ + cam->v4ldev = cmmb_v4l_template; + cam->v4ldev.debug = 0; + ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); + if (ret) + goto out_free; + /* + * If so requested, try to get our DMA buffers now. + */ + if (!alloc_bufs_at_read) { + if (cmmb_alloc_dma_bufs(cam)) + printk("Unable to alloc DMA buffers at load" + " will try again later."); + } + + mutex_unlock(&cam->s_mutex); + cmmb_add_dev(cam); + + if (pdata->power_off) + pdata->power_off(); + + printk(KERN_NOTICE "CMMB Demodulator detected\n"); + + return ret; + +out_free: + kfree(cam); +out: + return ret; +} + +static int cmmb_remove(struct spi_device *spi) +{ + struct cmmb_dev *cam = cmmb_find_by_spi(spi); + + if (cam == NULL) { + printk(KERN_WARNING "cmmb_remove on unknown spi %p\n", spi); + return -ENODEV; + } + mutex_lock(&cam->s_mutex); + if (cam->users > 0) + printk("Removing a device with users!\n"); + + cmmb_remove_dev(cam); +/* No unlock - it no longer exists */ + return 0; +} + +static struct spi_driver cmmb_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = cmmb_probe, + .remove = __devexit_p(cmmb_remove), +}; + +/* + * Module initialization + */ + +static int __init cmmb_module_init(void) +{ + printk(KERN_NOTICE "CMMB Demodulator driver, at your service\n"); + + return spi_register_driver(&cmmb_driver); +} + +static void __exit cmmb_module_exit(void) +{ + spi_unregister_driver(&cmmb_driver); +} + +module_init(cmmb_module_init); +module_exit(cmmb_module_exit); diff --git a/drivers/media/video/ov3640.c b/drivers/media/video/ov3640.c new file mode 100644 index 00000000000000..2ccf73bbcc5c78 --- /dev/null +++ b/drivers/media/video/ov3640.c @@ -0,0 +1,1894 @@ +/* + * A V4L2 driver for OmniVision OV3640 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "pxa168_camera.h" + +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov3640 sensors"); +MODULE_LICENSE("GPL"); + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ + +#define QXGA_WIDTH 2048 +#define QXGA_HEIGHT 1536 +#define UXGA_WIDTH 1600 +#define UXGA_HEIGHT 1200 +#define XGA_WIDTH 1024 +#define XGA_HEIGHT 768 +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * Our nominal (default) frame rate. + */ +#define OV3640_FRAME_RATE 30 + +/* + * The 3640 sits on i2c with ID 0x42 + */ +#define OV3640_I2C_ADDR 0x42 + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x00 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + +/*for OV3640 porting*/ +#define REG_CLKRC 0x3011 +#define REG_PIDH 0x300a +#define REG_PIDL 0x300b +#define REG_SYS 0x3012 +#define SYS_RESET 0x80 +/* + * Information we maintain about a known sensor. + */ +struct ov3640_format_struct; /* coming later */ +struct ov3640_info { + struct ov3640_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ + +struct regval_list { + u16 reg_num; + unsigned char value; +}; + +static struct regval_list ov3640_default_regs[] = { + { REG_COM7, COM7_RESET }, + /* + * Clock scale: 3 = 15fps + * 2 = 20fps + * 1 = 30fps + */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ + { REG_TSLB, 0x04 }, /* OV */ + { REG_COM7, 0 }, /* VGA */ + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, + { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, + { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, + + { REG_COM3, 0 }, { REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, { REG_COM10, 0x0 }, + + /* Gamma curve values */ + { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, + { REG_GAIN, 0 }, { REG_AECH, 0 }, + { REG_COM4, 0x40 }, /* magic reserved bit */ + { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, + { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, + { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, + { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ + { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, + { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, + { REG_HAECC7, 0x94 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, + { REG_RED, 0x60 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + + +/* + * Here we'll try to encapsulate the changes for just the output + * video format. + * + * RGB656 and YUV422 come from OV; RGB444 is homebrewed. + * + * IMPORTANT RULE: the first entry must be for COM7, see ov3640_s_fmt for why. + */ + +/*TODO - ov3640_fmt_yuv422_qxga can't work. configuration should be correct*/ +static struct regval_list ov3640_fmt_yuv422_qxga[] = { + //mipi supported // 10 fps + 0x304d, 0x45, /*Rev2A */ + 0x3087, 0x16, /*Rev2A */ + 0x30aa, 0x45, /*Rev2A */ + 0x30b0, 0xff, /*Rev2A */ + 0x30b1, 0xff, + 0x30b2, 0x10, + 0x30d7, 0x10, /*Rev2A */ + + 0x309e, 0x00, /*terry */ + 0x3602, 0x26, /*2a ;SOL/EOL on */ + 0x3603, 0x4D, /*ecc */ + 0x364c, 0x04, /*ecc */ + 0x360c, 0x12, /*virtual channel 0 */ + 0x361e, 0x00, + 0x361f, 0x11, /*pclk_period, terry */ + 0x3633, 0x32, /*terry, increase hs_prepare */ + 0x3629, 0x3c, /*terry, increase clk_prepare */ + 0x300e, 0x39, /*7.5fps terry */ + 0x300f, 0xa1, /*terry */ + + 0x3010, 0xa1, /*high mipi spd, 81 ;terry */ //ENABLE_TWO_LANES + //0x3010, 0xa2, /*low down mipi spd, 81 ;terry */ //ENABLE_TWO_LANES + + 0x3011, 0x01, + 0x304c, 0x81, /*Rev2A */ + + 0x3018, 0x38, /*aec */ + 0x3019, 0x30, /*06142007 */ + 0x301a, 0x61, /*06142007 */ + 0x307d, 0x00, /*aec isp 06142007 */ + 0x3087, 0x02, /*06142007 */ + 0x3082, 0x20, /*06142007 */ + + 0x303c, 0x08, /*aec weight */ + 0x303d, 0x18, + 0x303e, 0x06, + 0x303F, 0x0c, + 0x3030, 0x62, + 0x3031, 0x26, + 0x3032, 0xe6, + 0x3033, 0x6e, + 0x3034, 0xea, + 0x3035, 0xae, + 0x3036, 0xa6, + 0x3037, 0x6a, + + 0x3015, 0x12, /*07182007 8x gain, auto 1/2 */ + 0x3014, 0x04, /*06142007 auto frame off */ + 0x3013, 0xf7, /*07182007 */ + + 0x3104, 0x02, + 0x3105, 0xfd, + 0x3106, 0x00, + 0x3107, 0xff, + 0x3308, 0xa5, + 0x3316, 0xff, + 0x3317, 0x00, + 0x3087, 0x02, + 0x3082, 0x20, + 0x3300, 0x13, + 0x3301, 0xd6, + 0x3302, 0xef, + + 0x30b8, 0x20, /*10 */ + 0x30b9, 0x17, /*18 */ + 0x30ba, 0x04, /*00 */ + 0x30bb, 0x08, /*1f */ + + 0x3100, 0x02, /*YUV */ /*Format */ + 0x3304, 0x00, + 0x3400, 0x00, + 0x3404, 0x02, /*terry - output format: YUV422PACKED(UYVY)*/ + + 0x3020, 0x01, /*QXGA *//*Size */ + 0x3021, 0x1d, + 0x3022, 0x00, + 0x3023, 0x0a, + 0x3024, 0x08, + 0x3025, 0x18, + 0x3026, 0x06, + 0x3027, 0x0c, + 0x335f, 0x68, + 0x3360, 0x18, + 0x3361, 0x0c, + 0x3362, 0x68, + 0x3363, 0x08, + 0x3364, 0x04, + 0x3403, 0x42, + 0x3088, 0x08, + 0x3089, 0x00, + 0x308a, 0x06, + 0x308b, 0x00, + + 0x3507, 0x06, + 0x350a, 0x4f, + 0x3600, 0xc4, + 0xffff, 0xff, +}; + + +static struct regval_list ov3640_fmt_yuv422_vga[] = { + {0x3002, 0x06 }, {0x3003, 0x1F }, {0x3001, 0x12 }, {0x304d, 0x45 }, + {0x30aa, 0x45 }, {0x30B0, 0xff }, {0x30B1, 0xff }, {0x30B2, 0x10 }, + {0x30d7, 0x10 }, {0x3047, 0x00 }, {0x3018, 0x60 }, {0x3019, 0x58 }, + {0x301A, 0xa1 }, {0x3087, 0x02 }, {0x3082, 0x20 }, {0x303C, 0x08 }, + {0x303d, 0x18 }, {0x303e, 0x06 }, + {0x303f, 0x0c }, {0x3030, 0x62 }, {0x3031, 0x26 }, {0x3032, 0xe6 }, + {0x3033, 0x6e }, {0x3034, 0xea }, {0x3035, 0xae }, {0x3036, 0xa6 }, + {0x3037, 0x6a }, {0x3015, 0x12 }, {0x3013, 0xfd }, {0x3104, 0x02 }, + {0x3105, 0xfd }, {0x3106, 0x00 }, {0x3107, 0xff }, {0x3308, 0xa5 }, + {0x3316, 0xff }, {0x3317, 0x00 }, {0x3087, 0x02 }, {0x3082, 0x20 }, + {0x3300, 0x13 }, {0x3301, 0xd6 }, {0x3302, 0xef }, {0x30B8, 0x20 }, + {0x30B9, 0x17 }, {0x30BA, 0x04 }, {0x30BB, 0x08 }, + + {0x3507, 0x06 }, + {0x350a, 0x4f }, {0x3600, 0xc4 }, {0x332B, 0x00 }, {0x332D, 0x45 }, + {0x332D, 0x60 }, {0x332F, 0x03 }, + {0x3100, 0x02 }, {0x3304, 0xfc }, {0x3400, 0x00 }, {0x3404, 0x02 }, /* YUV422 */ + {0x3601, 0x01 }, {0x302a, 0x06 }, {0x302b, 0x20 }, + {0x300E, 0x32 }, {0x300F, 0x21 }, {0x3010, 0x21 }, {0x3011, 0x01 }, /* QXGA PLL setting*/ + {0x304c, 0x81 }, + {0x3602, 0x22 }, {0x361E, 0x00 }, {0x3622, 0x18 }, {0x3623, 0x69 }, /* CSI setting */ + {0x3626, 0x00 }, {0x3627, 0xf0 }, {0x3628, 0x00 }, {0x3629, 0x26 }, + {0x362A, 0x00 }, {0x362B, 0x5f }, {0x362C, 0xd0 }, {0x362D, 0x3c }, + {0x3632, 0x10 }, {0x3633, 0x28 }, {0x3603, 0x4d }, {0x364C, 0x04 }, + {0x309e, 0x00 }, + {0x3020, 0x01 }, {0x3021, 0x1d }, {0x3022, 0x00 }, {0x3023, 0x0a }, /* crop window setting*/ + {0x3024, 0x08 }, {0x3025, 0x18 }, {0x3026, 0x06 }, {0x3027, 0x0c }, + {0x335f, 0x68 }, {0x3360, 0x18 }, {0x3361, 0x0c }, + {0x3362, 0x12 }, {0x3363, 0x88 }, {0x3364, 0xe4 }, {0x3403, 0x42 }, /* VGA */ + {0x3088, 0x02 }, {0x3089, 0x80 }, {0x308a, 0x01 }, {0x308b, 0xe0 }, + {0x3355, 0x04 }, {0x3354, 0x01 }, {0x335e, 0x28 }, /* brightness */ + {0x3355, 0x04 }, {0x335c, 0x20 }, {0x335d, 0x20 }, /* contrast */ + //0x306c, 0x00, //Enable color bar + //0x307b, 0x42, //Select color bar + //0x307d, 0x80 , //Enable color bar + {0xffff, 0x00ff} /* End of file marker (0xFFFF)*/ +}; + +static struct regval_list ov3640_fmt_yuv422_qvga[] = { + {0x3002, 0x06 }, {0x3003, 0x1F }, {0x3001, 0x12 }, {0x304d, 0x45 }, + {0x30aa, 0x45 }, {0x30B0, 0xff }, {0x30B1, 0xff }, {0x30B2, 0x10 }, + {0x30d7, 0x10 }, {0x3047, 0x00 }, {0x3018, 0x60 }, {0x3019, 0x58 }, + {0x301A, 0xa1 }, {0x3087, 0x02 }, {0x3082, 0x20 }, {0x303C, 0x08 }, + {0x303d, 0x18 }, {0x303e, 0x06 }, + {0x303f, 0x0c }, {0x3030, 0x62 }, {0x3031, 0x26 }, {0x3032, 0xe6 }, + {0x3033, 0x6e }, {0x3034, 0xea }, {0x3035, 0xae }, {0x3036, 0xa6 }, + {0x3037, 0x6a }, {0x3015, 0x12 }, {0x3013, 0xfd }, {0x3104, 0x02 }, + {0x3105, 0xfd }, {0x3106, 0x00 }, {0x3107, 0xff }, {0x3308, 0xa5 }, + {0x3316, 0xff }, {0x3317, 0x00 }, {0x3087, 0x02 }, {0x3082, 0x20 }, + {0x3300, 0x13 }, {0x3301, 0xd6 }, {0x3302, 0xef }, {0x30B8, 0x20 }, + {0x30B9, 0x17 }, {0x30BA, 0x04 }, {0x30BB, 0x08 }, {0x3507, 0x06 }, + {0x350a, 0x4f }, {0x3600, 0xc4 }, {0x332B, 0x00 }, {0x332D, 0x45 }, + {0x332D, 0x60 }, {0x332F, 0x03 }, + {0x3100, 0x02 }, {0x3304, 0xfc }, {0x3400, 0x00 }, {0x3404, 0x02 }, /* YUV422 */ + {0x3601, 0x01 }, {0x302a, 0x06 }, {0x302b, 0x20 }, + {0x300E, 0x32 }, {0x300F, 0x21 }, {0x3010, 0x21 }, {0x3011, 0x01 }, /* QXGA PLL setting*/ + {0x304c, 0x81 }, + {0x3602, 0x22 }, {0x361E, 0x00 }, {0x3622, 0x18 }, {0x3623, 0x69 }, /* CSI setting */ + {0x3626, 0x00 }, {0x3627, 0xf0 }, {0x3628, 0x00 }, {0x3629, 0x26 }, + {0x362A, 0x00 }, {0x362B, 0x5f }, {0x362C, 0xd0 }, {0x362D, 0x3c }, + {0x3632, 0x10 }, {0x3633, 0x28 }, {0x3603, 0x4d }, {0x364C, 0x04 }, + {0x309e, 0x00 }, + {0x3020, 0x01 }, {0x3021, 0x1d }, {0x3022, 0x00 }, {0x3023, 0x0a }, /* crop window setting*/ + {0x3024, 0x08 }, {0x3025, 0x18 }, {0x3026, 0x06 }, {0x3027, 0x0c }, + {0x335f, 0x68 }, {0x3360, 0x18 }, {0x3361, 0x0c }, + {0x3362, 0x01 }, {0x3363, 0x48 }, {0x3364, 0xf4 }, {0x3403, 0x42 }, /* QVGA */ + {0x3088, 0x01 }, {0x3089, 0x40 }, {0x308a, 0x00 }, {0x308b, 0xf0 }, + {0x3355, 0x04 }, {0x3354, 0x01 }, {0x335e, 0x28 }, /* brightness */ + {0x3355, 0x04 }, {0x335c, 0x20 }, {0x335d, 0x20 }, /* contrast */ + 0xffff, 0xff, +}; + +/*TODO - ov3640_fmt_yuv422_qcif can't work*/ +static struct regval_list ov3640_fmt_yuv422_qcif[] = { + //QVGA + //0x3012, 0x0080, // + 0x304d, 0x45, // /*Rev2A */ + 0x3087, 0x16, // /*Rev2A */ + 0x30aa, 0x45, // /*Rev2A */ + 0x30b0, 0xff, // /*Rev2A */ + 0x30b1, 0xff, // + 0x30b2, 0x10, // + 0x30d7, 0x10, // /*Rev2A */ + + 0x309e, 0x00, // /*terry */ + 0x3602, 0x26, // /*2a ;SOL/EOL on */ + 0x3603, 0x4D, // /*ecc */ + 0x364c, 0x04, // /*ecc */ + 0x360c, 0x12, // /*virtual channel 0 */ + 0x361e, 0x00, // + 0x361f, 0x11, // /*pclk_period, terry */ + 0x3633, 0x32, // /*terry, increase hs_prepare */ + 0x3629, 0x3c, // /*terry, increase clk_prepare */ + 0x300e, 0x39, // /*15fps terry */ + 0x300f, 0xa1, // /*terry */ + + 0x3010, 0xa2, // /*mipi spd, 81 ;terry */ + + 0x3011, 0x00, // + 0x304c, 0x84, // /*Rev2A */ + + 0x3018, 0x38, // /*aec */ + 0x3019, 0x30, // /*06142007 */ + 0x301a, 0x61, // /*06142007 */ + 0x307d, 0x00, // /*aec isp 06142007 */ + 0x3087, 0x02, // /*06142007 */ + 0x3082, 0x20, // /*06142007 */ + + 0x303c, 0x08, // /*aec weight */ + 0x303d, 0x18, // + 0x303e, 0x06, // + 0x303F, 0x0c, // + 0x3030, 0x62, // + 0x3031, 0x26, // + 0x3032, 0xe6, // + 0x3033, 0x6e, // + 0x3034, 0xea, // + 0x3035, 0xae, // + 0x3036, 0xa6, // + 0x3037, 0x6a, // + + 0x3015, 0x12, // /*07182007 8x gain, auto 1/2 */ + 0x3014, 0x04, // /*06142007 auto frame off */ + 0x3013, 0xf7, // /*07182007 */ + + 0x3104, 0x02, // + 0x3105, 0xfd, // + 0x3106, 0x00, // + 0x3107, 0xff, // + 0x3308, 0xa5, // + 0x3316, 0xff, // + 0x3317, 0x00, // + 0x3087, 0x02, // + 0x3082, 0x20, // + 0x3300, 0x13, // + 0x3301, 0xd6, // + 0x3302, 0xef, // + + 0x30b8, 0x20, // /*10 */ + 0x30b9, 0x17, // /*18 */ + 0x30ba, 0x04, // /*00 */ + 0x30bb, 0x08, // /*1f */ + + 0x3100, 0x02, // /*YUV */ + 0x3304, 0x00, // + 0x3400, 0x00, // + 0x3404, 0x02, // /*terry - output format: YUV422PACKED(UYVY)*/ + + 0x3302, 0xef, // /* */ + 0x3020, 0x01, // + 0x3021, 0x1d, // + 0x3022, 0x00, // + 0x3023, 0x0a, // + 0x3024, 0x08, // + 0x3025, 0x18, // + 0x3026, 0x06, // + 0x3027, 0x0c, // + + 0x335f, 0x68, // + 0x3360, 0x18, // + 0x3361, 0x0c, // + + 0x3362, 0x00, // QVGA + 0x3363, 0xb8, // QVGA + 0x3364, 0x94, // QVGA + + 0x3403, 0x42, // QVGA + + 0x3088, 0x00, // QVGA + 0x3089, 0xb0, // QVGA + 0x308a, 0x00, // QVGA + 0x308b, 0x90, // QVGA + + + 0x3507, 0x06, // + 0x350a, 0x4f, // + 0x3600, 0xc4, // + 0xffff, 0xff, + +}; + +static struct regval_list ov3640_fmt_jpeg_vga[] = { + {0x3002, 0x06 }, {0x3003, 0x1F }, {0x3001, 0x12 }, {0x304d, 0x45 }, + {0x30aa, 0x45 }, {0x30B0, 0xff }, {0x30B1, 0xff }, {0x30B2, 0x10 }, + {0x30d7, 0x10 }, {0x3047, 0x00 }, {0x3018, 0x60 }, {0x3019, 0x58 }, + {0x301A, 0xa1 }, {0x303C, 0x08 }, {0x303d, 0x18 }, {0x303e, 0x06 }, + {0x303f, 0x0c }, {0x3030, 0x62 }, {0x3031, 0x26 }, {0x3032, 0xe6 }, + {0x3033, 0x6e }, {0x3034, 0xea }, {0x3035, 0xae }, {0x3036, 0xa6 }, + {0x3037, 0x6a }, {0x3015, 0x12 }, {0x3013, 0xfd }, {0x3104, 0x02 }, + {0x3105, 0xfd }, {0x3106, 0x00 }, {0x3107, 0xff }, {0x3308, 0xa5 }, + {0x3316, 0xff }, {0x3317, 0x00 }, {0x3087, 0x02 }, {0x3082, 0x20 }, + {0x3300, 0x13 }, {0x3301, 0xd6 }, {0x3302, 0xef }, {0x30B8, 0x20 }, + {0x30B9, 0x17 }, {0x30BA, 0x04 }, {0x30BB, 0x08 }, {0x3507, 0x06 }, + {0x350a, 0x4f }, {0x3600, 0xc4 }, {0x332B, 0x00 }, {0x332D, 0x45 }, + {0x332D, 0x60 }, {0x332F, 0x03 }, + {0x3100, 0x32 }, {0x3304, 0xfc }, {0x3404, 0x22 }, {0x3500, 0x00 }, /* JPEG */ + {0x3610, 0x0c }, + {0x300E, 0x32 }, {0x300F, 0x21 }, {0x3010, 0x21 }, {0x3011, 0x01 }, /* QXGA PLL setting*/ + {0x304c, 0x81 }, + {0x3602, 0x22 }, {0x361E, 0x00 }, {0x3622, 0x18 }, {0x3623, 0x69 }, /* CSI setting */ + {0x3626, 0x00 }, {0x3627, 0xf0 }, {0x3628, 0x00 }, {0x3629, 0x26 }, + {0x362A, 0x00 }, {0x362B, 0x5f }, {0x362C, 0xd0 }, {0x362D, 0x3c }, + {0x3632, 0x10 }, {0x3633, 0x28 }, {0x3603, 0x4d }, {0x364C, 0x04 }, + {0x309e, 0x00 }, + {0x3020, 0x01 }, {0x3021, 0x1d }, {0x3022, 0x00 }, {0x3023, 0x0a }, /* crop window setting*/ + {0x3024, 0x08 }, {0x3025, 0x18 }, {0x3026, 0x06 }, {0x3027, 0x0c }, + {0x335f, 0x68 }, {0x3360, 0x18 }, {0x3361, 0x0c }, + {0x3362, 0x12 }, {0x3363, 0x88 }, {0x3364, 0xe4 }, {0x3403, 0x42 }, /* VGA */ + {0x3088, 0x02 }, {0x3089, 0x80 }, {0x308a, 0x01 }, {0x308b, 0xe0 }, + + {0x3355, 0x04 }, {0x3354, 0x01 }, {0x335e, 0x28 }, /* brightness */ + {0x3355, 0x04 }, {0x335c, 0x20 }, {0x335d, 0x20 }, /* contrast */ + {0xffff, 0x00ff} /* End of file marker (0xFFFF)*/ + +}; + +/*TODO - ov3640_fmt_jpeg_qxga can't work*/ +static struct regval_list ov3640_fmt_jpeg_qxga[] = { + //2048x1536 JPG@15fps + + 0x3012, 0x80, + 0x304d, 0x45, //Rev2A + 0x30a7, 0x5e, //Rev2C mi + 0x3087, 0x16, //Rev2A + 0x309C, 0x1a, //Rev2C + 0x30a2, 0xe4, //Rev2C + 0x30aa, 0x42, //Rev2C + 0x30b0, 0xff, //Rev2A + 0x30b1, 0xff, + 0x30b2, 0x10, + 0x30d7, 0x10,//Rev2A + + 0x309e, 0x00, //terry + 0x3602, 0x26, //2a //SOL/EOL on + 0x3603, 0x4D, //ecc + 0x364c, 0x04, //ecc + 0x360c, 0x12, //virtual channel 0 + 0x361e, 0x00, + 0x361f, 0x11, //pclk_period, terry + 0x3633, 0x32, //terry, increase hs_prepare + 0x3629, 0x3c, //terry, increase clk_prepare + 0x300e, 0x39, //15fps terry + 0x300f, 0xa1, //terry + 0x3010, 0xa2, //high mipi spd, 2lane + 0x3011, 0x00, + 0x304c, 0x84, //Rev2A + + 0x30d9, 0x0d, //Rev2C + 0x30db, 0x08, //Rev2C + 0x3016, 0x82, //Rev2C + + 0x3018, 0x38, //aec + 0x3019, 0x30, //06142007 + 0x301a, 0x61, //06142007 + 0x307d, 0x00, //aec isp 06142007 + 0x3087, 0x02, //06142007 + 0x3082, 0x20, //06142007 + + 0x303c, 0x08, //aec weight + 0x303d, 0x18, + 0x303e, 0x06, + 0x303F, 0x0c, + 0x3030, 0x62, + 0x3031, 0x26, + 0x3032, 0xe6, + 0x3033, 0x6e, + 0x3034, 0xea, + 0x3035, 0xae, + 0x3036, 0xa6, + 0x3037, 0x6a, + + 0x3015, 0x12, //07182007 8x gain, auto 1/2 + 0x3014, 0x04, //06142007 auto frame off + 0x3013, 0xf7, //07182007 + + 0x3104, 0x02, + 0x3105, 0xfd, + 0x3106, 0x00, + 0x3107, 0xff, + + 0x3300, 0x13, + 0x3301, 0xde, + 0x3302, 0xef, + + 0x3308, 0xa5, + 0x3316, 0xff, + 0x3317, 0x00, + 0x3312, 0x26, + 0x3314, 0x42, + 0x3313, 0x2b, + 0x3315, 0x42, + 0x3310, 0xd0, + 0x3311, 0xbd, + 0x330c, 0x18, + 0x330d, 0x18, + 0x330e, 0x56, + 0x330f, 0x5c, + 0x330b, 0x1c, + 0x3306, 0x5c, + 0x3307, 0x11, + + 0x336a, 0x52, //052207 + 0x3370, 0x46, + 0x3376, 0x38, + + 0x30b8, 0x20, //10 + 0x30b9, 0x17, //18 + 0x30ba, 0x04, //00 + 0x30bb, 0x08, //1f + + //Format + 0x3100, 0x32, //JPG + 0x3304, 0x00, + 0x3400, 0x02, + 0x3404, 0x22, + 0x3500, 0x00, // + 0x3610, 0x80, //0c + + //Size QXGA + 0x3302, 0xcf, + 0x3088, 0x08, + 0x3089, 0x00, + 0x308a, 0x06, + 0x308b, 0x00, + + 0x3507, 0x06, + 0x350a, 0x4f, + 0x3600, 0xc4, + + 0xffff, 0xff, +}; + +static struct regval_list ov3640_fmt_rgb565[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0x0 }, + { REG_COM15, COM15_RGB565 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov3640_fmt_rgb444[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ + { REG_COM1, 0x40 }, /* Magic reserved bit */ + { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ + { 0xff, 0xff }, +}; + +static struct regval_list ov3640_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; + +/* + * Low-level register I/O. + */ +/*issue that OV sensor must write then read. 3640 register is 16bit!!!*/ +static int ov3640_read(struct i2c_client *c, u16 reg, + unsigned char *value) +{ + u8 data; + u8 address[2]; + address[0] = reg>>8; + address[1] = reg; + i2c_smbus_write_byte_data(c,address[0],address[1]); + data = i2c_smbus_read_byte(c); + *value = data; + return 0; +} + + +static int ov3640_write(struct i2c_client *c, u16 reg, + unsigned char value) +{ + u8 data[3]; + data[0] = reg>>8; + data[1] = reg; + data[2]= value; + i2c_master_send(c, data, 3); + if (reg == REG_SYS && (value & SYS_RESET)) + msleep(2); /* Wait for reset to run */ + return 0; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov3640_write_array(struct i2c_client *c, struct regval_list *vals) +{ + int i = 0; + while (vals->reg_num != 0xffff || vals->value != 0xff) { + int ret = ov3640_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + if (i == 0) + mdelay(5); + i++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static void ov3640_reset(struct i2c_client *client) +{ + ov3640_write(client, REG_SYS, SYS_RESET); + msleep(1); +} + + +static int ov3640_init(struct i2c_client *client) //TODO - currently not needed on 3640 +{ + return ov3640_write_array(client, ov3640_default_regs); +} + + + +static int ov3640_detect(struct i2c_client *client) +{ + unsigned char v; + int ret; + + /* + * no MID register found. OK, we know we have an OmniVision chip...but which one? + */ + ret = ov3640_read(client, REG_PIDH, &v); + if (ret < 0) + return ret; + if (v != 0x36) + return -ENODEV; +#if 0 + ret = ov3640_read(client, REG_PIDL, &v); + if (ret < 0) + return ret; + if (v != 0x4c) /* maybe this version is not true */ + return -ENODEV; +#endif + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + */ +static struct ov3640_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; + int bpp; /* bits per pixel */ +} ov3640_formats[] = { + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = ov3640_fmt_yuv422_qxga, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, //TODO + .bpp = 16, + }, + { + .desc = "YUYV422 planar", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .regs = ov3640_fmt_yuv422_qxga, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "YUYV 4:2:0", + .pixelformat = V4L2_PIX_FMT_YUV420, + .regs = ov3640_fmt_yuv422_qxga, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 12, + }, + { + .desc = "JFIF JPEG", + .pixelformat = V4L2_PIX_FMT_JPEG, + .regs = ov3640_fmt_jpeg_qxga, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = ov3640_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = ov3640_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "Raw RGB Bayer", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .regs = ov3640_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + .bpp = 8, + }, +}; +#define N_OV3640_FMTS ARRAY_SIZE(ov3640_formats) + + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov3640_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; +/*TODO - also can use ccic size register 0x34 to do same thing for cropping...anyway, sensor doing it is better? + 0x3020~0x3027*/ +static struct ov3640_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ + /* h/vref stuff */ +} ov3640_win_sizes[] = { + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = NULL, + // .regs = ov3640_qcif_regs, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 164, /* Empirically determined */ + .hstop = 20, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, +#if 0 + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, +#endif + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* QXGA */ + { + .width = QXGA_WIDTH, + .height = QXGA_HEIGHT, + .com7_bit = 0, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov3640_win_sizes)) + + +/* + * Store a set of start/stop values into the camera. //TODO - not used for 3640? + */ +static int ov3640_set_hw(struct i2c_client *client, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; + /* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov3640_write(client, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov3640_write(client, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov3640_read(client, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov3640_write(client, REG_HREF, v); + /* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov3640_write(client, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov3640_write(client, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov3640_read(client, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov3640_write(client, REG_VREF, v); + return ret; +} + + +static int ov3640_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct ov3640_format_struct *ofmt; + + if (fmt->index >= N_OV3640_FMTS) + return -EINVAL; + + ofmt = ov3640_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + + +static int ov3640_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct ov3640_format_struct **ret_fmt, + struct ov3640_win_size **ret_wsize) +{ + int index; + struct ov3640_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + for (index = 0; index < N_OV3640_FMTS; index++) + if (ov3640_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_OV3640_FMTS){ + printk("unsupported format!\n"); + return -EINVAL; + } + if (ret_fmt != NULL) + *ret_fmt = ov3640_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov3640_win_sizes; wsize < ov3640_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width <= wsize->width && pix->height <= wsize->height) + break; + if (wsize >= ov3640_win_sizes + N_WIN_SIZES){ + printk("size exceed and set as QXGA!\n"); + wsize--; /* Take the smallest one */ + } + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ +#if 0 + pix->width = wsize->width; + pix->height = wsize->height; +#endif + pix->bytesperline = pix->width*ov3640_formats[index].bpp/8; + pix->sizeimage = pix->height*pix->bytesperline; + printk("ov3640_try_fmt: pix->width is %d, pix->height is %d\n", pix->width, pix->height); + + if (ret_fmt == NULL) + return 0; + switch (pix->pixelformat) + { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_YUV422P: + case V4L2_PIX_FMT_YUV420: + switch (wsize->width) + { + case QXGA_WIDTH: + (*ret_fmt)->regs = ov3640_fmt_yuv422_qxga; + break; + case VGA_WIDTH: + (*ret_fmt)->regs = ov3640_fmt_yuv422_vga; + break; + case QVGA_WIDTH: + (*ret_fmt)->regs = ov3640_fmt_yuv422_qvga; + break; + case QCIF_WIDTH: + (*ret_fmt)->regs = ov3640_fmt_yuv422_qcif; + break; + default: + printk("unsupported size!\n"); + break; + } + break; + case V4L2_PIX_FMT_JPEG: + switch (wsize->width) + { + case QXGA_WIDTH: + (*ret_fmt)->regs = ov3640_fmt_jpeg_qxga; + break; + case VGA_WIDTH: + (*ret_fmt)->regs = ov3640_fmt_jpeg_vga; + break; + default: + printk("unsupported size!\n"); + break; + } + break; + default: + printk("unsupported format!\n"); + break; + } + return 0; +} + +/* + * Set a format. + */ +static int ov3640_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct ov3640_format_struct *ovfmt; + struct ov3640_win_size *wsize; + ret = ov3640_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + ov3640_write_array(c, ovfmt->regs); + // ov3640_set_hw(); //TODO + return ret; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov3640_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + unsigned char clkrc; + int ret; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = ov3640_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = OV3640_FRAME_RATE; + if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + return 0; +} + +static int ov3640_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + unsigned char clkrc; + int ret, div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + /* + * CLKRC has a reserved bit, so let's preserve it. + */ + ret = ov3640_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator*OV3640_FRAME_RATE)/tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + clkrc = (clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = OV3640_FRAME_RATE/div; + return ov3640_write(c, REG_CLKRC, clkrc); +} + +static int ov3640_s_input(struct i2c_client *c, int *id) +{ + return 0; +} + +/* + * Code for dealing with controls. + */ + + +/*TODO - need to port below register codes for 3640...maybe not used*/ + + +static int ov3640_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov3640_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov3640_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += ov3640_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov3640_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov3640_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov3640_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov3640_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov3640_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov3640_sine(theta); +} + + + + +static void ov3640_calc_cmatrix(struct ov3640_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov3640_sine(info->hue); + costh = ov3640_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov3640_t_sat(struct i2c_client *client, int value) +{ + struct ov3640_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov3640_calc_cmatrix(info, matrix); + ret = ov3640_store_cmatrix(client, matrix); + return ret; +} + +static int ov3640_q_sat(struct i2c_client *client, __s32 *value) +{ + struct ov3640_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int ov3640_t_hue(struct i2c_client *client, int value) +{ + struct ov3640_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov3640_calc_cmatrix(info, matrix); + ret = ov3640_store_cmatrix(client, matrix); + return ret; +} + + +static int ov3640_q_hue(struct i2c_client *client, __s32 *value) +{ + struct ov3640_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov3640_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + else + return 128 - (v & 0x7f); +} + + +static unsigned char ov3640_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + else + return (128 - v) | 0x80; +} + +static int ov3640_t_brightness(struct i2c_client *client, int value) +{ + unsigned char com8, v; + int ret; + + ov3640_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov3640_write(client, REG_COM8, com8); + v = ov3640_abs_to_sm(value); + ret = ov3640_write(client, REG_BRIGHT, v); + return ret; +} + +static int ov3640_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov3640_read(client, REG_BRIGHT, &v); + + *value = ov3640_sm_to_abs(v); + return ret; +} + +static int ov3640_t_contrast(struct i2c_client *client, int value) +{ + return ov3640_write(client, REG_CONTRAS, (unsigned char) value); +} + +static int ov3640_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov3640_read(client, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov3640_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov3640_read(client, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov3640_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov3640_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov3640_write(client, REG_MVFP, v); + return ret; +} + + + +static int ov3640_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov3640_read(client, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov3640_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov3640_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov3640_write(client, REG_MVFP, v); + return ret; +} + + +static struct ov3640_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} ov3640_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov3640_t_brightness, + .query = ov3640_q_brightness, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX ov3640 spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov3640_t_contrast, + .query = ov3640_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov3640_t_sat, + .query = ov3640_q_sat, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov3640_t_hue, + .query = ov3640_q_hue, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov3640_t_vflip, + .query = ov3640_q_vflip, + }, + { + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov3640_t_hflip, + .query = ov3640_q_hflip, + }, +}; +#define N_CONTROLS (ARRAY_SIZE(ov3640_controls)) + +static struct ov3640_control *ov3640_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (ov3640_controls[i].qc.id == id) + return ov3640_controls + i; + return NULL; +} + + +static int ov3640_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct ov3640_control *ctrl = ov3640_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int ov3640_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov3640_control *octrl = ov3640_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +static int ov3640_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov3640_control *octrl = ov3640_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + + + + +int ccic_sensor_attach(struct i2c_client *client); + + +/* + * Basic i2c stuff. + */ +extern struct clk *pxa168_ccic_gate_clk; +static int __devinit ov3640_probe(struct i2c_client *client) +{ + int ret; + struct ov3640_info *info; + struct sensor_platform_data *pdata; + pdata = client->dev.platform_data; + + clk_enable(pxa168_ccic_gate_clk); + ccic_set_clock_mipi(); + + pdata->power_on(1, 1); + /* + * Set up our info structure. + */ + info = kzalloc(sizeof (struct ov3640_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &ov3640_formats[1]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + /* + * Make sure it's an ov3640 + */ + ret = ov3640_detect(client); + if (ret) + goto out_free_info; + printk(KERN_NOTICE "OmniVision ov3640 sensor detected\n"); + ret = ccic_sensor_attach(client); + if (ret) + goto out_free_info; + + pdata->power_on(0, 1); + ccic_disable_clock(); + return 0; + +out_free_info: + kfree(info); +out_free: + return ret; +} + + +static int ov3640_remove(struct i2c_client *client) +{ + return 0; //TODO +} + + +static int ov3640_streamon(struct i2c_client *client) +{ + unsigned char val; + ov3640_read(client, 0x3086, &val); + val &= ~0x03; + ov3640_write(client, 0x3086, val); + return 0; +} + +static int ov3640_streamoff(struct i2c_client *client) +{ + unsigned char val; + ov3640_read(client, 0x3086, &val); + val |= 0x03; + ov3640_write(client, 0x3086, val); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov3640_g_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return ov3640_read(client, (u16)reg->reg, (unsigned char *)&(reg->val)); +} + +static int ov3640_s_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return ov3640_write(client, (u16)reg->reg, (unsigned char)reg->val); +} +#endif +static int ov3640_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + switch (cmd) { + case VIDIOC_DBG_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV3640, 0); + + case VIDIOC_INT_RESET: + ov3640_reset(client); + return 0; + + case VIDIOC_INT_INIT: + return 0;//ov3640_init(client); //TODO - should get 3640 default register values + + case VIDIOC_ENUM_FMT: + return ov3640_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + case VIDIOC_TRY_FMT: + return ov3640_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + case VIDIOC_S_FMT: + return ov3640_s_fmt(client, (struct v4l2_format *) arg); + case VIDIOC_QUERYCTRL: + return ov3640_queryctrl(client, (struct v4l2_queryctrl *) arg); + case VIDIOC_S_CTRL: + return ov3640_s_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_G_CTRL: + return ov3640_g_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_S_PARM: + return ov3640_s_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_G_PARM: + return ov3640_g_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_S_INPUT: + return ov3640_s_input(client, (int *) arg); + case VIDIOC_STREAMON: + return ov3640_streamon(client); + case VIDIOC_STREAMOFF: + return ov3640_streamoff(client); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return ov3640_g_register(client, (struct v4l2_dbg_register *) arg); + case VIDIOC_DBG_S_REGISTER: + return ov3640_s_register(client, (struct v4l2_dbg_register *) arg); +#endif + } + return -EINVAL; +} + +static struct i2c_device_id ov3640_idtable[] = { + { "ov3640", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ov3640_idtable); + +static struct i2c_driver ov3640_driver = { + .driver = { + .name = "ov3640", + }, + .id_table = ov3640_idtable, + .command = ov3640_command, + .probe = ov3640_probe, + .remove = ov3640_remove, +}; + + +/* + * Module initialization + */ +static int __init ov3640_mod_init(void) +{ + printk(KERN_NOTICE "OmniVision ov3640 sensor driver, at your service\n"); + return i2c_add_driver(&ov3640_driver); +} + +static void __exit ov3640_mod_exit(void) +{ + i2c_del_driver(&ov3640_driver); +} + +late_initcall(ov3640_mod_init); +//module_init(ov3640_mod_init); +module_exit(ov3640_mod_exit); + diff --git a/drivers/media/video/ov529_drv.c b/drivers/media/video/ov529_drv.c new file mode 100644 index 00000000000000..503474c6f8c6ef --- /dev/null +++ b/drivers/media/video/ov529_drv.c @@ -0,0 +1,1076 @@ +/* + * linux/drivers/media/video/ov529_drv.c - ov529 camera controller driver + * + * Based on linux/drivers/media/video/pxa168_camera.c + * + * Copyright: (C) Copyright 2009 Marvell International Ltd. + * Weili Xia + * + * A driver for the OV529 single chip camera controller from OmniVision + * technology. Currently works with the Omnivision OV7740 sensor. + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * Copyright 2006-7 Jonathan Corbet + * + * Written by Weili Xia, wlxia@marvell.com. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#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 "ov529_hw_ops.h" +#include "ov529_drv.h" +#include +#include + +#if defined(CONFIG_DVFM) +#include +static int dvfm_dev_idx; +#endif + +#define DRIVER_DESC "ov529 Camera Controller Driver" +#define DRIVER_VERSION "v0.0.1" + +#define OV529_VER 0x01 + +/* #define OV529_DBG */ +#undef OV529_DBG +#ifdef OV529_DBG +#define DRIVER_NAME "ov529" +#define ov_dbg(f, msg...) \ + printk(KERN_ERR DRIVER_NAME " [%s()]: " f, __func__, ## msg) +#else +#define ov_dbg(f, msg...) +#endif + +#ifdef SMC_USE_DMA +#define WORKQUEUE_DELAY 2 +#else +#define WORKQUEUE_DELAY 3 +#endif + +#define MIN_BUFFER_NUM 1 +#define MAX_BUFFER_NUM 3 + +struct ov529_frame ov529_frames[] = { + { + .index = 0, + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .interval_type = V4L2_FRMIVAL_TYPE_DISCRETE, + .frameinterval = 1, + }, + { + .index = 1, + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .interval_type = V4L2_FRMIVAL_TYPE_DISCRETE, + .frameinterval = 1, + }, +}; + +static volatile int current_imagesize; +static volatile int last_imagesize; + +/* work queue for transfering image from ov529 */ +static struct workqueue_struct *workqueue; + +static int ov529_recv_img(struct ov529_encoder *ov529, int index); + +static int ov529_counter = 0; +/* delayed work */ +static void ov529_iotransfer(struct work_struct *work) +{ + struct ov529_encoder *ov529 = container_of(work, struct ov529_encoder, iotransfer.work); + struct delayed_work *iotransfer = &ov529->iotransfer; + struct ov529_buffer *buffer; + unsigned long flags; + int ret; + + spin_lock_irqsave(&ov529->list_lock, flags); + if (ov529->state != S_STREAMING) + goto out; + + if (list_empty(&ov529->buf_avail)) + goto out; + + buffer = list_entry(ov529->buf_avail.next, + struct ov529_buffer, list); + + ret = ov529_recv_img(ov529, buffer->v4lbuf.index); + if (ret < 0) { + buffer->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + goto out; + } + ov_dbg("buffer %d filled up\n", buffer->v4lbuf.index); + + if (ov529_counter == 500) + ov529_counter = 0; + if (ov529_counter == 0) + ov_dbg("500 frames\n"); + ov529_counter++; + + buffer->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + list_move_tail(&buffer->list, &ov529->buf_full); + wake_up(&ov529->iowait); + +out: + spin_unlock_irqrestore(&ov529->list_lock, flags); + queue_delayed_work(workqueue, iotransfer, WORKQUEUE_DELAY); +} + +/* configure ov529 */ +static int ov529_configure(struct ov529_encoder *ov529) +{ + /* + struct ov529_format *fmt = ov529->curr_fmt; + struct ov529_frame *frame = fmt->curr_frame; + */ + return 0; +} + +static int ov529_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "ov529"); + strcpy(cap->card, "ov529"); + cap->version = OV529_VER; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +/* + * We only have one input - the ov529 encoder + */ +static int ov529_vidioc_enum_input(struct file *filp, void *priv, + struct v4l2_input *input) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + if (input->index != ov529->curr_devid) + return -EINVAL; + + /* ov529 should have the same intf as a camera */ + input->type = V4L2_INPUT_TYPE_CAMERA; + /* input->std = V4L2_STD_ALL; */ + strcpy(input->name, "ov529"); + return 0; +} + +static int ov529_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + *i = ov529->curr_devid; + return 0; +} + +static int ov529_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + /* If the required sensor is the same as the current + * active one, return immediately. + */ + if (i == ov529->curr_devid) + return 0; + else + return -ENODEV; +} + +static int ov529_vidioc_enum_fmt_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmt) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + struct ov529_format *format; + int ret = -EINVAL; + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&ov529->s_mutex); + + if (fmt->index >= ov529->nformats) + goto out; + + format = &ov529->fmt[fmt->index]; + + fmt->flags = 0; + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + strncpy(fmt->description, format->name, + sizeof fmt->description); + fmt->description[sizeof fmt->description - 1] = 0; + fmt->pixelformat = format->fcc; + ret = 0; +out: + mutex_unlock(&ov529->s_mutex); + return ret; +} + +static int ov529_vidioc_try_fmt_cap (struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix = &fmt->fmt.pix; + struct ov529_encoder *ov529 = video_drvdata(filp); + struct ov529_format *format = NULL; + struct ov529_frame *frame = NULL; + int ret = 0; + int i; + + mutex_lock(&ov529->s_mutex); + + /* + * find out whether this format is valid for ov529 + */ + for (i = 0; i < ov529->nformats; i++) { + if (ov529->fmt[i].fcc == pix->pixelformat) { + format = &ov529->fmt[i]; + break; + } + } + if (format == NULL) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < format->nframes; i++) { + if (format->frame[i].width <= pix->width && + format->frame[i].height <= pix->height) { + frame = &format->frame[i]; + break; + } + } + if (frame == NULL) { + ret = -EINVAL; + goto out; + } + + pix->width = frame->width; + pix->height = frame->height; + pix->bytesperline = frame->width * format->bpp / 8; + pix->sizeimage = 50 * 1024; + pix->field = V4L2_FIELD_NONE; + +out: + mutex_unlock(&ov529->s_mutex); + printk(KERN_ERR "%s, returns %d\n", __func__, ret); + return ret; +} + +static int ov529_vidioc_s_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + int ret; + + /* could not change format if ov529 is busy */ + if (ov529->state != S_IDLE) + return -EBUSY; + + /* try the format */ + ret = ov529_vidioc_try_fmt_cap(filp, priv, fmt); + if (ret) + return ret; + + /* find out which format and frame + * should be curr_fmt and curr_frame. + */ + /* + ov529->curr_fmt = + ov529->curr_fmt->curr_frame = + */ + + ov529_configure(ov529); + + /* mutex_lock(&cam->s_mutex); */ + + return 0; +} + +static int ov529_vidioc_g_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + struct ov529_format *format = ov529->curr_fmt; + struct ov529_frame *frame = format->curr_frame; +/* + if (fmt->type != ov529->type) + return -EINVAL; +*/ + if (format == NULL || frame == NULL) + return -EINVAL; + + fmt->fmt.pix.pixelformat = format->fcc; + fmt->fmt.pix.width = frame->width; + fmt->fmt.pix.height = frame->height; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.sizeimage = 50 * 1024; + fmt->fmt.pix.bytesperline = format->bpp * frame->width / 8; + /* fmt->fmt.pix.sizeimage = frame->height * fmt->fmt.pix.bytesperline; */ + fmt->fmt.pix.colorspace = format->colorspace; + fmt->fmt.pix.priv = 0; + + return 0; +} + +static int ov529_setup_buf(struct ov529_encoder *ov529, int index) +{ + struct ov529_buffer *buf = ov529->buf + index; + + /* Hardcode length, from ov sample code */ + buf->v4lbuf.length = PAGE_ALIGN(50 * 1024); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + + /* + * Offset: must be 32-bit even on a 64-bit system. videobuf-dma-sg + * just uses the length times the index, but the spec warns + * against doing just that - vma merging problems. So we + * leave a gap between each pair of buffers. + */ + buf->v4lbuf.m.offset = index * buf->v4lbuf.length; + return 0; +} + +static int ov529_free_buf(struct ov529_encoder *ov529) +{ + int i; + + for (i = 0; i < ov529->nbufs; i++) + if (ov529->buf[i].vma_use_count > 0) + return -EBUSY; + + /* + * Free the ov529_buffer data structures. + */ + for (i = 0; i < ov529->nbufs; i++) + vfree(ov529->buf[i].buffer); + ov529->nbufs = 0; + kfree(ov529->buf); + ov529->buf = NULL; + + /* re-init the buffer lists. */ + INIT_LIST_HEAD(&ov529->buf_avail); + INIT_LIST_HEAD(&ov529->buf_full); + + return 0; +} + +static int ov529_vidioc_reqbufs(struct file *filp, void *priv, + struct v4l2_requestbuffers *req) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + int ret = 0; + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&ov529->s_mutex); + + if (req->count < MIN_BUFFER_NUM) + req->count = MIN_BUFFER_NUM; + else if (req->count > MAX_BUFFER_NUM) + req->count = MAX_BUFFER_NUM; + + if (ov529->nbufs > 0) { + ret = ov529_free_buf(ov529); + if (ret) + goto out; + } + + ov529->buf = kzalloc((req->count * sizeof(struct ov529_buffer)), GFP_KERNEL); + if (ov529->buf == NULL) { + ret = -ENOMEM; + goto out; + } + + for (ov529->nbufs = 0; ov529->nbufs < req->count; ov529->nbufs++) { + ret = ov529_setup_buf(ov529, ov529->nbufs); + if (ret) + goto out; + } + +out: + mutex_unlock(&ov529->s_mutex); + return ret; +} + +static int ov529_vidioc_querybuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + int ret = -EINVAL; + + mutex_lock(&ov529->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + + if (buf->index < 0 || buf->index >= ov529->nbufs) + goto out; + + *buf = ov529->buf[buf->index].v4lbuf; + ret = 0; +out: + mutex_unlock(&ov529->s_mutex); + return ret; +} + +static int ov529_vidioc_qbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + struct ov529_buffer *buffer; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&ov529->s_mutex); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= ov529->nbufs) + goto out; + buffer = ov529->buf + buf->index; + if (buffer->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) { + ret = 0; /* Already queued?? */ + goto out; + } + if (buffer->v4lbuf.flags & V4L2_BUF_FLAG_DONE) { + /* Spec doesn't say anything, seems appropriate tho */ + ret = -EBUSY; + goto out; + } + buffer->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + + spin_lock_irqsave(&ov529->list_lock, flags); + list_add_tail(&buffer->list, &ov529->buf_avail); + spin_unlock_irqrestore(&ov529->list_lock, flags); + ov_dbg("buffer %d queued\n", buffer->v4lbuf.index); + + ret = 0; + +out: + mutex_unlock(&ov529->s_mutex); + return ret; +} + +#define IMAGE_BLOCK_SIZE 1024 +static int ov529_recv_img(struct ov529_encoder *ov529, int index) +{ + struct ov529_buffer *curr_buf = ov529->buf + index; + char *buf = curr_buf->buffer; + int length = curr_buf->v4lbuf.length; + int imagesize = 0; + int drop_image = 0; + + /* FIXME: + * implement a timeout mechanism? + */ + /* ov_dbg("image ready\n"); */ + + /* Set first two bytes to zero. + * These two bytes are used to verify whether a frame is correct. + */ + buf[0] = 0x0; + buf[1] = 0x0; + while (!drop_image) { + /* ov_dbg("imagesize: %d\n", imagesize); */ + while (ov529->ops.data_ready()); + + ov529->ops.recv_data((char *)(buf + imagesize), IMAGE_BLOCK_SIZE); + if ((buf[0] == 0xFF) && (buf[1] == 0xD8)) { + imagesize += IMAGE_BLOCK_SIZE; + if (imagesize >= length) { + ov_dbg("overflow\n"); + return -ENOMEM; + } + + if (buf[imagesize - 2] == 0xFF && buf[imagesize - 1] == 0xD9) { + current_imagesize = imagesize; + /* + if((last_imagesize + 0x200) < current_imagesize) { + last_imagesize = current_imagesize; + drop_image = 1; + printk(KERN_ERR "frame Error 0\n"); + } else if(current_imagesize < (last_imagesize - 0x200)) { + last_imagesize = current_imagesize; + drop_image = 1; + printk(KERN_ERR "frame Error 1\n"); + } + */ + if (drop_image == 0) { + last_imagesize = current_imagesize; + ov_dbg("frame end, %d\r\n", imagesize); + curr_buf->v4lbuf.bytesused = current_imagesize; + return imagesize; + } + } + } else { + printk("receive image data error\n"); + drop_image = 1; + return -EINVAL; + } + } + + return -EINVAL; +} + +static int ov529_vidioc_dqbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + struct ov529_buffer *buffer; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&ov529->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (ov529->state != S_STREAMING) + goto out; + + if (list_empty(&ov529->buf_full) && filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto out; + } + + while (list_empty(&ov529->buf_full) && ov529->state == S_STREAMING) { + mutex_unlock(&ov529->s_mutex); + if (wait_event_interruptible(ov529->iowait, + !list_empty(&ov529->buf_full))) { + ret = -ERESTARTSYS; + goto out_unlocked; + } + mutex_lock(&ov529->s_mutex); + } + /* + buffer = ov529->buf + buf->index; + ret = ov529_recv_img(ov529); + if (ret < 0) { + buffer->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + *buf = buffer->v4lbuf; + goto out; + } + buffer->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + */ + spin_lock_irqsave(&ov529->list_lock, flags); + buffer = list_entry(ov529->buf_full.next, + struct ov529_buffer, list); + list_del_init(&buffer->list); + spin_unlock_irqrestore(&ov529->list_lock, flags); + + /* dequeued, clear V4L2_BUF_FLAG_DONE */ + buffer->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + *buf = buffer->v4lbuf; + ret = 0; +out: + mutex_unlock(&ov529->s_mutex); +out_unlocked: + return ret; +} + +static int ov529_vidioc_streamon(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + int ret = -EINVAL; + /* + if((cam->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) && + BUS_IS_PARALLEL(cam->bus_type[sensor_selected])){ + sof = 7; + jpeg_cnt = 0; + } + */ + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&ov529->s_mutex); + if (ov529->state != S_IDLE || ov529->nbufs == 0) + goto out; + + ret = ov529->ops.streamon(); + if (ret == 0) + ov529->state = S_STREAMING; + + queue_delayed_work(workqueue, &ov529->iotransfer, 10); +out: + mutex_unlock(&ov529->s_mutex); + return ret; +} + +static int ov529_vidioc_streamoff(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + int ret = 0; + + mutex_lock(&ov529->s_mutex); + ov529->state = S_IDLE; + cancel_delayed_work(&ov529->iotransfer); + mutex_unlock(&ov529->s_mutex); + + return ret; +} + +static int ov529_vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parms) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(parms, 0, sizeof *parms); + + parms->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parms->parm.capture.capturemode = 0; + parms->parm.capture.timeperframe.numerator = 1; + parms->parm.capture.timeperframe.denominator = 25; + parms->parm.capture.extendedmode = 0; + parms->parm.capture.readbuffers = 0; + printk(KERN_ERR "%s, %d\n", __func__, __LINE__); + return 0; +} + +static int ov529_vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parms) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + parms->parm.capture.readbuffers = 3; + + return 0; +} + +static long ov529_v4l_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(file); + struct ov529_encoder *ov529 = video_drvdata(file); + int ret = 0; + + /* v4l_printk_ioctl(cmd); */ + + /* Handle some specific cmds */ + switch (cmd) { + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = (struct v4l2_frmsizeenum *)arg; + struct ov529_format *format = NULL; + struct ov529_frame *frame; + int i; + + /* Look for the given pixel format */ + for (i = 0; i < ov529->nformats; i++) { + if (ov529->fmt[i].fcc == + fsize->pixel_format) { + format = &ov529->fmt[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + if (fsize->index >= format->nframes) + return -EINVAL; + + frame = &format->frame[fsize->index]; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = frame->width; + fsize->discrete.height = frame->height; + + return ret; + } + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = (struct v4l2_frmivalenum *)arg; + struct ov529_format *format = NULL; + struct ov529_frame *frame = NULL; + int i; + + for (i = 0; i < ov529->nformats; i++) { + if (ov529->fmt[i].fcc == fival->pixel_format) { + format = &ov529->fmt[i]; + break; + } + } + if (format == NULL) + return -EINVAL; + + for (i = 0; i < format->nframes; i++) { + if (format->frame[i].width == fival->width && + format->frame[i].height == fival->height) { + frame = &format->frame[i]; + break; + } + } + if (frame == NULL) + return -EINVAL; + + /* FIXME: + * Hard code, currently, CCIC outputs around 19.5MHz clock, + * which results in 25 fps. + */ + if (fival->index >= frame->interval_type) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = frame->frameinterval; + fival->discrete.denominator = 25; + + return 0; + } + default: + break; + } + + /* Handle other ioctl cmds with standard interface */ + ret = video_ioctl2(file, cmd, arg); + + return ret; +} + +static int ov529_v4l_open(struct file *filp) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + int ret = 0; + + if (ov529 == NULL) + return -EINVAL; + + mutex_lock(&ov529->s_mutex); + /* only support one ov529 now */ + ov529->curr_devid = 0; + /* send clock, power on reset */ + ret = ov529->ops.startup(); + ov529->state = S_IDLE; + mutex_unlock(&ov529->s_mutex); + return ret; +} + +static int ov529_v4l_release(struct file *filp) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + mutex_lock(&ov529->s_mutex); + ov529_free_buf(ov529); + ov529->ops.remove(); + mutex_unlock(&ov529->s_mutex); + + return 0; +} + +/* FIXME: Use mmap. + * read is not supported currently. + */ +static ssize_t ov529_v4l_read(struct file *filp, + char __user *buffer, size_t len, loff_t *pos) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + mutex_lock(&ov529->s_mutex); + + mutex_unlock(&ov529->s_mutex); + return 0; +} + +static unsigned int ov529_v4l_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + + poll_wait(filp, &ov529->iowait, pt); + if (!list_empty(&ov529->buf_full)) + return POLLIN | POLLRDNORM;; + + return 0; +} + +static void ov529_vm_open(struct vm_area_struct *vma) +{ + struct ov529_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count++; +} + +static void ov529_vm_close(struct vm_area_struct *vma) +{ + struct ov529_buffer *buffer = vma->vm_private_data; + buffer->vma_use_count--; +} + +static struct vm_operations_struct ov529_vm_ops = { + .open = ov529_vm_open, + .close = ov529_vm_close +}; + +static int ov529_v4l_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct ov529_encoder *ov529 = video_drvdata(filp); + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct ov529_buffer *vbuf = NULL; + int ret = 0; + int i; + + mutex_lock(&ov529->s_mutex); + + for (i = 0; i < ov529->nbufs; i++) { + if (ov529->buf[i].v4lbuf.m.offset == offset) { + vbuf = ov529->buf + i; + break; + } + } + + if (vbuf == NULL) { + ret = -EINVAL; + goto out; + } + + ret = remap_vmalloc_range(vma, vbuf->buffer, 0); + if (ret) + goto out; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = vbuf; + vma->vm_ops = &ov529_vm_ops; + vbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + + ov529_vm_open(vma); +out: + mutex_unlock(&ov529->s_mutex); + + return 0; +} + +static const struct v4l2_file_operations ov529_v4l_fops = { + .owner = THIS_MODULE, + .open = ov529_v4l_open, + .release = ov529_v4l_release, + .read = ov529_v4l_read, + .poll = ov529_v4l_poll, + .mmap = ov529_v4l_mmap, + .ioctl = ov529_v4l_ioctl, +}; + +struct v4l2_ioctl_ops ov529_ioctl_ops = { + .vidioc_querycap = ov529_vidioc_querycap, /* done */ + .vidioc_enum_fmt_vid_cap = ov529_vidioc_enum_fmt_cap, /* done */ + .vidioc_try_fmt_vid_cap = ov529_vidioc_try_fmt_cap, /* half done */ + .vidioc_s_fmt_vid_cap = ov529_vidioc_s_fmt_cap, /* half done */ + .vidioc_g_fmt_vid_cap = ov529_vidioc_g_fmt_cap, /* done */ + .vidioc_enum_input = ov529_vidioc_enum_input, /* done */ + .vidioc_g_input = ov529_vidioc_g_input, /* done */ + .vidioc_s_input = ov529_vidioc_s_input, /* done */ + .vidioc_reqbufs = ov529_vidioc_reqbufs, /* not */ + .vidioc_querybuf = ov529_vidioc_querybuf, + .vidioc_qbuf = ov529_vidioc_qbuf, + .vidioc_dqbuf = ov529_vidioc_dqbuf, + + .vidioc_streamon = ov529_vidioc_streamon, + .vidioc_streamoff = ov529_vidioc_streamoff, +/* + .vidioc_queryctrl = ov529_vidioc_queryctrl, + .vidioc_g_ctrl = ov529_vidioc_g_ctrl, + .vidioc_s_ctrl = ov529_vidioc_s_ctrl, +*/ + .vidioc_g_parm = ov529_vidioc_g_parm, + .vidioc_s_parm = ov529_vidioc_s_parm, +/* + .vidioc_cropcap = ov529_vidioc_cropcap, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = ov529_vidioc_g_register, + .vidioc_s_register = ov529_vidioc_s_register, +#endif +*/ +}; + +static int ov529_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + struct video_device *vdev; + struct ov529_encoder *ov529; + struct ov529_format *fmt; + + /* Initialize ov529_encoder data structure */ + if ((ov529 = kzalloc(sizeof(struct ov529_encoder), GFP_KERNEL)) == NULL) + return -ENOMEM; + ov529->ops = ov529_smc_ops; + strncpy(ov529->name, pdev->name, sizeof vdev->name); + mutex_init(&ov529->s_mutex); + spin_lock_init(&ov529->list_lock); + + /* find platform resource */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk("no IO memory resource defined\n"); + return -ENODEV; + } + + /* init the ov529 hw interface */ + ret = ov529->ops.init(pdev, res); + if (ret < 0) { + printk(KERN_ERR "%s, %d: err=%d\n", __func__, __LINE__, ret); + return ret; + } + + /* register video_device */ + vdev = video_device_alloc(); + if (vdev == NULL) + return -ENOMEM; + + vdev->parent = &pdev->dev; + vdev->minor = -1; + vdev->fops = &ov529_v4l_fops; + vdev->release = video_device_release; + vdev->ioctl_ops = &ov529_ioctl_ops; + strncpy(vdev->name, pdev->name, sizeof vdev->name); + + /* keep ov529_encoder in video_device */ + video_set_drvdata(vdev, ov529); + + if (video_register_device(vdev, VFL_TYPE_GRABBER, -1) < 0) { + /* dev->video.vdev = NULL; */ + ov529->ops.remove(); + video_device_release(vdev); + kfree(ov529); + return -EINVAL; + } + ov529->v4ldev = vdev; + + /* construct ov529 output format structure. + * now, only mjpeg will be used. + */ + fmt = kzalloc(sizeof(struct ov529_format), GFP_KERNEL); + if (fmt == NULL) { + ov529->ops.remove(); + video_unregister_device(vdev); + kfree(ov529); + return -ENOMEM; + } + + fmt->type = FMT_MJPEG; + fmt->index = 0; + fmt->bpp = 0; + strncpy(fmt->name, "MJPEG", sizeof fmt->name); + fmt->fcc = V4L2_PIX_FMT_JPEG; + fmt->flags = V4L2_FMT_FLAG_COMPRESSED; + + fmt->nframes = (sizeof(ov529_frames) / sizeof((ov529_frames)[0])); + fmt->frame = &ov529_frames[0]; + fmt->curr_frame = fmt->frame; + + ov529->nformats = 1; + ov529->fmt = ov529->curr_fmt = fmt; + + /* Initialize work queue for ov529 data transfer */ + workqueue = create_singlethread_workqueue("kov529d"); + if (!workqueue) + return -ENOMEM; + INIT_DELAYED_WORK(&ov529->iotransfer, ov529_iotransfer); + init_waitqueue_head(&ov529->iowait); + + /* Initialize lists for queueing buffer */ + INIT_LIST_HEAD(&ov529->buf_avail); + INIT_LIST_HEAD(&ov529->buf_full); + + return 0; +} + + +static int ov529_remove(struct platform_device *pdev) +{ + /* video_unregister_device(&cam->v4ldev); */ + destroy_workqueue(workqueue); + /* kfree(ov529); */ + /* ov529->ops.remove(); */ + return 0; +} + +#ifdef CONFIG_PM +/* + * Basic power management. + * TODO: Currently not implemented. + */ +static int ov529_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int ov529_resume(struct platform_device *dev) +{ + return 0; +} + +#endif /* CONFIG_PM */ + +static struct platform_driver ov529_driver = { + .driver = { + .name = "pxa168-ov529" + }, + .probe = ov529_probe, + .remove = ov529_remove, +#ifdef CONFIG_PM + .suspend = ov529_suspend, + .resume = ov529_resume, +#endif + +}; + +static int __devinit ov529_init(void) +{ + /* set current and last image size to 0 */ + current_imagesize = last_imagesize = 0; + +#ifdef CONFIG_DVFM + dvfm_register("OV529", &dvfm_dev_idx); +#endif + return platform_driver_register(&ov529_driver); +} + +static void __exit ov529_exit(void) +{ + platform_driver_unregister(&ov529_driver); +#ifdef CONFIG_DVFM + dvfm_unregister("OV529", &dvfm_dev_idx); +#endif +} + +module_init(ov529_init); +module_exit(ov529_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/video/ov529_drv.h b/drivers/media/video/ov529_drv.h new file mode 100644 index 00000000000000..8c6557d72088ca --- /dev/null +++ b/drivers/media/video/ov529_drv.h @@ -0,0 +1,88 @@ +/* + * Provide definitions for the ov529 camera controler driver. + * + * Copyright 2009 Marvell International Ltd. + * Weili Xia + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 + +enum ov529_fmt_type { + FMT_UNDEFINED, + FMT_MJPEG, +}; + +enum ov529_state { + S_NOTREADY, /* Not yet initialized */ + S_IDLE, /* Just hanging around */ + S_FLAKED, /* Some sort of problem */ + S_SINGLEREAD, /* In read() */ + S_SPECREAD, /* Speculative read (for future read()) */ + S_STREAMING /* Streaming data */ +}; + +struct ov529_frame { + char index; + int width; + int height; + char interval_type; + unsigned int frameinterval; +}; + + +struct ov529_format { + char type; + char index; + char bpp; + char colorspace; + unsigned int fcc; + unsigned int flags; + + char name[32]; + + unsigned int nframes; + struct ov529_frame *frame; + struct ov529_frame *curr_frame; +}; + +struct ov529_buffer { + struct list_head list; + struct v4l2_buffer v4lbuf; + char *buffer; /* Where it lives in kernel space */ + struct ov529_encoder *owner; + struct vm_area_struct *svma; + unsigned long vma_use_count; +}; + +struct ov529_encoder { + enum ov529_state state; + char name[32]; + + struct ov529_hw_ops ops; + + struct platform_device *pdev; + struct video_device *v4ldev; + unsigned int curr_devid; + + unsigned int nformats; + struct ov529_format *fmt; + struct ov529_format *curr_fmt; + + unsigned int nbufs; /* How many are alloc'd */ + int next_buf; /* Next to consume (dev_lock) */ + struct ov529_buffer *buf; /* Only one buffer avail */ + struct list_head buf_avail; /* Available for data (we own) (dev_lock) */ + struct list_head buf_full; /* With data (user space owns) (dev_lock) */ + + struct delayed_work iotransfer; + wait_queue_head_t iowait; + + struct mutex s_mutex; /* Access to this structure */ + spinlock_t list_lock; /* Access to device */ +}; diff --git a/drivers/media/video/ov529_hw_ops.h b/drivers/media/video/ov529_hw_ops.h new file mode 100644 index 00000000000000..2c64c931b74e7c --- /dev/null +++ b/drivers/media/video/ov529_hw_ops.h @@ -0,0 +1,73 @@ +/* + * Provide definitions for SMC interface operations of the ov529 + * camera controler driver. + * + * Copyright 2009 Marvell International Ltd. + * Weili Xia + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#ifndef __OV529_HW_INTF_H__ +#define __OV529_HW_INTF_H__ + +#include + +/* #define SMC_USE_DMA */ +#undef SMC_USE_DMA + +enum ov529_cmd_id { + INVALID, + INITIAL, + SET_REG = 3, + GET_PIC, + SNAPSHOT, + SAVEDATA, + RESET = 8, + PWR_OFF, + DATA, + GET_REG, + DOWNLOAD_PROG, + SYNC, + ACK, + NACK, + SEL_IMG_QUALITY, + SET_LIGHT_COND, + DIG_ZOOM, + SET_LIGHT_FREQ +}; + +enum ov529_pic_type { + SNAP_PIC = 1, + PREV_PIC, + SERIAL_FLASH_PIC, + COMPRESSION_PREV_PIC = 5, + PLAYBACK_PIC, +}; + +struct ov529_cmd { + char ff1; + char ff2; + char ff3; + char id; + char p1; + char p2; + char p3; + char p4; +}; + +struct ov529_hw_ops { + char name[32]; + int (*init)(struct platform_device *pdev, struct resource *res); + int (*remove)(void); + int (*startup)(void); + int (*data_ready)(void); + int (*streamon)(void); + int (*recv_data)(char *buf, int size); + int (*send_cmd)(int id, int p1, int p2, int p3, int p4); +}; + +extern struct ov529_hw_ops ov529_smc_ops; + +#endif diff --git a/drivers/media/video/ov529_smc.c b/drivers/media/video/ov529_smc.c new file mode 100644 index 00000000000000..ddbdbdf812041c --- /dev/null +++ b/drivers/media/video/ov529_smc.c @@ -0,0 +1,316 @@ +/* + * linux/drivers/media/video/ov529_smc.c - ov529 SMC interface driver. + * + * Copyright: (C) Copyright 2009 Marvell International Ltd. + * Weili Xia + * + * An abstraction layer which describes how SMC interface controls ov529. + * + * Written by Weili Xia, wlxia@marvell.com. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ov529_hw_ops.h" +#include "pxa168_camera.h" + +static struct ov529_platform_data *ov529_platform_ops; +static unsigned char __iomem *smc_addr; +static unsigned long smc_phy_addr; +struct clk *smc_clk; + +#ifdef SMC_USE_DMA +static int smc_dma; +static void *rxdma_addr; +static dma_addr_t rxdma_addr_phys; +static wait_queue_head_t dma_wait; +static int dma_finish_flags; + +/* smc dma operation */ +static void pxa_smc_transmit_dma_start(int count) +{ + if (!(DCSR(smc_dma) & DCSR_STOPSTATE)) + return; + + DALGN |= 1 << smc_dma; + + DCSR(smc_dma) = DCSR_NODESC; + DSADR(smc_dma) = smc_phy_addr; + DTADR(smc_dma) = rxdma_addr_phys; + DCMD(smc_dma) = DCMD_INCTRGADDR | DCMD_ENDIRQEN | count; + DCSR(smc_dma) |= DCSR_RUN; +} + +static void pxa_smc_dma_irq(int channel, void *data) +{ + volatile unsigned long dcsr; + + DCSR(channel) &= ~DCSR_RUN; + dcsr = DCSR(channel); + /* printk(KERN_ERR "%s, %d", __func__, __LINE__); */ + if (dcsr & DCSR_BUSERR) { + DCSR(channel) |= DCSR_BUSERR; + printk(KERN_ERR "%s(): DMA channel bus error\n", __func__); + dma_finish_flags = 2; + } + + if ((dcsr & DCSR_ENDINTR) || (dcsr & DCSR_STOPSTATE)) { + if (dcsr & DCSR_ENDINTR) + DCSR(channel) |= DCSR_ENDINTR; + if (dcsr & DCSR_STOPSTATE) + DCSR(channel) &= ~DCSR_STOPSTATE; + dma_finish_flags = 1; + } + /* printk(KERN_ERR "wake up dma_wait\n"); */ + wake_up(&dma_wait); + + return; +} +#endif + +static void ov529_prepare_cmd(struct ov529_cmd *cmd) +{ + if (cmd) { + memset(cmd, 0x0, sizeof(*cmd)); + cmd->ff1 = cmd->ff2 = cmd->ff3 = 0xff; + } +} + +static int ov529_smc_send_cmd(int id, int p1, int p2, int p3, int p4) +{ + struct ov529_cmd cmd, resp; + int i, j; + + ov529_prepare_cmd(&cmd); + cmd.id = id; + cmd.p1 = p1; + cmd.p2 = p2; + cmd.p3 = p3; + cmd.p4 = p4; + memset(&resp, 0x0, sizeof(resp)); + + for (i = 0; i < 8; i++) { + *smc_addr = *((unsigned char *)&cmd + i); + } + msleep(3); + for (i = 0; i < 8; i++) { + *((unsigned char *)&resp + i) = *smc_addr; + } + + printk(KERN_DEBUG "%s, %d: resp.id=%d, resp.p1=%d\n", __func__, __LINE__, resp.id, resp.p1); + if ((resp.id == ACK) && (resp.p1 == cmd.id)) { + printk(KERN_ERR "cmd 0x%X ack\n", resp.p1); + return 0; + } else { + if (resp.id == NACK) { + printk(KERN_ERR "NAK counter: %d\n", resp.p2); + printk(KERN_ERR "Error number: 0x%X\n", resp.p3); + } + return -EINVAL; + } +} + +static int ov529_smc_data_ready(void) +{ + return ov529_platform_ops->get_sel_uart(); +} + +static int ov529_smc_streamon(void) +{ + int ret = 0; + /* + * Send cmd GET PICTURE to ov529. + */ + /* ov529_platform_ops->turnon_sensor(); */ + + ov529_platform_ops->set_pcd(PCD_CMD); + ret = ov529_smc_send_cmd(GET_PIC, COMPRESSION_PREV_PIC, 0, 0, 0); + ov529_platform_ops->set_pcd(PCD_DATA); + msleep(1000); + return ret; +} + +static int ov529_smc_recv_img(char *buf, int size) +{ + int i; + int ret = 0; +#ifdef SMC_USE_DMA + char *dma_buf = (char *)rxdma_addr; +#endif + + ov529_platform_ops->set_rts(0); + +#ifndef SMC_USE_DMA + for (i = 0; i < size; i++) { + *(buf + i) = *smc_addr; + } +#else + //printk(KERN_ERR "%s, %d\n", __func__, __LINE__); + /* kick off dma transfer */ + pxa_smc_transmit_dma_start(size); + /* wait on dma finish */ + dma_finish_flags = 0; + if (wait_event_interruptible(dma_wait, dma_finish_flags)) { + ret = -ERESTARTSYS; + goto out; + } + memcpy(buf, dma_buf, size); + printk(KERN_ERR "0x%X, 0x%X, 0x%X, 0x%X\n", buf[0], buf[1], buf[2], buf[3]); + printk(KERN_ERR "0x%X, 0x%X, 0x%X, 0x%X\n", buf[8], buf[9], buf[10], buf[11]); + if (dma_finish_flags != 1) + ret = -EINVAL; +#endif +out: + ov529_platform_ops->set_rts(1); + return ret; +} + +static int ov529_smc_startup(void) +{ + int ret = 0; + + /* switch the clock freq of smc from 31.2 MHz to 62.4 MHz */ + clk_enable(smc_clk); + + ov529_platform_ops->set_ptype(1); + msleep(10); + + /* FIXME: is this necessary */ + ccic_set_clock_parallel(); + ccic_enable_vclk(); + + /* After power on reset, it will take ov529 more than + * one second to finish initialization*/ + /* ov529_platform_ops->turnon_sensor(); */ + msleep(10); + ov529_platform_ops->power_on(1); + msleep(1500); + ov529_platform_ops->set_pcd(PCD_CMD); + + /* send SYNC and INITIAL commands to ov529 */ + ret = ov529_smc_send_cmd(SYNC, 0, 0, 0, 0); + if (ret < 0) { + printk(KERN_ERR "%s, %d\n", __func__, __LINE__); + goto err; + } + msleep(100); + + ret = ov529_smc_send_cmd(INITIAL, 0, 0x87, 0, 0x07); + +err: + return ret; +} + +static int ov529_smc_init(struct platform_device *pdev, struct resource *res) +{ + struct ov529_platform_data *data = pdev->dev.platform_data; + + if (data == NULL) + return -EINVAL; + + ov529_platform_ops = data; + printk(KERN_ERR "ov529 intf's name: %s\n", ov529_platform_ops->name); + if (ov529_platform_ops->init() < 0) + return -EIO; + + ov529_platform_ops->power_on(1); + msleep(10); + /* Put ov529 to reset */ + ov529_platform_ops->power_on(0); + /* msleep(300); */ + + /* record the physical address for dma */ + smc_phy_addr = res->start; + smc_addr = ioremap_nocache(res->start, 0x1); + if (!smc_addr) { + printk(KERN_ERR "Unable to ioremap ov529 io mem\n"); + ov529_platform_ops->release(); + return -EINVAL; + } + + /* Tune smc freq to 62.4 MHz */ + smc_clk = clk_get(&pdev->dev, "SMCCLK"); + if (IS_ERR(smc_clk)) { + dev_err(&pdev->dev, "unable to get SMCCLK"); + return PTR_ERR(smc_clk); + } + +#ifdef SMC_USE_DMA + init_waitqueue_head(&dma_wait); + + smc_dma = pxa_request_dma("pxa-smc", DMA_PRIO_HIGH, pxa_smc_dma_irq, NULL); + if (smc_dma < 0) { + printk(KERN_ERR "error requesting smc dma\n"); + return -EBUSY; + } + printk(KERN_ERR "smc dma channel %d\n", smc_dma); + + rxdma_addr = dma_alloc_coherent(NULL, 4096, &rxdma_addr_phys, GFP_KERNEL); + if (!rxdma_addr) { + printk(KERN_ERR "dma_alloc_coherent failed\n"); + pxa_free_dma(smc_dma); + return -EINVAL; + } +#endif + + return 0; +} + +static int ov529_smc_remove(void) +{ + /* power off ov529 */ + ov529_platform_ops->power_on(0); + + /* unmap remapped address */ + /* iounmap((void *)smc_addr); */ + /* release gpio */ + /* ov529_platform_ops->release(); */ + + return 0; +} + +/* + * SMC specific functions for + * ov529 hardware level operations. + */ +struct ov529_hw_ops ov529_smc_ops = { + .name = "smc", + .init = ov529_smc_init, + .remove = ov529_smc_remove, + .startup = ov529_smc_startup, + .data_ready = ov529_smc_data_ready, + .streamon = ov529_smc_streamon, + .recv_data = ov529_smc_recv_img, + .send_cmd = ov529_smc_send_cmd, +}; + + + + + + diff --git a/drivers/media/video/ov7660.c b/drivers/media/video/ov7660.c new file mode 100644 index 00000000000000..dbac58ab4a7777 --- /dev/null +++ b/drivers/media/video/ov7660.c @@ -0,0 +1,1491 @@ +/* + * A V4L2 driver for OmniVision OV7660 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "pxa168_camera.h" + +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov7660 sensors"); +MODULE_LICENSE("GPL"); + + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * Our nominal (default) frame rate. + */ +#define OV7660_FRAME_RATE 30 + +/* + * The 7660 sits on i2c with ID 0x42 + */ +#define OV7660_I2C_ADDR 0x42 + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clocl control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x00 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + +/* + * Information we maintain about a known sensor. + */ +struct ov7660_format_struct; /* coming later */ +struct ov7660_info { + struct ov7660_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov7660_default_regs[] = { + { REG_COM7, COM7_RESET }, + /* + * Clock scale: 3 = 15fps + * 2 = 20fps + * 1 = 30fps + */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ + { REG_TSLB, 0x04 }, /* OV */ + { REG_COM7, 0 }, /* VGA */ + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, + { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, + { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, + + { REG_COM3, 0 }, { REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, { REG_COM10, 0x0 }, + + /* Gamma curve values */ + { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, + { REG_GAIN, 0 }, { REG_AECH, 0 }, + { REG_COM4, 0x40 }, /* magic reserved bit */ + { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, + { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, + { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, + { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ + { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, + { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, + { REG_HAECC7, 0x94 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, + { REG_RED, 0x60 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + + +/* + * Here we'll try to encapsulate the changes for just the output + * video format. + * + * RGB656 and YUV422 come from OV; RGB444 is homebrewed. + * + * IMPORTANT RULE: the first entry must be for COM7, see ov7660_s_fmt for why. + */ + + +static struct regval_list ov7660_fmt_yuv422[] = { +#if 0 + { REG_COM7, 0x0 }, /* Selects YUV mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0 }, + { REG_COM15, COM15_R00FF }, + { REG_COM9, 0x18 }, /* 4x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +#endif + 0x12, 0x80, + 0x11, 0x80, + 0x92, 0x48, + 0x93, 0x01, + 0x9d, 0x62, + 0x9e, 0x52, + 0x3b, 0x02, + 0x13, 0xf2, + 0x10, 0x00, + 0x00, 0x00, + 0x01, 0x80, + 0x02, 0x80, + 0x13, 0xf7, + 0x12, 0x08, + 0x04, 0x04, + 0x18, 0x80, + 0x17, 0x28, + 0x32, 0x92, + 0x19, 0x02, + 0x1a, 0x4a, + 0x03, 0x00, + 0x0e, 0x84, + 0x0f, 0x62, + 0x15, 0x02, + 0x16, 0x02, + 0x1b, 0x01, + 0x1e, 0x01, + 0x29, 0x3c, + 0x33, 0x02, + 0x34, 0x00, + 0x35, 0x84, + 0x36, 0x03, + 0x38, 0x13, + 0x39, 0x43, + 0x3a, 0x0c, /* output format - UYVY */ + 0x3c, 0x6c, + 0x3d, 0x90, + 0x3f, 0x29, + 0x40, 0xc1, + 0x41, 0x20, + 0x6b, 0x0f, + 0xa1, 0xc8, + 0x69, 0x80, + 0x43, 0xf0, + 0x44, 0x10, + 0x45, 0x78, + 0x46, 0xa8, + 0x47, 0x60, + 0x48, 0x80, + 0x59, 0xba, + 0x5a, 0x9a, + 0x5b, 0x22, + 0x5c, 0xb9, + 0x5d, 0x9b, + 0x5e, 0x10, + 0x5f, 0xe0, + 0x60, 0x85, + 0x61, 0x60, + 0x9f, 0x9d, + 0xa0, 0xa0, + 0x4f, 0x66, + 0x50, 0x6b, + 0x51, 0x05, + 0x52, 0x19, + 0x53, 0x40, + 0x54, 0x59, + 0x55, 0x40, + 0x56, 0x40, + 0x57, 0x40, + 0x58, 0x0d, + 0x8b, 0xcc, + 0x8c, 0xcc, + 0x8d, 0xcf, + 0x6c, 0x40, + 0x6d, 0x30, + 0x6e, 0x4b, + 0x6f, 0x60, + 0x70, 0x70, + 0x71, 0x70, + 0x72, 0x70, + 0x73, 0x70, + 0x74, 0x60, + 0x75, 0x60, + 0x76, 0x50, + 0x77, 0x48, + 0x78, 0x3a, + 0x79, 0x2e, + 0x7a, 0x28, + 0x7b, 0x22, + 0x7c, 0x04, + 0x7d, 0x07, + 0x7e, 0x10, + 0x7f, 0x28, + 0x80, 0x36, + 0x81, 0x44, + 0x82, 0x52, + 0x83, 0x60, + 0x84, 0x6c, + 0x85, 0x78, + 0x86, 0x8c, + 0x87, 0x9e, + 0x88, 0xbb, + 0x89, 0xd2, + 0x8a, 0xe6, + 0x14, 0x2e, + 0x24, 0x68, + 0x25, 0x58, + 0xff, 0xff, +}; + +static struct regval_list ov7660_fmt_rgb565[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0x0 }, + { REG_COM15, COM15_RGB565 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7660_fmt_rgb444[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ + { REG_COM1, 0x40 }, /* Magic reserved bit */ + { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ + { 0xff, 0xff }, +}; + +static struct regval_list ov7660_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; + +/* + * Low-level register I/O. + */ + +static int ov7660_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int ret; + + i2c_smbus_write_byte(c, reg); + *value = i2c_smbus_read_byte(c); + return 0; + + ret = i2c_smbus_read_byte_data(c, reg); + if (ret >= 0) + *value = (unsigned char) ret; + return ret; +} + + +static int ov7660_write(struct i2c_client *c, unsigned char reg, + unsigned char value) +{ + int ret = i2c_smbus_write_byte_data(c, reg, value); + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(2); /* Wait for reset to run */ + return ret; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7660_write_array(struct i2c_client *c, struct regval_list *vals) +{ + int i = 0; + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7660_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + if (i == 0) + mdelay(5); + i++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static void ov7660_reset(struct i2c_client *client) +{ + ov7660_write(client, REG_COM7, COM7_RESET); + msleep(1); +} + + +static int ov7660_init(struct i2c_client *client) +{ + return ov7660_write_array(client, ov7660_default_regs); +} + + + +static int ov7660_detect(struct i2c_client *client) +{ + unsigned char v; + int ret; + + //ret = ov7660_init(client); + //if (ret < 0) + // return ret; + ret = ov7660_read(client, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7660_read(client, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7660_read(client, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + ret = ov7660_read(client, REG_VER, &v); + if (ret < 0) + return ret; + if (v != 0x60) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + */ +static struct ov7660_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; + int bpp; /* bits per pixel */ +} ov7660_formats[] = { + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = ov7660_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .regs = ov7660_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "YUYV 4:2:0", + .pixelformat = V4L2_PIX_FMT_YUV420, + .regs = ov7660_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 12, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = ov7660_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = ov7660_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "Raw RGB Bayer", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .regs = ov7660_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + .bpp = 8, + }, +}; +#define N_OV7660_FMTS ARRAY_SIZE(ov7660_formats) + + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov7660_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; + +static struct ov7660_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ + /* h/vref stuff */ +} ov7660_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 164, /* Empirically determined */ + .hstop = 20, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = ov7660_qcif_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov7660_win_sizes)) + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7660_set_hw(struct i2c_client *client, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; + /* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7660_write(client, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7660_write(client, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7660_read(client, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov7660_write(client, REG_HREF, v); + /* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov7660_write(client, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7660_write(client, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7660_read(client, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov7660_write(client, REG_VREF, v); + return ret; +} + + +static int ov7660_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct ov7660_format_struct *ofmt; + + if (fmt->index >= N_OV7660_FMTS) + return -EINVAL; + + ofmt = ov7660_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + + +static int ov7660_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct ov7660_format_struct **ret_fmt, + struct ov7660_win_size **ret_wsize) +{ + int index; + struct ov7660_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + for (index = 0; index < N_OV7660_FMTS; index++) + if (ov7660_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_OV7660_FMTS) + return -EINVAL; + if (ret_fmt != NULL) + *ret_fmt = ov7660_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov7660_win_sizes; wsize < ov7660_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width >= wsize->width && pix->height >= wsize->height) + break; + if (wsize >= ov7660_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + pix->width = wsize->width; + pix->height = wsize->height; + pix->bytesperline = pix->width*ov7660_formats[index].bpp/8; + pix->sizeimage = pix->height*pix->bytesperline; + return 0; +} + +/* + * Set a format. + */ +static int ov7660_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct ov7660_format_struct *ovfmt; + struct ov7660_win_size *wsize; + + ret = ov7660_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + ov7660_write_array(c, ovfmt->regs); + + return ret; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov7660_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + unsigned char clkrc; + int ret; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = ov7660_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = OV7660_FRAME_RATE; + if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + return 0; +} + +static int ov7660_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + unsigned char clkrc; + int ret, div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + /* + * CLKRC has a reserved bit, so let's preserve it. + */ + ret = ov7660_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator*OV7660_FRAME_RATE)/tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + clkrc = (clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = OV7660_FRAME_RATE/div; + return ov7660_write(c, REG_CLKRC, clkrc); +} + +static int ov7660_s_input(struct i2c_client *c, int *id) +{ + return 0; +} + +/* + * Code for dealing with controls. + */ + + + + + +static int ov7660_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7660_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7660_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += ov7660_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7660_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7660_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7660_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7660_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7660_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7660_sine(theta); +} + + + + +static void ov7660_calc_cmatrix(struct ov7660_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7660_sine(info->hue); + costh = ov7660_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov7660_t_sat(struct i2c_client *client, int value) +{ + struct ov7660_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7660_calc_cmatrix(info, matrix); + ret = ov7660_store_cmatrix(client, matrix); + return ret; +} + +static int ov7660_q_sat(struct i2c_client *client, __s32 *value) +{ + struct ov7660_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int ov7660_t_hue(struct i2c_client *client, int value) +{ + struct ov7660_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7660_calc_cmatrix(info, matrix); + ret = ov7660_store_cmatrix(client, matrix); + return ret; +} + + +static int ov7660_q_hue(struct i2c_client *client, __s32 *value) +{ + struct ov7660_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov7660_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + else + return 128 - (v & 0x7f); +} + + +static unsigned char ov7660_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + else + return (128 - v) | 0x80; +} + +static int ov7660_t_brightness(struct i2c_client *client, int value) +{ + unsigned char com8, v; + int ret; + + ov7660_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7660_write(client, REG_COM8, com8); + v = ov7660_abs_to_sm(value); + ret = ov7660_write(client, REG_BRIGHT, v); + return ret; +} + +static int ov7660_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7660_read(client, REG_BRIGHT, &v); + + *value = ov7660_sm_to_abs(v); + return ret; +} + +static int ov7660_t_contrast(struct i2c_client *client, int value) +{ + return ov7660_write(client, REG_CONTRAS, (unsigned char) value); +} + +static int ov7660_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7660_read(client, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov7660_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7660_read(client, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov7660_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7660_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov7660_write(client, REG_MVFP, v); + return ret; +} + + + +static int ov7660_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7660_read(client, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov7660_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7660_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov7660_write(client, REG_MVFP, v); + return ret; +} + + +static struct ov7660_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} ov7660_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7660_t_brightness, + .query = ov7660_q_brightness, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX ov7660 spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7660_t_contrast, + .query = ov7660_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7660_t_sat, + .query = ov7660_q_sat, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7660_t_hue, + .query = ov7660_q_hue, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7660_t_vflip, + .query = ov7660_q_vflip, + }, + { + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7660_t_hflip, + .query = ov7660_q_hflip, + }, +}; +#define N_CONTROLS (ARRAY_SIZE(ov7660_controls)) + +static struct ov7660_control *ov7660_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (ov7660_controls[i].qc.id == id) + return ov7660_controls + i; + return NULL; +} + + +static int ov7660_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct ov7660_control *ctrl = ov7660_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int ov7660_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7660_control *octrl = ov7660_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +static int ov7660_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7660_control *octrl = ov7660_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + + + + +int ccic_sensor_attach(struct i2c_client *client); + +/* + * Basic i2c stuff. + */ +extern struct clk *pxa168_ccic_gate_clk; +static int __devinit ov7660_probe(struct i2c_client *client) +{ + int ret; + struct ov7660_info *info; + struct sensor_platform_data *pdata; + pdata = client->dev.platform_data; + + /* + * Set up our info structure. + */ + + clk_enable(pxa168_ccic_gate_clk); + ccic_set_clock_parallel(); //clock must be enabled before power on. + + pdata->power_on(1, 0); + + info = kzalloc(sizeof (struct ov7660_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &ov7660_formats[0]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + + /* + * Make sure it's an ov7660 + */ + ret = ov7660_detect(client); + if (ret) + goto out_free_info; + printk(KERN_NOTICE "OmniVision ov7660 sensor detected\n"); + + ccic_sensor_attach(client); + pdata->power_on(0, 0); //for power optimization + ccic_disable_clock(); + return 0; + +out_free_info: + kfree(info); +out_free: + return ret; +} + + +static int ov7660_remove(struct i2c_client *client) +{ + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7660_g_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return ov7660_read(client, (unsigned char)reg->reg, (unsigned char *)&(reg->val)); +} + +static int ov7660_s_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return ov7660_write(client, (unsigned char)reg->reg, (unsigned char)reg->val); +} +#endif + +static int ov7660_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + switch (cmd) { + case VIDIOC_DBG_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7660, 0); + + case VIDIOC_INT_RESET: + ov7660_reset(client); + return 0; + + case VIDIOC_INT_INIT: + return 0;//ov7660_init(client); + + case VIDIOC_ENUM_FMT: + return ov7660_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + case VIDIOC_TRY_FMT: + return ov7660_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + case VIDIOC_S_FMT: + return ov7660_s_fmt(client, (struct v4l2_format *) arg); + case VIDIOC_QUERYCTRL: + return ov7660_queryctrl(client, (struct v4l2_queryctrl *) arg); + case VIDIOC_S_CTRL: + return ov7660_s_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_G_CTRL: + return ov7660_g_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_S_PARM: + return ov7660_s_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_G_PARM: + return ov7660_g_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_S_INPUT: + return ov7660_s_input(client, (int *) arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return ov7660_g_register(client, (struct v4l2_dbg_register *) arg); + case VIDIOC_DBG_S_REGISTER: + return ov7660_s_register(client, (struct v4l2_dbg_register *) arg); +#endif + } + return -EINVAL; +} + +static struct i2c_device_id ov7660_idtable[] = { + { "ov7660", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ov7660_idtable); + +static struct i2c_driver ov7660_driver = { + .driver = { + .name = "ov7660", + }, + .id_table = ov7660_idtable, + .command = ov7660_command, + .probe = ov7660_probe, + .remove = ov7660_remove, +}; + + +/* + * Module initialization + */ +static int __init ov7660_mod_init(void) +{ + printk(KERN_NOTICE "OmniVision ov7660 sensor driver, at your service\n"); + return i2c_add_driver(&ov7660_driver); +} + +static void __exit ov7660_mod_exit(void) +{ + i2c_del_driver(&ov7660_driver); +} + +late_initcall(ov7660_mod_init); +//module_init(ov7660_mod_init); +module_exit(ov7660_mod_exit); + diff --git a/drivers/media/video/ov7670_pxa.c b/drivers/media/video/ov7670_pxa.c new file mode 100644 index 00000000000000..08b41b6497f22f --- /dev/null +++ b/drivers/media/video/ov7670_pxa.c @@ -0,0 +1,1539 @@ +/* + * A V4L2 driver for OmniVision OV7670 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "pxa168_camera.h" + +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); +MODULE_LICENSE("GPL"); + + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * Our nominal (default) frame rate. + */ +#define OV7670_FRAME_RATE 30 + +/* + * The 7670 sits on i2c with ID 0x42 + */ +#define OV7670_I2C_ADDR 0x42 + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clocl control */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_VGA 0x00 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + +/* + * Information we maintain about a known sensor. + */ +struct ov7670_format_struct; /* coming later */ +struct ov7670_info { + struct ov7670_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + + +/* + * The default register settings, as obtained from OmniVision. There + * is really no making sense of most of these - lots of "reserved" values + * and such. + * + * These settings give VGA YUYV. + */ + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +static struct regval_list ov7670_default_regs[] = { + { REG_COM7, COM7_RESET }, + /* + * Clock scale: 3 = 15fps + * 2 = 20fps + * 1 = 30fps + */ + { REG_CLKRC, 0x1 }, /* OV: clock scale (30 fps) */ + { REG_TSLB, 0x04 }, /* OV */ + { REG_COM7, 0 }, /* VGA */ + /* + * Set the hardware window. These values from OV don't entirely + * make sense - hstop is less than hstart. But they work... + */ + { REG_HSTART, 0x13 }, { REG_HSTOP, 0x01 }, + { REG_HREF, 0xb6 }, { REG_VSTART, 0x02 }, + { REG_VSTOP, 0x7a }, { REG_VREF, 0x0a }, + + { REG_COM3, 0 }, { REG_COM14, 0 }, + /* Mystery scaling numbers */ + { 0x70, 0x3a }, { 0x71, 0x35 }, + { 0x72, 0x11 }, { 0x73, 0xf0 }, + { 0xa2, 0x02 }, { REG_COM10, 0x0 }, + + /* Gamma curve values */ + { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, + + /* AGC and AEC parameters. Note we start by disabling those features, + then turn them only after tweaking the values. */ + { REG_COM8, COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT }, + { REG_GAIN, 0 }, { REG_AECH, 0 }, + { REG_COM4, 0x40 }, /* magic reserved bit */ + { REG_COM9, 0x18 }, /* 4x gain + magic rsvd bit */ + { REG_BD50MAX, 0x05 }, { REG_BD60MAX, 0x07 }, + { REG_AEW, 0x95 }, { REG_AEB, 0x33 }, + { REG_VPT, 0xe3 }, { REG_HAECC1, 0x78 }, + { REG_HAECC2, 0x68 }, { 0xa1, 0x03 }, /* magic */ + { REG_HAECC3, 0xd8 }, { REG_HAECC4, 0xd8 }, + { REG_HAECC5, 0xf0 }, { REG_HAECC6, 0x90 }, + { REG_HAECC7, 0x94 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC }, + + /* Almost all of these are magic "reserved" values. */ + { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, + { 0x21, 0x02 }, { 0x22, 0x91 }, + { 0x29, 0x07 }, { 0x33, 0x0b }, + { 0x35, 0x0b }, { 0x37, 0x1d }, + { 0x38, 0x71 }, { 0x39, 0x2a }, + { REG_COM12, 0x78 }, { 0x4d, 0x40 }, + { 0x4e, 0x20 }, { REG_GFIX, 0 }, + { 0x6b, 0x4a }, { 0x74, 0x10 }, + { 0x8d, 0x4f }, { 0x8e, 0 }, + { 0x8f, 0 }, { 0x90, 0 }, + { 0x91, 0 }, { 0x96, 0 }, + { 0x9a, 0 }, { 0xb0, 0x84 }, + { 0xb1, 0x0c }, { 0xb2, 0x0e }, + { 0xb3, 0x82 }, { 0xb8, 0x0a }, + + /* More reserved magic, some of which tweaks white balance */ + { 0x43, 0x0a }, { 0x44, 0xf0 }, + { 0x45, 0x34 }, { 0x46, 0x58 }, + { 0x47, 0x28 }, { 0x48, 0x3a }, + { 0x59, 0x88 }, { 0x5a, 0x88 }, + { 0x5b, 0x44 }, { 0x5c, 0x67 }, + { 0x5d, 0x49 }, { 0x5e, 0x0e }, + { 0x6c, 0x0a }, { 0x6d, 0x55 }, + { 0x6e, 0x11 }, { 0x6f, 0x9f }, /* "9e for advance AWB" */ + { 0x6a, 0x40 }, { REG_BLUE, 0x40 }, + { REG_RED, 0x60 }, + { REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC|COM8_AWB }, + + /* Matrix coefficients */ + { 0x4f, 0x80 }, { 0x50, 0x80 }, + { 0x51, 0 }, { 0x52, 0x22 }, + { 0x53, 0x5e }, { 0x54, 0x80 }, + { 0x58, 0x9e }, + + { REG_COM16, COM16_AWBGAIN }, { REG_EDGE, 0 }, + { 0x75, 0x05 }, { 0x76, 0xe1 }, + { 0x4c, 0 }, { 0x77, 0x01 }, + { REG_COM13, 0xc3 }, { 0x4b, 0x09 }, + { 0xc9, 0x60 }, { REG_COM16, 0x38 }, + { 0x56, 0x40 }, + + { 0x34, 0x11 }, { REG_COM11, COM11_EXP|COM11_HZAUTO }, + { 0xa4, 0x88 }, { 0x96, 0 }, + { 0x97, 0x30 }, { 0x98, 0x20 }, + { 0x99, 0x30 }, { 0x9a, 0x84 }, + { 0x9b, 0x29 }, { 0x9c, 0x03 }, + { 0x9d, 0x4c }, { 0x9e, 0x3f }, + { 0x78, 0x04 }, + + /* Extra-weird stuff. Some sort of multiplexor register */ + { 0x79, 0x01 }, { 0xc8, 0xf0 }, + { 0x79, 0x0f }, { 0xc8, 0x00 }, + { 0x79, 0x10 }, { 0xc8, 0x7e }, + { 0x79, 0x0a }, { 0xc8, 0x80 }, + { 0x79, 0x0b }, { 0xc8, 0x01 }, + { 0x79, 0x0c }, { 0xc8, 0x0f }, + { 0x79, 0x0d }, { 0xc8, 0x20 }, + { 0x79, 0x09 }, { 0xc8, 0x80 }, + { 0x79, 0x02 }, { 0xc8, 0xc0 }, + { 0x79, 0x03 }, { 0xc8, 0x40 }, + { 0x79, 0x05 }, { 0xc8, 0x30 }, + { 0x79, 0x26 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* + * Here we'll try to encapsulate the changes for just the output + * video format. + * + * RGB656 and YUV422 come from OV; RGB444 is homebrewed. + * + * IMPORTANT RULE: the first entry must be for COM7, see ov7670_s_fmt for why. + */ + +static struct regval_list ov7670_fmt_yuv422[] = { + //OV7673 setting, June 6, 2005 + //YCbCr, VGA + //15fps @ 24MHz input clock + //output format: UYVY(CbY0CrY1) (0x3a[3]=1, 0x3d[0]=1) spec is wrong. + 0x12, 0x80, + 0x11, 0x03, + 0x3a, 0x0c, + 0x12, 0x00, + 0x17, 0x13, + 0x18, 0x01, + 0x32, 0xb6, + 0x19, 0x02, + 0x1a, 0x7a, + 0x03, 0x0a, + 0x0c, 0x00, + 0x3e, 0x00, + 0x70, 0x3a, + 0x71, 0x35, + 0x72, 0x11, + 0x73, 0xf0, + 0xa2, 0x02, + 0x7a, 0x20, + 0x7b, 0x10, + 0x7c, 0x1e, + 0x7d, 0x35, + 0x7e, 0x5a, + 0x7f, 0x69, + 0x80, 0x76, + 0x81, 0x80, + 0x82, 0x88, + 0x83, 0x8f, + 0x84, 0x96, + 0x85, 0xa3, + 0x86, 0xaf, + 0x87, 0xc4, + 0x88, 0xd7, + 0x89, 0xe8, + // + 0x13, 0xe0, + 0x00, 0x00, + 0x10, 0x00, + 0x0d, 0x40, + 0x14, 0x18, + 0xa5, 0x05, + 0xab, 0x07, + 0x24, 0x95, + 0x25, 0x33, + 0x26, 0xe3, + 0x9f, 0x78, + 0xa0, 0x68, + 0xa1, 0x03, + 0xa6, 0xd8, + 0xa7, 0xd8, + 0xa8, 0xf0, + 0xa9, 0x90, + 0xaa, 0x94, + 0x13, 0xe5, + // + 0x0e, 0x61, + 0x0f, 0x4b, + 0x16, 0x02, + 0x1e, 0x07, + 0x21, 0x02, + 0x22, 0x91, + 0x29, 0x07, + 0x33, 0x0b, + 0x35, 0x0b, + 0x37, 0x1d, + 0x38, 0x71, + 0x39, 0x2a, + 0x3c, 0x78, + 0x4d, 0x40, + 0x4e, 0x20, + 0x69, 0x00, + 0x6b, 0x4a, + 0x74, 0x10, + 0x8d, 0x4f, + 0x8e, 0x00, + 0x8f, 0x00, + 0x90, 0x00, + 0x91, 0x00, + 0x96, 0x00, + 0x9a, 0x80, + 0xb0, 0x84, + 0xb1, 0x0c, + 0xb2, 0x0e, + 0xb3, 0x82, + 0xb8, 0x0a, + // + 0x43, 0x0a, + 0x44, 0xf0, + 0x45, 0x34, + 0x46, 0x58, + 0x47, 0x28, + 0x48, 0x3a, + 0x59, 0x88, + 0x5a, 0x88, + 0x5b, 0x44, + 0x5c, 0x67, + 0x5d, 0x49, + 0x5e, 0x0e, + 0x6c, 0x0a, + 0x6d, 0x55, + 0x6e, 0x11, + 0x6f, 0x9f, //9e for advance AWB + 0x6a, 0x40, + 0x01, 0x40, + 0x02, 0x40, + 0x13, 0xe7, + // + 0x4f, 0x80, + 0x50, 0x80, + 0x51, 0x00, + 0x52, 0x22, + 0x53, 0x5e, + 0x54, 0x80, + 0x58, 0x9e, + // + 0x41, 0x08, + 0x3f, 0x00, + 0x75, 0x05, + 0x76, 0xe1, + 0x4c, 0x00, + 0x77, 0x01, + 0x3d, 0xc1, + 0x4b, 0x09, + 0xc9, 0x60, + 0x41, 0x38, + 0x56, 0x40, + // + 0x34, 0x11, + 0x3b, 0x02, + 0xa4, 0x88, + 0x96, 0x00, + 0x97, 0x30, + 0x98, 0x20, + 0x99, 0x30, + 0x9a, 0x84, + 0x9b, 0x29, + 0x9c, 0x03, + 0x9d, 0x4c, + 0x9e, 0x3f, + 0x78, 0x04, + // + 0x79, 0x01, + 0xc8, 0xf0, + 0x79, 0x0f, + 0xc8, 0x00, + 0x79, 0x10, + 0xc8, 0x7e, + 0x79, 0x0a, + 0xc8, 0x80, + 0x79, 0x0b, + 0xc8, 0x01, + 0x79, 0x0c, + 0xc8, 0x0f, + 0x79, 0x0d, + 0xc8, 0x20, + 0x79, 0x09, + 0xc8, 0x80, + 0x79, 0x02, + 0xc8, 0xc0, + 0x79, 0x03, + 0xc8, 0x40, + 0x79, 0x05, + 0xc8, 0x30, + 0x79, 0x26, + // + 0xf1, 0x10, + 0x0f, 0x1d, + 0x0f, 0x1f, + 0xff, 0xff, + +}; + +static struct regval_list ov7670_fmt_rgb565[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, 0 }, /* No RGB444 please */ + { REG_COM1, 0x0 }, + { REG_COM15, COM15_RGB565 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT }, + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_rgb444[] = { + { REG_COM7, COM7_RGB }, /* Selects RGB mode */ + { REG_RGB444, R444_ENABLE }, /* Enable xxxxrrrr ggggbbbb */ + { REG_COM1, 0x40 }, /* Magic reserved bit */ + { REG_COM15, COM15_R01FE|COM15_RGB565 }, /* Data range needed? */ + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA|COM13_UVSAT|0x2 }, /* Magic rsvd bit */ + { 0xff, 0xff }, +}; + +static struct regval_list ov7670_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; + +/* + * Low-level register I/O. + */ + +static int ov7670_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int ret; + + i2c_smbus_write_byte(c, reg); + *value = i2c_smbus_read_byte(c); + return 0; + + ret = i2c_smbus_read_byte_data(c, reg); + if (ret >= 0) + *value = (unsigned char) ret; + return ret; +} + + +static int ov7670_write(struct i2c_client *c, unsigned char reg, + unsigned char value) +{ + int ret = i2c_smbus_write_byte_data(c, reg, value); + if (reg == REG_COM7 && (value & COM7_RESET)) + msleep(2); /* Wait for reset to run */ + return ret; +} + + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) +{ + int i = 0; + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7670_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + if (i == 0) + mdelay(5); + i++; + } + return 0; +} + + +/* + * Stuff that knows about the sensor. + */ +static void ov7670_reset(struct i2c_client *client) +{ + ov7670_write(client, REG_COM7, COM7_RESET); + msleep(1); +} + + +static int ov7670_init(struct i2c_client *client) +{ + return ov7670_write_array(client, ov7670_default_regs); +} + + + +static int ov7670_detect(struct i2c_client *client) +{ + unsigned char v; + int ret; + + //ret = ov7670_init(client); + //if (ret < 0) + // return ret; + ret = ov7670_read(client, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7670_read(client, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7670_read(client, REG_PID, &v); + if (ret < 0) + return ret; + if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + ret = ov7670_read(client, REG_VER, &v); + if (ret < 0) + return ret; + // if (v != 0x60) /* PID + VER = 0x76 / 0x73 */ + if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ + return -ENODEV; + return 0; +} + + +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + */ +static struct ov7670_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; + int cmatrix[CMATRIX_LEN]; + int bpp; /* bits per pixel */ +} ov7670_formats[] = { + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "YUYV 4:2:0", + .pixelformat = V4L2_PIX_FMT_YUV420, + .regs = ov7670_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 12, + // .bpp = 16, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = ov7670_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = ov7670_fmt_rgb565, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "Raw RGB Bayer", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .regs = ov7670_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + .bpp = 8, + }, +}; +#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) + + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list ov7670_qcif_regs[] = { + { REG_COM3, COM3_SCALEEN|COM3_DCWEN }, + { REG_COM3, COM3_DCWEN }, + { REG_COM14, COM14_DCWEN | 0x01}, + { 0x73, 0xf1 }, + { 0xa2, 0x52 }, + { 0x7b, 0x1c }, + { 0x7c, 0x28 }, + { 0x7d, 0x3c }, + { 0x7f, 0x69 }, + { REG_COM9, 0x38 }, + { 0xa1, 0x0b }, + { 0x74, 0x19 }, + { 0x9a, 0x80 }, + { 0x43, 0x14 }, + { REG_COM13, 0xc0 }, + { 0xff, 0xff }, +}; + +static struct ov7670_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ + /* h/vref stuff */ +} ov7670_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .com7_bit = COM7_FMT_VGA, + .hstart = 158, /* These values from */ + .hstop = 14, /* Omnivision */ + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QVGA */ + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .com7_bit = COM7_FMT_QVGA, + .hstart = 164, /* Empirically determined */ + .hstop = 20, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = ov7670_qcif_regs, + }, +}; + +#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) + +/* + * Store a set of start/stop values into the camera. + */ +static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; + /* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(client, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += ov7670_write(client, REG_HREF, v); + /* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(client, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += ov7670_write(client, REG_VREF, v); + return ret; +} + + +static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct ov7670_format_struct *ofmt; + + if (fmt->index >= N_OV7670_FMTS) + return -EINVAL; + + ofmt = ov7670_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + + +static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct ov7670_format_struct **ret_fmt, + struct ov7670_win_size **ret_wsize) +{ + int index; + struct ov7670_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + for (index = 0; index < N_OV7670_FMTS; index++) + if (ov7670_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_OV7670_FMTS) + return -EINVAL; + if (ret_fmt != NULL) + *ret_fmt = ov7670_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ + for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width >= wsize->width && pix->height >= wsize->height) + break; + if (wsize >= ov7670_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + pix->width = wsize->width; + pix->height = wsize->height; + pix->bytesperline = pix->width*ov7670_formats[index].bpp/8; + pix->sizeimage = pix->height*pix->bytesperline; + return 0; +} + +/* + * Set a format. + */ +static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct ov7670_format_struct *ovfmt; + struct ov7670_win_size *wsize; + + ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + ov7670_write_array(c, ovfmt->regs); + + return ret; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + unsigned char clkrc; + int ret; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = ov7670_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = OV7670_FRAME_RATE; + if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + return 0; +} + +static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + unsigned char clkrc; + int ret, div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + /* + * CLKRC has a reserved bit, so let's preserve it. + */ + ret = ov7670_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; + if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + clkrc = (clkrc & 0x80) | div; + tpf->numerator = 1; + tpf->denominator = OV7670_FRAME_RATE/div; + return ov7670_write(c, REG_CLKRC, clkrc); +} + +static int ov7670_s_input(struct i2c_client *c, int *id) +{ + return 0; +} + +/* + * Code for dealing with controls. + */ + + + + + +static int ov7670_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7670_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7670_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7670_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7670_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7670_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7670_sine(theta); +} + + + + +static void ov7670_calc_cmatrix(struct ov7670_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7670_sine(info->hue); + costh = ov7670_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int ov7670_t_sat(struct i2c_client *client, int value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(client, matrix); + return ret; +} + +static int ov7670_q_sat(struct i2c_client *client, __s32 *value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int ov7670_t_hue(struct i2c_client *client, int value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7670_calc_cmatrix(info, matrix); + ret = ov7670_store_cmatrix(client, matrix); + return ret; +} + + +static int ov7670_q_hue(struct i2c_client *client, __s32 *value) +{ + struct ov7670_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char ov7670_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + else + return 128 - (v & 0x7f); +} + + +static unsigned char ov7670_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + else + return (128 - v) | 0x80; +} + +static int ov7670_t_brightness(struct i2c_client *client, int value) +{ + unsigned char com8, v; + int ret; + + ov7670_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + ov7670_write(client, REG_COM8, com8); + v = ov7670_abs_to_sm(value); + ret = ov7670_write(client, REG_BRIGHT, v); + return ret; +} + +static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7670_read(client, REG_BRIGHT, &v); + + *value = ov7670_sm_to_abs(v); + return ret; +} + +static int ov7670_t_contrast(struct i2c_client *client, int value) +{ + return ov7670_write(client, REG_CONTRAS, (unsigned char) value); +} + +static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7670_read(client, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7670_read(client, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int ov7670_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7670_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += ov7670_write(client, REG_MVFP, v); + return ret; +} + + + +static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7670_read(client, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int ov7670_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7670_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += ov7670_write(client, REG_MVFP, v); + return ret; +} + + +static struct ov7670_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} ov7670_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_brightness, + .query = ov7670_q_brightness, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX ov7670 spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_contrast, + .query = ov7670_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_sat, + .query = ov7670_q_sat, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7670_t_hue, + .query = ov7670_q_hue, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7670_t_vflip, + .query = ov7670_q_vflip, + }, + { + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7670_t_hflip, + .query = ov7670_q_hflip, + }, +}; +#define N_CONTROLS (ARRAY_SIZE(ov7670_controls)) + +static struct ov7670_control *ov7670_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (ov7670_controls[i].qc.id == id) + return ov7670_controls + i; + return NULL; +} + + +static int ov7670_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct ov7670_control *ctrl = ov7670_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + + + + +int ccic_sensor_attach(struct i2c_client *client); + +/* + * Basic i2c stuff. + */ +extern struct clk *pxa168_ccic_gate_clk; +static int __devinit ov7670_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret; + struct ov7670_info *info; + struct sensor_platform_data *pdata; + pdata = client->dev.platform_data; + + /* + * Set up our info structure. + */ + clk_enable(pxa168_ccic_gate_clk); + ccic_set_clock_parallel(); //clock must be enabled before power on. + + pdata->power_on(1, 0); + + info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &ov7670_formats[0]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + + /* + * Make sure it's an ov7670 + */ + ret = ov7670_detect(client); + if (ret) { + printk("%s: failed to detect ov7670!\n", __func__); + goto out_free_info; + } + printk(KERN_NOTICE "OmniVision ov7670 sensor detected\n"); + + ccic_sensor_attach(client); + pdata->power_on(0, 0); //for power optimization + ccic_disable_clock(); + return 0; + +out_free_info: + kfree(info); +out_free: + return ret; +} + + +static int ov7670_remove(struct i2c_client *client) +{ + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7670_g_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return ov7670_read(client, (unsigned char)reg->reg, (unsigned char *)&(reg->val)); +} + +static int ov7670_s_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return ov7670_write(client, (unsigned char)reg->reg, (unsigned char)reg->val); +} +#endif + +static int ov7670_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + switch (cmd) { + case VIDIOC_DBG_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); + + case VIDIOC_INT_RESET: + ov7670_reset(client); + return 0; + + case VIDIOC_ENUM_FMT: + return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + case VIDIOC_TRY_FMT: + return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + case VIDIOC_S_FMT: + return ov7670_s_fmt(client, (struct v4l2_format *) arg); + case VIDIOC_QUERYCTRL: + return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg); + case VIDIOC_S_CTRL: + return ov7670_s_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_G_CTRL: + return ov7670_g_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_S_PARM: + return ov7670_s_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_G_PARM: + return ov7670_g_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_S_INPUT: + return ov7670_s_input(client, (int *) arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return ov7670_g_register(client, (struct v4l2_dbg_register *) arg); + case VIDIOC_DBG_S_REGISTER: + return ov7670_s_register(client, (struct v4l2_dbg_register *) arg); +#endif + } + return -EINVAL; +} + +static struct i2c_device_id ov7670_idtable[] = { + { "ov7670", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ov7670_idtable); + +static struct i2c_driver ov7670_driver = { + .driver = { + .name = "ov7670", + }, + .id_table = ov7670_idtable, + .command = ov7670_command, + .probe = ov7670_probe, + .remove = ov7670_remove, +}; + + +/* + * Module initialization + */ +static int __init ov7670_mod_init(void) +{ + printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n"); + return i2c_add_driver(&ov7670_driver); +} + +static void __exit ov7670_mod_exit(void) +{ + i2c_del_driver(&ov7670_driver); +} + +late_initcall(ov7670_mod_init); +//module_init(ov7670_mod_init); +module_exit(ov7670_mod_exit); + diff --git a/drivers/media/video/ov7740.c b/drivers/media/video/ov7740.c new file mode 100644 index 00000000000000..b53b4da7b39e33 --- /dev/null +++ b/drivers/media/video/ov7740.c @@ -0,0 +1,2813 @@ +/* + * A V4L2 driver for OmniVision OV7740 cameras. + * + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "pxa168_camera.h" + +MODULE_AUTHOR("Tony Teng "); +MODULE_DESCRIPTION("pxa168 low-level OmniVision ov7740 sensors"); +MODULE_LICENSE("GPL"); + + +#define DEBUG_YUV422 //what will using OV7740_debug_YUV422_pad_regs +//to fix the U/V conponents +#undef DEBUG_YUV422 + +#define DEBUG_PRINTK +#undef DEBUG_PRINTK + + +/* + * Our nominal (default) frame rate. + */ +#define OV7740_FRAME_RATE 30 //norminal frame rate, 30~60, +//depends on the input clk & clk reg config +//30fps if inputclk=24M, 0x11=0x01,0x55=def +//60fps if inputclk=24M, 0x11=def,0x55=def +/* + * The 7740 sits on i2c with ID 0x21 + */ +#define OV7740_I2C_ADDR 0x21 //0x42 for write, 0x43 for read + +//*******************************************************************************// +//OV7740 register table +//*******************************************************************************// +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BGAIN 0x01 /* blue gain */ +#define REG_RGAIN 0x02 /* red gain */ +#define REG_GGAIN 0x03 /* green gain */ +#define REG_REG04 0x04 /* analog setting, dont change*/ +#define REG_BAVG 0x05 /* b channel average */ +#define REG_GAVG 0x06 /* g channel average */ +#define REG_RAVG 0x07 /* r channel average */ + +#define REG_REG0C 0x0C /* filp enable */ +#define REG0C_FLIP_MASK 0x80 +#define REG0C_MIRROR_MASK 0x40 + +#define REG_REG0E 0x0E /* blc line */ +#define REG_HAEC 0x0F /* auto exposure cntrl */ +#define REG_AEC 0x10 /* auto exposure cntrl */ + +#define REG_CLK 0x11 /* Clock control */ +#define REG_REG55 0x55 /* Clock PLL DIV/PreDiv */ + +#define REG_REG12 0x12 + +#define REG_REG13 0x13 /* auto/manual AGC, AEC, Write Balance*/ +#define REG13_AEC_EN 0x01 +#define REG13_AGC_EN 0x04 + +#define REG_REG14 0x14 +#define REG_REG15 0x15 +#define REG_REG16 0x16 + +#define REG_MIDH 0x1C /* manufacture id byte */ +#define REG_MIDL 0x1D /* manufacture id byre */ +#define REG_PIDH 0x0A /* Product ID MSB */ +#define REG_PIDL 0x0B /* Product ID LSB */ + +#define REG_84 0x84 /* lots of stuff */ +#define REG_REG38 0x38 /* sub-addr */ + +//sensor output size ctrl +#define REG_AHSTART 0x17 /* Horiz start high bits */ +#define REG_AHSIZE 0x18 +#define REG_AVSTART 0x19 /* Vert start high bits */ +#define REG_AVSIZE 0x1A +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ + +//dsp output size ctrl +#define REG_HOUTSIZE 0x31 +#define REG_VOUTSIZE 0x32 +#define REG_HVSIZEOFF 0x33 +#define REG_REG34 0x34 /* DSP output size H/V LSB*/ + +#define REG12_RESET 0x80 /* Register reset */ + +//contrast +#define REG_YGAIN 0xE2 //ygain for contrast control + +//brightness +#define REG_YBRIGHT 0xE3 +#define REG_SGNSET 0xE4 +#define SGNSET_YBRIGHT_MASK 0x08 + +/* saturation */ +#define REG_USAT 0xDD +#define REG_VSAT 0xDE + +/* + * Information we maintain about a known sensor. + */ +struct ov7740_format_struct; /* coming later */ +struct ov7740_info {//current config + struct ov7740_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +/* + * The default register settings, as obtained from OmniVision. + * + * These settings give VGA YUYV. + */ +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + + +/* + * ACCORDING TO OV FAE ANDY, ALL THE FOLLOWING YUV FORMAT CONFIG IS *YUV422* + * YUYV: Y0U0Y1V1,Y2U2Y3V3,... + * HOW TO DEBUG YUV FORMAT & SOME IMPORTANT REG: + * OX11 CLK DIV: SELECT THE OUTPUT PCLK & VSYNC; + * VSYNC=60FPS WHEN SYSCLK=24M + * + * 0X13[0]=1,[3]=0, DEBUG *Y* + * 0XDA[7],[4],[3], FIX *YUV* + * + */ + +/* + * start of ov7740 register configuration + */ + +// OV7740 YCbCr CIF (352x288) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_CIF_YUYV_30fps[] = { + //reg addr, val + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01},//clk + + {0x12, 0x00}, + + {0xd5, 0x10},//scale smth ctrl + {0x0c, 0x12},/*YUV output,YUYV*/ + {0x0d, 0x34},//nc + + {0x17, 0x25},//AHSTART + {0x18, 0xa0},//AHSIZE + {0x19, 0x03},//AVSTART + {0x1a, 0xf0},//AVSIZE + {0x1b, 0x89},//PSHFT + + {0x22, 0x03},//nc + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0x58}, + {0x32, 0x90}, + {0x33, 0xc4}, + //beginof nc + {0x35, 0x05}, + {0x36, 0x3f}, + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + //endof nc + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x3f}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //0x50/60Hz auto detection is XCLK dependent + //0xthe following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00}, //00/80 for manual/auto + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 YCbCr QCIF (176x144) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_QCIF_YCbCr_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + {0x12, 0x00}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x89}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0x2c}, + {0x32, 0x48}, + {0x33, 0xc4}, + {0x35, 0x05}, + {0x36, 0x3f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x3f}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependent + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto 50_60Hz + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 YCbCr QQVGA (160x120) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_QQVGA_YCbCr_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + {0x12, 0x00}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x89}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0x28}, + {0x32, 0x3c}, + {0x33, 0xc4}, + {0x35, 0x05}, + {0x36, 0x3f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x3f}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependent + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto 50_60Hz + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 YCbCr QVGA (320x240) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_QVGA_YCbCr_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + {0x12, 0x00}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x89}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0x50}, + {0x32, 0x78}, + {0x33, 0xc4}, + {0x35, 0x05}, + {0x36, 0x3f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x3f}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependent + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 YCbCr VGA (640x480) BT.656 +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_VGA_BT656_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + {0x12, 0x20}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x16, 0x10}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x89}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0xa0}, + {0x32, 0xf0}, + {0x33, 0xc0}, + {0x34, 0x04}, + {0x35, 0x05}, + {0x36, 0x3f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x32}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependent + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 Raw8 VGA (640x480) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_VGA_Raw8_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + + {0x12, 0x01}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x8a}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0xa0}, + {0x32, 0xf0}, + {0x33, 0xf4}, + {0x3a, 0xb4}, + {0x36, 0x2f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x32}, + {0x83, 0x05}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependant + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto + + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 Raw10 VGA (640x480) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_VGA_Raw10_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + {0x12, 0x01}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x8a}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0xa0}, + {0x32, 0xf0}, + {0x33, 0xf4}, + {0x3a, 0xb4}, + {0x36, 0x2f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x32}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependant + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 YCbCr VGA (640x480) +// 30fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_VGA_YCbCr_30fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01}, + {0x12, 0x00}, + {0xd5, 0x10}, + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x89}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0xa0}, + {0x32, 0xf0}, + {0x33, 0xc4}, + {0x35, 0x05}, + {0x36, 0x3f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x32}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependent + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +// OV7740 YCbCr VGA (640x480) +// 60fps at 24MHz input clock, 16x gain ceiling +// Date: 4/28/2009 +static struct regval_list OV7740_VGA_YCbCr_60fps[] = { + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x00}, + {0x12, 0x00}, + {0xd5, 0x10}, + {0x0c, 0x12},/*YUV output,YUYV*/ + {0x0d, 0x34}, + {0x17, 0x25}, + {0x18, 0xa0}, + {0x19, 0x03}, + {0x1a, 0xf0}, + {0x1b, 0x89}, + {0x22, 0x03}, + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0xa0}, + {0x32, 0xf0}, + {0x33, 0xc4}, + {0x35, 0x05}, + {0x36, 0x3f}, + + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x08}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xf5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x2e}, + {0x51, 0xfc}, + {0x52, 0x10}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x01}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x32}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //50/60Hz auto detection is XCLK dependent + //the following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00},//00/80 for manual/auto 50_60Hz + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +static struct regval_list OV7740_CIF_YCbCr_30fps[] = { + //reg addr, val + {0x12, 0x80}, + {0x13, 0x00}, + + {0x55, 0x40},//div + {0x11, 0x01},//clk + + {0x12, 0x00}, + + {0xd5, 0x10},//scale smth ctrl + {0x0c, 0x02},/*YUV output,YUYV*/ + {0x0d, 0x34},//nc + + {0x17, 0x25},//AHSTART + {0x18, 0xa0},//AHSIZE + {0x19, 0x03},//AVSTART + {0x1a, 0xf0},//AVSIZE + {0x1b, 0x89},//PSHFT + + {0x22, 0x03},//nc + {0x29, 0x17}, + {0x2b, 0xf8}, + {0x2c, 0x01}, + {0x31, 0x58}, + {0x32, 0x90}, + {0x33, 0xc4}, + //beginof nc + {0x35, 0x05}, + {0x36, 0x3f}, + {0x04, 0x60}, + {0x27, 0x80}, + {0x3d, 0x0f}, + {0x3e, 0x82}, + {0x3f, 0x40}, + {0x40, 0x7f}, + {0x41, 0x6a}, + {0x42, 0x29}, + {0x44, 0xe5}, + {0x45, 0x41}, + {0x47, 0x42}, + {0x48, 0x00}, + {0x49, 0x61}, + {0x4a, 0xa1}, + {0x4b, 0x46}, + {0x4c, 0x18}, + {0x4d, 0x50}, + {0x4e, 0x13}, + //endof nc + {0x64, 0x00}, + {0x67, 0x88}, + {0x68, 0x1a}, + + {0x14, 0x38}, + {0x24, 0x3c}, + {0x25, 0x30}, + {0x26, 0x72}, + {0x50, 0x97}, + {0x51, 0x7e}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x20, 0x00}, + {0x21, 0x23}, + {0x38, 0x14}, + {0xe9, 0x00}, + {0x56, 0x55}, + {0x57, 0xff}, + {0x58, 0xff}, + {0x59, 0xff}, + {0x5f, 0x04}, + {0xec, 0x00}, + {0x13, 0xff}, + + {0x80, 0x7d}, + {0x81, 0x3f}, + {0x82, 0x3f}, + {0x83, 0x01}, + {0x38, 0x11}, + {0x84, 0x70}, + {0x85, 0x00}, + {0x86, 0x03}, + {0x87, 0x01}, + {0x88, 0x05}, + {0x89, 0x30}, + {0x8d, 0x30}, + {0x8f, 0x85}, + {0x93, 0x30}, + {0x95, 0x85}, + {0x99, 0x30}, + {0x9b, 0x85}, + {0x9c, 0x08}, + {0x9d, 0x12}, + {0x9e, 0x23}, + {0x9f, 0x45}, + {0xa0, 0x55}, + {0xa1, 0x64}, + {0xa2, 0x72}, + {0xa3, 0x7f}, + {0xa4, 0x8b}, + {0xa5, 0x95}, + {0xa6, 0xa7}, + {0xa7, 0xb5}, + {0xa8, 0xcb}, + {0xa9, 0xdd}, + {0xaa, 0xec}, + {0xab, 0x1a}, + + {0xce, 0x78}, + {0xcf, 0x6e}, + {0xd0, 0x0a}, + {0xd1, 0x0c}, + {0xd2, 0x84}, + {0xd3, 0x90}, + {0xd4, 0x1e}, + + {0x5a, 0x24}, + {0x5b, 0x1f}, + {0x5c, 0x88}, + {0x5d, 0x60}, + + {0xac, 0x6e}, + {0xbe, 0xff}, + {0xbf, 0x00}, + + //0x50/60Hz auto detection is XCLK dependent + //0xthe following is based on XCLK = 24MHz + {0x70, 0x00}, + {0x71, 0x34}, + {0x74, 0x28}, + {0x75, 0x98}, + {0x76, 0x00}, + {0x77, 0x08}, + {0x78, 0x01}, + {0x79, 0xc2}, + {0x7d, 0x02}, + {0x7a, 0x4e}, + {0x7b, 0x1f}, + {0xEC, 0x00}, //00/80 for manual/auto + {0x7c, 0x0c}, + + {0xda, 0x07}, /* Enable config brigntness, contrast, hue and satuation */ + + {0xFF, 0xFF}, /* END MARKER */ +}; + +/* + * end of ov7740 register configuration::position_tag + */ + + +//default ov config, rgb 30fps +static struct regval_list OV7740_default_regs[] = { + { REG_REG12, REG12_RESET }, + + { REG_REG55, 0x40 }, /* Clock PLL DIV/PreDiv */ + { REG_CLK, 0x01 }, //clk, 24MHz + + { 0xff, 0xff }, /* END MARKER */ +}; + +static struct regval_list OV7740_debug_regs[] = { + { REG_REG12, REG12_RESET }, + { REG_REG13, 0x87 }, //auto AGC, AEC + + { REG_REG55, 0x40 }, /* Clock PLL DIV/PreDiv */ + { REG_CLK, 0x01 }, //clk, 24MHz + + //{ REG_REG12, 0x00 }, /* VGA_YUV */ + //{ REG_REG12, 0x01 }, /* VGA_RGBraw */ + + //debug mode + { REG_REG38, 0x17 }, //debug color bar + { REG_84, 0x02 }, //0x02/0x00 ouput data are + //colorbar/normal data + { 0xff, 0xff }, /* END MARKER */ +}; + +//behind the other config reglist to generate corresponding color bar for debug +static struct regval_list OV7740_colorbar_pad_regs[] = { + //donot reset + { REG_REG38, 0x17 }, //debug color bar + { REG_84, 0x02 }, //0x02/0x00 ouput data are + //colorbar/normal data + { 0xff, 0xff }, /* END MARKER */ +}; + +#ifdef DEBUG_YUV422 +//behind the other config reglist to generate corresponding color bar for debug +static struct regval_list OV7740_debug_YUV422_pad_regs[] = { + //donot reset + + {0x13,0x80},//disable AEC + {0x0F,0xFF}, + {0x10,0xFF},//max exposure + + {0xE0,0xAB},//V + {0xDF,0xCD},//U + //{0xE0,0xFF},//V + //{0xDF,0xFF},//U + //{0x0c, 0x12},/*YUV output,YUYV*/ + + {0xDA,0x18},//enable UV debug + + { 0xff, 0xff }, /* END MARKER */ +}; +#endif + +/* ov7740 reg setting for optimizing ov7740 on IPCAM */ +static struct regval_list OV7740_ipcam_opti_regs[] = { + {0x80, 0x7f}, + {0x8c, 0x01}, + {0x8a, 0x4f}, + {0x8b, 0xf7}, + {0x8d, 0x3a}, + {0x8f, 0xf3}, + {0x8e, 0xcb}, + + {0x92, 0x01}, + {0x90, 0x52}, + {0x91, 0xf5}, + {0x93, 0x34}, + {0x95, 0x73}, + {0x94, 0x79}, + + {0x98, 0x01}, + {0x96, 0x4f}, + {0x97, 0xf5}, + {0x99, 0x31}, + {0x9b, 0x73}, + {0x9a, 0x1e}, + + /* PLL divider to find out around 22fps + * from 19.5 MHz mclk input */ + {0x14, 0x3a}, + {0x2b, 0x28}, + {0x2c, 0x02}, + + {0xff, 0xff}, /* END MARKER */ +}; + +#if 0 +static int ov7740_read(struct i2c_client *c, u8 reg,u8 *value) +{ + i2c_master_send(c,®,1);//i2c_smbus_write_byte(g_client,reg_addr); + return i2c_master_recv(c,value,1); +} + +static int ov7740_write(struct i2c_client *c, u8 reg,u8 value) +{ + return i2c_smbus_write_byte_data(c,reg,value); +} +#endif + +/* + * Low-level register I/O. + */ +static int ov7740_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ +#ifdef DEBUG_PRINTK + printk("##!!## %s called\n",__FUNCTION__); +#endif + i2c_smbus_write_byte(c, reg); + *value = i2c_smbus_read_byte(c); + + return 0; +} + +static int ov7740_write(struct i2c_client *c, unsigned char reg, + unsigned char value) +{ +#ifdef DEBUG_PRINTK + printk("##!!## %s called,reg=0x%x,val=0x%x\n",__FUNCTION__,reg,value); +#endif + int ret = i2c_smbus_write_byte_data(c, reg, value); + if (reg == REG_REG12 && (value & REG12_RESET)) + msleep(2); /* Wait for reset to run */ +#ifdef DEBUG_PRINTK + unsigned char v=0; + ov7740_read(c, reg, &v); + printk("::0x%x,0x%x", reg, v); +#endif + return ret; +} + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int ov7740_write_array(struct i2c_client *c, struct regval_list *vals) +{ +#ifdef DEBUG_PRINTK + printk("##!!## %s called\n",__FUNCTION__); +#endif + int i = 0; + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = ov7740_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + //if (i == 0) + if (vals->reg_num == REG_REG12) + mdelay(5);//delay if reset + i++; + } + return 0; +} + +/* + * Stuff that knows about the sensor. + */ +static int ov7740_reset(struct i2c_client *client) +{ +#ifdef DEBUG_PRINTK + printk("##!!## %s called\n",__FUNCTION__); +#endif + int ret; + ret = ov7740_write(client, REG_REG12, REG12_RESET); + msleep(1); + return ret; +} + +static int ov7740_init(struct i2c_client *client) +{ + int ret; + ret = ov7740_write_array(client, OV7740_CIF_YCbCr_30fps); + //ov7740_write_array(client, OV7740_debug_regs); + //ov7740_write_array(client, OV7740_VGA_YCbCr_60fps); + + /* ret += ov7740_write_array(client, OV7740_colorbar_pad_regs);//color bar */ + return ret; +} + +static int ov7740_detect(struct i2c_client *client) +{ +#ifdef DEBUG_PRINTK + printk("##!!## %s called\n",__FUNCTION__); +#endif + unsigned char v = 0; + int ret; + + ret = ov7740_read(client, REG_MIDH, &v); + if (ret < 0) + return ret; + if (v != 0x7f) /* OV manuf. id. */ + return -ENODEV; + ret = ov7740_read(client, REG_MIDL, &v); + if (ret < 0) + return ret; + if (v != 0xa2) + return -ENODEV; + /* + * OK, we know we have an OmniVision chip...but which one? + */ + ret = ov7740_read(client, REG_PIDH, &v); + if (ret < 0) + return ret; + if (v != 0x77) + return -ENODEV; + ret = ov7740_read(client, REG_PIDL, &v); + if (ret < 0) + return ret; + /* could be 0x42, 0x41 or 0x40 */ + if ((v != 0x42) && (v != 0x40) && (v != 0x41)) + return -ENODEV; + return 0; +} + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 +#define QQVGA_WIDTH 160 +#define QQVGA_HEIGHT 120 + +//*******************************************************************************// +//*******************************************************************************// +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + * + * *The SIZE should sort with LARGE->SMALL order* + */ +static struct ov7740_format_struct { + __u8 *desc; + __u32 pixelformat; + int width; + int height; + int fps; + struct regval_list *regs; + int bpp; /* bits per pixel */ +} ov7740_formats[] = { + { + .desc = "OV7740 VGA YUV422 30fps", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .fps = 30, + .regs = OV7740_VGA_YCbCr_30fps, + .bpp = 16, + }, + {//high precision + .desc = "OV7740 VGA RAW10 30fps", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .fps = 30, + .regs = OV7740_VGA_Raw10_30fps, + .bpp = 10, + }, + { + .desc = "OV7740 CIF YUV422 30fps", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .fps = 30, + .regs = OV7740_CIF_YCbCr_30fps, + .bpp = 16, + }, + { + .desc = "OV7740 QVGA YUV422 30fps", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .fps = 30, + .regs = OV7740_QVGA_YCbCr_30fps, + .bpp = 16, + }, + { + .desc = "OV7740 QCIF YUV422 30fps", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .fps = 30, + .regs = OV7740_QCIF_YCbCr_30fps, + .bpp = 16, + }, + { + .desc = "OV7740 QQVGA YUV422 30fps", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .width = QQVGA_WIDTH, + .height = QQVGA_HEIGHT, + .fps = 30, + .regs = OV7740_QQVGA_YCbCr_30fps, + .bpp = 16, + }, + { + .desc = "OV7740 CIF YUV422pack 30fps", + .pixelformat = V4L2_PIX_FMT_YUYV, + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .fps = 30, + .regs = OV7740_CIF_YUYV_30fps, + .bpp = 16, + }, + { + .desc = "OV7740 VGA YUV420 30fps", + .pixelformat = V4L2_PIX_FMT_YUV420, + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .fps = 30, + .regs = OV7740_VGA_YCbCr_30fps, + .bpp = 12, + }, + { + .desc = "OV7740 CIF YUV420 30fps", + .pixelformat = V4L2_PIX_FMT_YUV420, + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .fps = 30, + .regs = OV7740_CIF_YCbCr_30fps, + .bpp = 12, + }, + { + .desc = "OV7740 QVGA YUV420 30fps", + .pixelformat = V4L2_PIX_FMT_YUV420, + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + .fps = 30, + .regs = OV7740_QVGA_YCbCr_30fps, + .bpp = 12, + }, + { + .desc = "OV7740 QCIF YUV420 30fps", + .pixelformat = V4L2_PIX_FMT_YUV420, + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .fps = 30, + .regs = OV7740_QCIF_YCbCr_30fps, + .bpp = 12, + }, + { + .desc = "OV7740 QQVGA YUV420 30fps", + .pixelformat = V4L2_PIX_FMT_YUV420, + .width = QQVGA_WIDTH, + .height = QQVGA_HEIGHT, + .fps = 30, + .regs = OV7740_QQVGA_YCbCr_30fps, + .bpp = 12, + }, + { + .desc = "OV7740 VGA RAW8 30fps", + .pixelformat = V4L2_PIX_FMT_RGB24,//FIXME + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .fps = 30, + .regs = OV7740_VGA_Raw8_30fps, + .bpp = 8, + }, + { + .desc = "OV7740 VGA BT656 30fps", + .pixelformat = NULL,//FIXME:not support by current CCIC + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .fps = 30, + .regs = OV7740_VGA_BT656_30fps, + //.bpp = 16,//TODO + }, +}; +#define N_OV7740_FMTS ARRAY_SIZE(ov7740_formats) //total number of ov7740 format + +#define V4L2_PIX_FMT_RGB24 v4l2_fourcc('R', 'G', 'B', '3') /* 24 RGB-8-8-8 */ + +static int ov7740_cropcap(struct i2c_client *c, struct v4l2_cropcap *ccap) +{ + struct ov7740_format_struct *ovfmt = ov7740_formats; + + ccap->bounds.left = 0; + ccap->bounds.top = 0; + ccap->bounds.width = ovfmt->width; + ccap->bounds.height = ovfmt->height; + + ccap->defrect = ccap->bounds; + + ccap->pixelaspect.numerator = 1; + ccap->pixelaspect.denominator = 1; +} + +//select in ov cam foramt by index NUM +static int ov7740_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct ov7740_format_struct *ofmt; + + if (fmt->index >= N_OV7740_FMTS) + return -EINVAL; + + ofmt = ov7740_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + +static int ov7740_g_fmt(struct i2c_client *client, struct v4l2_pix_format *pix) +{ + struct ov7740_format_struct *ofmt; + int i; + + for (i = 0; i < N_OV7740_FMTS; i++){ + if (ov7740_formats[i].pixelformat == pix->pixelformat){ + if (pix->width >= ov7740_formats[i].width && + pix->height >= ov7740_formats[i].height){ + break; + } + } + } + + if (i >= N_OV7740_FMTS) + return -EINVAL; + + ofmt = ov7740_formats + i; + + pix->bytesperline = ofmt->width * ofmt->bpp / 8; + pix->sizeimage = ofmt->height * pix->bytesperline; + + return 0; +} + +static int ov7740_enum_framesizes(struct i2c_client *client, struct v4l2_frmsizeenum *fsize) +{ + struct ov7740_format_struct *ofmt = NULL; + int i, index = -1; + + if (fsize->index >= N_OV7740_FMTS) + return -EINVAL; + + for (i = 0; i < N_OV7740_FMTS; i++) { + if (ov7740_formats[i].pixelformat == + fsize->pixel_format) { + index++; + if (index == fsize->index) + break; + } + } + + if (index < fsize->index) + return -EINVAL; + + if (i >= N_OV7740_FMTS) + return -EINVAL; + + ofmt = ov7740_formats + i; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + /* fsize->pixel_format = ofmt->pixelformat; */ + fsize->discrete.width = ofmt->width; + fsize->discrete.height = ofmt->height; + + return 0; +} + +// select image FORMAT and PIXEL SIZE +static int ov7740_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct ov7740_format_struct **ret_fmt) +{ + int index; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + //find the required pixelformat in ov7740_formats[] + // + //Round requested image size down to the nearest + //we support, but not below the smallest. + for (index = 0; index < N_OV7740_FMTS; index++){ + if (ov7740_formats[index].pixelformat == pix->pixelformat){ + if (pix->width >= ov7740_formats[index].width && + pix->height >= ov7740_formats[index].height){ + break; + } + } + } + + if (index >= N_OV7740_FMTS) + return -EINVAL; + //set the returnpixel format of ov7740 which satisfy v4l2 + if (ret_fmt != NULL) + *ret_fmt = ov7740_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + /* + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + */ + + /* + * Note the size we'll actually handle. + */ + pix->width = ov7740_formats[index].width; + pix->height = ov7740_formats[index].height; + pix->bytesperline = ov7740_formats[index].width*ov7740_formats[index].bpp/8;//TODO:##!!##? + pix->sizeimage = ov7740_formats[index].height*pix->bytesperline; + pix->field = V4L2_FIELD_NONE; + return 0; +} + +/* + * Set a format. + */ +static int ov7740_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ +#ifdef DEBUG_PRINTK + printk("##!!##%s called\n",__FUNCTION__); +#endif + int ret; + struct ov7740_format_struct *ovfmt; + + //select image FORMAT and PIXEL SIZE + ret = ov7740_try_fmt(c, fmt, &ovfmt); + if (ret) + return ret; + + //initialize cam sensor according to selected format + ov7740_write_array(c, ovfmt->regs); + if (machine_is_ipcam()) + ov7740_write_array(c, OV7740_ipcam_opti_regs); + +#ifdef DEBUG_YUV422 + ov7740_write_array(c, OV7740_colorbar_pad_regs);//color bar + //ov7740_write_array(c,OV7740_debug_YUV422_pad_regs); +#endif + + return ret; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int ov7740_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + return 0; +} + +//keep denom=require rate, num=1 or 0 +static int ov7740_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + return 0; +} + +static int ov7740_s_input(struct i2c_client *c, int *id) +{ + return 0; +} + + +#if 0 +#define CMATRIX_LEN 6 + +static int ov7740_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = ov7740_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += ov7740_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += ov7740_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int ov7740_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int ov7740_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = ov7740_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - ov7740_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int ov7740_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return ov7740_sine(theta); +} + + + + +static void ov7740_calc_cmatrix(struct ov7740_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = ov7740_sine(info->hue); + costh = ov7740_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + +static int ov7740_t_sat(struct i2c_client *client, int value) +{ + return 0; + struct ov7740_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + ov7740_calc_cmatrix(info, matrix); + ret = ov7740_store_cmatrix(client, matrix); + return ret; +} + +static int ov7740_q_sat(struct i2c_client *client, __s32 *value) +{ + return 0; + struct ov7740_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int ov7740_t_hue(struct i2c_client *client, int value) +{ + return 0; + struct ov7740_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + ov7740_calc_cmatrix(info, matrix); + ret = ov7740_store_cmatrix(client, matrix); + return ret; +} + + +static int ov7740_q_hue(struct i2c_client *client, __s32 *value) +{ + return 0; + struct ov7740_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} +#endif + +/* set saturation */ +static int ov7740_t_saturation(struct i2c_client *client, int value) +{ + int ret = 0; + + /* REG_USAT and REG_VSAT should hold the same value; */ + ret = ov7740_write(client, REG_USAT, value); + ret = ov7740_write(client, REG_VSAT, value); + + return ret; +} + +/* get saturation */ +static int ov7740_q_saturation(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret; + + /* REG_USAT and REG_VSAT holds the same value */ + ret = ov7740_read(client, REG_USAT, &v); + *value = v; + return ret; +} + +//set brightness +static int ov7740_t_brightness(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + //disable AEC/AGC + ret = ov7740_read(client, REG_REG13, &v); + v &= ~REG13_AEC_EN; + ret = ov7740_write(client, REG_REG13, v); + + ret = ov7740_read(client, REG_REG13, &v); + v &= ~REG13_AGC_EN; + ret = ov7740_write(client, REG_REG13, v); + + //set brightness + if(value >= 0){ + ret += ov7740_write(client, REG_YBRIGHT, (value & 0xFF)); + ov7740_read(client, REG_SGNSET, &v); + v &= ~SGNSET_YBRIGHT_MASK; + ret += ov7740_write(client, REG_SGNSET, v); + } else{ + value = -value; + ret += ov7740_write(client, REG_YBRIGHT, (value & 0xFF)); + ov7740_read(client, REG_SGNSET, &v); + v |= SGNSET_YBRIGHT_MASK; + ret += ov7740_write(client, REG_SGNSET, v); + } + + return ret; +} + +//get brightness +static int ov7740_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + unsigned char sgn; + int ret; + ret = ov7740_read(client, REG_YBRIGHT, &v); + ret += ov7740_read(client, REG_SGNSET, &sgn); + *value = v;//TODO + if((sgn & SGNSET_YBRIGHT_MASK) == SGNSET_YBRIGHT_MASK) + *value = -(*value); + return ret; +} + +//TODO:any other regs required? +static int ov7740_t_contrast(struct i2c_client *client, int value) +{ + return ov7740_write(client, REG_YGAIN, (unsigned char) value); +} + +static int ov7740_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = ov7740_read(client, REG_YGAIN, &v); + *value = v; + return ret; +} + +static int ov7740_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7740_read(client, REG_REG0C, &v); + *value = (v & REG_REG0C) == REG0C_MIRROR_MASK; + return ret; +} + + +static int ov7740_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7740_read(client, REG_REG0C, &v); + if (value) + v |= REG0C_MIRROR_MASK; + else + v &= ~REG0C_MIRROR_MASK; + msleep(10); + ret += ov7740_write(client, REG_REG0C, v); + return ret; +} + + + +static int ov7740_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = ov7740_read(client, REG_REG0C, &v); + *value = (v & REG0C_MIRROR_MASK) == REG0C_MIRROR_MASK; + return ret; +} + + +static int ov7740_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = ov7740_read(client, REG_REG0C, &v); + if (value) + v |= REG0C_FLIP_MASK; + else + v &= ~REG0C_FLIP_MASK; + msleep(10); + ret += ov7740_write(client, REG_REG0C, v); + return ret; +} + +static struct ov7740_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} ov7740_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS,//the tag used to search inside ov ctrl[] + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = -255, + .maximum = 255, + .step = 1, + .default_value = 0x00, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7740_t_brightness,//set quality + .query = ov7740_q_brightness,//get quality + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x20, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7740_t_contrast, + .query = ov7740_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7740_t_saturation, + .query = ov7740_q_saturation, + }, +#if 0 + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = ov7740_t_hue, + .query = ov7740_q_hue, + }, +#endif + {//flip vertical + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7740_t_vflip, + .query = ov7740_q_vflip, + }, + {//flip h + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = ov7740_t_hflip, + .query = ov7740_q_hflip, + }, +}; +#define N_CONTROLS (ARRAY_SIZE(ov7740_controls)) + +static struct ov7740_control *ov7740_find_control(__u32 id) +{ + int i; + + //find the ctrl property required by v4l2 by expression of id + for (i = 0; i < N_CONTROLS; i++) + if (ov7740_controls[i].qc.id == id) + return ov7740_controls + i; + return NULL; +} + + +//search in ov7740_control[] by id +static int ov7740_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct ov7740_control *ctrl = ov7740_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int ov7740_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7740_control *octrl = ov7740_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +//find in ov ctrl[] by id and set sensor +static int ov7740_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct ov7740_control *octrl = ov7740_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value);//##!!## tag + if (ret >= 0) + return 0; + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +//get ov register value directly, for debug usage +static int ov7740_g_register(struct i2c_client *client, struct v4l2_register * reg) +{ + return ov7740_read(client, (unsigned char)reg->reg, (unsigned char *)&(reg->val)); +} + +//set ov register value directly, for debug usage +static int ov7740_s_register(struct i2c_client *client, struct v4l2_register * reg) +{ + return ov7740_write(client, (unsigned char)reg->reg, (unsigned char)reg->val); +} +#endif + +static int ov7740_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ +#ifdef DEBUG_PRINTK + printk("##!!##%s called\n",__FUNCTION__); +#endif + switch (cmd) { + case VIDIOC_DBG_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, + V4L2_IDENT_OV7740, 0); + + case VIDIOC_INT_RESET: + return ov7740_reset(client); + + case VIDIOC_INT_INIT: + return ov7740_init(client); + + case VIDIOC_CROPCAP: + return ov7740_cropcap(client, (struct v4l2_cropcap *)arg); + + case VIDIOC_ENUM_FMT: + return ov7740_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + + case VIDIOC_G_FMT: + return ov7740_g_fmt(client, (struct v4l2_pix_format *)arg); + + case VIDIOC_ENUM_FRAMESIZES: + return ov7740_enum_framesizes(client, (struct v4l2_frmsizeenum *)arg); + + case VIDIOC_TRY_FMT: + return ov7740_try_fmt(client, (struct v4l2_format *) arg, NULL); + + case VIDIOC_S_FMT:/*select and set nearest format [format & win size]*/ + return ov7740_s_fmt(client, (struct v4l2_format *) arg); + + case VIDIOC_QUERYCTRL:/*search in ov7740_control[] by id*/ + return ov7740_queryctrl(client, (struct v4l2_queryctrl *) arg); + + case VIDIOC_S_CTRL:/*set ov ctrl*/ + return ov7740_s_ctrl(client, (struct v4l2_control *) arg); + + case VIDIOC_G_CTRL: + return ov7740_g_ctrl(client, (struct v4l2_control *) arg); + + case VIDIOC_S_PARM:/*not implemented*/ + return ov7740_s_parm(client, (struct v4l2_streamparm *) arg); + + case VIDIOC_G_PARM:/*not implemented*/ + return ov7740_g_parm(client, (struct v4l2_streamparm *) arg); + + case VIDIOC_S_INPUT:/*not implemented*/ + return ov7740_s_input(client, (int *) arg); + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER:/*get register val for debug*/ + return ov7740_g_register(client, (struct v4l2_register *) arg); + + case VIDIOC_DBG_S_REGISTER:/*set register val for debug*/ + return ov7740_s_register(client, (struct v4l2_register *) arg); +#endif + } + return -EINVAL; +} + +//**************************************************************************// +//**************************************************************************// + +int ccic_sensor_attach(struct i2c_client *client); +//int ccic_sensor_detach(struct i2c_client *client); + +/* + * Basic i2c stuff. + */ +extern struct clk *pxa168_ccic_gate_clk; +static int __devinit ov7740_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) +{ + int ret; + struct ov7740_info *info; + struct sensor_platform_data *pdata; + pdata = client->dev.platform_data; + + /* + * ccic_set_clock_parallel(); + * ccic_enable_mclk(); + */ + clk_enable(pxa168_ccic_gate_clk); + ccic_set_clock_parallel();/*clock must be enabled before power on.*/ + + pdata->power_on(1, 0); + info = kzalloc(sizeof (struct ov7740_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &ov7740_formats[0]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + + /* + * Make sure it's an ov7740 + */ + + ret = ov7740_detect(client); + if (ret) + goto out_free_info; + printk(KERN_INFO"OmniVision ov7740 sensor detected\n"); + +#ifdef DEBUG_YUV422 + ov7740_init(client); + //while(1); +#endif + + ccic_sensor_attach(client); + + /*ccic_disable_mclk();*/ + ccic_disable_clock(); + + pdata->power_on(0, 0); //for power optimization + + printk(KERN_NOTICE "OV7740 i2c-probe detected\n"); + return 0; + +out_free_info: + kfree(info); +out_free: + return ret; +} + + +static int ov7740_remove(struct i2c_client *client) +{ + struct ov7740_info *info; + struct sensor_platform_data *pdata; + + //info = client->dev.driver_data; + info = i2c_get_clientdata(client); + kfree(info); + + //ccic_sensor_detach(client);//TODO:why declared static + + /*ccic_disable_mclk();*/ + ccic_disable_clock(); + + pdata = client->dev.platform_data; + pdata->power_on(0, 0); //for power optimization + printk(KERN_NOTICE "OV7740 i2c-remove\n"); + return 0; +} + +static struct i2c_device_id ov7740_idtable[] = { + {"ov7740", 0 }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ov7740_idtable); + +static struct i2c_driver ov7740_driver = { + .driver = { + .name = "ov7740", + }, + .id_table = ov7740_idtable, + .command = ov7740_command, + .probe = ov7740_probe, + .remove = ov7740_remove, +}; + +/* + * Module initialization + */ +static int __init ov7740_mod_init(void) +{ + printk(KERN_NOTICE"OmniVision ov7740 sensor driver, at your service\n"); + return i2c_add_driver(&ov7740_driver); +} + +static void __exit ov7740_mod_exit(void) +{ + i2c_del_driver(&ov7740_driver); +} + +late_initcall(ov7740_mod_init); +//module_init(ov7740_mod_init); +module_exit(ov7740_mod_exit); + diff --git a/drivers/media/video/pxa168_camera.c b/drivers/media/video/pxa168_camera.c new file mode 100644 index 00000000000000..3f7db7ab7d344f --- /dev/null +++ b/drivers/media/video/pxa168_camera.c @@ -0,0 +1,2590 @@ +/* + * linux/drivers/media/video/pxa910_camera.c - PXA9XX MMCI driver + * + * Based on linux/drivers/media/video/cafe_ccic.c + * + * Copyright: (C) Copyright 2008 Marvell International Ltd. + * Mingwei Wang + * + * A driver for the CMOS camera controller in the Marvell 88ALP01 "cafe" + * multifunction chip. Currently works with the Omnivision OV7670 + * sensor. + * + * The data sheet for this device can be found at: + * http://www.marvell.com/products/pcconn/88ALP01.jsp + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * Copyright 2006-7 Jonathan Corbet + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ + +#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 "pxa168_camera.h" +#include + +#include +#include +#include +#include + + +#if defined(CONFIG_DVFM) +#include +static int dvfm_dev_idx; +#endif + +#define CCIC_VERSION 0x000001 +/* + * Parameters. + */ +MODULE_AUTHOR("Jonathan Corbet "); +MODULE_DESCRIPTION("Marvell 88ALP01 CMOS Camera Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("Video"); + +/* + * Internal DMA buffer management. Since the controller cannot do S/G I/O, + * we must have physically contiguous buffers to bring frames into. + * These parameters control how many buffers we use, whether we + * allocate them at load time (better chance of success, but nails down + * memory) or when somebody tries to use the camera (riskier), and, + * for load-time allocation, how big they should be. + * + * The controller can cycle through three buffers. We could use + * more by flipping pointers around, but it probably makes little + * sense. + */ + +#define DMA_POOL 0 +#define MAX_DMA_BUFS 3 +static int alloc_bufs_at_read = 0; +module_param(alloc_bufs_at_read, bool, 0444); +MODULE_PARM_DESC(alloc_bufs_at_read, + "Non-zero value causes DMA buffers to be allocated when the " + "video capture device is read, rather than at module load " + "time. This saves memory, but decreases the chances of " + "successfully getting those buffers."); + +static int n_dma_bufs = 3; //3 frame buffers +module_param(n_dma_bufs, uint, 0644); +MODULE_PARM_DESC(n_dma_bufs, + "The number of DMA buffers to allocate. Can be either two " + "(saves memory, makes timing tighter) or three."); + +//static int dma_buf_size = 2048 * 1536 * 2; /* Worst case */ +static int dma_buf_size = VGA_WIDTH * VGA_HEIGHT * 2; /* Worst case */ +module_param(dma_buf_size, uint, 0444); +MODULE_PARM_DESC(dma_buf_size, + "The size of the allocated DMA buffers. If actual operating " + "parameters require larger buffers, an attempt to reallocate " + "will be made."); + +static int min_buffers = 1; +module_param(min_buffers, uint, 0644); +MODULE_PARM_DESC(min_buffers, + "The minimum number of streaming I/O buffers we are willing " + "to work with."); + +static int max_buffers = 10; +module_param(max_buffers, uint, 0644); +MODULE_PARM_DESC(max_buffers, + "The maximum number of streaming I/O buffers an application " + "will be allowed to allocate. These buffers are big and live " + "in vmalloc space."); + +static int flip = 0; +module_param(flip, bool, 0444); +MODULE_PARM_DESC(flip, + "If set, the sensor will be instructed to flip the image " + "vertically."); + + +enum ccic_state { + S_NOTREADY, /* Not yet initialized */ + S_IDLE, /* Just hanging around */ + S_FLAKED, /* Some sort of problem */ + S_SINGLEREAD, /* In read() */ + S_SPECREAD, /* Speculative read (for future read()) */ + S_STREAMING /* Streaming data */ +}; + +/* + * Tracking of streaming I/O buffers. + */ +struct ccic_sio_buffer { + struct list_head list; + struct v4l2_buffer v4lbuf; + char *buffer; /* Where it lives in kernel space */ + int mapcount; + struct ccic_camera *cam; + struct vm_area_struct *svma; +}; + +/* + * A description of one of our devices. + * Locking: controlled by s_mutex. Certain fields, however, require + * the dev_lock spinlock; they are marked as such by comments. + * dev_lock is also required for access to device registers. + */ +struct ccic_camera +{ + enum ccic_state state; + unsigned long flags; /* Buffer status, mainly (dev_lock) */ + int users; /* How many open FDs */ + struct file *owner; /* Who has data access (v4l2) */ + + /* + * Subsystem structures. + */ + int irq; + struct platform_device *pdev; + struct video_device v4ldev; + struct i2c_adapter i2c_adapter; + struct i2c_client *sensor; + struct i2c_client *sensors[SENSOR_MAX]; + unsigned int bus_type[SENSOR_MAX]; /* parrallel or MIPI */ + + unsigned char __iomem *regs; + struct list_head dev_list; /* link to other devices */ + + /* DMA buffers */ + unsigned int nbufs; /* How many are alloc'd */ + int next_buf; /* Next to consume (dev_lock) */ + unsigned int dma_buf_size; /* allocated size */ + int order[MAX_DMA_BUFS]; /* Internal buffer addresses */ + void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */ + dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */ + unsigned int specframes; /* Unconsumed spec frames (dev_lock) */ + unsigned int sequence; /* Frame sequence number */ + unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual buffers */ + + /* Streaming buffers */ + unsigned int n_sbufs; /* How many we have */ + struct ccic_sio_buffer *sb_bufs; /* The array of housekeeping structs */ + struct list_head sb_avail; /* Available for data (we own) (dev_lock) */ + struct list_head sb_full; /* With data (user space owns) (dev_lock) */ + struct tasklet_struct s_tasklet; + + /* Current operating parameters */ + u32 sensor_type; /* Currently ov7670 only */ + struct v4l2_pix_format pix_format; + + /* Locks */ + struct mutex s_mutex; /* Access to this structure */ + spinlock_t dev_lock; /* Access to device */ + + /* Misc */ + wait_queue_head_t iowait; /* Waiting on frame data */ +#ifdef CONFIG_VIDEO_ADV_DEBUG + struct dentry *dfs_regs; + struct dentry *dfs_cam_regs; +#endif +}; + +#define BUS_PARALLEL 0x01 +#define BUS_MIPI 0x02 +#define BUS_IS_PARALLEL(b) (b & BUS_PARALLEL) +#define BUS_IS_MIPI(b) (b & BUS_MIPI) + +/* + * Status flags. Always manipulated with bit operations. + */ +#define CF_BUF0_VALID 0 /* Buffers valid - first three */ +#define CF_BUF1_VALID 1 +#define CF_BUF2_VALID 2 +#define CF_DMA_ACTIVE 3 /* A frame is incoming */ +#define CF_CONFIG_NEEDED 4 /* Must configure hardware */ + +static int detected_high = 0; +static int detected_low = 0; +static int sensor_selected = 2; +static int sof, jpeg_cnt, lines; + +struct clk *rst_clk; +struct clk *pxa168_ccic_gate_clk; +spinlock_t reg_lock; +EXPORT_SYMBOL(pxa168_ccic_gate_clk); + +static int ccic_enable_clk(struct ccic_camera *cam, struct sensor_platform_data *pdata); + +/* + * Start over with DMA buffers - dev_lock needed. + */ +static void ccic_reset_buffers(struct ccic_camera *cam) +{ + int i; + + cam->next_buf = -1; + for (i = 0; i < cam->nbufs; i++) + clear_bit(i, &cam->flags); + cam->specframes = 0; +} + +static inline int ccic_needs_config(struct ccic_camera *cam) +{ + return test_bit(CF_CONFIG_NEEDED, &cam->flags); +} + +static void ccic_set_config_needed(struct ccic_camera *cam, int needed) +{ + if (needed) + set_bit(CF_CONFIG_NEEDED, &cam->flags); + else + clear_bit(CF_CONFIG_NEEDED, &cam->flags); +} + + + + +/* + * Debugging and related. + */ +#define cam_err(cam, fmt, arg...) \ + dev_err(&(cam)->pdev->dev, fmt, ##arg); +#define cam_warn(cam, fmt, arg...) \ + dev_warn(&(cam)->pdev->dev, fmt, ##arg); +#define cam_dbg(cam, fmt, arg...) \ + dev_dbg(&(cam)->pdev->dev, fmt, ##arg); + + +/* ---------------------------------------------------------------------*/ +/* + * We keep a simple list of known devices to search at open time. + */ +static LIST_HEAD(ccic_dev_list); +static DEFINE_MUTEX(ccic_dev_list_lock); +static int __ccic_cam_cmd(struct ccic_camera *cam, int cmd, void *arg); +static int __ccic_cam_reset(struct ccic_camera *cam); + +static void ccic_add_dev(struct ccic_camera *cam) +{ + mutex_lock(&ccic_dev_list_lock); + list_add_tail(&cam->dev_list, &ccic_dev_list); + mutex_unlock(&ccic_dev_list_lock); +} + +static void ccic_remove_dev(struct ccic_camera *cam) +{ + mutex_lock(&ccic_dev_list_lock); + list_del(&cam->dev_list); + mutex_unlock(&ccic_dev_list_lock); +} + +static struct ccic_camera *ccic_find_dev(int minor) +{ + struct ccic_camera *cam; + + mutex_lock(&ccic_dev_list_lock); + list_for_each_entry(cam, &ccic_dev_list, dev_list) { + if (cam->v4ldev.minor == minor) + goto done; + } + cam = NULL; + done: + mutex_unlock(&ccic_dev_list_lock); + return cam; +} + + +static struct ccic_camera *ccic_find_by_pdev(struct platform_device *pdev) +{ + struct ccic_camera *cam; + + mutex_lock(&ccic_dev_list_lock); + list_for_each_entry(cam, &ccic_dev_list, dev_list) { + if (cam->pdev == pdev) + goto done; + } + cam = NULL; + done: + mutex_unlock(&ccic_dev_list_lock); + return cam; +} + + +/* ------------------------------------------------------------------------ */ +/* + * Device register I/O + */ +static inline void ccic_reg_write(struct ccic_camera *cam, unsigned int reg, + unsigned int val) +{ + __raw_writel(val, cam->regs + reg); +} + +static inline unsigned int ccic_reg_read(struct ccic_camera *cam, + unsigned int reg) +{ + return __raw_readl(cam->regs + reg); +} + + +static inline void ccic_reg_write_mask(struct ccic_camera *cam, unsigned int reg, + unsigned int val, unsigned int mask) +{ + unsigned int v = ccic_reg_read(cam, reg); + + v = (v & ~mask) | (val & mask); + ccic_reg_write(cam, reg, v); +} + +static inline void ccic_reg_clear_bit(struct ccic_camera *cam, + unsigned int reg, unsigned int val) +{ + ccic_reg_write_mask(cam, reg, 0, val); +} + +static inline void ccic_reg_set_bit(struct ccic_camera *cam, + unsigned int reg, unsigned int val) +{ + ccic_reg_write_mask(cam, reg, val, val); +} + +/*provided for sensor calling to enable clock at begining of probe*/ +void ccic_set_clock(unsigned int reg, unsigned int val) +{ + struct ccic_camera *cam; + cam = ccic_find_dev(0); + ccic_reg_write(cam, reg, val); +} + +unsigned int ccic_get_clock(unsigned int reg) +{ + struct ccic_camera *cam; + cam = ccic_find_dev(0); + return ccic_reg_read(cam, reg); +} +/* +1. APMU CCIC clock reset control +2. CCIC power on +3. enable MCLK +*/ +void ccic_set_clock_mipi(void) +{ + unsigned long flags; + + clk_set_rate(rst_clk, 0x6abf); + spin_lock_irqsave(®_lock, flags); + //enable MIPI by setting bit 25/26 of apmu_debug register + __raw_writel(0x06000000 | __raw_readl(APMU_CCIC_DBG), APMU_CCIC_DBG); + spin_unlock_irqrestore(®_lock, flags); + + clk_enable(pxa168_ccic_gate_clk); + + /* + * workaround for stress streamon/streamoff: + * CCIC_CTRL_1[30] (DMAREQCTRL) should be set during capture + * to avoid DMA deadly waiting in case of two SOF without EOF between them. + */ + ccic_set_clock(REG_CTRL1, 0x4a00003c); //CCIC power on. bit28 (PWRDNEN) must be cleared. + ccic_set_clock(REG_CLKCTRL, (0x2<<29 | 0xc)); //enable MCLK +} + +void ccic_set_clock_parallel(void) +{ + clk_set_rate(rst_clk, 0xbff); + clk_enable(pxa168_ccic_gate_clk); + if (machine_is_aspenite() || machine_is_ttc_dkb()) { + + ccic_set_clock(REG_CTRL1, 0x0c00003c); //Kevin //CCIC power on. bit28 (PWRDNEN) must be cleared. + ccic_set_clock(REG_CLKCTRL, 0x40000004); //enable MCLK + ccic_set_clock(0x1ec, 0x00004); //undocumented register??? + } else if (machine_is_avengers_lite() || machine_is_ipcam()) { + clk_set_rate(rst_clk, 0x5b); + clk_enable(pxa168_ccic_gate_clk); + + ccic_set_clock(REG_CTRL1, 0x4c00003c); + ccic_set_clock(REG_CLKCTRL, 0x40000004); /* enable MCLK */ + ccic_set_clock(0x1ec, 0x00004); /* undocumented register??? */ + } else { + clk_set_rate(rst_clk, 0x5b); + + ccic_set_clock(REG_CTRL1, 0x4c00003c); //CCIC power on. bit28 (PWRDNEN) must be cleared. + ccic_set_clock(REG_CLKCTRL, 0x2); //enable MCLK + } +} +EXPORT_SYMBOL (ccic_set_clock_parallel); + +void ccic_disable_clock(void) +{ + unsigned long flags; + + clk_set_rate(rst_clk, 0x6800); + ccic_set_clock(REG_CLKCTRL, 0x0); + clk_set_rate(pxa168_ccic_gate_clk, 0x0); + ccic_set_clock(REG_CTRL1, 0x0); + /* disable MIPI */ + spin_lock_irqsave(®_lock, flags); + __raw_writel((~0x06000000) & __raw_readl(APMU_CCIC_DBG), APMU_CCIC_DBG); + spin_unlock_irqrestore(®_lock, flags); +} +EXPORT_SYMBOL (ccic_disable_clock); + +void ccic_disable_mclk(void) +{ + ccic_set_clock(REG_CLKCTRL, 0x0); +} + +void ccic_enable_vclk(void) +{ + ccic_set_clock(REG_CLKCTRL, 0x40000004); +} +EXPORT_SYMBOL(ccic_enable_vclk); + +void ccic_enable_mclk(struct ccic_camera *cam) +{ + if (BUS_IS_MIPI(cam->bus_type[sensor_selected])){ + ccic_set_clock(REG_CLKCTRL, (0x2<<29 | 0xc)); + } else if (sensor_selected == SENSOR_LOW) { + if (machine_is_avengers_lite() || machine_is_ipcam()) + ccic_set_clock(REG_CLKCTRL, 0x40000004); + else + ccic_set_clock(REG_CLKCTRL, 0x2); + } +} + +/* Somebody is on the bus */ +static void ccic_ctlr_stop_dma(struct ccic_camera *cam); +static void ccic_ctlr_power_down(struct ccic_camera *cam); + +int ccic_sensor_attach(struct i2c_client *client) +{ + struct ccic_camera *cam; + int ret; + struct v4l2_dbg_chip_ident chip; + + cam = ccic_find_dev(0); //TODO - only support one camera controller + if (cam == NULL){ + printk("didn't find camera device!\n"); + return -ENODEV; + } + /* + * Don't talk to chips we don't recognize. + */ + + mutex_lock(&cam->s_mutex); + cam->sensor = client; + ret = __ccic_cam_reset(cam); + if (ret) + goto out; + chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR; + chip.match.addr = cam->sensor->addr; + chip.ident = V4L2_IDENT_NONE; + ret = __ccic_cam_cmd(cam, VIDIOC_DBG_G_CHIP_IDENT, &chip); + if (ret) + goto out; + cam->sensor_type = chip.ident; + + if ((cam->sensor_type == V4L2_IDENT_OV7660) || (cam->sensor_type == V4L2_IDENT_OV7670) || (cam->sensor_type == V4L2_IDENT_SIV120A)) { + cam->sensors[SENSOR_LOW] = client; + cam->bus_type[SENSOR_LOW] = BUS_PARALLEL; + detected_low = 1; + sensor_selected = SENSOR_LOW; /* Set default sensor */ + } else if (cam->sensor_type == V4L2_IDENT_OV3640 || cam->sensor_type == V4L2_IDENT_CLI5001) { + cam->sensors[SENSOR_HIGH] = client; + cam->bus_type[SENSOR_HIGH] = BUS_PARALLEL; + detected_high = 1; + if (cam->sensor_type == V4L2_IDENT_OV3640) + cam->bus_type[SENSOR_HIGH] = BUS_MIPI; + sensor_selected = SENSOR_HIGH; /* Set default sensor */ + } else if (cam->sensor_type == V4L2_IDENT_OV7740) { + cam->sensors[SENSOR_LOW] = client; + detected_low = 1; + sensor_selected = SENSOR_LOW; /* Set default sensor */ + printk(KERN_ERR "camera: ov7740 attached\n"); + } else { + cam_err(cam, "Unsupported sensor type %d at addr 0x%x", cam->sensor_type, cam->sensor->addr); + ret = -EINVAL; + goto out; + } +/* Get/set parameters? */ + ret = 0; + cam->state = S_IDLE; + out: + ccic_ctlr_power_down(cam); + cam->sensor = NULL; + mutex_unlock(&cam->s_mutex); + return ret; + +} +EXPORT_SYMBOL (ccic_sensor_attach); + +/* ------------------------------------------------------------------- */ +/* + * Deal with the controller. + */ + +/* + * Do everything we think we need to have the interface operating + * according to the desired format. + */ +static void ccic_ctlr_dma(struct ccic_camera *cam) +{ + struct v4l2_pix_format *fmt = &cam->pix_format; + + ccic_reg_write(cam, REG_Y0BAR, cam->dma_handles[0]); + ccic_reg_write(cam, REG_Y1BAR, cam->dma_handles[1]); + + if (fmt->pixelformat == V4L2_PIX_FMT_YUV422P) { + ccic_reg_write(cam, REG_U0BAR, cam->dma_handles[0] + fmt->width*fmt->height); + ccic_reg_write(cam, REG_V0BAR, cam->dma_handles[0] + fmt->width*fmt->height + fmt->width*fmt->height/2); + ccic_reg_write(cam, REG_U1BAR, cam->dma_handles[1] + fmt->width*fmt->height); + ccic_reg_write(cam, REG_V1BAR, cam->dma_handles[1] + fmt->width*fmt->height + fmt->width*fmt->height/2); + } + if (fmt->pixelformat == V4L2_PIX_FMT_YUV420) { + ccic_reg_write(cam, REG_U0BAR, cam->dma_handles[0] + fmt->width*fmt->height); + ccic_reg_write(cam, REG_V0BAR, cam->dma_handles[0] + fmt->width*fmt->height + fmt->width*fmt->height/4); + ccic_reg_write(cam, REG_U1BAR, cam->dma_handles[1] + fmt->width*fmt->height); + ccic_reg_write(cam, REG_V1BAR, cam->dma_handles[1] + fmt->width*fmt->height + fmt->width*fmt->height/4); + } + + /* + * Store the first two Y buffers (we aren't supporting + * planar formats for now, so no UV bufs). Then either + * set the third if it exists, or tell the controller + * to just use two. + */ + if (cam->nbufs > 2) { + ccic_reg_write(cam, REG_Y2BAR, cam->dma_handles[2]); + if (fmt->pixelformat == V4L2_PIX_FMT_YUV422P) { + ccic_reg_write(cam, REG_U2BAR, cam->dma_handles[2] + fmt->width*fmt->height); + ccic_reg_write(cam, REG_V2BAR, cam->dma_handles[2] + fmt->width*fmt->height + fmt->width*fmt->height/2); + } + if (fmt->pixelformat == V4L2_PIX_FMT_YUV420) { + ccic_reg_write(cam, REG_U2BAR, cam->dma_handles[2] + fmt->width*fmt->height); + ccic_reg_write(cam, REG_V2BAR, cam->dma_handles[2] + fmt->width*fmt->height + fmt->width*fmt->height/4); + } + + ccic_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); + } + else + ccic_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); +} + +static void ccic_ctlr_image(struct ccic_camera *cam) +{ + int imgsz; + struct v4l2_pix_format *fmt = &cam->pix_format; + int widthy = 0, widthuv = 0; + + if (fmt->pixelformat == V4L2_PIX_FMT_YUV420) + imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | (((fmt->bytesperline)*4/3) & IMGSZ_H_MASK); + else + imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) | (fmt->bytesperline & IMGSZ_H_MASK); + printk("%s: CCIC input image size is %x\n", __func__, imgsz); + /* YPITCH just drops the last two bits */ + //ccic_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline, + // IMGP_YP_MASK); + switch (fmt->pixelformat) { + case V4L2_PIX_FMT_YUYV: + widthy = fmt->width*2; + widthuv = fmt->width*2; + break; + case V4L2_PIX_FMT_RGB565: + widthy = fmt->width*2; + widthuv = 0; + break; + case V4L2_PIX_FMT_JPEG: + if (BUS_IS_MIPI(cam->bus_type[sensor_selected])){ + widthy = 0; + widthuv = 0; + imgsz = 0x1fff3fff; + }else{/* same as 422pack for parallel */ + widthy = fmt->width*2; + widthuv = fmt->width*2; + } + break; + case V4L2_PIX_FMT_YUV422P: + widthy = fmt->width; + widthuv = fmt->width/2; + break; + case V4L2_PIX_FMT_YUV420: + widthy = fmt->width; + widthuv = fmt->width/2; + break; + default: + break; + } + ccic_reg_write(cam, REG_IMGPITCH, widthuv << 16 | widthy); + ccic_reg_write(cam, REG_IMGSIZE, imgsz); + ccic_reg_write(cam, REG_IMGOFFSET, 0x0); +/* + * Tell the controller about the image format we are using. + */ + switch (cam->pix_format.pixelformat) { + case V4L2_PIX_FMT_YUV422P: + ccic_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV|C0_YUV_PLANAR|C0_YUVE_YVYU, /*the endianness of sensor output is UYVY(Y1CrY0Cb)*/ + C0_DF_MASK); + break; + case V4L2_PIX_FMT_YUV420: + ccic_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV|C0_YUV_420PL|C0_YUVE_YVYU, /*the endianness of sensor output is UYVY(Y1CrY0Cb)*/ + C0_DF_MASK); + break; + + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_JPEG: //C0_YUV_PACKED must be set for JPEG?! + ccic_reg_write_mask(cam, REG_CTRL0, + C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV, /*the endianness of sensor output is UYVY(Y1CrY0Cb)*/ + C0_DF_MASK); + break; + + case V4L2_PIX_FMT_RGB444: + ccic_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB, + C0_DF_MASK); + /* Alpha value? */ + break; + + case V4L2_PIX_FMT_RGB565: + ccic_reg_write_mask(cam, REG_CTRL0, + C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR, + C0_DF_MASK); + break; + + default: + cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat); + break; + } + /* + * Make sure it knows we want to use hsync/vsync. + */ + ccic_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK); + printk("%s:REG_CTRL0 = %x\n ", __func__, ccic_get_clock(REG_CTRL0)); +} + + +/* + * Configure the controller for operation; caller holds the + * device mutex. + */ +static int ccic_ctlr_configure(struct ccic_camera *cam) +{ + unsigned long flags; + spin_lock_irqsave(&cam->dev_lock, flags); + ccic_ctlr_dma(cam); + ccic_ctlr_image(cam); + ccic_set_config_needed(cam, 0); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return 0; +} + +static void ccic_ctlr_irq_enable(struct ccic_camera *cam) +{ + /* + * Clear any pending interrupts, since we do not + * expect to have I/O active prior to enabling. + */ + ccic_reg_write(cam, REG_IRQSTAT, FRAMEIRQS); + ccic_reg_set_bit(cam, REG_IRQMASK, FRAMEIRQS); +} + +static void ccic_ctlr_irq_disable(struct ccic_camera *cam) +{ + ccic_reg_clear_bit(cam, REG_IRQMASK, FRAMEIRQS); +} + +/* + * Make the controller start grabbing images. Everything must + * be set up before doing this. + */ +static void ccic_ctlr_start(struct ccic_camera *cam) +{ + /* set_bit performs a read, so no other barrier should be + needed here */ + ccic_reg_set_bit(cam, REG_CTRL0, C0_ENABLE); +} + +static void ccic_ctlr_stop(struct ccic_camera *cam) +{ + ccic_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); +} + +void ccic_ctlr_init(struct ccic_camera *cam) +{ + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + /* + * Make sure it's not powered down. + */ + ccic_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); + /* + * Turn off the enable bit. It sure should be off anyway, + * but it's good to be sure. + */ + ccic_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE); + /* + * Mask all interrupts. + */ + ccic_reg_write(cam, REG_IRQMASK, 0); + /* + * Clock the sensor appropriately. Controller clock should + * be 48MHz, sensor "typical" value is half that. + */ + spin_unlock_irqrestore(&cam->dev_lock, flags); +} +/* + * Stop the controller, and don't return until we're really sure that no + * further DMA is going on. + */ +static void ccic_ctlr_stop_dma(struct ccic_camera *cam) +{ + unsigned long flags; + + /* + * Theory: stop the camera controller (whether it is operating + * or not). Delay briefly just in case we race with the SOF + * interrupt, then wait until no DMA is active. + */ + spin_lock_irqsave(&cam->dev_lock, flags); + ccic_ctlr_stop(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); + mdelay(1); + wait_event_timeout(cam->iowait, + !test_bit(CF_DMA_ACTIVE, &cam->flags), HZ); + if (test_bit(CF_DMA_ACTIVE, &cam->flags)) + cam_err(cam, "Timeout waiting for DMA to end\n"); + /* This would be bad news - what now? */ + spin_lock_irqsave(&cam->dev_lock, flags); + + /*CSI2/DPHY need to be cleared, or no EOF will be received*/ + ccic_reg_write(cam, REG_CSI2_DPHY3, 0x0); + ccic_reg_write(cam, REG_CSI2_DPHY6, 0x0); + ccic_reg_write(cam, REG_CSI2_DPHY5, 0x0); + ccic_reg_write(cam, REG_CSI2_CTRL0, 0x0); + + cam->state = S_IDLE; + ccic_ctlr_irq_disable(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +/* + * Power up and down. + */ +void ccic_ctlr_power_up(struct ccic_camera *cam) +{ + unsigned long flags; + + /* + * Part one of the sensor dance: turn the global + * GPIO signal on. + */ + printk("%s: ccic is power up!!!\n", __func__); + spin_lock_irqsave(&cam->dev_lock, flags); + ccic_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +static void ccic_ctlr_power_down(struct ccic_camera *cam) +{ + unsigned long flags; + printk("%s: ccic is power downed!!!\n", __func__); + spin_lock_irqsave(&cam->dev_lock, flags); + ccic_reg_set_bit(cam, REG_CTRL1, C1_PWRDWN); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + +/* -------------------------------------------------------------------- */ +/* + * Communications with the sensor. + */ + +static int __ccic_cam_cmd(struct ccic_camera *cam, int cmd, void *arg) +{ + struct i2c_client *sc = cam->sensor; + int ret; + + if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL) + return -EINVAL; + ret = sc->driver->command(sc, cmd, arg); + if (ret == -EPERM) /* Unsupported command */ + return 0; + return ret; +} + +static int __ccic_cam_reset(struct ccic_camera *cam) +{ + int zero = 0; + return __ccic_cam_cmd(cam, VIDIOC_INT_RESET, &zero); +} + +/* + * We have found the sensor on the i2c. Let's try to have a + * conversation. + */ + +static int ccic_cam_configure(struct ccic_camera *cam) +{ + struct v4l2_format fmt; + int ret, zero = 0; + + if (cam->state != S_IDLE) + return -EINVAL; + fmt.fmt.pix = cam->pix_format; + ret = __ccic_cam_cmd(cam, VIDIOC_S_FMT, &fmt); + + /* + * OV7670 does weird things if flip is set *before* format... + */ +// ret += ccic_cam_set_flip(cam); //TODO - no need to flip... + return ret; +} + +/* -------------------------------------------------------------------- */ +/* + * DMA buffer management. These functions need s_mutex held. + */ + +/* FIXME: this is inefficient as hell, since dma_alloc_coherent just + * does a get_free_pages() call, and we waste a good chunk of an orderN + * allocation. Should try to allocate the whole set in one chunk. + */ +static int ccic_alloc_dma_bufs(struct ccic_camera *cam, int loadtime) +{ + int i; + + ccic_set_config_needed(cam, 1); + if (loadtime) + cam->dma_buf_size = dma_buf_size; + else + cam->dma_buf_size = cam->pix_format.sizeimage; + if (n_dma_bufs > 3) + n_dma_bufs = 3; + + cam->nbufs = 0; + for (i = 0; i < n_dma_bufs; i++) { +#if DMA_POOL + cam->dma_bufs[i] = dma_alloc_coherent(&cam->pdev->dev, + cam->dma_buf_size, cam->dma_handles + i, + GFP_KERNEL); +#else + cam->order[i] = get_order(cam->dma_buf_size); + cam->dma_bufs[i] = (void *)__get_free_pages(GFP_KERNEL, cam->order[i]); + cam->dma_handles[i] = __pa(cam->dma_bufs[i]); +#endif + if (cam->dma_bufs[i] == NULL) { + cam_warn(cam, "Failed to allocate DMA buffer\n"); + break; + } + /* For debug, remove eventually */ + memset(cam->dma_bufs[i], 0xcc, cam->dma_buf_size); + (cam->nbufs)++; + } + + switch (cam->nbufs) { + case 1: +#if DMA_POOL + dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size, + cam->dma_bufs[0], cam->dma_handles[0]); +#else + free_pages((unsigned long)cam->dma_bufs[0], cam->order[0]); +#endif + cam->nbufs = 0; + case 0: + cam_err(cam, "Insufficient DMA buffers, cannot operate\n"); + return -ENOMEM; + + case 2: + if (n_dma_bufs > 2) + cam_warn(cam, "Will limp along with only 2 buffers\n"); + break; + } + return 0; +} + +static void ccic_free_dma_bufs(struct ccic_camera *cam) +{ + int i; + + for (i = 0; i < cam->nbufs; i++) { +#if DMA_POOL + dma_free_coherent(&cam->pdev->dev, cam->dma_buf_size, + cam->dma_bufs[i], cam->dma_handles[i]); +#else + free_pages((unsigned long)cam->dma_bufs[i], cam->order[i]); +#endif + cam->dma_bufs[i] = NULL; + } + cam->nbufs = 0; +} + + + + + +/* ----------------------------------------------------------------------- */ +/* + * Here starts the V4L2 interface code. + */ + +/* + * Read an image from the device. + */ +static ssize_t ccic_deliver_buffer(struct ccic_camera *cam, + char __user *buffer, size_t len, loff_t *pos) +{ + int bufno; + unsigned long flags; + + spin_lock_irqsave(&cam->dev_lock, flags); + if (cam->next_buf < 0) { + cam_err(cam, "deliver_buffer: No next buffer\n"); + spin_unlock_irqrestore(&cam->dev_lock, flags); + return -EIO; + } + bufno = cam->next_buf; + clear_bit(bufno, &cam->flags); + if (++(cam->next_buf) >= cam->nbufs) + cam->next_buf = 0; + if (! test_bit(cam->next_buf, &cam->flags)) + cam->next_buf = -1; + cam->specframes = 0; + spin_unlock_irqrestore(&cam->dev_lock, flags); + + if (len > cam->pix_format.sizeimage) + len = cam->pix_format.sizeimage; +#if !DMA_POOL + dma_sync_single_for_device(&cam->pdev->dev, + cam->dma_handles[bufno], + len, DMA_FROM_DEVICE); +#endif + if (copy_to_user(buffer, cam->dma_bufs[bufno], len)) + return -EFAULT; + (*pos) += len; + return len; +} + +/* + * Get everything ready, and start grabbing frames. + */ +static int ccic_read_setup(struct ccic_camera *cam, enum ccic_state state) +{ + int ret; + unsigned long flags; + + /* + * Configuration. If we still don't have DMA buffers, + * make one last, desperate attempt. + */ + if (cam->nbufs == 0) + if (ccic_alloc_dma_bufs(cam, 0)) + return -ENOMEM; + + if (ccic_needs_config(cam)) { + ccic_cam_configure(cam); + ret = ccic_ctlr_configure(cam); + if (ret) + return ret; + } + + /* + * Turn it loose. + */ + spin_lock_irqsave(&cam->dev_lock, flags); + ccic_reset_buffers(cam); + ccic_ctlr_irq_enable(cam); + cam->state = state; + + + if (BUS_IS_MIPI(cam->bus_type[sensor_selected])){ + //TODO DPHY clock tunning + ccic_reg_write(cam, REG_CSI2_DPHY6, 0x0a00); + ccic_reg_write(cam, REG_CSI2_DPHY3, 0x0a06); + ccic_reg_write(cam, REG_CSI2_DPHY5, 0x33); + if ((cam->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + && (!cpu_is_pxa910_Ax())){ + ccic_reg_write(cam, REG_CSI2_DPHY6, 0x0802); + ccic_reg_write(cam, REG_CSI2_DPHY3, 0x0804); + } + if(!cpu_is_pxa910_Ax())/* old stepping IC */ + ccic_reg_write(cam, REG_CSI2_DPHY5, 0x3c0); + + ccic_reg_write(cam, REG_CSI2_CTRL0, 0x43); + } else { + ccic_reg_write(cam, REG_CSI2_DPHY3, 0x0); + ccic_reg_write(cam, REG_CSI2_DPHY6, 0x0); + ccic_reg_write(cam, REG_CSI2_DPHY5, 0x0); + ccic_reg_write(cam, REG_CSI2_CTRL0, 0x0); + } + + + ccic_ctlr_start(cam); + spin_unlock_irqrestore(&cam->dev_lock, flags); + __ccic_cam_cmd(cam, VIDIOC_STREAMON, NULL); + return 0; +} + + +static ssize_t ccic_v4l_read(struct file *filp, + char __user *buffer, size_t len, loff_t *pos) +{ + struct ccic_camera *cam = filp->private_data; + int ret = 0; + + /* + * Perhaps we're in speculative read mode and already + * have data? + */ + mutex_lock(&cam->s_mutex); + if (cam->state == S_SPECREAD) { + if (cam->next_buf >= 0) { + ret = ccic_deliver_buffer(cam, buffer, len, pos); + if (ret != 0) + goto out_unlock; + } + } else if (cam->state == S_FLAKED || cam->state == S_NOTREADY) { + ret = -EIO; + goto out_unlock; + } else if (cam->state != S_IDLE) { + ret = -EBUSY; + goto out_unlock; + } + + /* + * v4l2: multiple processes can open the device, but only + * one gets to grab data from it. + */ + if (cam->owner && cam->owner != filp) { + ret = -EBUSY; + goto out_unlock; + } + cam->owner = filp; + + /* + * Do setup if need be. + */ + if (cam->state != S_SPECREAD) { + ret = ccic_read_setup(cam, S_SINGLEREAD); + if (ret) + goto out_unlock; + } + /* + * Wait for something to happen. This should probably + * be interruptible (FIXME). + */ + wait_event_timeout(cam->iowait, cam->next_buf >= 0, HZ); + if (cam->next_buf < 0) { + cam_err(cam, "read() operation timed out\n"); + ccic_ctlr_stop_dma(cam); + ret = -EIO; + goto out_unlock; + } + /* + * Give them their data and we should be done. + */ + ret = ccic_deliver_buffer(cam, buffer, len, pos); + + out_unlock: + mutex_unlock(&cam->s_mutex); + return ret; +} + +/* + * Streaming I/O support. + */ + +static int ccic_vidioc_streamon(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct ccic_camera *cam = filp->private_data; + int ret = -EINVAL; + + if((cam->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) && + BUS_IS_PARALLEL(cam->bus_type[sensor_selected])){ + sof = 7; + jpeg_cnt = 0; + } + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&cam->s_mutex); + if (cam->state != S_IDLE || cam->n_sbufs == 0) + goto out_unlock; + + cam->sequence = 0; +// ccic_enable_mclk(); //for power optimization + ret = ccic_read_setup(cam, S_STREAMING); + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + +static int ccic_vidioc_streamoff(struct file *filp, void *priv, + enum v4l2_buf_type type) +{ + struct ccic_camera *cam = filp->private_data; + int ret = -EINVAL; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + mutex_lock(&cam->s_mutex); + if (cam->state != S_STREAMING) + goto out_unlock; + + __ccic_cam_cmd(cam, VIDIOC_STREAMOFF, NULL); + ccic_ctlr_stop_dma(cam); +// ccic_disable_mclk(); //for power optimization + ret = 0; + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + + +static int ccic_setup_siobuf(struct ccic_camera *cam, int index) +{ + struct ccic_sio_buffer *buf = cam->sb_bufs + index; + + INIT_LIST_HEAD(&buf->list); + buf->v4lbuf.length = PAGE_ALIGN(cam->pix_format.sizeimage); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + buf->mapcount = 0; + buf->cam = cam; + + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + /* + * Offset: must be 32-bit even on a 64-bit system. videobuf-dma-sg + * just uses the length times the index, but the spec warns + * against doing just that - vma merging problems. So we + * leave a gap between each pair of buffers. + */ + buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; + return 0; +} + +static int ccic_free_sio_buffers(struct ccic_camera *cam) +{ + int i; + + /* + * If any buffers are mapped, we cannot free them at all. + */ + for (i = 0; i < cam->n_sbufs; i++) + if (cam->sb_bufs[i].mapcount > 0) + return -EBUSY; + /* + * OK, let's do it. + */ + for (i = 0; i < cam->n_sbufs; i++) + vfree(cam->sb_bufs[i].buffer); + cam->n_sbufs = 0; + kfree(cam->sb_bufs); + cam->sb_bufs = NULL; + INIT_LIST_HEAD(&cam->sb_avail); + INIT_LIST_HEAD(&cam->sb_full); + return 0; +} + + + +static int ccic_vidioc_reqbufs(struct file *filp, void *priv, + struct v4l2_requestbuffers *req) +{ + struct ccic_camera *cam = filp->private_data; + int ret = 0; /* Silence warning */ + + /* + * Make sure it's something we can do. User pointers could be + * implemented without great pain, but that's not been done yet. + */ + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + /* + * If they ask for zero buffers, they really want us to stop streaming + * (if it's happening) and free everything. Should we check owner? + */ + mutex_lock(&cam->s_mutex); + if (req->count == 0) { + if (cam->state == S_STREAMING) + ccic_ctlr_stop_dma(cam); + ret = ccic_free_sio_buffers (cam); + goto out; + } + /* + * Device needs to be idle and working. We *could* try to do the + * right thing in S_SPECREAD by shutting things down, but it + * probably doesn't matter. + */ + if (cam->state != S_IDLE || (cam->owner && cam->owner != filp)) { + ret = -EBUSY; + goto out; + } + cam->owner = filp; + + if (req->count < min_buffers) + req->count = min_buffers; + else if (req->count > max_buffers) + req->count = max_buffers; + if (cam->n_sbufs > 0) { + ret = ccic_free_sio_buffers(cam); + if (ret) + goto out; + } + + cam->sb_bufs = kzalloc(req->count*sizeof(struct ccic_sio_buffer), + GFP_KERNEL); + if (cam->sb_bufs == NULL) { + ret = -ENOMEM; + goto out; + } + for (cam->n_sbufs = 0; cam->n_sbufs < req->count; (cam->n_sbufs++)) { + ret = ccic_setup_siobuf(cam, cam->n_sbufs); + if (ret) + break; + } + + if (cam->n_sbufs == 0) /* no luck at all - ret already set */ + kfree(cam->sb_bufs); + req->count = cam->n_sbufs; /* In case of partial success */ + + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int ccic_vidioc_querybuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct ccic_camera *cam = filp->private_data; + int ret = -EINVAL; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= cam->n_sbufs) + goto out; + *buf = cam->sb_bufs[buf->index].v4lbuf; + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int ccic_vidioc_qbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct ccic_camera *cam = filp->private_data; + struct ccic_sio_buffer *sbuf; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out; + if (buf->index < 0 || buf->index >= cam->n_sbufs) + goto out; + sbuf = cam->sb_bufs + buf->index; + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) { + ret = 0; /* Already queued?? */ + goto out; + } + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_DONE) { + /* Spec doesn't say anything, seems appropriate tho */ + ret = -EBUSY; + goto out; + } + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + spin_lock_irqsave(&cam->dev_lock, flags); + list_add(&sbuf->list, &cam->sb_avail); + spin_unlock_irqrestore(&cam->dev_lock, flags); + ret = 0; + + flush_cache_range(sbuf->svma, (unsigned long)sbuf->buffer, (unsigned long)sbuf->buffer + cam->pix_format.sizeimage); + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int ccic_vidioc_dqbuf(struct file *filp, void *priv, + struct v4l2_buffer *buf) +{ + struct ccic_camera *cam = filp->private_data; + struct ccic_sio_buffer *sbuf; + int ret = -EINVAL; + unsigned long flags; + + mutex_lock(&cam->s_mutex); + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + goto out_unlock; + if (cam->state != S_STREAMING) + goto out_unlock; + if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto out_unlock; + } + + while (list_empty(&cam->sb_full) && cam->state == S_STREAMING) { + mutex_unlock(&cam->s_mutex); + if (wait_event_interruptible(cam->iowait, + !list_empty(&cam->sb_full))) { + ret = -ERESTARTSYS; + goto out; + } + mutex_lock(&cam->s_mutex); + } + + if (cam->state != S_STREAMING) + ret = -EINTR; + else { + spin_lock_irqsave(&cam->dev_lock, flags); + /* Should probably recheck !list_empty() here */ + sbuf = list_entry(cam->sb_full.next, + struct ccic_sio_buffer, list); + list_del_init(&sbuf->list); + spin_unlock_irqrestore(&cam->dev_lock, flags); + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + *buf = sbuf->v4lbuf; + ret = 0; + } + + out_unlock: + mutex_unlock(&cam->s_mutex); + out: + return ret; +} + + + +static void ccic_v4l_vm_open(struct vm_area_struct *vma) +{ + struct ccic_sio_buffer *sbuf = vma->vm_private_data; + /* + * Locking: done under mmap_sem, so we don't need to + * go back to the camera lock here. + */ + sbuf->mapcount++; + + /* FIXME: + * Workaround only. The svma now could only be set + * by the first process opens the driver. + */ + if (!sbuf->svma) + sbuf->svma = vma; +} + + +static void ccic_v4l_vm_close(struct vm_area_struct *vma) +{ + struct ccic_sio_buffer *sbuf = vma->vm_private_data; + + mutex_lock(&sbuf->cam->s_mutex); + sbuf->mapcount--; + /* Docs say we should stop I/O too... */ + if (sbuf->mapcount == 0) { + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; + sbuf->svma = 0; + } + mutex_unlock(&sbuf->cam->s_mutex); +} + +static struct vm_operations_struct ccic_v4l_vm_ops = { + .open = ccic_v4l_vm_open, + .close = ccic_v4l_vm_close +}; + + +static int ccic_v4l_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct ccic_camera *cam = filp->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + int ret = -EINVAL; + int i; + struct ccic_sio_buffer *sbuf = NULL; + + //if (! (vma->vm_flags & VM_WRITE) || ! (vma->vm_flags & VM_SHARED)) + //if (! (vma->vm_flags & VM_SHARED)) + // return -EINVAL; + /* + * Find the buffer they are looking for. + */ + mutex_lock(&cam->s_mutex); + for (i = 0; i < cam->n_sbufs; i++) + if (cam->sb_bufs[i].v4lbuf.m.offset == offset) { + sbuf = cam->sb_bufs + i; + break; + } + if (sbuf == NULL) + goto out; + + ret = remap_vmalloc_range(vma, sbuf->buffer, 0); + if (ret) + goto out; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = sbuf; + vma->vm_ops = &ccic_v4l_vm_ops; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + ccic_v4l_vm_open(vma); + ret = 0; + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + + + +static int ccic_v4l_open(struct file *filp) +{ + struct video_device *vd = video_devdata(filp); + struct ccic_camera *cam = container_of(vd, struct ccic_camera, v4ldev); + int ret = 0; + + if (cam == NULL) + return -ENODEV; +#ifdef CONFIG_DVFM + dvfm_disable_op_name("apps_idle", dvfm_dev_idx); + dvfm_disable_op_name("apps_sleep", dvfm_dev_idx); + dvfm_disable_op_name("sys_sleep", dvfm_dev_idx); +#endif + filp->private_data = cam; + + mutex_lock(&cam->s_mutex); + if (cam->users == 0) { + ccic_ctlr_power_up(cam); + ccic_set_config_needed(cam, 1); + /* FIXME make sure this is complete */ + } + + cam->sensor = cam->sensors[sensor_selected]; + mutex_unlock(&cam->s_mutex); + + if (cam->users == 0) { + if (cam->sensor) { + printk(KERN_ERR "Enable cam clk\n"); + ret = ccic_enable_clk(cam, cam->sensor->dev.platform_data); + + ((struct sensor_platform_data *)cam->sensor->dev.platform_data)->power_on(1, sensor_selected); + + __ccic_cam_reset(cam); + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_S_INPUT, &sensor_selected); + mutex_unlock(&cam->s_mutex); + } else { + printk(KERN_ERR "weird, could not find default sensor\n"); + ret = -ENODEV; + } + } + + mutex_lock(&cam->s_mutex); + (cam->users)++; + mutex_unlock(&cam->s_mutex); + + return ret; +} + +static int ccic_v4l_release(struct file *filp) +{ + struct ccic_camera *cam = filp->private_data; + struct sensor_platform_data *pdata; + pdata = cam->sensor->dev.platform_data; + + mutex_lock(&cam->s_mutex); + (cam->users)--; + if (filp == cam->owner) { + ccic_ctlr_stop_dma(cam); + ccic_free_sio_buffers(cam); + cam->owner = NULL; + } + if (cam->users == 0) { + ccic_disable_mclk(); + pdata->power_on(0, sensor_selected); +// if (detected_high == 1) //only 7660 can be powered off +// pdata->power_off(1); + ccic_ctlr_power_down(cam); + if (alloc_bufs_at_read) + ccic_free_dma_bufs(cam); + } + mutex_unlock(&cam->s_mutex); +#ifdef CONFIG_DVFM + dvfm_enable_op_name("apps_idle", dvfm_dev_idx); + dvfm_enable_op_name("apps_sleep", dvfm_dev_idx); + dvfm_enable_op_name("sys_sleep", dvfm_dev_idx); +#endif + return 0; +} + +int dump_register(struct ccic_camera *cam) +{ + unsigned int irqs; + spin_lock(&cam->dev_lock); + + irqs = ccic_reg_read(cam, REG_IRQSTAT); + printk("CCIC: REG_IRQSTAT is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_IRQSTATRAW); + printk("CCIC: REG_IRQSTATRAW is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_IRQMASK); + printk("CCIC: REG_IRQMASK is %x\n\n", irqs); + + irqs = ccic_reg_read(cam, REG_IMGPITCH); + printk("CCIC: REG_IMGPITCH is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_IMGSIZE); + printk("CCIC: REG_IMGSIZE is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_IMGOFFSET); + printk("CCIC: REG_IMGOFFSET is %x\n\n", irqs); + + irqs = ccic_reg_read(cam, REG_CTRL0); + printk("CCIC: REG_CTRL0 is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_CTRL1); + printk("CCIC: REG_CTRL1 is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_CLKCTRL); + printk("CCIC: REG_CLKCTRL is %x\n\n", irqs); + + irqs = ccic_reg_read(cam, REG_CSI2_DPHY3); + printk("CCIC: REG_CSI2_DPHY3 is %x\n", irqs); + irqs = ccic_reg_read(cam, REG_CSI2_DPHY5); + printk("CCIC: REG_CSI2_DPHY5 is %x\n\n", irqs); + irqs = ccic_reg_read(cam, REG_CSI2_DPHY6); + printk("CCIC: REG_CSI2_DPHY6 is %x\n\n", irqs); + irqs = ccic_reg_read(cam, REG_CSI2_CTRL0); + printk("CCIC: REG_CSI2_CTRL0 is %x\n\n", irqs); + spin_unlock(&cam->dev_lock); + return 0; +} + +static unsigned int ccic_v4l_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct ccic_camera *cam = filp->private_data; + + poll_wait(filp, &cam->iowait, pt); + if (cam->next_buf >= 0) + return POLLIN | POLLRDNORM; + return 0; +} + + + +static int ccic_vidioc_queryctrl(struct file *filp, void *priv, + struct v4l2_queryctrl *qc) +{ + struct ccic_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_QUERYCTRL, qc); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int ccic_vidioc_g_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct ccic_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_G_CTRL, ctrl); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int ccic_vidioc_s_ctrl(struct file *filp, void *priv, + struct v4l2_control *ctrl) +{ + struct ccic_camera *cam = filp->private_data; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_S_CTRL, ctrl); + mutex_unlock(&cam->s_mutex); + return ret; +} + + + + + +static int ccic_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strcpy(cap->driver, "pxa168_camera"); + strcpy(cap->card, "pxa168_camera"); + cap->version = CCIC_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + + +/* + * The default format we use until somebody says otherwise. + */ + +static struct v4l2_pix_format ccic_def_pix_format = { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .pixelformat = V4L2_PIX_FMT_YUYV, + .field = V4L2_FIELD_NONE, + .bytesperline = CIF_WIDTH*2, + .sizeimage = CIF_WIDTH*VGA_HEIGHT*2, +}; + +/* +static struct v4l2_pix_format ccic_def_pix_format = { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + .pixelformat = V4L2_PIX_FMT_YUV420, + .field = V4L2_FIELD_NONE, + .bytesperline = 640 * 12 / 8, //VGA_WIDTH*2, + .sizeimage = 640 * 480 * 12 / 8, //VGA_WIDTH*VGA_HEIGHT*2, +}; +*/ + +static int ccic_vidioc_enum_fmt_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmt) +{ + struct ccic_camera *cam = priv; + int ret; + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt); + mutex_unlock(&cam->s_mutex); + return ret; +} + + +static int ccic_vidioc_try_fmt_cap (struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct ccic_camera *cam = priv; + int ret; + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_TRY_FMT, fmt); + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int ccic_vidioc_s_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *fmt) +{ + struct ccic_camera *cam = priv; + int ret; + /* + * Can't do anything if the device is not idle + * Also can't if there are streaming buffers in place. + */ + if (cam->state != S_IDLE) { + return -EBUSY; + } + /* + * See if the formatting works in principle. + */ + ret = ccic_vidioc_try_fmt_cap(filp, priv, fmt); + if (ret) + return ret; + /* + * Now we start to change things for real, so let's do it + * under lock. + */ + mutex_lock(&cam->s_mutex); + cam->pix_format = fmt->fmt.pix; + + /* + * Make sure we have appropriate DMA buffers. + */ + ret = -ENOMEM; + if (cam->nbufs > 0 && cam->dma_buf_size < cam->pix_format.sizeimage) + ccic_free_dma_bufs(cam); + if (cam->nbufs == 0) { + if (ccic_alloc_dma_bufs(cam, 0)) + goto out; + } + /* + * It looks like this might work, so let's program the sensor. + */ + ret = ccic_cam_configure(cam); + if (! ret) + ret = ccic_ctlr_configure(cam); + out: + mutex_unlock(&cam->s_mutex); + return ret; +} + +/* + * Return our stored notion of how the camera is/should be configured. + * The V4l2 spec wants us to be smarter, and actually get this from + * the camera (and not mess with it at open time). Someday. + */ +static int ccic_vidioc_g_fmt_cap(struct file *filp, void *priv, + struct v4l2_format *f) +{ + struct ccic_camera *cam = priv; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix = cam->pix_format; + return 0; +} + +/* + * We only have one input - the sensor - so minimize the nonsense here. + */ +static int ccic_vidioc_enum_input(struct file *filp, void *priv, + struct v4l2_input *input) +{ + if ((input->index == 0 && detected_low == 0) || + (input->index == 1 && detected_high == 0) || + (input->index != 0 && input->index != 1)) + return -EINVAL; + + input->type = V4L2_INPUT_TYPE_CAMERA; + /* input->std = V4L2_STD_ALL; */ /* Not sure what should go here */ + strcpy(input->name, "Camera"); + return 0; +} + +static int ccic_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = sensor_selected; + return 0; +} + +static int ccic_enable_clk(struct ccic_camera *cam, struct sensor_platform_data *pdata) +{ + /*TODO need to do below reset for stress test*/ + ccic_disable_clock(); + + if (BUS_IS_MIPI(cam->bus_type[sensor_selected])) + ccic_set_clock_mipi(); + else + ccic_set_clock_parallel(); + + ccic_enable_mclk(cam); + if (pdata->platform_set) + pdata->platform_set(sensor_selected, rst_clk);//set specific platform clk + return 0; +} + +static int ccic_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + struct ccic_camera *cam = filp->private_data; + int ret = 0; + + /* If the required sensor is the same as the current + * active one, return immediately. + */ + if (i == sensor_selected) + return ret; + + if (((i == SENSOR_LOW) && (detected_low == 1)) || ((i == SENSOR_HIGH) && (detected_high == 1))) { + /* switch off the previous sensor power */ + ((struct sensor_platform_data *)cam->sensor->dev.platform_data)->power_on(0, sensor_selected); + cam->sensor = cam->sensors[i]; + sensor_selected = i; + } else { + printk(KERN_ERR "requested sensor %d is NOT attached!\n", i); + return -EINVAL; + } + ret = ccic_enable_clk(cam, cam->sensor->dev.platform_data); + + ((struct sensor_platform_data *)cam->sensor->dev.platform_data)->power_on(1, sensor_selected); + __ccic_cam_reset(cam); + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_S_INPUT, &sensor_selected); + mutex_unlock(&cam->s_mutex); + + return ret; +} + +/* from vivi.c */ +static int ccic_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +{ + return 0; +} + +/* + * G/S_PARM. Most of this is done by the sensor, but we are + * the level which controls the number of read buffers. + */ +static int ccic_vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parms) +{ + struct ccic_camera *cam = priv; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_G_PARM, parms); + mutex_unlock(&cam->s_mutex); + parms->parm.capture.readbuffers = n_dma_bufs; + return ret; +} + +static int ccic_vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parms) +{ + struct ccic_camera *cam = priv; + int ret; + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_S_PARM, parms); + mutex_unlock(&cam->s_mutex); + parms->parm.capture.readbuffers = n_dma_bufs; + return ret; +} + +static int ccic_vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *a) +{ + struct ccic_camera *cam = fh; + struct v4l2_cropcap *ccap = a; + int ret; + + //printk(KERN_ERR "ccic_vidioc_cropcap\n"); + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_CROPCAP, ccap); + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int ccic_vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct ccic_camera *cam = fh; + int ret; + + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_ENUM_FRAMESIZES, fsize); + mutex_unlock(&cam->s_mutex); + return ret; +} + +static void ccic_v4l_dev_release(struct video_device *vd) +{ + struct ccic_camera *cam = container_of(vd, struct ccic_camera, v4ldev); + + kfree(cam); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/*for register access*/ +static int ccic_vidioc_g_register(struct file *filp, void *priv, + struct v4l2_dbg_register *reg) +{ + struct ccic_camera *cam = priv; + int ret; + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_DBG_G_REGISTER, reg); + mutex_unlock(&cam->s_mutex); + return ret; +} + +static int ccic_vidioc_s_register(struct file *filp, void *priv, + struct v4l2_dbg_register *reg) +{ + struct ccic_camera *cam = priv; + int ret; + mutex_lock(&cam->s_mutex); + ret = __ccic_cam_cmd(cam, VIDIOC_DBG_S_REGISTER, reg); + mutex_unlock(&cam->s_mutex); + return ret; + +} +#endif + +static long ccic_v4l_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video_device *vdev = video_devdata(file); + struct ccic_camera *cam = container_of(vdev, struct ccic_camera, v4ldev); + long ret; + + /* Handle some specific cmds */ + switch (cmd) { + case VIDIOC_ENUM_FRAMESIZES: + { + ret = ccic_vidioc_enum_framesizes(file, (void *)cam, + (struct v4l2_frmsizeenum *) arg); + return ret; + } + default: + break; + } + /* Handle other ioctl cmds with standard interface */ + ret = video_ioctl2(file, cmd, arg); + + return ret; +} + +/* + * This template device holds all of those v4l2 methods; we + * clone it for specific real devices. + */ + +static const struct v4l2_file_operations ccic_v4l_fops = { + .owner = THIS_MODULE, + .open = ccic_v4l_open, + .release = ccic_v4l_release, + .read = ccic_v4l_read, + .poll = ccic_v4l_poll, + .mmap = ccic_v4l_mmap, + .ioctl = ccic_v4l_ioctl, +}; +/* upgrade changes from .25 to .28 */ +struct v4l2_ioctl_ops ccic_ioctl_ops = { + .vidioc_querycap = ccic_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = ccic_vidioc_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = ccic_vidioc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = ccic_vidioc_s_fmt_cap, + .vidioc_g_fmt_vid_cap = ccic_vidioc_g_fmt_cap, + .vidioc_enum_input = ccic_vidioc_enum_input, + .vidioc_g_input = ccic_vidioc_g_input, + .vidioc_s_input = ccic_vidioc_s_input, + .vidioc_s_std = ccic_vidioc_s_std, + .vidioc_reqbufs = ccic_vidioc_reqbufs, + .vidioc_querybuf = ccic_vidioc_querybuf, + .vidioc_qbuf = ccic_vidioc_qbuf, + .vidioc_dqbuf = ccic_vidioc_dqbuf, + .vidioc_streamon = ccic_vidioc_streamon, + .vidioc_streamoff = ccic_vidioc_streamoff, + .vidioc_queryctrl = ccic_vidioc_queryctrl, + .vidioc_g_ctrl = ccic_vidioc_g_ctrl, + .vidioc_s_ctrl = ccic_vidioc_s_ctrl, + .vidioc_g_parm = ccic_vidioc_g_parm, + .vidioc_s_parm = ccic_vidioc_s_parm, + .vidioc_cropcap = ccic_vidioc_cropcap, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = ccic_vidioc_g_register, + .vidioc_s_register = ccic_vidioc_s_register, +#endif +}; + +static struct video_device ccic_v4l_template = { + .name = "pxa168-camera", +// .type = VFL_TYPE_GRABBER, +// .type2 = VID_TYPE_CAPTURE, + .vfl_type = VFL_TYPE_GRABBER, //upgrade changes from .25 to .28 + + .minor = -1, /* Get one dynamically */ + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ + + .fops = &ccic_v4l_fops, + .release = ccic_v4l_dev_release, + + .ioctl_ops = &ccic_ioctl_ops, //upgrade changes from .25 to .28 +}; + + + + +/* ---------------------------------------------------------------------- */ +/* + * Interrupt handler stuff + */ + + + +static void ccic_frame_tasklet(unsigned long data) +{ + struct ccic_camera *cam = (struct ccic_camera *) data; + int i; + unsigned long flags; + struct ccic_sio_buffer *sbuf; + spin_lock_irqsave(&cam->dev_lock, flags); + for (i = 0; i < cam->nbufs; i++) { + int bufno = cam->next_buf; + if (bufno < 0) { /* "will never happen" */ + cam_err(cam, "No valid bufs in tasklet!\n"); + break; + } + if (++(cam->next_buf) >= cam->nbufs) + cam->next_buf = 0; + if (! test_bit(bufno, &cam->flags)) + continue; + if (list_empty(&cam->sb_avail)) { + clear_bit(bufno, &cam->flags); //drop current frame because of buffer unavailable + break; /* Leave it valid, hope for better later */ + } + clear_bit(bufno, &cam->flags); +#if !DMA_POOL + dma_sync_single_for_device(&cam->pdev->dev, + cam->dma_handles[bufno], + cam->pix_format.sizeimage, + DMA_FROM_DEVICE); +#endif + if ((cam->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) && ((((char *)cam->dma_bufs[bufno])[0] != 0xff) || (((char *)cam->dma_bufs[bufno])[1] != 0xd8))) { + spin_unlock_irqrestore(&cam->dev_lock, flags); + printk("%s: JPEG ERROR !!! dropped this frame\n", __func__); + return; //drop current JPEG frame because of wrong header + } + + sbuf = list_entry(cam->sb_avail.next, + struct ccic_sio_buffer, list); + /* + * Drop the lock during the big copy. This *should* be safe... + */ + spin_unlock_irqrestore(&cam->dev_lock, flags); + memcpy(sbuf->buffer, cam->dma_bufs[bufno], + cam->pix_format.sizeimage); + if (cam->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) { + /* we can not get line data in parallel mode for new frame vsync resets line register */ + sbuf->v4lbuf.bytesused = cam->pix_format.bytesperline * lines; + }else{ + sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage; + } + sbuf->v4lbuf.sequence = cam->buf_seq[bufno]; + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + spin_lock_irqsave(&cam->dev_lock, flags); + list_move_tail(&sbuf->list, &cam->sb_full); + break; //just exit once current frame done + } + if (! list_empty(&cam->sb_full)) + wake_up(&cam->iowait); + spin_unlock_irqrestore(&cam->dev_lock, flags); +} + + + +static void ccic_frame_complete(struct ccic_camera *cam, int frame) +{ + /* + * Basic frame housekeeping. + */ + if (test_bit(frame, &cam->flags) && printk_ratelimit()) + cam_err(cam, "Frame overrun on %d, frames lost\n", frame); + set_bit(frame, &cam->flags); + clear_bit(CF_DMA_ACTIVE, &cam->flags); + if (cam->next_buf < 0) + cam->next_buf = frame; + cam->buf_seq[frame] = ++(cam->sequence); + + switch (cam->state) { + /* + * If in single read mode, try going speculative. + */ + case S_SINGLEREAD: + cam->state = S_SPECREAD; + cam->specframes = 0; + wake_up(&cam->iowait); + break; + + /* + * If we are already doing speculative reads, and nobody is + * reading them, just stop. + */ + case S_SPECREAD: + if (++(cam->specframes) >= cam->nbufs) { + ccic_ctlr_stop(cam); + ccic_ctlr_irq_disable(cam); + cam->state = S_IDLE; + } + wake_up(&cam->iowait); + break; + /* + * For the streaming case, we defer the real work to the + * camera tasklet. + * + * FIXME: if the application is not consuming the buffers, + * we should eventually put things on hold and restart in + * vidioc_dqbuf(). + */ + case S_STREAMING: + tasklet_schedule(&cam->s_tasklet); + break; + + default: + cam_err(cam, "Frame interrupt in non-operational state\n"); + break; + } +} + + + + +static void ccic_frame_irq(struct ccic_camera *cam, unsigned int irqs) +{ + unsigned int frame; + + /* + * Handle any frame completions. There really should + * not be more than one of these, or we have fallen + * far behind. + */ + for (frame = 0; frame < cam->nbufs; frame++) + if (irqs & (IRQ_EOF0 << frame)) + ccic_frame_complete(cam, frame); + + /* We workaround parallel interface JPEG mode. We can not detect EOF with IRQ, for JPEG file size is variable. + * We set lines as large as that JPEG data never fullfill and use SOF as frame complete indication. */ + if((cam->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) && + BUS_IS_PARALLEL(cam->bus_type[sensor_selected])){ + for (frame = 0; frame < cam->nbufs; frame++){ + /* collect data according to SOF flag */ + if(sof & (1 << frame)){ + if ((jpeg_cnt % 2) && ((((char *)cam->dma_bufs[frame])[0] != 0xff) || (((char *)cam->dma_bufs[frame])[1] != 0xd8))){ + jpeg_cnt ++; + } + + lines = ccic_reg_read(cam, REG_LNNUM); + if(jpeg_cnt %2) /* drop even frames */ + ccic_frame_complete(cam, frame); + sof &= ~(1 << frame); /* mark this buffer is handled */ + } + /* mark new SOF for data later collection */ + if (irqs & (IRQ_SOF0 << frame)){ + sof |= 1 << frame; + jpeg_cnt ++; + } + } + } + /* + * If a frame starts, note that we have DMA active. This + * code assumes that we won't get multiple frame interrupts + * at once; may want to rethink that. + */ + if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) + set_bit(CF_DMA_ACTIVE, &cam->flags); +} + + + +//#define SOF_DEBUG +static irqreturn_t ccic_irq(int irq, void *data) +{ + struct ccic_camera *cam = data; + unsigned int irqs; + + spin_lock(&cam->dev_lock); +#ifdef SOF_DEBUG + unsigned int irqs_raw; + irqs_raw = ccic_reg_read(cam, REG_IRQSTATRAW); +#endif + irqs = ccic_reg_read(cam, REG_IRQSTAT); + ccic_reg_write(cam, REG_IRQSTAT, irqs); //clear irqs here +#ifdef SOF_DEBUG + static unsigned int first = 0, second = 0; + static int counter = 0; + printk("%s: REG_IRQRAWSTAT = %x\n", __func__, irqs_raw); + if (counter++ % 2) + second = irqs; + else + first = irqs; + if (first == second) { + printk("\n########## %s: TWO CONTINUOUS SOF/EOF OCCURED!!! ##########\n\n", __func__); + } +#endif + if ((irqs & ALLIRQS) == 0) { + spin_unlock(&cam->dev_lock); + return IRQ_NONE; + } + if (irqs & FRAMEIRQS) + ccic_frame_irq(cam, irqs); + if (irqs & TWSIIRQS) { + ccic_reg_write(cam, REG_IRQSTAT, TWSIIRQS); + } + spin_unlock(&cam->dev_lock); + return IRQ_HANDLED; +} + + +/* -------------------------------------------------------------------------- */ +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * Debugfs stuff. + */ + +static char ccic_debug_buf[1024]; +static struct dentry *ccic_dfs_root; + +static void ccic_dfs_setup(void) +{ + ccic_dfs_root = debugfs_create_dir("pxa910_camera", NULL); + if (IS_ERR(ccic_dfs_root)) { + ccic_dfs_root = NULL; /* Never mind */ + printk(KERN_NOTICE "pxa910_camera unable to set up debugfs\n"); + } +} + +static void ccic_dfs_shutdown(void) +{ + if (ccic_dfs_root) + debugfs_remove(ccic_dfs_root); +} + +static int ccic_dfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t ccic_dfs_read_regs(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct ccic_camera *cam = file->private_data; + char *s = ccic_debug_buf; + int offset; + + for (offset = 0; offset < 0x44; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + ccic_reg_read(cam, offset)); + for (offset = 0x88; offset <= 0x90; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + ccic_reg_read(cam, offset)); + for (offset = 0xb4; offset <= 0xbc; offset += 4) + s += sprintf(s, "%02x: %08x\n", offset, + ccic_reg_read(cam, offset)); + for (offset = 0x3000; offset <= 0x300c; offset += 4) + s += sprintf(s, "%04x: %08x\n", offset, + ccic_reg_read(cam, offset)); + return simple_read_from_buffer(buf, count, ppos, ccic_debug_buf, + s - ccic_debug_buf); +} + +static const struct file_operations ccic_dfs_reg_ops = { + .owner = THIS_MODULE, + .read = ccic_dfs_read_regs, + .open = ccic_dfs_open +}; + +static ssize_t ccic_dfs_read_cam(struct file *file, + char __user *buf, size_t count, loff_t *ppos) +{ + struct ccic_camera *cam = file->private_data; + char *s = ccic_debug_buf; + int offset; + struct v4l2_dbg_register reg; + + if ((! cam->sensor) || (sensor_selected != SENSOR_LOW)) //3640 has too many registers so only support 7660 + return -EINVAL; + mutex_lock(&cam->s_mutex); + for (offset = 0x0; offset < 0x8a; offset++) + { + reg.reg = offset; + __ccic_cam_cmd(cam, VIDIOC_DBG_G_REGISTER, ®); + s += sprintf(s, "%02x: %02x\n", offset, (u8)reg.val); + } + mutex_unlock(&cam->s_mutex); + return simple_read_from_buffer(buf, count, ppos, ccic_debug_buf, + s - ccic_debug_buf); +} + +static const struct file_operations ccic_dfs_cam_ops = { + .owner = THIS_MODULE, + .read = ccic_dfs_read_cam, + .open = ccic_dfs_open +}; + + + +static void ccic_dfs_cam_setup(struct ccic_camera *cam) +{ + char fname[40]; + + if (!ccic_dfs_root) + return; + sprintf(fname, "regs-%d", cam->v4ldev.minor); + cam->dfs_regs = debugfs_create_file(fname, 0444, ccic_dfs_root, + cam, &ccic_dfs_reg_ops); + sprintf(fname, "cam-%d", cam->v4ldev.minor); + cam->dfs_cam_regs = debugfs_create_file(fname, 0444, ccic_dfs_root, + cam, &ccic_dfs_cam_ops); +} + + +static void ccic_dfs_cam_shutdown(struct ccic_camera *cam) +{ + if (! IS_ERR(cam->dfs_regs)) + debugfs_remove(cam->dfs_regs); + if (! IS_ERR(cam->dfs_cam_regs)) + debugfs_remove(cam->dfs_cam_regs); +} + +#else + +#define ccic_dfs_setup() +#define ccic_dfs_shutdown() +#define ccic_dfs_cam_setup(cam) +#define ccic_dfs_cam_shutdown(cam) +#endif /* CONFIG_VIDEO_ADV_DEBUG */ + + + + +static int pxa910_camera_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + struct ccic_camera *cam; + /* + * Start putting together one of our big camera structures. + */ + ret = -ENOMEM; + rst_clk = clk_get(&pdev->dev, "CCICRSTCLK"); + if (IS_ERR(rst_clk)) { + dev_err(&pdev->dev, "unable to get CCICRSTCLK"); + return PTR_ERR(rst_clk); + } + + pxa168_ccic_gate_clk = clk_get(&pdev->dev, "CCICGATECLK"); + if (IS_ERR(pxa168_ccic_gate_clk)) { + dev_err(&pdev->dev, "unable to get CCICGATECLK"); + return PTR_ERR(pxa168_ccic_gate_clk); + } + + cam = kzalloc(sizeof(struct ccic_camera), GFP_KERNEL); + if (cam == NULL) + goto out; + platform_set_drvdata(pdev, cam); + mutex_init(&cam->s_mutex); + mutex_lock(&cam->s_mutex); + spin_lock_init(&cam->dev_lock); + cam->state = S_NOTREADY; + ccic_set_config_needed(cam, 1); + init_waitqueue_head(&cam->iowait); + cam->pdev = pdev; + cam->pix_format = ccic_def_pix_format; + INIT_LIST_HEAD(&cam->dev_list); + INIT_LIST_HEAD(&cam->sb_avail); + INIT_LIST_HEAD(&cam->sb_full); + tasklet_init(&cam->s_tasklet, ccic_frame_tasklet, (unsigned long) cam); + + //cam_ctx->platform_ops = pdev->dev.platform_data; + //if (cam_ctx->platform_ops == NULL) { + // printk("camera no platform data defined\n"); + // return -ENODEV; + //} + + cam->irq = platform_get_irq(pdev, 0); + if (cam->irq < 0) + return -ENXIO; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + printk("no IO memory resource defined\n"); + return -ENODEV; + } + + ret = -EIO; + cam->regs = ioremap(res->start, SZ_4K); + if (! cam->regs) { + printk(KERN_ERR "Unable to ioremap pxa910-camera regs\n"); + goto out_free; + } + ret = request_irq(cam->irq, ccic_irq, IRQF_SHARED, "pxa168-camera", cam); + if (ret) + goto out_iounmap; + /* + * Initialize the controller and leave it powered up. It will + * stay that way until the sensor driver shows up. + */ + ccic_ctlr_init(cam); + ccic_ctlr_power_up(cam); + /* + * Set up I2C/SMBUS communications. We have to drop the mutex here + * because the sensor could attach in this call chain, leading to + * unsightly deadlocks. + */ + mutex_unlock(&cam->s_mutex); /* attach can deadlock */ + /* + * Get the v4l2 setup done. + */ + mutex_lock(&cam->s_mutex); + cam->v4ldev = ccic_v4l_template; + cam->v4ldev.debug = 0; +// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; +// cam->v4ldev.dev = &pdev->dev; + cam->v4ldev.dev = pdev->dev; //upgrade changes from .25 to .28 + + /* + cam->v4ldev.v4l2_dev = (struct v4l2_device *)kzalloc(sizeof(struct v4l2_device), GFP_ATOMIC); + ret = v4l2_device_register(&pdev->dev, &cam->v4ldev.v4l2_dev); + if (ret) { + printk(KERN_ERR "ccic: v4l2_device_register_failed.\n"); + goto out_freeirq; + } + */ + cam->v4ldev.parent = &pdev->dev; + ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); + if (ret) + goto out_freeirq; + /* + * If so requested, try to get our DMA buffers now. + */ + if (!alloc_bufs_at_read) { + if (ccic_alloc_dma_bufs(cam, 1)) + cam_warn(cam, "Unable to alloc DMA buffers at load" + " will try again later."); + } + ccic_dfs_setup(); + ccic_dfs_cam_setup(cam); + mutex_unlock(&cam->s_mutex); + ccic_add_dev(cam); +// ccic_ctlr_power_down(cam); //for power optimization + return 0; + + out_freeirq: + ccic_ctlr_power_down(cam); + free_irq(cam->irq, cam); + out_iounmap: + iounmap(cam->regs); + out_free: + kfree(cam); + out: + return ret; +} + + +/* + * Shut down an initialized device + */ +static void ccic_shutdown(struct ccic_camera *cam) +{ +/* FIXME: Make sure we take care of everything here */ + ccic_dfs_cam_shutdown(cam); + if (cam->n_sbufs > 0) + /* What if they are still mapped? Shouldn't be, but... */ + ccic_free_sio_buffers(cam); + ccic_remove_dev(cam); + ccic_ctlr_stop_dma(cam); + ccic_ctlr_power_down(cam); + ccic_free_dma_bufs(cam); + free_irq(cam->irq, cam); + iounmap(cam->regs); + video_unregister_device(&cam->v4ldev); + /* kfree(cam); done in v4l_release () */ +} + +static int pxa910_camera_remove(struct platform_device *pdev) +{ + struct ccic_camera *cam = ccic_find_by_pdev(pdev); + + if (cam == NULL) { + printk(KERN_WARNING "remove on unknown pdev %p\n", pdev); + return -ENODEV; + } + mutex_lock(&cam->s_mutex); + if (cam->users > 0) + cam_warn(cam, "Removing a device with users!\n"); + ccic_shutdown(cam); + /* No unlock - it no longer exists */ + + return 0; +} + +#ifdef CONFIG_PM +/* + * Basic power management. + */ +static int ccic_suspend(struct platform_device *dev, pm_message_t state) +{ + struct ccic_camera *cam = platform_get_drvdata(dev); + struct sensor_platform_data *pdata; + enum ccic_state cstate; + + cstate = cam->state; /* HACK - stop_dma sets to idle */ + pdata = cam->sensor->dev.platform_data; + ccic_ctlr_stop_dma(cam); + /* we just keep sensor in idle state in first state. reset sensor need more configuration */ + //pdata->power_on(0, sensor_selected); + ccic_ctlr_power_down(cam); + ccic_disable_clock(); + cam->state = cstate; + return 0; +} + + +static int ccic_resume(struct platform_device *dev) +{ + struct ccic_camera *cam = platform_get_drvdata(dev); + int ret = 0; + + ccic_ctlr_init(cam); + ccic_ctlr_power_down(cam); + + mutex_lock(&cam->s_mutex); + if (cam->users > 0) { + ccic_ctlr_power_up(cam); + ccic_enable_clk(cam, cam->sensor->dev.platform_data); + } + mutex_unlock(&cam->s_mutex); + + set_bit(CF_CONFIG_NEEDED, &cam->flags); + if (cam->state == S_SPECREAD) + cam->state = S_IDLE; /* Don't bother restarting */ + else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING) + ret = ccic_read_setup(cam, cam->state); + return ret; +} + +#endif /* CONFIG_PM */ + +static struct platform_driver pxa910_camera_driver = { + .driver = { + .name = "pxa168-camera" + }, + .probe = pxa910_camera_probe, + .remove = pxa910_camera_remove, +#ifdef CONFIG_PM + .suspend = ccic_suspend, + .resume = ccic_resume, +#endif + +}; + +static int __devinit pxa910_camera_init(void) +{ +#ifdef CONFIG_DVFM + dvfm_register("Camera", &dvfm_dev_idx); +#endif + return platform_driver_register(&pxa910_camera_driver); +} + +static void __exit pxa910_camera_exit(void) +{ + platform_driver_unregister(&pxa910_camera_driver); +#ifdef CONFIG_DVFM + dvfm_unregister("Camera", &dvfm_dev_idx); +#endif +} + +module_init(pxa910_camera_init); +module_exit(pxa910_camera_exit); + diff --git a/drivers/media/video/pxa168_camera.h b/drivers/media/video/pxa168_camera.h new file mode 100644 index 00000000000000..f3406257489dee --- /dev/null +++ b/drivers/media/video/pxa168_camera.h @@ -0,0 +1,145 @@ +/* + * Register definitions for the m88alp01 camera interface. Offsets in bytes + * as given in the spec. + * + * Copyright 2006 One Laptop Per Child Association, Inc. + * + * Written by Jonathan Corbet, corbet@lwn.net. + * + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#define REG_Y0BAR 0x00 +#define REG_Y1BAR 0x04 +#define REG_Y2BAR 0x08 +#define REG_U0BAR 0x0c +#define REG_U1BAR 0x10 +#define REG_U2BAR 0x14 +#define REG_V0BAR 0x18 +#define REG_V1BAR 0x1C +#define REG_V2BAR 0x20 + +//for MIPI enable +#define REG_CSI2_CTRL0 0x100 +#define REG_CSI2_DPHY0 0x120 +#define REG_CSI2_DPHY1 0x124 +#define REG_CSI2_DPHY2 0x128 +#define REG_CSI2_DPHY3 0x12c +#define REG_CSI2_DPHY4 0x130 +#define REG_CSI2_DPHY5 0x134 +#define REG_CSI2_DPHY6 0x138 + +/* ... */ + +#define REG_IMGPITCH 0x24 /* Image pitch register */ +#define IMGP_YP_SHFT 2 /* Y pitch params */ +#define IMGP_YP_MASK 0x00003ffc /* Y pitch field */ +#define IMGP_UVP_SHFT 18 /* UV pitch (planar) */ +#define IMGP_UVP_MASK 0x3ffc0000 +#define REG_IRQSTATRAW 0x28 /* RAW IRQ Status */ +#define IRQ_EOF0 0x00000001 /* End of frame 0 */ +#define IRQ_EOF1 0x00000002 /* End of frame 1 */ +#define IRQ_EOF2 0x00000004 /* End of frame 2 */ +#define IRQ_SOF0 0x00000008 /* Start of frame 0 */ +#define IRQ_SOF1 0x00000010 /* Start of frame 1 */ +#define IRQ_SOF2 0x00000020 /* Start of frame 2 */ +#define IRQ_OVERFLOW 0x00000040 /* FIFO overflow */ +#define IRQ_TWSIW 0x00000100 /* TWSI (smbus) write */ +#define IRQ_TWSIR 0x00000200 /* TWSI read */ +#define IRQ_TWSIE 0x00000400 /* TWSI error */ +#define TWSIIRQS (IRQ_TWSIW|IRQ_TWSIR|IRQ_TWSIE) +#define FRAMEIRQS (IRQ_EOF0|IRQ_EOF1|IRQ_EOF2|IRQ_SOF0|IRQ_SOF1|IRQ_SOF2) +#define ALLIRQS (TWSIIRQS|FRAMEIRQS|IRQ_OVERFLOW) +#define REG_IRQMASK 0x2c /* IRQ mask - same bits as IRQSTAT */ +#define REG_IRQSTAT 0x30 /* IRQ status / clear */ + +#define REG_IMGSIZE 0x34 /* Image size */ +#define IMGSZ_V_MASK 0x1fff0000 +#define IMGSZ_V_SHIFT 16 +#define IMGSZ_H_MASK 0x00003fff +#define REG_IMGOFFSET 0x38 /* IMage offset */ + +#define REG_CTRL0 0x3c /* Control 0 */ +#define C0_ENABLE 0x00000001 /* Makes the whole thing go */ + +/* Mask for all the format bits */ +#define C0_DF_MASK 0x00fffffc /* Bits 2-23 */ + +/* RGB ordering */ +#define C0_RGB4_RGBX 0x00000000 +#define C0_RGB4_XRGB 0x00000004 +#define C0_RGB4_BGRX 0x00000008 +#define C0_RGB4_XBGR 0x0000000c +#define C0_RGB5_RGGB 0x00000000 +#define C0_RGB5_GRBG 0x00000004 +#define C0_RGB5_GBRG 0x00000008 +#define C0_RGB5_BGGR 0x0000000c + +/* Spec has two fields for DIN and DOUT, but they must match, so + combine them here. */ +#define C0_DF_YUV 0x00000000 /* Data is YUV */ +#define C0_DF_RGB 0x000000a0 /* ... RGB */ +#define C0_DF_BAYER 0x00000140 /* ... Bayer */ +/* 8-8-8 must be missing from the below - ask */ +#define C0_RGBF_565 0x00000000 +#define C0_RGBF_444 0x00000800 +#define C0_RGB_BGR 0x00001000 /* Blue comes first */ +#define C0_YUV_PLANAR 0x00000000 /* YUV 422 planar format */ +#define C0_YUV_PACKED 0x00008000 /* YUV 422 packed */ +#define C0_YUV_420PL 0x0000a000 /* YUV 420 planar */ +/* Think that 420 packed must be 111 - ask */ +#define C0_YUVE_YUYV 0x00000000 /* Y1CbY0Cr */ +#define C0_YUVE_YVYU 0x00010000 /* Y1CrY0Cb */ +#define C0_YUVE_VYUY 0x00020000 /* CrY1CbY0 */ +#define C0_YUVE_UYVY 0x00030000 /* CbY1CrY0 */ +#define C0_YUVE_XYUV 0x00000000 /* 420: .YUV */ +#define C0_YUVE_XYVU 0x00010000 /* 420: .YVU */ +#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */ +#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */ +/* Bayer bits 18,19 if needed */ +#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */ +#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */ +#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */ +#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */ +#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */ +#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */ +#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */ + + +#define REG_CTRL1 0x40 /* Control 1 */ +#define C1_444ALPHA 0x00f00000 /* Alpha field in RGB444 */ +#define C1_ALPHA_SHFT 20 +#define C1_DMAB32 0x00000000 /* 32-byte DMA burst */ +#define C1_DMAB16 0x02000000 /* 16-byte DMA burst */ +#define C1_DMAB64 0x04000000 /* 64-byte DMA burst */ +#define C1_DMAB_MASK 0x06000000 +#define C1_TWOBUFS 0x08000000 /* Use only two DMA buffers */ +#define C1_PWRDWN 0x10000000 /* Power down */ + +#define REG_LNNUM 0x60 /* lines num DMA filled */ +#define REG_CLKCTRL 0x88 /* Clock control */ +#define CLK_DIV_MASK 0x0000ffff /* Upper bits RW "reserved" */ + + +/* + * Useful stuff that probably belongs somewhere global. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 + +#define SENSOR_LOW 0 +#define SENSOR_HIGH 1 +#define SENSOR_MAX 2 + +struct ccic_camera; + +void ccic_set_clock(unsigned int reg, unsigned int val); +unsigned int ccic_get_clock(unsigned int reg); +void ccic_set_clock_mipi(void); +void ccic_set_clock_parallel(void); +void ccic_disable_mclk(void); +void ccic_enable_vclk(void); +void ccic_enable_mclk(struct ccic_camera *cam); +void ccic_disable_clock(void); diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 04bf5c11308d43..0285e44af5d7bf 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -40,6 +40,8 @@ #include #include +#include "pxa_camera.h" + #define PXA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) #define PXA_CAM_DRV_NAME "pxa27x-camera" diff --git a/drivers/media/video/pxa_camera.h b/drivers/media/video/pxa_camera.h new file mode 100644 index 00000000000000..89cbfc9a35c59f --- /dev/null +++ b/drivers/media/video/pxa_camera.h @@ -0,0 +1,95 @@ +/* Camera Interface */ +#define CICR0 __REG(0x50000000) +#define CICR1 __REG(0x50000004) +#define CICR2 __REG(0x50000008) +#define CICR3 __REG(0x5000000C) +#define CICR4 __REG(0x50000010) +#define CISR __REG(0x50000014) +#define CIFR __REG(0x50000018) +#define CITOR __REG(0x5000001C) +#define CIBR0 __REG(0x50000028) +#define CIBR1 __REG(0x50000030) +#define CIBR2 __REG(0x50000038) + +#define CICR0_DMAEN (1 << 31) /* DMA request enable */ +#define CICR0_PAR_EN (1 << 30) /* Parity enable */ +#define CICR0_SL_CAP_EN (1 << 29) /* Capture enable for slave mode */ +#define CICR0_ENB (1 << 28) /* Camera interface enable */ +#define CICR0_DIS (1 << 27) /* Camera interface disable */ +#define CICR0_SIM (0x7 << 24) /* Sensor interface mode mask */ +#define CICR0_TOM (1 << 9) /* Time-out mask */ +#define CICR0_RDAVM (1 << 8) /* Receive-data-available mask */ +#define CICR0_FEM (1 << 7) /* FIFO-empty mask */ +#define CICR0_EOLM (1 << 6) /* End-of-line mask */ +#define CICR0_PERRM (1 << 5) /* Parity-error mask */ +#define CICR0_QDM (1 << 4) /* Quick-disable mask */ +#define CICR0_CDM (1 << 3) /* Disable-done mask */ +#define CICR0_SOFM (1 << 2) /* Start-of-frame mask */ +#define CICR0_EOFM (1 << 1) /* End-of-frame mask */ +#define CICR0_FOM (1 << 0) /* FIFO-overrun mask */ + +#define CICR1_TBIT (1 << 31) /* Transparency bit */ +#define CICR1_RGBT_CONV (0x3 << 29) /* RGBT conversion mask */ +#define CICR1_PPL (0x7ff << 15) /* Pixels per line mask */ +#define CICR1_RGB_CONV (0x7 << 12) /* RGB conversion mask */ +#define CICR1_RGB_F (1 << 11) /* RGB format */ +#define CICR1_YCBCR_F (1 << 10) /* YCbCr format */ +#define CICR1_RGB_BPP (0x7 << 7) /* RGB bis per pixel mask */ +#define CICR1_RAW_BPP (0x3 << 5) /* Raw bis per pixel mask */ +#define CICR1_COLOR_SP (0x3 << 3) /* Color space mask */ +#define CICR1_DW (0x7 << 0) /* Data width mask */ + +#define CICR2_BLW (0xff << 24) /* Beginning-of-line pixel clock + wait count mask */ +#define CICR2_ELW (0xff << 16) /* End-of-line pixel clock + wait count mask */ +#define CICR2_HSW (0x3f << 10) /* Horizontal sync pulse width mask */ +#define CICR2_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock + wait count mask */ +#define CICR2_FSW (0x7 << 0) /* Frame stabilization + wait count mask */ + +#define CICR3_BFW (0xff << 24) /* Beginning-of-frame line clock + wait count mask */ +#define CICR3_EFW (0xff << 16) /* End-of-frame line clock + wait count mask */ +#define CICR3_VSW (0x3f << 10) /* Vertical sync pulse width mask */ +#define CICR3_BFPW (0x3f << 3) /* Beginning-of-frame pixel clock + wait count mask */ +#define CICR3_LPF (0x7ff << 0) /* Lines per frame mask */ + +#define CICR4_MCLK_DLY (0x3 << 24) /* MCLK Data Capture Delay mask */ +#define CICR4_PCLK_EN (1 << 23) /* Pixel clock enable */ +#define CICR4_PCP (1 << 22) /* Pixel clock polarity */ +#define CICR4_HSP (1 << 21) /* Horizontal sync polarity */ +#define CICR4_VSP (1 << 20) /* Vertical sync polarity */ +#define CICR4_MCLK_EN (1 << 19) /* MCLK enable */ +#define CICR4_FR_RATE (0x7 << 8) /* Frame rate mask */ +#define CICR4_DIV (0xff << 0) /* Clock divisor mask */ + +#define CISR_FTO (1 << 15) /* FIFO time-out */ +#define CISR_RDAV_2 (1 << 14) /* Channel 2 receive data available */ +#define CISR_RDAV_1 (1 << 13) /* Channel 1 receive data available */ +#define CISR_RDAV_0 (1 << 12) /* Channel 0 receive data available */ +#define CISR_FEMPTY_2 (1 << 11) /* Channel 2 FIFO empty */ +#define CISR_FEMPTY_1 (1 << 10) /* Channel 1 FIFO empty */ +#define CISR_FEMPTY_0 (1 << 9) /* Channel 0 FIFO empty */ +#define CISR_EOL (1 << 8) /* End of line */ +#define CISR_PAR_ERR (1 << 7) /* Parity error */ +#define CISR_CQD (1 << 6) /* Camera interface quick disable */ +#define CISR_CDD (1 << 5) /* Camera interface disable done */ +#define CISR_SOF (1 << 4) /* Start of frame */ +#define CISR_EOF (1 << 3) /* End of frame */ +#define CISR_IFO_2 (1 << 2) /* FIFO overrun for Channel 2 */ +#define CISR_IFO_1 (1 << 1) /* FIFO overrun for Channel 1 */ +#define CISR_IFO_0 (1 << 0) /* FIFO overrun for Channel 0 */ + +#define CIFR_FLVL2 (0x7f << 23) /* FIFO 2 level mask */ +#define CIFR_FLVL1 (0x7f << 16) /* FIFO 1 level mask */ +#define CIFR_FLVL0 (0xff << 8) /* FIFO 0 level mask */ +#define CIFR_THL_0 (0x3 << 4) /* Threshold Level for Channel 0 FIFO */ +#define CIFR_RESET_F (1 << 3) /* Reset input FIFOs */ +#define CIFR_FEN2 (1 << 2) /* FIFO enable for channel 2 */ +#define CIFR_FEN1 (1 << 1) /* FIFO enable for channel 1 */ +#define CIFR_FEN0 (1 << 0) /* FIFO enable for channel 0 */ + diff --git a/drivers/media/video/siv120a.c b/drivers/media/video/siv120a.c new file mode 100644 index 00000000000000..9b67c4abd6aa8c --- /dev/null +++ b/drivers/media/video/siv120a.c @@ -0,0 +1,1208 @@ +/* + * A V4L2 driver for Seti SIV120A cameras. + * + * Copyright 2009 Marvell Inc. Written + * by Jun Nie with substantial information from Seti Inc. + * This file may be distributed under the terms of the GNU General + * Public License, version 2. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "pxa168_camera.h" + +MODULE_AUTHOR("Jun Nie "); +MODULE_DESCRIPTION("A low-level driver for Seti SIV120A sensors"); +MODULE_LICENSE("GPL"); + + +/* + * Basic window sizes. These probably belong somewhere more globally + * useful. + */ +#define VGA_WIDTH 640 +#define VGA_HEIGHT 480 +#define QVGA_WIDTH 320 +#define QVGA_HEIGHT 240 +#define CIF_WIDTH 352 +#define CIF_HEIGHT 288 +#define QCIF_WIDTH 176 +#define QCIF_HEIGHT 144 + +/* + * Our nominal (default) frame rate. + */ +#define SIV120A_FRAME_RATE 30 + +/* + * The SIV120A sits on i2c with ID 0x66 + */ +#define SIV120A_I2C_ADDR 0x66 + +/* Sensor Registers */ +#define REG_CHIP_ID 0x1 +#define REG_VERSION 0x2 + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x54 +#define REG_CMATRIX_END 0x6E + +struct regval_list { + unsigned char reg_num; + unsigned char value; +}; + +#if 0 +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ + + +/* + * Information we maintain about a known sensor. + */ +struct siv120a_format_struct; /* coming later */ +#endif +struct siv120a_info { + struct siv120a_format_struct *fmt; /* Current format */ + unsigned char sat; /* Saturation value */ + int hue; /* Hue value */ +}; + +/* + * + * YUV422, modified from seti setting + * + */ +static struct regval_list siv120a_init_reg[] = { + {0x00, 0x00}, + {0x03, 0x55}, + {0x04, 0x00}, + {0x05, 0x06}, /* VGA */ + {0x10, 0x01}, + {0x13, 0x1a}, + {0x17, 0x82}, + {0x42, 0x32}, + {0x43, 0x80}, + {0xff, 0xff}, +}; + +static struct regval_list siv120a_fmt_yuv422[] = { + + //#AE Block: expose + {0x00, 0x01}, + {0x10, 0x00}, + {0x11, 0x10}, + {0x40, 0x5f}, + + //AWB Block: white balance + {0x00, 0x02}, + {0x10, 0xd3}, + {0x11, 0x00}, + {0x15, 0xfe}, + {0x16, 0x80}, + {0x17, 0xea}, + {0x18, 0x80}, + {0x19, 0xb0}, + {0x1a, 0x68}, + {0x1b, 0xb0}, + {0x1c, 0x64}, + {0x1d, 0x90}, + {0x1e, 0x70}, + {0x20, 0xe8}, + {0x21, 0x20}, + {0x22, 0x9d}, + {0x23, 0x18}, + {0x25, 0x20}, + {0x26, 0x0f}, + {0x27, 0x0d}, + {0x28, 0x90}, + {0x29, 0xd0}, + {0x2a, 0x90}, + {0x30, 0x05}, + {0x46, 0x64}, + + //RGB to YCbCr (CSC) no used + {0x50, 0x33}, + {0x51, 0x20}, + {0x52, 0xe5}, + {0x53, 0xfb}, + {0x54, 0x13}, + {0x55, 0x26}, + {0x56, 0x07}, + {0x57, 0xf5}, + {0x58, 0xea}, + {0x59, 0x21}, + {0x63, 0xbd}, + {0x64, 0xc2}, + {0x65, 0xbd}, + {0x66, 0xc2}, + {0x67, 0xef}, + {0x68, 0xa0}, + {0x69, 0xef}, + {0x6a, 0xa0}, + + //#### IDP + {0x00, 0x03}, + {0x10, 0xEF}, + //{0x12, 0x3D}, // cmcs Selects YUV422 mode */ + {0x12, 0x1D}, // UYVY YUV422 mode */ + {0x11, 0x5d}, // sync timing + {0x14, 0xf5}, + + //#shading _0415 + {0x2c, 0x44}, + {0x2d, 0x22}, + {0X2e, 0x11}, + {0x32, 0xA8}, + + //#Gamma + {0x34, 0x00}, + {0x35, 0x08}, + {0x36, 0x11}, + {0x37, 0x25}, + {0x38, 0x45}, + {0x39, 0x5f}, + {0x3a, 0x74}, + {0x3b, 0x87}, + {0x3c, 0x97}, + {0x3d, 0xa5}, + {0x3e, 0xb2}, + {0x3f, 0xc9}, + {0x40, 0xdd}, + {0x41, 0xf0}, + {0x42, 0xf8}, + {0x43, 0xff}, + {0x44, 0xbb}, + {0x4b, 0x40}, + {0x4c, 0xa1}, + {0x4d, 0x08}, + {0x4e, 0xff}, + {0x4f, 0x05}, + {0x50, 0x80}, + {0x51, 0x40}, + {0x52, 0xff}, + {0x53, 0x13}, + + {0x70, 0x20}, + {0x71, 0x30}, + {0x73, 0x06}, + {0x74, 0x06}, + {0x76, 0x40}, + {0x79, 0x20}, + {0x7a, 0x30}, + {0x7b, 0x06}, + {0x7c, 0x06}, + {0x7d, 0x20}, + {0x7f, 0x40}, + + {0x86, 0x10}, + {0x87, 0x10}, + + {0x92, 0x44}, + //# windows size + {0xa0, 0x24}, + {0xa1, 0x00}, + {0xa2, 0x80}, + {0xa3, 0x00}, + {0xa4, 0xE0}, + + //#Sensor On + {0x00, 0x00}, + {0x03, 0x55}, + + //#AE On + {0x00, 0x01}, + {0x10, 0x80}, + //[END] + + {0x00, 0x00}, + {0xff, 0xff}, +}; + +/* + * Low-level register I/O. + */ + +static int siv120a_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int ret; + + ret = i2c_smbus_read_byte_data(c, reg); + if (ret >= 0) + *value = (unsigned char) ret; + return ret; +} + +static int siv120a_write(struct i2c_client *c, unsigned char reg, + unsigned char value) +{ + int ret = i2c_smbus_write_byte_data(c, reg, value); + return ret; +} + +/* + * Write a list of register settings; ff/ff stops the process. + */ +static int siv120a_write_array(struct i2c_client *c, struct regval_list *vals) +{ + int i = 0; + while (vals->reg_num != 0xff || vals->value != 0xff) { + int ret = siv120a_write(c, vals->reg_num, vals->value); + if (ret < 0) + return ret; + vals++; + if (i == 0) + mdelay(5); + i++; + } + return 0; +} + + +static int siv120a_init(struct i2c_client *client) +{ + return siv120a_write_array(client, siv120a_init_reg); +} + +static int siv120a_detect(struct i2c_client *client) +{ + unsigned char v; + int ret; + + /*ret = siv120a_init(client); + if (ret < 0) + return ret; + */ + ret = siv120a_read(client, REG_CHIP_ID, &v); + if (ret < 0) + return ret; + if (v != 0x12) /* chip id. */ + return -ENODEV; + + ret = siv120a_read(client, REG_VERSION, &v); + printk("find seti camera chip version 0x%x\n", v); + if (ret < 0) + return ret; + return 0; +} + +#if 0 +/* + * Stuff that knows about the sensor. + */ +static void siv120a_reset(struct i2c_client *client) +{ + siv120a_write(client, REG_COM7, COM7_RESET); + msleep(1); +} + +#endif +/* + * Store information about the video data format. The color matrix + * is deeply tied into the format, so keep the relevant values here. + * The magic matrix nubmers come from OmniVision. + */ +static struct siv120a_format_struct { + __u8 *desc; + __u32 pixelformat; + struct regval_list *regs; + //int cmatrix[CMATRIX_LEN]; + int bpp; /* bits per pixel */ +} siv120a_formats[] = { + /*{ + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUYV, + .regs = siv120a_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + }, + { + .desc = "YUYV 4:2:2", + .pixelformat = V4L2_PIX_FMT_YUV422P, + .regs = siv120a_fmt_yuv422, + .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 16, + },*/ + { + .desc = "YUYV 4:2:0", + .pixelformat = V4L2_PIX_FMT_YUV420, + .regs = siv120a_fmt_yuv422, + //.cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 12, + }, + /*{ + .desc = "RGB 565", + .pixelformat = V4L2_PIX_FMT_RGB565, + .regs = siv120a_rgb565_qvga, + //.cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "RGB 444", + .pixelformat = V4L2_PIX_FMT_RGB444, + .regs = siv120a_fmt_rgb444, + .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 16, + }, + { + .desc = "Raw RGB Bayer", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .regs = siv120a_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + .bpp = 8, + },*/ +}; +#define N_SIV120A_FMTS ARRAY_SIZE(siv120a_formats) + +/* + * Then there is the issue of window sizes. Try to capture the info here. + */ + +/* + * QCIF mode is done (by OV) in a very strange way - it actually looks like + * VGA with weird scaling options - they do *not* use the canned QCIF mode + * which is allegedly provided by the sensor. So here's the weird register + * settings. + */ +static struct regval_list siv120a_qcif_regs[] = { +}; + +static struct siv120a_win_size { + int width; + int height; + unsigned char com7_bit; + int hstart; /* Start/stop values for the camera. Note */ + int hstop; /* that they do not always make complete */ + int vstart; /* sense to humans, but evidently the sensor */ + int vstop; /* will do the right thing... */ + struct regval_list *regs; /* Regs to tweak */ + /* h/vref stuff */ +} siv120a_win_sizes[] = { + /* VGA */ + { + .width = VGA_WIDTH, + .height = VGA_HEIGHT, + //.com7_bit = COM7_FMT_VGA, + .hstart = 158, + .hstop = 14, + .vstart = 10, + .vstop = 490, + .regs = NULL, + }, + /* QVGA */ +#if 0 + { + .width = QVGA_WIDTH, + .height = QVGA_HEIGHT, + //.com7_bit = COM7_FMT_QVGA, + .hstart = 164, + .hstop = 20, + .vstart = 14, + .vstop = 494, + .regs = siv120a_qvga, + }, + /* CIF */ + { + .width = CIF_WIDTH, + .height = CIF_HEIGHT, + .com7_bit = COM7_FMT_CIF, + .hstart = 170, /* Empirically determined */ + .hstop = 90, + .vstart = 14, + .vstop = 494, + .regs = NULL, + }, + /* QCIF */ + { + .width = QCIF_WIDTH, + .height = QCIF_HEIGHT, + .com7_bit = COM7_FMT_VGA, /* see comment above */ + .hstart = 456, /* Empirically determined */ + .hstop = 24, + .vstart = 14, + .vstop = 494, + .regs = siv120a_qcif_regs, + }, +#endif +}; + +#define N_WIN_SIZES (ARRAY_SIZE(siv120a_win_sizes)) + +#if 0 +/* + * Store a set of start/stop values into the camera. + */ +static int siv120a_set_hw(struct i2c_client *client, int hstart, int hstop, + int vstart, int vstop) +{ + int ret; + unsigned char v; + /* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = siv120a_write(client, REG_HSTART, (hstart >> 3) & 0xff); + ret += siv120a_write(client, REG_HSTOP, (hstop >> 3) & 0xff); + ret += siv120a_read(client, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += siv120a_write(client, REG_HREF, v); + /* + * Vertical: similar arrangement, but only 10 bits. + */ + ret += siv120a_write(client, REG_VSTART, (vstart >> 2) & 0xff); + ret += siv120a_write(client, REG_VSTOP, (vstop >> 2) & 0xff); + ret += siv120a_read(client, REG_VREF, &v); + v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); + msleep(10); + ret += siv120a_write(client, REG_VREF, v); + return ret; +} + +#endif + +static int siv120a_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +{ + struct siv120a_format_struct *ofmt; + + if (fmt->index >= N_SIV120A_FMTS) + return -EINVAL; + + ofmt = siv120a_formats + fmt->index; + fmt->flags = 0; + strcpy(fmt->description, ofmt->desc); + fmt->pixelformat = ofmt->pixelformat; + return 0; +} + +static int siv120a_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, + struct siv120a_format_struct **ret_fmt, + struct siv120a_win_size **ret_wsize) +{ + int index; + struct siv120a_win_size *wsize; + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + for (index = 0; index < N_SIV120A_FMTS; index++) + if (siv120a_formats[index].pixelformat == pix->pixelformat) + break; + if (index >= N_SIV120A_FMTS) + return -EINVAL; + if (ret_fmt != NULL) + *ret_fmt = siv120a_formats + index; + /* + * Fields: the OV devices claim to be progressive. + */ + /*if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + */ + /* + * Round requested image size down to the nearest + * we support, but not below the smallest. + */ +#if 0 + for (wsize = siv120a_win_sizes; wsize < siv120a_win_sizes + N_WIN_SIZES; + wsize++) + if (pix->width >= wsize->width && pix->height >= wsize->height) + break; + if (wsize >= siv120a_win_sizes + N_WIN_SIZES) + wsize--; /* Take the smallest one */ + if (ret_wsize != NULL) + *ret_wsize = wsize; + /* + * Note the size we'll actually handle. + */ + pix->width = wsize->width; + pix->height = wsize->height; +#endif + pix->bytesperline = pix->width*siv120a_formats[index].bpp/8; + pix->sizeimage = pix->height*pix->bytesperline; + return 0; +} + +/* + * Set a format. + */ +static int siv120a_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +{ + int ret; + struct siv120a_format_struct *ovfmt; + struct siv120a_win_size *wsize; + + ret = siv120a_try_fmt(c, fmt, &ovfmt, &wsize); + if (ret) + return ret; + + siv120a_write_array(c, ovfmt->regs); + + return ret; +} + +/* + * Implement G/S_PARM. There is a "high quality" mode we could try + * to do someday; for now, we just do the frame rate tweak. + */ +static int siv120a_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + unsigned char clkrc; + int ret; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + /*ret = siv120a_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + */ + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = SIV120A_FRAME_RATE; + /*if ((clkrc & CLK_EXT) == 0 && (clkrc & CLK_SCALE) > 1) + cp->timeperframe.denominator /= (clkrc & CLK_SCALE); + */ + return 0; +} + +static int siv120a_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + unsigned char clkrc; + int ret, div; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + /* + * CLKRC has a reserved bit, so let's preserve it. + */ + /*ret = siv120a_read(c, REG_CLKRC, &clkrc); + if (ret < 0) + return ret; + */ + if (tpf->numerator == 0 || tpf->denominator == 0) + div = 1; /* Reset to full rate */ + else + div = (tpf->numerator*SIV120A_FRAME_RATE)/tpf->denominator; + /*if (div == 0) + div = 1; + else if (div > CLK_SCALE) + div = CLK_SCALE; + clkrc = (clkrc & 0x80) | div; + */ + tpf->numerator = 1; + tpf->denominator = SIV120A_FRAME_RATE/div; + return 0; + //return siv120a_write(c, REG_CLKRC, clkrc); +} + +static int siv120a_s_input(struct i2c_client *c, int *id) +{ + return 0; +} +#if 0 + +/* + * Code for dealing with controls. + */ + + + + + +static int siv120a_store_cmatrix(struct i2c_client *client, + int matrix[CMATRIX_LEN]) +{ + int i, ret; + unsigned char signbits; + + /* + * Weird crap seems to exist in the upper part of + * the sign bits register, so let's preserve it. + */ + ret = siv120a_read(client, REG_CMATRIX_SIGN, &signbits); + signbits &= 0xc0; + + for (i = 0; i < CMATRIX_LEN; i++) { + unsigned char raw; + + if (matrix[i] < 0) { + signbits |= (1 << i); + if (matrix[i] < -255) + raw = 0xff; + else + raw = (-1 * matrix[i]) & 0xff; + } + else { + if (matrix[i] > 255) + raw = 0xff; + else + raw = matrix[i] & 0xff; + } + ret += siv120a_write(client, REG_CMATRIX_BASE + i, raw); + } + ret += siv120a_write(client, REG_CMATRIX_SIGN, signbits); + return ret; +} + + +/* + * Hue also requires messing with the color matrix. It also requires + * trig functions, which tend not to be well supported in the kernel. + * So here is a simple table of sine values, 0-90 degrees, in steps + * of five degrees. Values are multiplied by 1000. + * + * The following naive approximate trig functions require an argument + * carefully limited to -180 <= theta <= 180. + */ +#define SIN_STEP 5 +static const int siv120a_sin_table[] = { + 0, 87, 173, 258, 342, 422, + 499, 573, 642, 707, 766, 819, + 866, 906, 939, 965, 984, 996, + 1000 +}; + +static int siv120a_sine(int theta) +{ + int chs = 1; + int sine; + + if (theta < 0) { + theta = -theta; + chs = -1; + } + if (theta <= 90) + sine = siv120a_sin_table[theta/SIN_STEP]; + else { + theta -= 90; + sine = 1000 - siv120a_sin_table[theta/SIN_STEP]; + } + return sine*chs; +} + +static int siv120a_cosine(int theta) +{ + theta = 90 - theta; + if (theta > 180) + theta -= 360; + else if (theta < -180) + theta += 360; + return siv120a_sine(theta); +} + + + + +static void siv120a_calc_cmatrix(struct siv120a_info *info, + int matrix[CMATRIX_LEN]) +{ + int i; + /* + * Apply the current saturation setting first. + */ + for (i = 0; i < CMATRIX_LEN; i++) + matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; + /* + * Then, if need be, rotate the hue value. + */ + if (info->hue != 0) { + int sinth, costh, tmpmatrix[CMATRIX_LEN]; + + memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); + sinth = siv120a_sine(info->hue); + costh = siv120a_cosine(info->hue); + + matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; + matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; + matrix[2] = (matrix[5]*sinth + matrix[2]*costh)/1000; + matrix[3] = (matrix[3]*costh - matrix[0]*sinth)/1000; + matrix[4] = (matrix[4]*costh - matrix[1]*sinth)/1000; + matrix[5] = (matrix[5]*costh - matrix[2]*sinth)/1000; + } +} + + + +static int siv120a_t_sat(struct i2c_client *client, int value) +{ + struct siv120a_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + info->sat = value; + siv120a_calc_cmatrix(info, matrix); + ret = siv120a_store_cmatrix(client, matrix); + return ret; +} + +static int siv120a_q_sat(struct i2c_client *client, __s32 *value) +{ + struct siv120a_info *info = i2c_get_clientdata(client); + + *value = info->sat; + return 0; +} + +static int siv120a_t_hue(struct i2c_client *client, int value) +{ + struct siv120a_info *info = i2c_get_clientdata(client); + int matrix[CMATRIX_LEN]; + int ret; + + if (value < -180 || value > 180) + return -EINVAL; + info->hue = value; + siv120a_calc_cmatrix(info, matrix); + ret = siv120a_store_cmatrix(client, matrix); + return ret; +} + + +static int siv120a_q_hue(struct i2c_client *client, __s32 *value) +{ + struct siv120a_info *info = i2c_get_clientdata(client); + + *value = info->hue; + return 0; +} + + +/* + * Some weird registers seem to store values in a sign/magnitude format! + */ +static unsigned char siv120a_sm_to_abs(unsigned char v) +{ + if ((v & 0x80) == 0) + return v + 128; + else + return 128 - (v & 0x7f); +} + + +static unsigned char siv120a_abs_to_sm(unsigned char v) +{ + if (v > 127) + return v & 0x7f; + else + return (128 - v) | 0x80; +} + +static int siv120a_t_brightness(struct i2c_client *client, int value) +{ + unsigned char com8, v; + int ret; + + siv120a_read(client, REG_COM8, &com8); + com8 &= ~COM8_AEC; + siv120a_write(client, REG_COM8, com8); + v = siv120a_abs_to_sm(value); + ret = siv120a_write(client, REG_BRIGHT, v); + return ret; +} + +static int siv120a_q_brightness(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = siv120a_read(client, REG_BRIGHT, &v); + + *value = siv120a_sm_to_abs(v); + return ret; +} + +static int siv120a_t_contrast(struct i2c_client *client, int value) +{ + return siv120a_write(client, REG_CONTRAS, (unsigned char) value); +} + +static int siv120a_q_contrast(struct i2c_client *client, __s32 *value) +{ + unsigned char v; + int ret = siv120a_read(client, REG_CONTRAS, &v); + + *value = v; + return ret; +} + +static int siv120a_q_hflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = siv120a_read(client, REG_MVFP, &v); + *value = (v & MVFP_MIRROR) == MVFP_MIRROR; + return ret; +} + + +static int siv120a_t_hflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = siv120a_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_MIRROR; + else + v &= ~MVFP_MIRROR; + msleep(10); /* FIXME */ + ret += siv120a_write(client, REG_MVFP, v); + return ret; +} + + + +static int siv120a_q_vflip(struct i2c_client *client, __s32 *value) +{ + int ret; + unsigned char v; + + ret = siv120a_read(client, REG_MVFP, &v); + *value = (v & MVFP_FLIP) == MVFP_FLIP; + return ret; +} + + +static int siv120a_t_vflip(struct i2c_client *client, int value) +{ + unsigned char v; + int ret; + + ret = siv120a_read(client, REG_MVFP, &v); + if (value) + v |= MVFP_FLIP; + else + v &= ~MVFP_FLIP; + msleep(10); /* FIXME */ + ret += siv120a_write(client, REG_MVFP, v); + return ret; +} + + +static struct siv120a_control { + struct v4l2_queryctrl qc; + int (*query)(struct i2c_client *c, __s32 *value); + int (*tweak)(struct i2c_client *c, int value); +} siv120a_controls[] = +{ + { + .qc = { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = siv120a_t_brightness, + .query = siv120a_q_brightness, + }, + { + .qc = { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 0x40, /* XXX siv120a spec */ + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = siv120a_t_contrast, + .query = siv120a_q_contrast, + }, + { + .qc = { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 256, + .step = 1, + .default_value = 0x80, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = siv120a_t_sat, + .query = siv120a_q_sat, + }, + { + .qc = { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "HUE", + .minimum = -180, + .maximum = 180, + .step = 5, + .default_value = 0, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .tweak = siv120a_t_hue, + .query = siv120a_q_hue, + }, + { + .qc = { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = siv120a_t_vflip, + .query = siv120a_q_vflip, + }, + { + .qc = { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .tweak = siv120a_t_hflip, + .query = siv120a_q_hflip, + }, +}; +#define N_CONTROLS (ARRAY_SIZE(siv120a_controls)) + +static struct siv120a_control *siv120a_find_control(__u32 id) +{ + int i; + + for (i = 0; i < N_CONTROLS; i++) + if (siv120a_controls[i].qc.id == id) + return siv120a_controls + i; + return NULL; +} + + +static int siv120a_queryctrl(struct i2c_client *client, + struct v4l2_queryctrl *qc) +{ + struct siv120a_control *ctrl = siv120a_find_control(qc->id); + + if (ctrl == NULL) + return -EINVAL; + *qc = ctrl->qc; + return 0; +} + +static int siv120a_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct siv120a_control *octrl = siv120a_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->query(client, &ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + +static int siv120a_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct siv120a_control *octrl = siv120a_find_control(ctrl->id); + int ret; + + if (octrl == NULL) + return -EINVAL; + ret = octrl->tweak(client, ctrl->value); + if (ret >= 0) + return 0; + return ret; +} + + +#endif + +int ccic_sensor_attach(struct i2c_client *client); +extern struct clk *pxa168_ccic_gate_clk; + +/* + * Basic i2c stuff. + */ +static int __devinit siv120a_probe(struct i2c_client *client) +{ + int ret; + struct siv120a_info *info; + struct sensor_platform_data *pdata; + pdata = client->dev.platform_data; + + /* + * Set up our info structure. + */ + clk_enable(pxa168_ccic_gate_clk); + ccic_set_clock_parallel(); //clock must be enabled before power on. + + pdata->power_on(1, 0); + + info = kzalloc(sizeof (struct siv120a_info), GFP_KERNEL); + if (! info) { + ret = -ENOMEM; + goto out_free; + } + info->fmt = &siv120a_formats[0]; + info->sat = 128; /* Review this */ + i2c_set_clientdata(client, info); + + /* + * Make sure it's an siv120a + */ + ret = siv120a_detect(client); + if (ret) { + printk("%s: failed to detect siv120a!\n", __func__); + goto out_free_info; + } + printk(KERN_NOTICE "siv120a sensor detected\n"); + + ret = ccic_sensor_attach(client); + if (ret) + goto out_free_info; + pdata->power_on(0, 0); //for power optimization + ccic_disable_clock(); + return 0; + +out_free_info: + kfree(info); +out_free: + return ret; +} + + +static int siv120a_remove(struct i2c_client *client) +{ + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int siv120a_g_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return siv120a_read(client, (unsigned char)reg->reg, (unsigned char *)&(reg->val)); +} + +static int siv120a_s_register(struct i2c_client *client, struct v4l2_dbg_register * reg) +{ + return siv120a_write(client, (unsigned char)reg->reg, (unsigned char)reg->val); +} +#endif + +static int siv120a_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + switch (cmd) { + case VIDIOC_DBG_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_SIV120A, 0); + + case VIDIOC_INT_RESET: + //siv120a_reset(client); + return 0; + + case VIDIOC_INT_INIT: + return siv120a_init(client); + + case VIDIOC_ENUM_FMT: + return siv120a_enum_fmt(client, (struct v4l2_fmtdesc *) arg); + case VIDIOC_TRY_FMT: + return siv120a_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); + case VIDIOC_S_FMT: + return siv120a_s_fmt(client, (struct v4l2_format *) arg); + case VIDIOC_QUERYCTRL: + return 0; + //return siv120a_queryctrl(client, (struct v4l2_queryctrl *) arg); + case VIDIOC_S_CTRL: + return 0; + //return siv120a_s_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_G_CTRL: + return 0; + //return siv120a_g_ctrl(client, (struct v4l2_control *) arg); + case VIDIOC_S_PARM: + return siv120a_s_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_G_PARM: + return siv120a_g_parm(client, (struct v4l2_streamparm *) arg); + case VIDIOC_S_INPUT: + return siv120a_s_input(client, (int *) arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return siv120a_g_register(client, (struct v4l2_dbg_register *) arg); + case VIDIOC_DBG_S_REGISTER: + return siv120a_s_register(client, (struct v4l2_dbg_register *) arg); +#endif + default: + printk("no handled siv120 cmd %x\n", cmd); + return 0; + } + return -EINVAL; +} + +static struct i2c_device_id siv120a_idtable[] = { + { "siv120a", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, siv120a_idtable); + +static struct i2c_driver siv120a_driver = { + .driver = { + .name = "siv120a", + }, + .id_table = siv120a_idtable, + .command = siv120a_command, + .probe = siv120a_probe, + .remove = siv120a_remove, +}; + + +/* + * Module initialization + */ +static int __init siv120a_mod_init(void) +{ + printk(KERN_NOTICE "NiceSeti siv120a sensor driver, at your service\n"); + return i2c_add_driver(&siv120a_driver); +} + +static void __exit siv120a_mod_exit(void) +{ + i2c_del_driver(&siv120a_driver); +} + +late_initcall(siv120a_mod_init); +module_exit(siv120a_mod_exit); + diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0d0d625fece288..ad2fc0234667d3 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -13,6 +13,10 @@ menuconfig MISC_DEVICES if MISC_DEVICES +config ANDROID_PMEM + bool "Android pmem allocator" + default y + config AD525X_DPOT tristate "Analog Devices AD525x Digital Potentiometers" depends on I2C && SYSFS @@ -173,6 +177,13 @@ config ENCLOSURE_SERVICES driver (SCSI/ATA) which supports enclosures or a SCSI enclosure device (SES) to use these services. +config KERNEL_DEBUGGER_CORE + bool "Kernel Debugger Core" + default n + ---help--- + Generic kernel debugging command processor used by low level + (interrupt context) platform-specific debuggers. + config SGI_XP tristate "Support communication between SGI SSIs" depends on NET @@ -327,6 +338,42 @@ config VMWARE_BALLOON To compile this driver as a module, choose M here: the module will be called vmware_balloon. +config UID_STAT + bool "UID based statistics tracking exported to /proc/uid_stat" + default n + +config WL127X_RFKILL + tristate "Bluetooth power control driver for TI wl127x" + depends on RFKILL + default n + ---help--- + Creates an rfkill entry in sysfs for power control of Bluetooth + TI wl127x chips. + +config APANIC + bool "Android kernel panic diagnostics driver" + default n + ---help--- + Driver which handles kernel panics and attempts to write + critical debugging data to flash. + +config APANIC_PLABEL + string "Android panic dump flash partition label" + depends on APANIC + default "kpanic" + ---help--- + If your platform uses a different flash partition label for storing + crashdumps, enter it here. + +config SD8XXX_RFKILL + tristate "Rfkill power control for Marvell sd8xxx wlan/bt" + depends on MMC_PXA_SDH + select RFKILL + default n + ---help--- + Creates an rfkill entry in sysfs for power control of Marvell + sd8xxx wlan/bt chips. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 7b6f7eefdf8d53..8b0f22c92e2abc 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -1,6 +1,7 @@ # # Makefile for misc devices that really don't fit anywhere else. # +obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ @@ -9,17 +10,27 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_ICS932S401) += ics932s401.o +obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o +obj-$(CONFIG_ANDROID_PMEM) += pmem.o obj-$(CONFIG_SGI_IOC4) += ioc4.o +obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o +obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o +obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o +obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o +obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o +obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o +obj-$(CONFIG_KERNEL_DEBUGGER_CORE) += kernel_debugger.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o obj-$(CONFIG_HP_ILO) += hpilo.o +obj-$(CONFIG_UID_STAT) += uid_stat.o obj-$(CONFIG_ISL29003) += isl29003.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o @@ -28,5 +39,7 @@ obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_IWMC3200TOP) += iwmc3200top/ obj-y += eeprom/ +obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o +obj-$(CONFIG_SD8XXX_RFKILL) += sd8x_rfkill.o obj-y += cb710/ obj-$(CONFIG_VMWARE_BALLOON) += vmware_balloon.o diff --git a/drivers/misc/apanic.c b/drivers/misc/apanic.c new file mode 100644 index 00000000000000..ca875f89da7a7f --- /dev/null +++ b/drivers/misc/apanic.c @@ -0,0 +1,606 @@ +/* drivers/misc/apanic.c + * + * Copyright (C) 2009 Google, Inc. + * Author: San Mehat + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void ram_console_enable_console(int); + +struct panic_header { + u32 magic; +#define PANIC_MAGIC 0xdeadf00d + + u32 version; +#define PHDR_VERSION 0x01 + + u32 console_offset; + u32 console_length; + + u32 threads_offset; + u32 threads_length; +}; + +struct apanic_data { + struct mtd_info *mtd; + struct panic_header curr; + void *bounce; + struct proc_dir_entry *apanic_console; + struct proc_dir_entry *apanic_threads; +}; + +static struct apanic_data drv_ctx; +static struct work_struct proc_removal_work; +static DEFINE_MUTEX(drv_mutex); + +static unsigned int *apanic_bbt; +static unsigned int apanic_erase_blocks; +static unsigned int apanic_good_blocks; + +static void set_bb(unsigned int block, unsigned int *bbt) +{ + unsigned int flag = 1; + + BUG_ON(block >= apanic_erase_blocks); + + flag = flag << (block%32); + apanic_bbt[block/32] |= flag; + apanic_good_blocks--; +} + +static unsigned int get_bb(unsigned int block, unsigned int *bbt) +{ + unsigned int flag; + + BUG_ON(block >= apanic_erase_blocks); + + flag = 1 << (block%32); + return apanic_bbt[block/32] & flag; +} + +static void alloc_bbt(struct mtd_info *mtd, unsigned int *bbt) +{ + int bbt_size; + apanic_erase_blocks = (mtd->size)>>(mtd->erasesize_shift); + bbt_size = (apanic_erase_blocks+32)/32; + + apanic_bbt = kmalloc(bbt_size*4, GFP_KERNEL); + memset(apanic_bbt, 0, bbt_size*4); + apanic_good_blocks = apanic_erase_blocks; +} +static void scan_bbt(struct mtd_info *mtd, unsigned int *bbt) +{ + int i; + + for (i = 0; i < apanic_erase_blocks; i++) { + if (mtd->block_isbad(mtd, i*mtd->erasesize)) + set_bb(i, apanic_bbt); + } +} + +#define APANIC_INVALID_OFFSET 0xFFFFFFFF + +static unsigned int phy_offset(struct mtd_info *mtd, unsigned int offset) +{ + unsigned int logic_block = offset>>(mtd->erasesize_shift); + unsigned int phy_block; + unsigned good_block = 0; + + for (phy_block = 0; phy_block < apanic_erase_blocks; phy_block++) { + if (!get_bb(phy_block, apanic_bbt)) + good_block++; + if (good_block == (logic_block + 1)) + break; + } + + if (good_block != (logic_block + 1)) + return APANIC_INVALID_OFFSET; + + return offset + ((phy_block-logic_block)<erasesize_shift); +} + +static void apanic_erase_callback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv; + wake_up(wait_q); +} + +static int apanic_proc_read(char *buffer, char **start, off_t offset, + int count, int *peof, void *dat) +{ + struct apanic_data *ctx = &drv_ctx; + size_t file_length; + off_t file_offset; + unsigned int page_no; + off_t page_offset; + int rc; + size_t len; + + if (!count) + return 0; + + mutex_lock(&drv_mutex); + + switch ((int) dat) { + case 1: /* apanic_console */ + file_length = ctx->curr.console_length; + file_offset = ctx->curr.console_offset; + break; + case 2: /* apanic_threads */ + file_length = ctx->curr.threads_length; + file_offset = ctx->curr.threads_offset; + break; + default: + pr_err("Bad dat (%d)\n", (int) dat); + mutex_unlock(&drv_mutex); + return -EINVAL; + } + + if ((offset + count) > file_length) { + mutex_unlock(&drv_mutex); + return 0; + } + + /* We only support reading a maximum of a flash page */ + if (count > ctx->mtd->writesize) + count = ctx->mtd->writesize; + + page_no = (file_offset + offset) / ctx->mtd->writesize; + page_offset = (file_offset + offset) % ctx->mtd->writesize; + + + if (phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize)) + == APANIC_INVALID_OFFSET) { + pr_err("apanic: reading an invalid address\n"); + mutex_unlock(&drv_mutex); + return -EINVAL; + } + rc = ctx->mtd->read(ctx->mtd, + phy_offset(ctx->mtd, (page_no * ctx->mtd->writesize)), + ctx->mtd->writesize, + &len, ctx->bounce); + + if (page_offset) + count -= page_offset; + memcpy(buffer, ctx->bounce + page_offset, count); + + *start = count; + + if ((offset + count) == file_length) + *peof = 1; + + mutex_unlock(&drv_mutex); + return count; +} + +static void mtd_panic_erase(void) +{ + struct apanic_data *ctx = &drv_ctx; + struct erase_info erase; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + int rc, i; + + init_waitqueue_head(&wait_q); + erase.mtd = ctx->mtd; + erase.callback = apanic_erase_callback; + erase.len = ctx->mtd->erasesize; + erase.priv = (u_long)&wait_q; + for (i = 0; i < ctx->mtd->size; i += ctx->mtd->erasesize) { + erase.addr = i; + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + if (get_bb(erase.addr>>ctx->mtd->erasesize_shift, apanic_bbt)) { + printk(KERN_WARNING + "apanic: Skipping erase of bad " + "block @%llx\n", erase.addr); + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + continue; + } + + rc = ctx->mtd->erase(ctx->mtd, &erase); + if (rc) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + printk(KERN_ERR + "apanic: Erase of 0x%llx, 0x%llx failed\n", + (unsigned long long) erase.addr, + (unsigned long long) erase.len); + if (rc == -EIO) { + if (ctx->mtd->block_markbad(ctx->mtd, + erase.addr)) { + printk(KERN_ERR + "apanic: Err marking blk bad\n"); + goto out; + } + printk(KERN_INFO + "apanic: Marked a bad block" + " @%llx\n", erase.addr); + set_bb(erase.addr>>ctx->mtd->erasesize_shift, + apanic_bbt); + continue; + } + goto out; + } + schedule(); + remove_wait_queue(&wait_q, &wait); + } + printk(KERN_DEBUG "apanic: %s partition erased\n", + CONFIG_APANIC_PLABEL); +out: + return; +} + +static void apanic_remove_proc_work(struct work_struct *work) +{ + struct apanic_data *ctx = &drv_ctx; + + mutex_lock(&drv_mutex); + mtd_panic_erase(); + memset(&ctx->curr, 0, sizeof(struct panic_header)); + if (ctx->apanic_console) { + remove_proc_entry("apanic_console", NULL); + ctx->apanic_console = NULL; + } + if (ctx->apanic_threads) { + remove_proc_entry("apanic_threads", NULL); + ctx->apanic_threads = NULL; + } + mutex_unlock(&drv_mutex); +} + +static int apanic_proc_write(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + schedule_work(&proc_removal_work); + return count; +} + +static void mtd_panic_notify_add(struct mtd_info *mtd) +{ + struct apanic_data *ctx = &drv_ctx; + struct panic_header *hdr = ctx->bounce; + size_t len; + int rc; + int proc_entry_created = 0; + + if (strcmp(mtd->name, CONFIG_APANIC_PLABEL)) + return; + + ctx->mtd = mtd; + + alloc_bbt(mtd, apanic_bbt); + scan_bbt(mtd, apanic_bbt); + + if (apanic_good_blocks == 0) { + printk(KERN_ERR "apanic: no any good blocks?!\n"); + goto out_err; + } + + rc = mtd->read(mtd, phy_offset(mtd, 0), mtd->writesize, + &len, ctx->bounce); + if (rc && rc == -EBADMSG) { + printk(KERN_WARNING + "apanic: Bad ECC on block 0 (ignored)\n"); + } else if (rc && rc != -EUCLEAN) { + printk(KERN_ERR "apanic: Error reading block 0 (%d)\n", rc); + goto out_err; + } + + if (len != mtd->writesize) { + printk(KERN_ERR "apanic: Bad read size (%d)\n", rc); + goto out_err; + } + + printk(KERN_INFO "apanic: Bound to mtd partition '%s'\n", mtd->name); + + if (hdr->magic != PANIC_MAGIC) { + printk(KERN_INFO "apanic: No panic data available\n"); + mtd_panic_erase(); + return; + } + + if (hdr->version != PHDR_VERSION) { + printk(KERN_INFO "apanic: Version mismatch (%d != %d)\n", + hdr->version, PHDR_VERSION); + mtd_panic_erase(); + return; + } + + memcpy(&ctx->curr, hdr, sizeof(struct panic_header)); + + printk(KERN_INFO "apanic: c(%u, %u) t(%u, %u)\n", + hdr->console_offset, hdr->console_length, + hdr->threads_offset, hdr->threads_length); + + if (hdr->console_length) { + ctx->apanic_console = create_proc_entry("apanic_console", + S_IFREG | S_IRUGO, NULL); + if (!ctx->apanic_console) + printk(KERN_ERR "%s: failed creating procfile\n", + __func__); + else { + ctx->apanic_console->read_proc = apanic_proc_read; + ctx->apanic_console->write_proc = apanic_proc_write; + ctx->apanic_console->size = hdr->console_length; + ctx->apanic_console->data = (void *) 1; + proc_entry_created = 1; + } + } + + if (hdr->threads_length) { + ctx->apanic_threads = create_proc_entry("apanic_threads", + S_IFREG | S_IRUGO, NULL); + if (!ctx->apanic_threads) + printk(KERN_ERR "%s: failed creating procfile\n", + __func__); + else { + ctx->apanic_threads->read_proc = apanic_proc_read; + ctx->apanic_threads->write_proc = apanic_proc_write; + ctx->apanic_threads->size = hdr->threads_length; + ctx->apanic_threads->data = (void *) 2; + proc_entry_created = 1; + } + } + + if (!proc_entry_created) + mtd_panic_erase(); + + return; +out_err: + ctx->mtd = NULL; +} + +static void mtd_panic_notify_remove(struct mtd_info *mtd) +{ + struct apanic_data *ctx = &drv_ctx; + if (mtd == ctx->mtd) { + ctx->mtd = NULL; + printk(KERN_INFO "apanic: Unbound from %s\n", mtd->name); + } +} + +static struct mtd_notifier mtd_panic_notifier = { + .add = mtd_panic_notify_add, + .remove = mtd_panic_notify_remove, +}; + +static int in_panic = 0; + +static int apanic_writeflashpage(struct mtd_info *mtd, loff_t to, + const u_char *buf) +{ + int rc; + size_t wlen; + int panic = in_interrupt() | in_atomic(); + + if (panic && !mtd->panic_write) { + printk(KERN_EMERG "%s: No panic_write available\n", __func__); + return 0; + } else if (!panic && !mtd->write) { + printk(KERN_EMERG "%s: No write available\n", __func__); + return 0; + } + + to = phy_offset(mtd, to); + if (to == APANIC_INVALID_OFFSET) { + printk(KERN_EMERG "apanic: write to invalid address\n"); + return 0; + } + + if (panic) + rc = mtd->panic_write(mtd, to, mtd->writesize, &wlen, buf); + else + rc = mtd->write(mtd, to, mtd->writesize, &wlen, buf); + + if (rc) { + printk(KERN_EMERG + "%s: Error writing data to flash (%d)\n", + __func__, rc); + return rc; + } + + return wlen; +} + +extern int log_buf_copy(char *dest, int idx, int len); +extern void log_buf_clear(void); + +/* + * Writes the contents of the console to the specified offset in flash. + * Returns number of bytes written + */ +static int apanic_write_console(struct mtd_info *mtd, unsigned int off) +{ + struct apanic_data *ctx = &drv_ctx; + int saved_oip; + int idx = 0; + int rc, rc2; + unsigned int last_chunk = 0; + + while (!last_chunk) { + saved_oip = oops_in_progress; + oops_in_progress = 1; + rc = log_buf_copy(ctx->bounce, idx, mtd->writesize); + if (rc < 0) + break; + + if (rc != mtd->writesize) + last_chunk = rc; + + oops_in_progress = saved_oip; + if (rc <= 0) + break; + if (rc != mtd->writesize) + memset(ctx->bounce + rc, 0, mtd->writesize - rc); + + rc2 = apanic_writeflashpage(mtd, off, ctx->bounce); + if (rc2 <= 0) { + printk(KERN_EMERG + "apanic: Flash write failed (%d)\n", rc2); + return idx; + } + if (!last_chunk) + idx += rc2; + else + idx += last_chunk; + off += rc2; + } + return idx; +} + +static int apanic(struct notifier_block *this, unsigned long event, + void *ptr) +{ + struct apanic_data *ctx = &drv_ctx; + struct panic_header *hdr = (struct panic_header *) ctx->bounce; + int console_offset = 0; + int console_len = 0; + int threads_offset = 0; + int threads_len = 0; + int rc; + + if (in_panic) + return NOTIFY_DONE; + in_panic = 1; +#ifdef CONFIG_PREEMPT + /* Ensure that cond_resched() won't try to preempt anybody */ + add_preempt_count(PREEMPT_ACTIVE); +#endif + touch_softlockup_watchdog(); + + if (!ctx->mtd) + goto out; + + if (ctx->curr.magic) { + printk(KERN_EMERG "Crash partition in use!\n"); + goto out; + } + console_offset = ctx->mtd->writesize; + + /* + * Write out the console + */ + console_len = apanic_write_console(ctx->mtd, console_offset); + if (console_len < 0) { + printk(KERN_EMERG "Error writing console to panic log! (%d)\n", + console_len); + console_len = 0; + } + + /* + * Write out all threads + */ + threads_offset = ALIGN(console_offset + console_len, + ctx->mtd->writesize); + if (!threads_offset) + threads_offset = ctx->mtd->writesize; + + ram_console_enable_console(0); + + log_buf_clear(); + show_state_filter(0); + threads_len = apanic_write_console(ctx->mtd, threads_offset); + if (threads_len < 0) { + printk(KERN_EMERG "Error writing threads to panic log! (%d)\n", + threads_len); + threads_len = 0; + } + + /* + * Finally write the panic header + */ + memset(ctx->bounce, 0, PAGE_SIZE); + hdr->magic = PANIC_MAGIC; + hdr->version = PHDR_VERSION; + + hdr->console_offset = console_offset; + hdr->console_length = console_len; + + hdr->threads_offset = threads_offset; + hdr->threads_length = threads_len; + + rc = apanic_writeflashpage(ctx->mtd, 0, ctx->bounce); + if (rc <= 0) { + printk(KERN_EMERG "apanic: Header write failed (%d)\n", + rc); + goto out; + } + + printk(KERN_EMERG "apanic: Panic dump sucessfully written to flash\n"); + + out: +#ifdef CONFIG_PREEMPT + sub_preempt_count(PREEMPT_ACTIVE); +#endif + in_panic = 0; + return NOTIFY_DONE; +} + +static struct notifier_block panic_blk = { + .notifier_call = apanic, +}; + +static int panic_dbg_get(void *data, u64 *val) +{ + apanic(NULL, 0, NULL); + return 0; +} + +static int panic_dbg_set(void *data, u64 val) +{ + BUG(); + return -1; +} + +DEFINE_SIMPLE_ATTRIBUTE(panic_dbg_fops, panic_dbg_get, panic_dbg_set, "%llu\n"); + +int __init apanic_init(void) +{ + register_mtd_user(&mtd_panic_notifier); + atomic_notifier_chain_register(&panic_notifier_list, &panic_blk); + debugfs_create_file("apanic", 0644, NULL, NULL, &panic_dbg_fops); + memset(&drv_ctx, 0, sizeof(drv_ctx)); + drv_ctx.bounce = (void *) __get_free_page(GFP_KERNEL); + INIT_WORK(&proc_removal_work, apanic_remove_proc_work); + printk(KERN_INFO "Android kernel panic handler initialized (bind=%s)\n", + CONFIG_APANIC_PLABEL); + return 0; +} + +module_init(apanic_init); diff --git a/drivers/misc/kernel_debugger.c b/drivers/misc/kernel_debugger.c new file mode 100644 index 00000000000000..f10eaa4dfc45e7 --- /dev/null +++ b/drivers/misc/kernel_debugger.c @@ -0,0 +1,79 @@ +/* drivers/android/kernel_debugger.c + * + * Guts of the kernel debugger. + * Needs something to actually push commands to it. + * + * Copyright (C) 2007-2008 Google, Inc. + * + * 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 + +#define dprintf(fmt...) (ctxt->printf(ctxt->cookie, fmt)) + +static void do_ps(struct kdbg_ctxt *ctxt) +{ + struct task_struct *g, *p; + unsigned state; + static const char stat_nam[] = "RSDTtZX"; + + dprintf("pid ppid prio task pc\n"); + read_lock(&tasklist_lock); + do_each_thread(g, p) { + state = p->state ? __ffs(p->state) + 1 : 0; + dprintf("%5d %5d %4d ", p->pid, p->parent->pid, p->prio); + dprintf("%-13.13s %c", p->comm, + state >= sizeof(stat_nam) ? '?' : stat_nam[state]); + if (state == TASK_RUNNING) + dprintf(" running\n"); + else + dprintf(" %08lx\n", thread_saved_pc(p)); + } while_each_thread(g, p); + read_unlock(&tasklist_lock); +} + +int log_buf_copy(char *dest, int idx, int len); +extern int do_syslog(int type, char __user *bug, int count); +static void do_sysrq(struct kdbg_ctxt *ctxt, char rq) +{ + char buf[128]; + int ret; + int idx = 0; + do_syslog(5 /* clear */, NULL, 0); + __handle_sysrq(rq, NULL, 0); + while (1) { + ret = log_buf_copy(buf, idx, sizeof(buf) - 1); + if (ret <= 0) + break; + buf[ret] = 0; + dprintf("%s", buf); + idx += ret; + } +} + +int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd) +{ + if (!strcmp(cmd, "ps")) + do_ps(ctxt); + if (!strcmp(cmd, "sysrq")) + do_sysrq(ctxt, 'h'); + if (!strncmp(cmd, "sysrq ", 6)) + do_sysrq(ctxt, cmd[6]); + + return 0; +} + diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c new file mode 100644 index 00000000000000..7f3b5321756045 --- /dev/null +++ b/drivers/misc/pmem.c @@ -0,0 +1,1345 @@ +/* drivers/android/pmem.c + * + * Copyright (C) 2007 Google, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define PMEM_MAX_DEVICES 10 +#define PMEM_MAX_ORDER 128 +#define PMEM_MIN_ALLOC PAGE_SIZE + +#define PMEM_DEBUG 1 + +/* indicates that a refernce to this file has been taken via get_pmem_file, + * the file should not be released until put_pmem_file is called */ +#define PMEM_FLAGS_BUSY 0x1 +/* indicates that this is a suballocation of a larger master range */ +#define PMEM_FLAGS_CONNECTED 0x1 << 1 +/* indicates this is a master and not a sub allocation and that it is mmaped */ +#define PMEM_FLAGS_MASTERMAP 0x1 << 2 +/* submap and unsubmap flags indicate: + * 00: subregion has never been mmaped + * 10: subregion has been mmaped, reference to the mm was taken + * 11: subretion has ben released, refernece to the mm still held + * 01: subretion has been released, reference to the mm has been released + */ +#define PMEM_FLAGS_SUBMAP 0x1 << 3 +#define PMEM_FLAGS_UNSUBMAP 0x1 << 4 + + +struct pmem_data { + /* in alloc mode: an index into the bitmap + * in no_alloc mode: the size of the allocation */ + int index; + /* see flags above for descriptions */ + unsigned int flags; + /* protects this data field, if the mm_mmap sem will be held at the + * same time as this sem, the mm sem must be taken first (as this is + * the order for vma_open and vma_close ops */ + struct rw_semaphore sem; + /* info about the mmaping process */ + struct vm_area_struct *vma; + /* task struct of the mapping process */ + struct task_struct *task; + /* process id of teh mapping process */ + pid_t pid; + /* file descriptor of the master */ + int master_fd; + /* file struct of the master */ + struct file *master_file; + /* a list of currently available regions if this is a suballocation */ + struct list_head region_list; + /* a linked list of data so we can access them for debugging */ + struct list_head list; +#if PMEM_DEBUG + int ref; +#endif +}; + +struct pmem_bits { + unsigned allocated:1; /* 1 if allocated, 0 if free */ + unsigned order:7; /* size of the region in pmem space */ +}; + +struct pmem_region_node { + struct pmem_region region; + struct list_head list; +}; + +#define PMEM_DEBUG_MSGS 0 +#if PMEM_DEBUG_MSGS +#define DLOG(fmt,args...) \ + do { printk(KERN_INFO "[%s:%s:%d] "fmt, __FILE__, __func__, __LINE__, \ + ##args); } \ + while (0) +#else +#define DLOG(x...) do {} while (0) +#endif + +struct pmem_info { + struct miscdevice dev; + /* physical start address of the remaped pmem space */ + unsigned long base; + /* vitual start address of the remaped pmem space */ + unsigned char __iomem *vbase; + /* total size of the pmem space */ + unsigned long size; + /* number of entries in the pmem space */ + unsigned long num_entries; + /* pfn of the garbage page in memory */ + unsigned long garbage_pfn; + /* index of the garbage page in the pmem space */ + int garbage_index; + /* the bitmap for the region indicating which entries are allocated + * and which are free */ + struct pmem_bits *bitmap; + /* indicates the region should not be managed with an allocator */ + unsigned no_allocator; + /* indicates maps of this region should be cached, if a mix of + * cached and uncached is desired, set this and open the device with + * O_SYNC to get an uncached region */ + unsigned cached; + unsigned buffered; + /* in no_allocator mode the first mapper gets the whole space and sets + * this flag */ + unsigned allocated; + /* for debugging, creates a list of pmem file structs, the + * data_list_sem should be taken before pmem_data->sem if both are + * needed */ + struct semaphore data_list_sem; + struct list_head data_list; + /* pmem_sem protects the bitmap array + * a write lock should be held when modifying entries in bitmap + * a read lock should be held when reading data from bits or + * dereferencing a pointer into bitmap + * + * pmem_data->sem protects the pmem data of a particular file + * Many of the function that require the pmem_data->sem have a non- + * locking version for when the caller is already holding that sem. + * + * IF YOU TAKE BOTH LOCKS TAKE THEM IN THIS ORDER: + * down(pmem_data->sem) => down(bitmap_sem) + */ + struct rw_semaphore bitmap_sem; + + long (*ioctl)(struct file *, unsigned int, unsigned long); + int (*release)(struct inode *, struct file *); +}; + +static struct pmem_info pmem[PMEM_MAX_DEVICES]; +static int id_count; + +#define PMEM_IS_FREE(id, index) !(pmem[id].bitmap[index].allocated) +#define PMEM_ORDER(id, index) pmem[id].bitmap[index].order +#define PMEM_BUDDY_INDEX(id, index) (index ^ (1 << PMEM_ORDER(id, index))) +#define PMEM_NEXT_INDEX(id, index) (index + (1 << PMEM_ORDER(id, index))) +#define PMEM_OFFSET(index) (index * PMEM_MIN_ALLOC) +#define PMEM_START_ADDR(id, index) (PMEM_OFFSET(index) + pmem[id].base) +#define PMEM_LEN(id, index) ((1 << PMEM_ORDER(id, index)) * PMEM_MIN_ALLOC) +#define PMEM_END_ADDR(id, index) (PMEM_START_ADDR(id, index) + \ + PMEM_LEN(id, index)) +#define PMEM_START_VADDR(id, index) (PMEM_OFFSET(id, index) + pmem[id].vbase) +#define PMEM_END_VADDR(id, index) (PMEM_START_VADDR(id, index) + \ + PMEM_LEN(id, index)) +#define PMEM_REVOKED(data) (data->flags & PMEM_FLAGS_REVOKED) +#define PMEM_IS_PAGE_ALIGNED(addr) (!((addr) & (~PAGE_MASK))) +#define PMEM_IS_SUBMAP(data) ((data->flags & PMEM_FLAGS_SUBMAP) && \ + (!(data->flags & PMEM_FLAGS_UNSUBMAP))) + +static int pmem_release(struct inode *, struct file *); +static int pmem_mmap(struct file *, struct vm_area_struct *); +static int pmem_open(struct inode *, struct file *); +static long pmem_ioctl(struct file *, unsigned int, unsigned long); + +struct file_operations pmem_fops = { + .release = pmem_release, + .mmap = pmem_mmap, + .open = pmem_open, + .unlocked_ioctl = pmem_ioctl, +}; + +static int get_id(struct file *file) +{ + return MINOR(file->f_dentry->d_inode->i_rdev); +} + +int is_pmem_file(struct file *file) +{ + int id; + + if (unlikely(!file || !file->f_dentry || !file->f_dentry->d_inode)) + return 0; + id = get_id(file); + if (unlikely(id >= PMEM_MAX_DEVICES)) + return 0; + if (unlikely(file->f_dentry->d_inode->i_rdev != + MKDEV(MISC_MAJOR, pmem[id].dev.minor))) + return 0; + return 1; +} + +static int has_allocation(struct file *file) +{ + struct pmem_data *data; + /* check is_pmem_file first if not accessed via pmem_file_ops */ + + if (unlikely(!file->private_data)) + return 0; + data = (struct pmem_data *)file->private_data; + if (unlikely(data->index < 0)) + return 0; + return 1; +} + +static int is_master_owner(struct file *file) +{ + struct file *master_file; + struct pmem_data *data; + int put_needed, ret = 0; + + if (!is_pmem_file(file) || !has_allocation(file)) + return 0; + data = (struct pmem_data *)file->private_data; + if (PMEM_FLAGS_MASTERMAP & data->flags) + return 1; + master_file = fget_light(data->master_fd, &put_needed); + if (master_file && data->master_file == master_file) + ret = 1; + fput_light(master_file, put_needed); + return ret; +} + +static int pmem_free(int id, int index) +{ + /* caller should hold the write lock on pmem_sem! */ + int buddy, curr = index; + DLOG("index %d\n", index); + + if (pmem[id].no_allocator) { + pmem[id].allocated = 0; + return 0; + } + /* clean up the bitmap, merging any buddies */ + pmem[id].bitmap[curr].allocated = 0; + /* find a slots buddy Buddy# = Slot# ^ (1 << order) + * if the buddy is also free merge them + * repeat until the buddy is not free or end of the bitmap is reached + */ + do { + buddy = PMEM_BUDDY_INDEX(id, curr); + if (PMEM_IS_FREE(id, buddy) && + PMEM_ORDER(id, buddy) == PMEM_ORDER(id, curr)) { + PMEM_ORDER(id, buddy)++; + PMEM_ORDER(id, curr)++; + curr = min(buddy, curr); + } else { + break; + } + } while (curr < pmem[id].num_entries); + + return 0; +} + +static void pmem_revoke(struct file *file, struct pmem_data *data); + +static int pmem_release(struct inode *inode, struct file *file) +{ + struct pmem_data *data = (struct pmem_data *)file->private_data; + struct pmem_region_node *region_node; + struct list_head *elt, *elt2; + int id = get_id(file), ret = 0; + + + down(&pmem[id].data_list_sem); + /* if this file is a master, revoke all the memory in the connected + * files */ + if (PMEM_FLAGS_MASTERMAP & data->flags) { + struct pmem_data *sub_data; + list_for_each(elt, &pmem[id].data_list) { + sub_data = list_entry(elt, struct pmem_data, list); + down_read(&sub_data->sem); + if (PMEM_IS_SUBMAP(sub_data) && + file == sub_data->master_file) { + up_read(&sub_data->sem); + pmem_revoke(file, sub_data); + } else + up_read(&sub_data->sem); + } + } + list_del(&data->list); + up(&pmem[id].data_list_sem); + + + down_write(&data->sem); + + /* if its not a conencted file and it has an allocation, free it */ + if (!(PMEM_FLAGS_CONNECTED & data->flags) && has_allocation(file)) { + down_write(&pmem[id].bitmap_sem); + ret = pmem_free(id, data->index); + up_write(&pmem[id].bitmap_sem); + } + + /* if this file is a submap (mapped, connected file), downref the + * task struct */ + if (PMEM_FLAGS_SUBMAP & data->flags) + if (data->task) { + put_task_struct(data->task); + data->task = NULL; + } + + file->private_data = NULL; + + list_for_each_safe(elt, elt2, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, list); + list_del(elt); + kfree(region_node); + } + BUG_ON(!list_empty(&data->region_list)); + + up_write(&data->sem); + kfree(data); + if (pmem[id].release) + ret = pmem[id].release(inode, file); + + return ret; +} + +static int pmem_open(struct inode *inode, struct file *file) +{ + struct pmem_data *data; + int id = get_id(file); + int ret = 0; + + DLOG("current %u file %p(%d)\n", current->pid, file, file_count(file)); + /* setup file->private_data to indicate its unmapped */ + /* you can only open a pmem device one time */ + if (file->private_data != NULL) + return -1; + data = kmalloc(sizeof(struct pmem_data), GFP_KERNEL); + if (!data) { + printk("pmem: unable to allocate memory for pmem metadata."); + return -1; + } + data->flags = 0; + data->index = -1; + data->task = NULL; + data->vma = NULL; + data->pid = 0; + data->master_file = NULL; +#if PMEM_DEBUG + data->ref = 0; +#endif + INIT_LIST_HEAD(&data->region_list); + init_rwsem(&data->sem); + + file->private_data = data; + INIT_LIST_HEAD(&data->list); + + down(&pmem[id].data_list_sem); + list_add(&data->list, &pmem[id].data_list); + up(&pmem[id].data_list_sem); + return ret; +} + +static unsigned long pmem_order(unsigned long len) +{ + int i; + + len = (len + PMEM_MIN_ALLOC - 1)/PMEM_MIN_ALLOC; + len--; + for (i = 0; i < sizeof(len)*8; i++) + if (len >> i == 0) + break; + return i; +} + +static int pmem_allocate(int id, unsigned long len) +{ + /* caller should hold the write lock on pmem_sem! */ + /* return the corresponding pdata[] entry */ + int curr = 0; + int end = pmem[id].num_entries; + int best_fit = -1; + unsigned long order = pmem_order(len); + + if (pmem[id].no_allocator) { + DLOG("no allocator"); + if ((len > pmem[id].size) || pmem[id].allocated) + return -1; + pmem[id].allocated = 1; + return len; + } + + if (order > PMEM_MAX_ORDER) + return -1; + DLOG("order %lx\n", order); + + /* look through the bitmap: + * if you find a free slot of the correct order use it + * otherwise, use the best fit (smallest with size > order) slot + */ + while (curr < end) { + if (PMEM_IS_FREE(id, curr)) { + if (PMEM_ORDER(id, curr) == (unsigned char)order) { + /* set the not free bit and clear others */ + best_fit = curr; + break; + } + if (PMEM_ORDER(id, curr) > (unsigned char)order && + (best_fit < 0 || + PMEM_ORDER(id, curr) < PMEM_ORDER(id, best_fit))) + best_fit = curr; + } + curr = PMEM_NEXT_INDEX(id, curr); + } + + /* if best_fit < 0, there are no suitable slots, + * return an error + */ + if (best_fit < 0) { + printk("pmem: no space left to allocate!\n"); + return -1; + } + + /* now partition the best fit: + * split the slot into 2 buddies of order - 1 + * repeat until the slot is of the correct order + */ + while (PMEM_ORDER(id, best_fit) > (unsigned char)order) { + int buddy; + PMEM_ORDER(id, best_fit) -= 1; + buddy = PMEM_BUDDY_INDEX(id, best_fit); + PMEM_ORDER(id, buddy) = PMEM_ORDER(id, best_fit); + } + pmem[id].bitmap[best_fit].allocated = 1; + return best_fit; +} + +static pgprot_t phys_mem_access_prot(struct file *file, pgprot_t vma_prot) +{ + int id = get_id(file); +#ifdef pgprot_noncached + if (pmem[id].cached == 0 || file->f_flags & O_SYNC) + return pgprot_noncached(vma_prot); +#endif +#ifdef pgprot_ext_buffered + else if (pmem[id].buffered) + return pgprot_ext_buffered(vma_prot); +#endif + return vma_prot; +} + +static unsigned long pmem_start_addr(int id, struct pmem_data *data) +{ + if (pmem[id].no_allocator) + return PMEM_START_ADDR(id, 0); + else + return PMEM_START_ADDR(id, data->index); + +} + +static void *pmem_start_vaddr(int id, struct pmem_data *data) +{ + return pmem_start_addr(id, data) - pmem[id].base + pmem[id].vbase; +} + +static unsigned long pmem_len(int id, struct pmem_data *data) +{ + if (pmem[id].no_allocator) + return data->index; + else + return PMEM_LEN(id, data->index); +} + +static int pmem_map_garbage(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + int i, garbage_pages = len >> PAGE_SHIFT; + + vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP | VM_SHARED | VM_WRITE; + for (i = 0; i < garbage_pages; i++) { + if (vm_insert_pfn(vma, vma->vm_start + offset + (i * PAGE_SIZE), + pmem[id].garbage_pfn)) + return -EAGAIN; + } + return 0; +} + +static int pmem_unmap_pfn_range(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + int garbage_pages; + DLOG("unmap offset %lx len %lx\n", offset, len); + + BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); + + garbage_pages = len >> PAGE_SHIFT; + zap_page_range(vma, vma->vm_start + offset, len, NULL); + pmem_map_garbage(id, vma, data, offset, len); + return 0; +} + +static int pmem_map_pfn_range(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + DLOG("map offset %lx len %lx\n", offset, len); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_start)); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(vma->vm_end)); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(len)); + BUG_ON(!PMEM_IS_PAGE_ALIGNED(offset)); + + if (io_remap_pfn_range(vma, vma->vm_start + offset, + (pmem_start_addr(id, data) + offset) >> PAGE_SHIFT, + len, vma->vm_page_prot)) { + return -EAGAIN; + } + return 0; +} + +static int pmem_remap_pfn_range(int id, struct vm_area_struct *vma, + struct pmem_data *data, unsigned long offset, + unsigned long len) +{ + /* hold the mm semp for the vma you are modifying when you call this */ + BUG_ON(!vma); + zap_page_range(vma, vma->vm_start + offset, len, NULL); + return pmem_map_pfn_range(id, vma, data, offset, len); +} + +static void pmem_vma_open(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct pmem_data *data = file->private_data; + int id = get_id(file); + /* this should never be called as we don't support copying pmem + * ranges via fork */ + BUG_ON(!has_allocation(file)); + down_write(&data->sem); + /* remap the garbage pages, forkers don't get access to the data */ + pmem_unmap_pfn_range(id, vma, data, 0, vma->vm_start - vma->vm_end); + up_write(&data->sem); +} + +static void pmem_vma_close(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct pmem_data *data = file->private_data; + + DLOG("current %u ppid %u file %p count %d\n", current->pid, + current->parent->pid, file, file_count(file)); + if (unlikely(!is_pmem_file(file) || !has_allocation(file))) { + printk(KERN_WARNING "pmem: something is very wrong, you are " + "closing a vm backing an allocation that doesn't " + "exist!\n"); + return; + } + down_write(&data->sem); + if (data->vma == vma) { + data->vma = NULL; + if ((data->flags & PMEM_FLAGS_CONNECTED) && + (data->flags & PMEM_FLAGS_SUBMAP)) + data->flags |= PMEM_FLAGS_UNSUBMAP; + } + /* the kernel is going to free this vma now anyway */ + up_write(&data->sem); +} + +static struct vm_operations_struct vm_ops = { + .open = pmem_vma_open, + .close = pmem_vma_close, +}; + +static int pmem_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct pmem_data *data; + int index; + unsigned long vma_size = vma->vm_end - vma->vm_start; + int ret = 0, id = get_id(file); + + if (vma->vm_pgoff || !PMEM_IS_PAGE_ALIGNED(vma_size)) { +#if PMEM_DEBUG + printk(KERN_ERR "pmem: mmaps must be at offset zero, aligned" + " and a multiple of pages_size.\n"); +#endif + return -EINVAL; + } + + data = (struct pmem_data *)file->private_data; + down_write(&data->sem); + /* check this file isn't already mmaped, for submaps check this file + * has never been mmaped */ + if ((data->flags & PMEM_FLAGS_MASTERMAP) || + (data->flags & PMEM_FLAGS_SUBMAP) || + (data->flags & PMEM_FLAGS_UNSUBMAP)) { +#if PMEM_DEBUG + printk(KERN_ERR "pmem: you can only mmap a pmem file once, " + "this file is already mmaped. %x\n", data->flags); +#endif + ret = -EINVAL; + goto error; + } + /* if file->private_data == unalloced, alloc*/ + if (data && data->index == -1) { + down_write(&pmem[id].bitmap_sem); + index = pmem_allocate(id, vma->vm_end - vma->vm_start); + up_write(&pmem[id].bitmap_sem); + data->index = index; + } + /* either no space was available or an error occured */ + if (!has_allocation(file)) { + ret = -EINVAL; + printk("pmem: could not find allocation for map.\n"); + goto error; + } + + if (pmem_len(id, data) < vma_size) { +#if PMEM_DEBUG + printk(KERN_WARNING "pmem: mmap size [%lu] does not match" + "size of backing region [%lu].\n", vma_size, + pmem_len(id, data)); +#endif + ret = -EINVAL; + goto error; + } + + vma->vm_pgoff = pmem_start_addr(id, data) >> PAGE_SHIFT; + vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_page_prot); + + if (data->flags & PMEM_FLAGS_CONNECTED) { + struct pmem_region_node *region_node; + struct list_head *elt; + if (pmem_map_garbage(id, vma, data, 0, vma_size)) { + printk("pmem: mmap failed in kernel!\n"); + ret = -EAGAIN; + goto error; + } + list_for_each(elt, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, + list); + DLOG("remapping file: %p %lx %lx\n", file, + region_node->region.offset, + region_node->region.len); + if (pmem_remap_pfn_range(id, vma, data, + region_node->region.offset, + region_node->region.len)) { + ret = -EAGAIN; + goto error; + } + } + data->flags |= PMEM_FLAGS_SUBMAP; + get_task_struct(current->group_leader); + data->task = current->group_leader; + data->vma = vma; +#if PMEM_DEBUG + data->pid = current->pid; +#endif + DLOG("submmapped file %p vma %p pid %u\n", file, vma, + current->pid); + } else { + if (pmem_map_pfn_range(id, vma, data, 0, vma_size)) { + printk(KERN_INFO "pmem: mmap failed in kernel!\n"); + ret = -EAGAIN; + goto error; + } + data->flags |= PMEM_FLAGS_MASTERMAP; + data->pid = current->pid; + } + vma->vm_ops = &vm_ops; +error: + up_write(&data->sem); + return ret; +} + +/* the following are the api for accessing pmem regions by other drivers + * from inside the kernel */ +int get_pmem_user_addr(struct file *file, unsigned long *start, + unsigned long *len) +{ + struct pmem_data *data; + if (!is_pmem_file(file) || !has_allocation(file)) { +#if PMEM_DEBUG + printk(KERN_INFO "pmem: requested pmem data from invalid" + "file.\n"); +#endif + return -1; + } + data = (struct pmem_data *)file->private_data; + down_read(&data->sem); + if (data->vma) { + *start = data->vma->vm_start; + *len = data->vma->vm_end - data->vma->vm_start; + } else { + *start = 0; + *len = 0; + } + up_read(&data->sem); + return 0; +} + +int get_pmem_addr(struct file *file, unsigned long *start, + unsigned long *vstart, unsigned long *len) +{ + struct pmem_data *data; + int id; + + if (!is_pmem_file(file) || !has_allocation(file)) { + return -1; + } + + data = (struct pmem_data *)file->private_data; + if (data->index == -1) { +#if PMEM_DEBUG + printk(KERN_INFO "pmem: requested pmem data from file with no " + "allocation.\n"); + return -1; +#endif + } + id = get_id(file); + + down_read(&data->sem); + *start = pmem_start_addr(id, data); + *len = pmem_len(id, data); + *vstart = (unsigned long)pmem_start_vaddr(id, data); + up_read(&data->sem); +#if PMEM_DEBUG + down_write(&data->sem); + data->ref++; + up_write(&data->sem); +#endif + return 0; +} + +int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, + unsigned long *len, struct file **filp) +{ + struct file *file; + + file = fget(fd); + if (unlikely(file == NULL)) { + printk(KERN_INFO "pmem: requested data from file descriptor " + "that doesn't exist."); + return -1; + } + + if (get_pmem_addr(file, start, vstart, len)) + goto end; + + if (filp) + *filp = file; + return 0; +end: + fput(file); + return -1; +} + +void put_pmem_file(struct file *file) +{ + struct pmem_data *data; + int id; + + if (!is_pmem_file(file)) + return; + id = get_id(file); + data = (struct pmem_data *)file->private_data; +#if PMEM_DEBUG + down_write(&data->sem); + if (data->ref == 0) { + printk("pmem: pmem_put > pmem_get %s (pid %d)\n", + pmem[id].dev.name, data->pid); + BUG(); + } + data->ref--; + up_write(&data->sem); +#endif + fput(file); +} + +void flush_pmem_file(struct file *file, unsigned long offset, unsigned long len) +{ + struct pmem_data *data; + int id; + void *vaddr; + struct pmem_region_node *region_node; + struct list_head *elt; + void *flush_start, *flush_end; + + if (!is_pmem_file(file) || !has_allocation(file)) { + return; + } + + id = get_id(file); + data = (struct pmem_data *)file->private_data; + if (!pmem[id].cached || file->f_flags & O_SYNC) + return; + + down_read(&data->sem); + vaddr = pmem_start_vaddr(id, data); + /* if this isn't a submmapped file, flush the whole thing */ + if (unlikely(!(data->flags & PMEM_FLAGS_CONNECTED))) { + dmac_flush_range(vaddr, vaddr + pmem_len(id, data)); + goto end; + } + /* otherwise, flush the region of the file we are drawing */ + list_for_each(elt, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, list); + if ((offset >= region_node->region.offset) && + ((offset + len) <= (region_node->region.offset + + region_node->region.len))) { + flush_start = vaddr + region_node->region.offset; + flush_end = flush_start + region_node->region.len; + dmac_flush_range(flush_start, flush_end); + break; + } + } +end: + up_read(&data->sem); +} + +static int pmem_connect(unsigned long connect, struct file *file) +{ + struct pmem_data *data = (struct pmem_data *)file->private_data; + struct pmem_data *src_data; + struct file *src_file; + int ret = 0, put_needed; + + down_write(&data->sem); + /* retrieve the src file and check it is a pmem file with an alloc */ + src_file = fget_light(connect, &put_needed); + DLOG("connect %p to %p\n", file, src_file); + if (!src_file) { + printk("pmem: src file not found!\n"); + ret = -EINVAL; + goto err_no_file; + } + if (unlikely(!is_pmem_file(src_file) || !has_allocation(src_file))) { + printk(KERN_INFO "pmem: src file is not a pmem file or has no " + "alloc!\n"); + ret = -EINVAL; + goto err_bad_file; + } + src_data = (struct pmem_data *)src_file->private_data; + + if (has_allocation(file) && (data->index != src_data->index)) { + printk("pmem: file is already mapped but doesn't match this" + " src_file!\n"); + ret = -EINVAL; + goto err_bad_file; + } + data->index = src_data->index; + data->flags |= PMEM_FLAGS_CONNECTED; + data->master_fd = connect; + data->master_file = src_file; + +err_bad_file: + fput_light(src_file, put_needed); +err_no_file: + up_write(&data->sem); + return ret; +} + +static void pmem_unlock_data_and_mm(struct pmem_data *data, + struct mm_struct *mm) +{ + up_write(&data->sem); + if (mm != NULL) { + up_write(&mm->mmap_sem); + mmput(mm); + } +} + +static int pmem_lock_data_and_mm(struct file *file, struct pmem_data *data, + struct mm_struct **locked_mm) +{ + int ret = 0; + struct mm_struct *mm = NULL; + *locked_mm = NULL; +lock_mm: + down_read(&data->sem); + if (PMEM_IS_SUBMAP(data)) { + mm = get_task_mm(data->task); + if (!mm) { +#if PMEM_DEBUG + printk("pmem: can't remap task is gone!\n"); +#endif + up_read(&data->sem); + return -1; + } + } + up_read(&data->sem); + + if (mm) + down_write(&mm->mmap_sem); + + down_write(&data->sem); + /* check that the file didn't get mmaped before we could take the + * data sem, this should be safe b/c you can only submap each file + * once */ + if (PMEM_IS_SUBMAP(data) && !mm) { + pmem_unlock_data_and_mm(data, mm); + up_write(&data->sem); + goto lock_mm; + } + /* now check that vma.mm is still there, it could have been + * deleted by vma_close before we could get the data->sem */ + if ((data->flags & PMEM_FLAGS_UNSUBMAP) && (mm != NULL)) { + /* might as well release this */ + if (data->flags & PMEM_FLAGS_SUBMAP) { + put_task_struct(data->task); + data->task = NULL; + /* lower the submap flag to show the mm is gone */ + data->flags &= ~(PMEM_FLAGS_SUBMAP); + } + pmem_unlock_data_and_mm(data, mm); + return -1; + } + *locked_mm = mm; + return ret; +} + +int pmem_remap(struct pmem_region *region, struct file *file, + unsigned operation) +{ + int ret; + struct pmem_region_node *region_node; + struct mm_struct *mm = NULL; + struct list_head *elt, *elt2; + int id = get_id(file); + struct pmem_data *data = (struct pmem_data *)file->private_data; + + /* pmem region must be aligned on a page boundry */ + if (unlikely(!PMEM_IS_PAGE_ALIGNED(region->offset) || + !PMEM_IS_PAGE_ALIGNED(region->len))) { +#if PMEM_DEBUG + printk("pmem: request for unaligned pmem suballocation " + "%lx %lx\n", region->offset, region->len); +#endif + return -EINVAL; + } + + /* if userspace requests a region of len 0, there's nothing to do */ + if (region->len == 0) + return 0; + + /* lock the mm and data */ + ret = pmem_lock_data_and_mm(file, data, &mm); + if (ret) + return 0; + + /* only the owner of the master file can remap the client fds + * that back in it */ + if (!is_master_owner(file)) { +#if PMEM_DEBUG + printk("pmem: remap requested from non-master process\n"); +#endif + ret = -EINVAL; + goto err; + } + + /* check that the requested range is within the src allocation */ + if (unlikely((region->offset > pmem_len(id, data)) || + (region->len > pmem_len(id, data)) || + (region->offset + region->len > pmem_len(id, data)))) { +#if PMEM_DEBUG + printk(KERN_INFO "pmem: suballoc doesn't fit in src_file!\n"); +#endif + ret = -EINVAL; + goto err; + } + + if (operation == PMEM_MAP) { + region_node = kmalloc(sizeof(struct pmem_region_node), + GFP_KERNEL); + if (!region_node) { + ret = -ENOMEM; +#if PMEM_DEBUG + printk(KERN_INFO "No space to allocate metadata!"); +#endif + goto err; + } + region_node->region = *region; + list_add(®ion_node->list, &data->region_list); + } else if (operation == PMEM_UNMAP) { + int found = 0; + list_for_each_safe(elt, elt2, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, + list); + if (region->len == 0 || + (region_node->region.offset == region->offset && + region_node->region.len == region->len)) { + list_del(elt); + kfree(region_node); + found = 1; + } + } + if (!found) { +#if PMEM_DEBUG + printk("pmem: Unmap region does not map any mapped " + "region!"); +#endif + ret = -EINVAL; + goto err; + } + } + + if (data->vma && PMEM_IS_SUBMAP(data)) { + if (operation == PMEM_MAP) + ret = pmem_remap_pfn_range(id, data->vma, data, + region->offset, region->len); + else if (operation == PMEM_UNMAP) + ret = pmem_unmap_pfn_range(id, data->vma, data, + region->offset, region->len); + } + +err: + pmem_unlock_data_and_mm(data, mm); + return ret; +} + +static void pmem_revoke(struct file *file, struct pmem_data *data) +{ + struct pmem_region_node *region_node; + struct list_head *elt, *elt2; + struct mm_struct *mm = NULL; + int id = get_id(file); + int ret = 0; + + data->master_file = NULL; + ret = pmem_lock_data_and_mm(file, data, &mm); + /* if lock_data_and_mm fails either the task that mapped the fd, or + * the vma that mapped it have already gone away, nothing more + * needs to be done */ + if (ret) + return; + /* unmap everything */ + /* delete the regions and region list nothing is mapped any more */ + if (data->vma) + list_for_each_safe(elt, elt2, &data->region_list) { + region_node = list_entry(elt, struct pmem_region_node, + list); + pmem_unmap_pfn_range(id, data->vma, data, + region_node->region.offset, + region_node->region.len); + list_del(elt); + kfree(region_node); + } + /* delete the master file */ + pmem_unlock_data_and_mm(data, mm); +} + +static void pmem_get_size(struct pmem_region *region, struct file *file) +{ + struct pmem_data *data = (struct pmem_data *)file->private_data; + int id = get_id(file); + + if (!has_allocation(file)) { + region->offset = 0; + region->len = 0; + return; + } else { + region->offset = pmem_start_addr(id, data); + region->len = pmem_len(id, data); + } + DLOG("offset %lx len %lx\n", region->offset, region->len); +} + + +static long pmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct pmem_data *data; + int id = get_id(file); + + switch (cmd) { + case PMEM_GET_PHYS: + { + struct pmem_region region; + DLOG("get_phys\n"); + if (!has_allocation(file)) { + region.offset = 0; + region.len = 0; + } else { + data = (struct pmem_data *)file->private_data; + region.offset = pmem_start_addr(id, data); + region.len = pmem_len(id, data); + } + printk(KERN_INFO "pmem: request for physical address of pmem region " + "from process %d.\n", current->pid); + if (copy_to_user((void __user *)arg, ®ion, + sizeof(struct pmem_region))) + return -EFAULT; + break; + } + case PMEM_MAP: + { + struct pmem_region region; + if (copy_from_user(®ion, (void __user *)arg, + sizeof(struct pmem_region))) + return -EFAULT; + data = (struct pmem_data *)file->private_data; + return pmem_remap(®ion, file, PMEM_MAP); + } + break; + case PMEM_UNMAP: + { + struct pmem_region region; + if (copy_from_user(®ion, (void __user *)arg, + sizeof(struct pmem_region))) + return -EFAULT; + data = (struct pmem_data *)file->private_data; + return pmem_remap(®ion, file, PMEM_UNMAP); + break; + } + case PMEM_GET_SIZE: + { + struct pmem_region region; + DLOG("get_size\n"); + pmem_get_size(®ion, file); + if (copy_to_user((void __user *)arg, ®ion, + sizeof(struct pmem_region))) + return -EFAULT; + break; + } + case PMEM_GET_TOTAL_SIZE: + { + struct pmem_region region; + DLOG("get total size\n"); + region.offset = 0; + get_id(file); + region.len = pmem[id].size; + if (copy_to_user((void __user *)arg, ®ion, + sizeof(struct pmem_region))) + return -EFAULT; + break; + } + case PMEM_ALLOCATE: + { + if (has_allocation(file)) + return -EINVAL; + data = (struct pmem_data *)file->private_data; + data->index = pmem_allocate(id, arg); + break; + } + case PMEM_CONNECT: + DLOG("connect\n"); + return pmem_connect(arg, file); + break; + case PMEM_CACHE_FLUSH: + { + struct pmem_region region; + DLOG("flush\n"); + if (copy_from_user(®ion, (void __user *)arg, + sizeof(struct pmem_region))) + return -EFAULT; + flush_pmem_file(file, region.offset, region.len); + break; + } + default: + if (pmem[id].ioctl) + return pmem[id].ioctl(file, cmd, arg); + return -EINVAL; + } + return 0; +} + +#if PMEM_DEBUG +static ssize_t debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t debug_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + struct list_head *elt, *elt2; + struct pmem_data *data; + struct pmem_region_node *region_node; + int id = (int)file->private_data; + const int debug_bufmax = 4096; + static char buffer[4096]; + int n = 0; + + DLOG("debug open\n"); + n = scnprintf(buffer, debug_bufmax, + "pid #: mapped regions (offset, len) (offset,len)...\n"); + + down(&pmem[id].data_list_sem); + list_for_each(elt, &pmem[id].data_list) { + data = list_entry(elt, struct pmem_data, list); + down_read(&data->sem); + n += scnprintf(buffer + n, debug_bufmax - n, "pid %u:", + data->pid); + list_for_each(elt2, &data->region_list) { + region_node = list_entry(elt2, struct pmem_region_node, + list); + n += scnprintf(buffer + n, debug_bufmax - n, + "(%lx,%lx) ", + region_node->region.offset, + region_node->region.len); + } + n += scnprintf(buffer + n, debug_bufmax - n, "\n"); + up_read(&data->sem); + } + up(&pmem[id].data_list_sem); + + n++; + buffer[n] = 0; + return simple_read_from_buffer(buf, count, ppos, buffer, n); +} + +static struct file_operations debug_fops = { + .read = debug_read, + .open = debug_open, +}; +#endif + +#if 0 +static struct miscdevice pmem_dev = { + .name = "pmem", + .fops = &pmem_fops, +}; +#endif + +int pmem_setup(struct android_pmem_platform_data *pdata, + long (*ioctl)(struct file *, unsigned int, unsigned long), + int (*release)(struct inode *, struct file *)) +{ + int err = 0; + int i, index = 0; + int id = id_count; + id_count++; + + pmem[id].no_allocator = pdata->no_allocator; + pmem[id].cached = pdata->cached; + pmem[id].buffered = pdata->buffered; + pmem[id].base = pdata->start; + pmem[id].size = pdata->size; + pmem[id].ioctl = ioctl; + pmem[id].release = release; + init_rwsem(&pmem[id].bitmap_sem); + init_MUTEX(&pmem[id].data_list_sem); + INIT_LIST_HEAD(&pmem[id].data_list); + pmem[id].dev.name = pdata->name; + pmem[id].dev.minor = id; + pmem[id].dev.fops = &pmem_fops; + printk(KERN_INFO "%s: %d init\n", pdata->name, pdata->cached); + + err = misc_register(&pmem[id].dev); + if (err) { + printk(KERN_ALERT "Unable to register pmem driver!\n"); + goto err_cant_register_device; + } + pmem[id].num_entries = pmem[id].size / PMEM_MIN_ALLOC; + + pmem[id].bitmap = kmalloc(pmem[id].num_entries * + sizeof(struct pmem_bits), GFP_KERNEL); + if (!pmem[id].bitmap) + goto err_no_mem_for_metadata; + + memset(pmem[id].bitmap, 0, sizeof(struct pmem_bits) * + pmem[id].num_entries); + + for (i = sizeof(pmem[id].num_entries) * 8 - 1; i >= 0; i--) { + if ((pmem[id].num_entries) & 1<name, S_IFREG | S_IRUGO, NULL, (void *)id, + &debug_fops); +#endif + return 0; +error_cant_remap: + kfree(pmem[id].bitmap); +err_no_mem_for_metadata: + misc_deregister(&pmem[id].dev); +err_cant_register_device: + return -1; +} + +static int pmem_probe(struct platform_device *pdev) +{ + struct android_pmem_platform_data *pdata; + + if (!pdev || !pdev->dev.platform_data) { + printk(KERN_ALERT "Unable to probe pmem!\n"); + return -1; + } + pdata = pdev->dev.platform_data; + return pmem_setup(pdata, NULL, NULL); +} + + +static int pmem_remove(struct platform_device *pdev) +{ + int id = pdev->id; + __free_page(pfn_to_page(pmem[id].garbage_pfn)); + misc_deregister(&pmem[id].dev); + return 0; +} + +static struct platform_driver pmem_driver = { + .probe = pmem_probe, + .remove = pmem_remove, + .driver = { .name = "android_pmem" } +}; + + +static int __init pmem_init(void) +{ + return platform_driver_register(&pmem_driver); +} + +static void __exit pmem_exit(void) +{ + platform_driver_unregister(&pmem_driver); +} + +module_init(pmem_init); +module_exit(pmem_exit); + diff --git a/drivers/misc/sd8x_rfkill.c b/drivers/misc/sd8x_rfkill.c new file mode 100644 index 00000000000000..e8013a38a95123 --- /dev/null +++ b/drivers/misc/sd8x_rfkill.c @@ -0,0 +1,322 @@ +/* + * rfkill power contorl for Marvell sd8xxx wlan/bt + * + * Copyright (C) 2009 Marvell, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#define SD8X_DEV_NAME "sd8x-rfkill" + +struct sd8x_rfkill_data { + enum rfkill_type type; + bool blocked; + struct sd8x_rfkill_platform_data *pdata; +}; +#define SD8X_DATA_SIZE (RFKILL_TYPE_BLUETOOTH + 1) +static struct sd8x_rfkill_data *local_sd8x_data[SD8X_DATA_SIZE]; + +int add_sd8x_rfkill_device(int gpio_power_down, int gpio_reset, + struct mmc_host ***pmmc) +{ + int ret; + struct platform_device *pdev = NULL; + struct sd8x_rfkill_platform_data *pdata = NULL; + + pdata = kzalloc(sizeof(struct sd8x_rfkill_platform_data), GFP_KERNEL); + if (!pdata) { + printk(KERN_CRIT "no memory\n"); + goto err_out; + } + pdata->gpio_power_down = gpio_power_down; + pdata->gpio_reset = gpio_reset; + + pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!pdev) { + printk(KERN_CRIT "no memory\n"); + goto err_out; + } + pdev->name = SD8X_DEV_NAME; + pdev->id = -1, + pdev->dev.platform_data = pdata; + + ret = platform_device_register(pdev); + if (ret) { + dev_err(&pdev->dev, + "unable to register device: %d\n", ret); + goto err_out; + } + *pmmc = &(pdata->mmc); + return 0; + +err_out: + if (pdata) + kfree(pdata); + if (pdev) + kfree(pdev); + pr_debug("%s: error\n", __func__); + return -1; +} +EXPORT_SYMBOL(add_sd8x_rfkill_device); + +static int sd8x_power_on(struct sd8x_rfkill_platform_data *pdata, int on) +{ + int gpio_power_down = pdata->gpio_power_down; + int gpio_reset = pdata->gpio_reset; + + pr_debug("%s: on=%d\n", __FUNCTION__, on); + if (gpio_request(gpio_power_down, "sd8xxx power down")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_power_down); + return -1; + } + + if(gpio_reset && gpio_request(gpio_reset, "sd8xxx reset")) { + printk(KERN_INFO "gpio %d request failed\n", gpio_reset); + gpio_free(gpio_power_down); + return -1; + } + + if (on) { + gpio_direction_output(gpio_power_down, 1); + if(gpio_reset) { + gpio_direction_output(gpio_reset, 0); + msleep(50); + gpio_direction_output(gpio_reset, 1); + } + } else { + gpio_direction_output(gpio_power_down, 0); + if(gpio_reset) + gpio_direction_output(gpio_reset, 0); + } + + if (!pdata->mmc) + printk(KERN_DEBUG "rfkill is not linked with mmc_host\n"); + else { + int wait = 100; + mmc_detect_change(pdata->mmc, msecs_to_jiffies(10)); + + while (--wait) { + if ((on && pdata->mmc->card) || + (!on && !pdata->mmc->card)) + break; + msleep(100); + } + if (!wait) + printk(KERN_INFO "rfkill fails to wait right bus re-scan result in 10 seconds\n"); + } + + gpio_free(gpio_power_down); + if(gpio_reset) + gpio_free(gpio_reset); + return 0; +} + +static int sd8x_set_block(void *data, bool blocked) +{ + bool pre_blocked, another_blocked; + int ret = 0; + struct sd8x_rfkill_data *sd8x_data = + (struct sd8x_rfkill_data *)data; + + if (!sd8x_data->pdata->wlan_rfkill && !sd8x_data->pdata->bt_rfkill) { + ret = sd8x_power_on(sd8x_data->pdata, 0); + return 0; + } + + if (!sd8x_data->pdata->wlan_rfkill || !sd8x_data->pdata->bt_rfkill) { + BUG_ON(!blocked); + sd8x_data->blocked = blocked; + return 0; + } + + pre_blocked = sd8x_data->blocked; + + if (sd8x_data->type == RFKILL_TYPE_WLAN) { + another_blocked = + local_sd8x_data[RFKILL_TYPE_BLUETOOTH]->blocked; + } else if (sd8x_data->type == RFKILL_TYPE_BLUETOOTH) { + another_blocked = local_sd8x_data[RFKILL_TYPE_WLAN]->blocked; + } else { + return 0; + } + + pr_debug("%s: try to set block state of type(%d) as %d, pre_blocked=%d," + " another_blocked=%d\n", __func__, sd8x_data->type, blocked, + pre_blocked, another_blocked); + if (!blocked && pre_blocked && another_blocked) { + ret = sd8x_power_on(sd8x_data->pdata, 1); + } else if (blocked && !pre_blocked && another_blocked) { + ret = sd8x_power_on(sd8x_data->pdata, 0); + } + sd8x_data->blocked = blocked; + + return ret; +} + +static struct rfkill * sd8x_rfkill_register(struct device *parent, + enum rfkill_type type, char *name, + struct sd8x_rfkill_platform_data *pdata) +{ + int err; + struct rfkill *dev = NULL; + struct rfkill_ops *ops = NULL; + struct sd8x_rfkill_data *data = NULL; + + ops = kzalloc(sizeof(struct rfkill_ops), GFP_KERNEL); + if (!ops) + goto err_out; + ops->set_block = sd8x_set_block; + + data = kzalloc(sizeof(struct sd8x_rfkill_data), GFP_KERNEL); + if (!data) + goto err_out; + data->type = type; + data->blocked = true; + data->pdata = pdata; + local_sd8x_data[type] = data; + + dev = rfkill_alloc(name, parent, type, ops, data); + if (!dev) + goto err_out; + + /* init device software states, and block it by default */ + rfkill_init_sw_state(dev, true); + + err = rfkill_register(dev); + if (err) + goto err_out; + + return dev; + +err_out: + if (ops) + kfree(ops); + if (data) + kfree(data); + if (dev) + rfkill_destroy(dev); + return 0; +} + +static void sd8x_rfkill_free(struct sd8x_rfkill_platform_data *pdata) +{ + int i; + + if (pdata->wlan_rfkill) { + rfkill_unregister(pdata->wlan_rfkill); + rfkill_destroy(pdata->wlan_rfkill); + } + if (pdata->bt_rfkill) { + rfkill_unregister(pdata->bt_rfkill); + rfkill_destroy(pdata->bt_rfkill); + } + + for (i = 0; i < SD8X_DATA_SIZE && local_sd8x_data[i]; i++) { + kfree(local_sd8x_data[i]); + } +} + +static int sd8x_rfkill_probe(struct platform_device *pdev) +{ + struct rfkill *rfkill = NULL; + struct sd8x_rfkill_platform_data *pdata = + pdev->dev.platform_data; + + rfkill = sd8x_rfkill_register(&pdev->dev, + RFKILL_TYPE_WLAN, "sd8xxx-wlan", pdata); + if (IS_ERR(rfkill)) + goto err_out; + pdata->wlan_rfkill = rfkill; + + rfkill = sd8x_rfkill_register(&pdev->dev, + RFKILL_TYPE_BLUETOOTH, "sd8xxx-bluetooth", pdata); + if (IS_ERR(rfkill)) + goto err_out; + pdata->bt_rfkill = rfkill; + + return 0; + +err_out: + sd8x_rfkill_free(pdata); + return -1; +} + +static int sd8x_rfkill_remove(struct platform_device *pdev) +{ + struct sd8x_rfkill_platform_data *pdata = + pdev->dev.platform_data; + + sd8x_rfkill_free(pdata); + + return 0; +} + +static int sd8x_rfkill_suspend(struct platform_device *pdev, pm_message_t pm_state) +{ + struct sd8x_rfkill_platform_data *pdata = + pdev->dev.platform_data; + + /* For WIFI module is totally powered off, we need marked state + * as blocked so as to trigger re-download firmware + * when rfkill framework restore original active status */ + if(PM_EVENT_SUSPEND == pm_state.event){ + rfkill_set_sw_state(pdata->wlan_rfkill, true); + rfkill_set_sw_state(pdata->bt_rfkill, true); + } + + return 0; +} + +static int sd8x_rfkill_resume(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver sd8x_rfkill_platform_driver = { + .probe = sd8x_rfkill_probe, + .remove = sd8x_rfkill_remove, + .driver = { + .name = SD8X_DEV_NAME, + .owner = THIS_MODULE, + }, + .suspend = sd8x_rfkill_suspend, + .resume = sd8x_rfkill_resume, +}; + +static int __init sd8x_rfkill_init(void) +{ + return platform_driver_register(&sd8x_rfkill_platform_driver); +} + +static void __exit sd8x_rfkill_exit(void) +{ + platform_driver_unregister(&sd8x_rfkill_platform_driver); +} + +module_init(sd8x_rfkill_init); +module_exit(sd8x_rfkill_exit); + +MODULE_ALIAS("platform:sd8x_rfkill"); +MODULE_DESCRIPTION("sd8x_rfkill"); +MODULE_AUTHOR("Marvell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c new file mode 100644 index 00000000000000..43a548bab7ac07 --- /dev/null +++ b/drivers/misc/uid_stat.c @@ -0,0 +1,153 @@ +/* drivers/misc/uid_stat.c + * + * Copyright (C) 2008 - 2009 Google, Inc. + * + * 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 +#include +#include +#include +#include + +static DEFINE_SPINLOCK(uid_lock); +static LIST_HEAD(uid_list); +static struct proc_dir_entry *parent; + +struct uid_stat { + struct list_head link; + uid_t uid; + atomic_t tcp_rcv; + atomic_t tcp_snd; +}; + +static struct uid_stat *find_uid_stat(uid_t uid) { + unsigned long flags; + struct uid_stat *entry; + + spin_lock_irqsave(&uid_lock, flags); + list_for_each_entry(entry, &uid_list, link) { + if (entry->uid == uid) { + spin_unlock_irqrestore(&uid_lock, flags); + return entry; + } + } + spin_unlock_irqrestore(&uid_lock, flags); + return NULL; +} + +static int tcp_snd_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +static int tcp_rcv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; + + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} + +/* Create a new entry for tracking the specified uid. */ +static struct uid_stat *create_stat(uid_t uid) { + unsigned long flags; + char uid_s[32]; + struct uid_stat *new_uid; + struct proc_dir_entry *entry; + + /* Create the uid stat struct and append it to the list. */ + if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) + return NULL; + + new_uid->uid = uid; + /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ + atomic_set(&new_uid->tcp_rcv, INT_MIN); + atomic_set(&new_uid->tcp_snd, INT_MIN); + + spin_lock_irqsave(&uid_lock, flags); + list_add_tail(&new_uid->link, &uid_list); + spin_unlock_irqrestore(&uid_lock, flags); + + sprintf(uid_s, "%d", uid); + entry = proc_mkdir(uid_s, parent); + + /* Keep reference to uid_stat so we know what uid to read stats from. */ + create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, + (void *) new_uid); + + create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, + (void *) new_uid); + + return new_uid; +} + +int update_tcp_snd(uid_t uid, int size) { + struct uid_stat *entry; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } + atomic_add(size, &entry->tcp_snd); + return 0; +} + +int update_tcp_rcv(uid_t uid, int size) { + struct uid_stat *entry; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } + atomic_add(size, &entry->tcp_rcv); + return 0; +} + +static int __init uid_stat_init(void) +{ + parent = proc_mkdir("uid_stat", NULL); + if (!parent) { + pr_err("uid_stat: failed to create proc entry\n"); + return -1; + } + return 0; +} + +__initcall(uid_stat_init); diff --git a/drivers/misc/wl127x-rfkill.c b/drivers/misc/wl127x-rfkill.c new file mode 100644 index 00000000000000..f5b95152948b2a --- /dev/null +++ b/drivers/misc/wl127x-rfkill.c @@ -0,0 +1,121 @@ +/* + * Bluetooth TI wl127x rfkill power control via GPIO + * + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2008 Texas Instruments + * Initial code: Pavan Savoy (wl127x_power.c) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +static int wl127x_rfkill_set_power(void *data, enum rfkill_state state) +{ + int nshutdown_gpio = (int) data; + + switch (state) { + case RFKILL_STATE_UNBLOCKED: + gpio_set_value(nshutdown_gpio, 1); + break; + case RFKILL_STATE_SOFT_BLOCKED: + gpio_set_value(nshutdown_gpio, 0); + break; + default: + printk(KERN_ERR "invalid bluetooth rfkill state %d\n", state); + } + return 0; +} + +static int wl127x_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; + enum rfkill_state default_state = RFKILL_STATE_SOFT_BLOCKED; /* off */ + + rc = gpio_request(pdata->nshutdown_gpio, "wl127x_nshutdown_gpio"); + if (unlikely(rc)) + return rc; + + rc = gpio_direction_output(pdata->nshutdown_gpio, 0); + if (unlikely(rc)) + return rc; + + rfkill_set_default(RFKILL_TYPE_BLUETOOTH, default_state); + wl127x_rfkill_set_power(NULL, default_state); + + pdata->rfkill = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + if (unlikely(!pdata->rfkill)) + return -ENOMEM; + + pdata->rfkill->name = "wl127x"; + pdata->rfkill->state = default_state; + /* userspace cannot take exclusive control */ + pdata->rfkill->user_claim_unsupported = 1; + pdata->rfkill->user_claim = 0; + pdata->rfkill->data = (void *) pdata->nshutdown_gpio; + pdata->rfkill->toggle_radio = wl127x_rfkill_set_power; + + rc = rfkill_register(pdata->rfkill); + + if (unlikely(rc)) + rfkill_free(pdata->rfkill); + + return 0; +} + +static int wl127x_rfkill_remove(struct platform_device *pdev) +{ + struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data; + + rfkill_unregister(pdata->rfkill); + rfkill_free(pdata->rfkill); + gpio_free(pdata->nshutdown_gpio); + + return 0; +} + +static struct platform_driver wl127x_rfkill_platform_driver = { + .probe = wl127x_rfkill_probe, + .remove = wl127x_rfkill_remove, + .driver = { + .name = "wl127x-rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init wl127x_rfkill_init(void) +{ + return platform_driver_register(&wl127x_rfkill_platform_driver); +} + +static void __exit wl127x_rfkill_exit(void) +{ + platform_driver_unregister(&wl127x_rfkill_platform_driver); +} + +module_init(wl127x_rfkill_init); +module_exit(wl127x_rfkill_exit); + +MODULE_ALIAS("platform:wl127x"); +MODULE_DESCRIPTION("wl127x-rfkill"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 3f2a912659aff9..76d921ce4d6da3 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -32,6 +32,13 @@ config MMC_BLOCK_BOUNCE If unsure, say Y here. +config MMC_BLOCK_PARANOID_RESUME + bool "Check card status on resume" + depends on MMC_BLOCK + default y + help + Nohelp + config SDIO_UART tristate "SDIO UART/GPS class support" help diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index cb9fbc83b09095..d7be674606eebe 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -269,6 +269,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; brq.data.blocks = blk_rq_sectors(req); + /* + * The block layer doesn't support all sector count + * restrictions, so we need to be prepared for too big + * requests. + */ + if (brq.data.blocks > card->host->max_blk_count) + brq.data.blocks = card->host->max_blk_count; + /* * The block layer doesn't support all sector count * restrictions, so we need to be prepared for too big @@ -445,20 +453,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) * as reported by the controller (which might be less than * the real number of written sectors, but never more). */ - if (mmc_card_sd(card)) { - u32 blocks; + if (mmc_card_sd(card)) { + u32 blocks; - blocks = mmc_sd_num_wr_blocks(card); - if (blocks != (u32)-1) { + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + spin_lock_irq(&md->lock); + ret = __blk_end_request(req, 0, blocks << 9); + spin_unlock_irq(&md->lock); + } + } else { spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, blocks << 9); + ret = __blk_end_request(req, 0, brq.data.bytes_xfered); spin_unlock_irq(&md->lock); } - } else { - spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, brq.data.bytes_xfered); - spin_unlock_irq(&md->lock); - } mmc_release_host(card->host); diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index bb22ffd76ef8a2..0eba6658233a35 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -16,3 +16,20 @@ config MMC_UNSAFE_RESUME This option sets a default which can be overridden by the module parameter "removable=0" or "removable=1". + +config MMC_EMBEDDED_SDIO + boolean "MMC embedded SDIO device support (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, support will be added for embedded SDIO + devices which do not contain the necessary enumeration + support in hardware to be properly detected. + +config MMC_PARANOID_SD_INIT + bool "Enable paranoid SD card initialization (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + If you say Y here, the MMC layer will be extra paranoid + about re-trying SD init requests. This can be a useful + work-around for buggy controllers and hardware. Enable + if you are experiencing issues with SD detection. diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3168ebd616b2ae..939ee504b9b5bf 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -353,7 +353,12 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz) * the core about its problems yet, so for now we just 32-bit * align the size. */ +#ifdef S0_HARDWARE_BROKEN +#warning ALIGN XFER TO 8 BYTES + sz = ((sz + 7) / 8) * 8; +#else sz = ((sz + 3) / 4) * 4; +#endif return sz; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 89f7a25b7ac12a..3f4e7790d1751a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -54,6 +54,7 @@ static const unsigned int tacc_mant[] = { __res & __mask; \ }) + /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ @@ -122,10 +123,15 @@ static int mmc_decode_csd(struct mmc_card *card) * v1.2 has extra information in bits 15, 11 and 10. */ csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { - printk(KERN_ERR "%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); - return -EINVAL; + if (csd_struct > 3) { + printk(KERN_WARNING "%s: unsupported CSD structure " + " version %d\n", mmc_hostname(card->host), csd_struct); + /* + * do not treat this as fatal. so far, revisions of the + * CSD and Extended CSD have maintained backwards + * compatibility, so there's a good chance this will + * work even if a new revision comes out. + */ } csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); @@ -222,8 +228,6 @@ static int mmc_read_ext_csd(struct mmc_card *card) ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - if (card->ext_csd.sectors) - mmc_card_set_blockaddr(card); } switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) { @@ -306,6 +310,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, int err; u32 cid[4]; unsigned int max_dtr; + u32 rocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -319,7 +324,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_go_idle(host); /* The extra bit indicates that we support high capacity */ - err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); + err = mmc_send_op_cond(host, ocr | MMC_CARD_SECTOR_ADDR, &rocr); if (err) goto err; @@ -407,6 +412,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, err = mmc_read_ext_csd(card); if (err) goto free_card; + + if (rocr & MMC_CARD_SECTOR_ADDR) + mmc_card_set_blockaddr(card); } /* @@ -429,6 +437,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + /* + * ensure eMMC private booting PARTITION is not enabled + */ + mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BOOT_CONFIG, 0x0); + /* * Compute bus speed. */ @@ -449,14 +463,33 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { unsigned ext_csd_bit, bus_width; - - if (host->caps & MMC_CAP_8_BIT_DATA) { - ext_csd_bit = EXT_CSD_BUS_WIDTH_8; - bus_width = MMC_BUS_WIDTH_8; - } else { - ext_csd_bit = EXT_CSD_BUS_WIDTH_4; - bus_width = MMC_BUS_WIDTH_4; - } + int temp_caps = host->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA); + + do { + if (temp_caps & MMC_CAP_8_BIT_DATA) { + ext_csd_bit = EXT_CSD_BUS_WIDTH_8; + bus_width = MMC_BUS_WIDTH_8; + } else { + ext_csd_bit = EXT_CSD_BUS_WIDTH_4; + bus_width = MMC_BUS_WIDTH_4; + } + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, ext_csd_bit); + mmc_set_bus_width(card->host, bus_width); + if (mmc_test_bus_width (card, 1<host, &mrq); + + if (cmd.error || data.error ) { + printk(KERN_INFO "Failed to send CMD19: %d %d\n", cmd.error, data.error); + return 0; + } + + /* Now read back */ + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + memset (&test_data_read, 0, sizeof(test_data_read)); + + cmd.opcode = MMC_BUSTEST_R; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.flags = MMC_DATA_READ; + data.blksz = sizeof(test_data_read); + data.blocks = 1; + data.sg = &sg; + data.sg_len = 1; + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, &test_data_read, sizeof(test_data_read)); + + /* + * The spec states that MMC_BUSTEST_W and BUSTEST_R accesses have a timeout + * of 64 clock cycles. + */ + data.timeout_ns = 0; + data.timeout_clks = 64; + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) { + printk(KERN_INFO "Failed to send CMD14: %d %d\n", cmd.error, data.error); + return 0; + } + +#if 0 +#warning PRINT RESULTS FROM CMD14 + printk (KERN_INFO "%s: Got %02X %02X %02X %02X\n", __FUNCTION__, + test_data_read[0], + test_data_read[1], + test_data_read[2], + test_data_read[3]); +#endif + + switch (bits) { + case 8: + return (test_data_read[0] == 0xaa && test_data_read[1] == 0x55); + case 4: + return (test_data_read[0] == 0xa5); + } + return 0; +} + static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 653eb8e841789d..ad79187a72d0ce 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -25,6 +25,7 @@ int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_cid(struct mmc_host *host, u32 *cid); int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); int mmc_spi_set_crc(struct mmc_host *host, int use_crc); +int mmc_test_bus_width(struct mmc_card *card, int bits); int mmc_card_sleepawake(struct mmc_host *host, int sleep); #endif diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5eac21df4809fe..226c04de460987 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -543,7 +543,7 @@ static void mmc_sd_detect(struct mmc_host *host) BUG_ON(!host); BUG_ON(!host->card); - + mmc_claim_host(host); /* diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 0d96080d44b098..477670ff2cc1e3 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -254,6 +254,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) struct mmc_command cmd; struct mmc_data data; struct scatterlist sg; + u32 temp[2]; BUG_ON(!card); BUG_ON(!card->host); @@ -282,7 +283,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) data.sg = &sg; data.sg_len = 1; - sg_init_one(&sg, scr, 8); + sg_init_one(&sg, temp, 8); mmc_set_data_timeout(&data, card); @@ -293,8 +294,8 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) if (data.error) return data.error; - scr[0] = be32_to_cpu(scr[0]); - scr[1] = be32_to_cpu(scr[1]); + scr[0] = be32_to_cpu(temp[0]); + scr[1] = be32_to_cpu(temp[1]); return 0; } diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 4a890dcb95ab41..5d69be00c05606 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -200,7 +200,7 @@ static void sdio_release_func(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); - sdio_free_func_cis(func); + sdio_free_func_cis(func); if (func->info) kfree(func->info); diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index ff27c8c7135540..df7745f1e9745e 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "sdio_ops.h" @@ -324,8 +325,12 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, ret = mmc_io_rw_extended(func->card, write, func->num, addr, incr_addr, buf, blocks, func->cur_blksize); - if (ret) + if (ret) { +#ifdef CONFIG_CPU_PXA168 + pxa_sdh_startclk(func->card->host); +#endif return ret; + } remainder -= size; buf += size; @@ -340,14 +345,21 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, ret = mmc_io_rw_extended(func->card, write, func->num, addr, incr_addr, buf, 1, size); - if (ret) + if (ret) { +#ifdef CONFIG_CPU_PXA168 + pxa_sdh_startclk(func->card->host); +#endif return ret; + } remainder -= size; buf += size; if (incr_addr) addr += size; } +#ifdef CONFIG_CPU_PXA168 + pxa_sdh_startclk(func->card->host); +#endif return 0; } @@ -382,6 +394,41 @@ u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) } EXPORT_SYMBOL_GPL(sdio_readb); +#if 0 +/** + * sdio_readb_ext - read a single byte from a SDIO function + * @func: SDIO function to access + * @addr: address to read + * @err_ret: optional status value from transfer + * @in: value to add to argument + * + * Reads a single byte from the address space of a given SDIO + * function. If there is a problem reading the address, 0xff + * is returned and @err_ret will contain the error code. + */ +unsigned char sdio_readb_ext(struct sdio_func *func, unsigned int addr, + int *err_ret, unsigned in) +{ + int ret; + unsigned char val; + + BUG_ON(!func); + + if (err_ret) + *err_ret = 0; + + ret = mmc_io_rw_direct(func->card, 0, func->num, addr, (u8)in, &val); + if (ret) { + if (err_ret) + *err_ret = ret; + return 0xFF; + } + + return val; +} +EXPORT_SYMBOL_GPL(sdio_readb_ext); +#endif + /** * sdio_writeb - write a single byte to a SDIO function * @func: SDIO function to access @@ -641,6 +688,36 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, } EXPORT_SYMBOL_GPL(sdio_f0_writeb); + +// this code is used to fix a h/w issue in the 8686 hardware +// for sdio 1 bit mode to work -- require ECSI to be enabled +// Enable continuous SPI Interrupt + +#warning ECSI HACK FOR 8686 + +#ifndef SDIO_BUS_ECSI_ENABLED +#define SDIO_BUS_ECSI_ENABLED (1<<5) +#endif +int sdio_force_ecsi_on_if_1bit_mode(struct sdio_func *func) +{ + int ret; + unsigned char val; + + ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IF, 0, &val); + if (ret) + return ret; + + // check if bus width set + if ((val & 0x3) == SDIO_BUS_WIDTH_1BIT) + { + val |= SDIO_BUS_ECSI_ENABLED; + + ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IF, val, NULL); + } + return ret; +} +EXPORT_SYMBOL_GPL(sdio_force_ecsi_on_if_1bit_mode); + /** * sdio_get_host_pm_caps - get host power management capabilities * @func: SDIO function attached to host diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2e13b94769fdce..143e0f94650c69 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -24,6 +24,28 @@ config MMC_PXA If unsure, say N. +config MMC_PXA_SDH + bool "Marvell PXA168/PXA910 SD Host Controller support" + depends on ARCH_PXA || ARCH_MMP + select MMC_SDHCI + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Marvell(R) PXA168/PXA910 SD Host Controller. + If you have a PXA168/PXA910 platform with SD Host Controller and a + card slot,say Y or M here. + + If unsure, say N. + +config MMC3 + tristate "Marvell MMC3 support" + depends on ARCH_PXA || ARCH_MMP + help + This selects the Marvell(R) MMC3 Host Controller. + If you have a PXAxxx platform with SD Host Controller and a + Card slot,say Y or M here. + + If unsure, say N. + config MMC_SDHCI tristate "Secure Digital Host Controller Interface support" depends on HAS_DMA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f4803977dfceb2..4391e59ba439a3 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -8,6 +8,7 @@ endif obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o +obj-$(CONFIG_MMC_PXA_SDH) += sdhci-pxa168.o obj-$(CONFIG_MMC_IMX) += imxmmc.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/host/pxa_sdh.c b/drivers/mmc/host/pxa_sdh.c new file mode 100644 index 00000000000000..e86500e8cfa10e --- /dev/null +++ b/drivers/mmc/host/pxa_sdh.c @@ -0,0 +1,1170 @@ +/* + * linux/drivers/mmc/host/pxa_sdh.c - PXAxxx SD Host driver + * + * Copyright (C) 2008-2009 Marvell International Ltd. + * Mingwei Wang + * Kevin Wang + * + * Based on linux/drivers/mmc/host/pxa.c - PXA MMCI driver + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * 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 "pxa_sdh.h" + +#ifdef CONFIG_DVFM +#include +static struct dvfm_lock dvfm_lock = { + .lock = SPIN_LOCK_UNLOCKED, + .count = 0, + .dev_idx = -1, +}; + +static void set_dvfm_constraint(void) +{ + spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags); + if (dvfm_lock.count++ == 0) { + /* Disable Low power mode */ + dvfm_disable_op_name("apps_idle", dvfm_lock.dev_idx); + dvfm_disable_op_name("apps_sleep", dvfm_lock.dev_idx); + dvfm_disable_op_name("sys_sleep", dvfm_lock.dev_idx); + } else + dvfm_lock.count--; + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); +} + +static void unset_dvfm_constraint(void) +{ + spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags); + if (dvfm_lock.count == 0) { + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); + return; + } + if (--dvfm_lock.count == 0) { + /* Enable Low power mode */ + dvfm_enable_op_name("apps_idle", dvfm_lock.dev_idx); + dvfm_enable_op_name("apps_sleep", dvfm_lock.dev_idx); + dvfm_enable_op_name("sys_sleep", dvfm_lock.dev_idx); + } else + dvfm_lock.count++; + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); +} +#else +static void set_dvfm_constraint(void) {} +static void unset_dvfm_constraint(void) {} +#endif + +#define DRIVER_NAME "pxa-sdh" + +#define ADMA_DESC_TBL_SIZE PAGE_SIZE +#define CLKRT_OFF (~0) +#define GET_REG(host, reg) readw(host->base + reg) +#define SET_REG(host, val, reg) writew(val, host->base + reg) +#define DATA_DIRECTION_READ(data) (data->flags & MMC_DATA_READ) +#define SET_REG_BIT(host, bit_mask, reg) \ + SET_REG(host, \ + GET_REG(host, reg) | (bit_mask), reg) +#define CLEAR_REG_BIT(host, bit_mask, reg) \ + SET_REG(host, \ + GET_REG(host, reg) & ~(bit_mask), reg) +#define SET_REG_BITS(host, bits_pos, bits_mask, val, reg) \ + SET_REG(host, \ + GET_REG(host, reg) & ~(bits_mask << bits_pos), reg); \ + SET_REG(host, \ + GET_REG(host, reg) | (val << bits_pos), reg) +#define GET_REG_BITS(host, bit_pos, bits_mask, reg) \ + ((GET_REG(host, reg) >> bit_pos) & bits_mask) +#define SG_NEED_PIO(sg) (sg_dma_len(sg) < 64) + +#if 0 +#define dev_dbg(dev, format, arg...) \ + dev_info(dev , format , ## arg) +#endif + +struct adma_desc_table { + u16 entry_attr; + u16 entry_length; + u32 entry_address; +}; + +struct pxa_sdh_host { + struct mmc_host *mmc; + spinlock_t lock; + struct resource *res; + void __iomem *base; + struct clk *clk; + u32 clkrate; + int irq; + u32 clkrt; + u32 segment; + u32 power_mode; + struct pxasdh_platform_data *pdata; + + struct mmc_request *mrq; + struct mmc_command *cmd; + + struct adma_desc_table *desc_tbl_addr; + dma_addr_t desc_tbl_dma_addr; + unsigned int sg_index; + u32 data_len; + unsigned int pio_sg_pos; + unsigned int dma_sg_len; +}; + +#ifdef CONFIG_MMC_DEBUG +static void __attribute__((unused)) dump_registers(struct pxa_sdh_host *host) +{ + unsigned int val; + int offset; + + for (offset = 0; offset < 0x60; offset += 4) { + if (offset == 0x20) + continue; + val = readl(host->base + offset); + printk(KERN_ERR "%08x: %08x\n", (unsigned int)host->base + offset, val); + } + for (offset = 0xE0; offset < 0xF0; offset += 4) { + val = readl(host->base + offset); + printk(KERN_ERR "%08x: %08x\n", (unsigned int)host->base + offset, val); + } + val = readl(host->base + 0xFC); + printk(KERN_ERR "%08x: %08x\n", (unsigned int)host->base + 0xFC, val); +} +#endif + +static int pxa_sdh_wait_reset(struct pxa_sdh_host *host) +{ + u32 timeout = 1000; + u16 val; + + do { + val = GET_REG(host, SD_TO_CTRL_SW_RST); + if (!(val & (SW_RST_DAT | SW_RST_CMD | SW_RST_ALL))) + break; + udelay(1); + } while (timeout--); + if (timeout) + return 0; + + dev_err(mmc_dev(host->mmc), "Fatal: Wait RESET timeout.\n"); + + return 1; +} + +static int pxa_sdh_get_cd(struct mmc_host *mmc) +{ +#if 1 + return 1; +#else + /*not stable on TTC EVB*/ + struct pxa_sdh_host *host = mmc_priv(mmc); + u32 timeout = 1000; + + if (pxa_sdh_wait_reset(host)) + return 0; + + do { + if (GET_REG(host, SD_PRESENT_STAT_2) & CARD_STABLE) + break; + } while (timeout--); + if (!timeout) { + dev_err(mmc_dev(mmc), "Card State is not stable!\n"); + } else if (GET_REG(host, SD_PRESENT_STAT_2) & CARD_DETECTED) { + dev_dbg(mmc_dev(mmc), "Card is detected!\n"); + return 1; + } + + dev_dbg(mmc_dev(mmc), "Card is not detected!\n"); + return 0; +#endif +} + +static void pxa_sdh_stop_clock(struct pxa_sdh_host *host) +{ + CLEAR_REG_BIT(host, EXT_CLK_EN, SD_CLOCK_CNTL); +} + +static void pxa_sdh_start_clock(struct pxa_sdh_host *host) +{ + u32 timeout = 1000; + + SET_REG_BIT(host, INT_CLK_EN, SD_CLOCK_CNTL); + do { + if (GET_REG(host, SD_CLOCK_CNTL) & INT_CLK_STABLE) + break; + udelay(1); + } while (timeout--); + if (!timeout) + dev_err(mmc_dev(host->mmc), "unable to start clock\n"); + + SET_REG_BITS(host, SD_FREQ_SEL_OFFSET, SD_FREQ_SEL_MASK, + host->clkrt, SD_CLOCK_CNTL); + + /* set as maximum value for data line timeout*/ + SET_REG_BITS(host, DAT_TO_VAL_OFFSET, DAT_TO_MASK, + (DAT_TO_MASK - 1), SD_TO_CTRL_SW_RST); + + SET_REG_BIT(host, EXT_CLK_EN, SD_CLOCK_CNTL); +} + +static void pxa_sdh_enable_irq(struct pxa_sdh_host *host, u32 mask) +{ + unsigned long flags; + u16 nor_mask, nor_int_mask, err_mask, nor_val, err_val; + + spin_lock_irqsave(&host->lock, flags); + nor_mask = (mask & 0xffff) & ~SD_NOR_I_STAT_RVD_MASK; + nor_int_mask = (mask & 0xffff) & ~SD_NOR_INT_EN_RVD_MASK; + err_mask = mask >> 16; + nor_val = GET_REG(host, SD_NOR_I_STAT_EN); + err_val = GET_REG(host, SD_ERR_I_STAT_EN); + + SET_REG(host, nor_mask, SD_NOR_I_STAT); + SET_REG(host, err_mask, SD_ERR_I_STAT); + SET_REG(host, nor_val | nor_int_mask, SD_NOR_INT_EN); + SET_REG(host, nor_val | nor_int_mask, SD_NOR_I_STAT_EN); + SET_REG(host, err_val | err_mask, SD_ERR_INT_EN); + SET_REG(host, err_val | err_mask, SD_ERR_I_STAT_EN); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxa_sdh_disable_irq(struct pxa_sdh_host *host, u32 mask) +{ + unsigned long flags; + u16 nor_mask, nor_int_mask, err_mask, nor_val, err_val; + + spin_lock_irqsave(&host->lock, flags); + nor_mask = (mask & 0xffff) & ~SD_NOR_I_STAT_RVD_MASK; + nor_int_mask = (mask & 0xffff) & ~SD_NOR_INT_EN_RVD_MASK; + err_mask = mask >> 16; + nor_val = GET_REG(host, SD_NOR_I_STAT_EN); + err_val = GET_REG(host, SD_ERR_I_STAT_EN); + + SET_REG(host, nor_mask, SD_NOR_I_STAT); + SET_REG(host, err_mask, SD_ERR_I_STAT); + SET_REG(host, nor_val & ~nor_int_mask, SD_NOR_INT_EN); + SET_REG(host, nor_val & ~nor_int_mask, SD_NOR_I_STAT_EN); + SET_REG(host, err_val & ~err_mask, SD_ERR_INT_EN); + SET_REG(host, err_val & ~err_mask, SD_ERR_I_STAT_EN); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxa_sdh_setup_sdma(struct pxa_sdh_host *host, struct scatterlist *sg, + unsigned int dir_read) +{ + dma_map_sg(mmc_dev(host->mmc), sg, 1, + dir_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + SET_REG(host, sg_dma_address(sg) & 0xffff, SD_SYS_ADDR_LOW); + SET_REG(host, sg_dma_address(sg) >> 16, SD_SYS_ADDR_HIGH); +} + +static u32 pxa_sdh_setup_adma2(struct pxa_sdh_host *host, struct scatterlist *sg, + unsigned int dir_read, u32 sg_len) +{ + u32 data_len = 0; + unsigned int i = 0; + + BUG_ON(sg_len != dma_map_sg(mmc_dev(host->mmc), sg, sg_len, + dir_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE)); + + for (i = 0; i < sg_len; i++) { + host->desc_tbl_addr[i].entry_address = sg_dma_address(&sg[i]); + host->desc_tbl_addr[i].entry_length = sg_dma_len(&sg[i]); + host->desc_tbl_addr[i].entry_attr = 0x0021; + data_len += sg_dma_len(&sg[i]); + BUG_ON(sg_dma_len(&sg[i]) > (1 << 16)); + } + + /*The Last Link, Set End bit*/ + host->desc_tbl_addr[sg_len - 1].entry_attr |= 0x2; + + SET_REG(host, host->desc_tbl_dma_addr & 0xffff, SD_ADMA_SADDR_1); + SET_REG(host, host->desc_tbl_dma_addr >> 16, SD_ADMA_SADDR_2); + + return data_len; +} + +static void pxa_sdh_setup_data(struct pxa_sdh_host *host) +{ + struct mmc_data *data = host->mrq->data; + unsigned int dir_read = DATA_DIRECTION_READ(data); + struct scatterlist *sg; + u16 blk_size = data->blksz & BLOCK_SIZE_MASK; + u32 data_len = 0; + + sg = &data->sg[host->sg_index]; + host->dma_sg_len = host->pio_sg_pos = 0; + + if (0 && SG_NEED_PIO(sg)) { + void *sg_va = sg_virt(sg); + data_len = sg_dma_len(sg); + + dev_dbg(mmc_dev(host->mmc), + "Using PIO(H%sC): addr=0x%x, len=0x%x, sg_index=%d/%d\n", + dir_read ? "<-": "->", (unsigned int)sg_va, sg_dma_len(sg), + host->sg_index + 1, data->sg_len); + pxa_sdh_disable_irq(host, DMA_INT); + if (dir_read) { + pxa_sdh_enable_irq(host, RX_RDY); + } else { + u16 i = 0; + pxa_sdh_enable_irq(host, TX_RDY); + for (; i < blk_size; i += sizeof(u32)) { + writel(*(u32*)(sg_va + i), host->base + SD_BUF_DPORT_0); + } + } + } else { + u16 dma_sel = DMA_SEL_SDMA; + for (host->dma_sg_len++; host->sg_index + host->dma_sg_len < data->sg_len;) { + if (SG_NEED_PIO(&sg[host->sg_index + host->dma_sg_len])) { + break; + } + host->dma_sg_len++; + } + if (host->dma_sg_len == 1) { + pxa_sdh_setup_sdma(host, sg, dir_read); + data_len = sg_dma_len(sg); + dev_dbg(mmc_dev(host->mmc), + "Using SDMA(H%sC): addr=0x%x, len=0x%x, sg_index=%d/%d\n", + dir_read ? "<-": "->", (unsigned int)sg_virt(sg), sg_dma_len(sg), + host->sg_index + 1, data->sg_len); + } else { + data_len = pxa_sdh_setup_adma2(host, sg, dir_read, host->dma_sg_len); + dma_sel = DMA_SEL_ADMA2_32; + dev_dbg(mmc_dev(host->mmc), + "Using ADMA2(H%sC): saddr=0x%x, len=0x%x, sg_index=%d-%d/%d\n", + dir_read ? "<-": "->", (unsigned int)sg_virt(sg), (unsigned int)data_len, + host->sg_index + 1, host->sg_index + host->dma_sg_len, data->sg_len); + } + SET_REG_BITS(host, DMA_SEL_OFFSET, DMA_SEL_MASK, dma_sel, SD_HOST_CTRL); + pxa_sdh_enable_irq(host, DMA_INT); + pxa_sdh_disable_irq(host, RX_RDY | TX_RDY); + + /* if 8-byte aligned set DMA burst size as 64 for better performance */ + /*SET_REG_BIT(host, DMA_BURST_SIZE, SD_CLK_BURST_SET); + do { + if (sg_dma_len(&sg[host->sg_index + i]) % 8) + CLEAR_REG_BIT(host, DMA_BURST_SIZE, SD_CLK_BURST_SET); + break; + i++; + } while (i < host->dma_sg_len);*/ + } + host->data_len = data_len; +} + +static void pxa_sdh_start_cmd(struct pxa_sdh_host *host, struct mmc_command *cmd) +{ + u16 resp = 0; + u16 xfrmd_val = 0; + u16 cmd_val = 0; + u16 val, mask; + + BUG_ON(host->cmd != NULL); + host->cmd = cmd; + + /*Set Response Type*/ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + break; + + case MMC_RSP_R1: /* r1, r5, r6, r7 */ + resp = CMD_RESP_48BIT; + cmd_val |= CMD_CRC_CHK_EN | CMD_IDX_CHK_EN; + break; + + case MMC_RSP_R2: /* r2 */ + resp = CMD_RESP_136BIT; + cmd_val |= CMD_CRC_CHK_EN; + break; + + case MMC_RSP_R3: /* r3, r4*/ + resp = CMD_RESP_48BIT; + break; + + case MMC_RSP_R1B: /* r1b */ + resp = CMD_RESP_48BITB; + cmd_val |= CMD_CRC_CHK_EN | CMD_IDX_CHK_EN; + break; + + default: + break; + } + + /*Set Transfer mode regarding to data flag*/ + if (cmd->data) + { + cmd_val |= DATA_PRESENT; + xfrmd_val |= BLK_CNT_EN; + if (cmd->data->blocks > 1) + xfrmd_val |= MULTI_BLK_SEL; + if (host->dma_sg_len) + xfrmd_val |= DMA_EN; + + if (DATA_DIRECTION_READ(cmd->data)) + xfrmd_val |= TO_HOST_DIR; + else + xfrmd_val &= ~TO_HOST_DIR; + } + + //if (cmd->opcode == 12) + // cmd_val |= host, CMD_TYPE_OFFSET, CMD_TYPE_MASK, CMD_TYPE_ABORT, SD_COMMAND); + SET_REG(host, cmd->arg & 0xffff, SD_ARG_LOW); + SET_REG(host, cmd->arg >> 16, SD_ARG_HIGH); + SET_REG(host, xfrmd_val, SD_TRANS_MODE); + cmd_val |= cmd->opcode << CMD_IDX_OFFSET | resp << RESP_TYPE_OFFSET; + dev_dbg(mmc_dev(host->mmc), "Starting CMD%d with ARGUMENT 0x%x\n", cmd->opcode, cmd->arg); + + if (!host->mmc->card || !mmc_card_sdio(host->mmc->card)) { + val = GET_REG(host, SD_PRESENT_STAT_2); + mask = CMD_LINE_LEVEL_MASK | DATA_LINE_LEVEL_MASK; + if ((val & mask) != mask) + dev_dbg(mmc_dev(host->mmc), "WARN: CMD/DATA pins are not all high, PRE_STAT=0x%04x\n", + GET_REG(host, SD_PRESENT_STAT_2)); + } + + /* FIXME workaround for DPF-532, host needs to delay + * when programming sdio controller (very bad) */ + if (!host->mmc->card) + udelay(10); + + SET_REG(host, cmd_val, SD_COMMAND); +} + +static void pxa_sdh_finish_request(struct pxa_sdh_host *host, + struct mmc_request *mrq) +{ + pxa_sdh_disable_irq(host, ~(CARD_INT | CARD_INS | CARD_REM)); + + if (host->mrq->data && host->mrq->data->error) + SET_REG_BIT(host, SW_RST_DAT, SD_TO_CTRL_SW_RST); + + dev_dbg(mmc_dev(host->mmc), "Finishing CMD%d(%s)\n", mrq->cmd->opcode, + (mrq->cmd->error || (mrq->data && mrq->data->error)) ? "failed" : "done"); + + host->mrq = NULL; + host->cmd = NULL; + host->sg_index = 0; + host->pio_sg_pos = 0; + host->dma_sg_len = 0; + + mmc_request_done(host->mmc, mrq); + unset_dvfm_constraint(); +} + +static int pxa_sdh_cmd_done(struct pxa_sdh_host *host) +{ + struct mmc_command *cmd = host->cmd; + u32 resp[8]; + + BUG_ON(!cmd); + host->cmd = NULL; + + /* get cmd response */ + resp[0] = GET_REG(host, SD_RESP_0); + resp[1] = GET_REG(host, SD_RESP_1); + resp[2] = GET_REG(host, SD_RESP_2); + resp[3] = GET_REG(host, SD_RESP_3); + resp[4] = GET_REG(host, SD_RESP_4); + resp[5] = GET_REG(host, SD_RESP_5); + resp[6] = GET_REG(host, SD_RESP_6); + resp[7] = readb(host->base + SD_RESP_7); + + if (cmd->flags & MMC_RSP_136) { + cmd->resp[0] = resp[5] >> 8 | resp[6] << 8 | resp[7] << 24; + cmd->resp[1] = resp[3] >> 8 | resp[4] << 8 | resp[5] << 24; + cmd->resp[2] = resp[1] >> 8 | resp[2] << 8 | resp[3] << 24; + cmd->resp[3] = resp[0] << 8 | resp[1] << 24; + } else { + cmd->resp[0] = resp[1] << 16 | resp[0]; + cmd->resp[1] = resp[3] << 16 | resp[2]; + cmd->resp[2] = resp[5] << 16 | resp[4]; + cmd->resp[3] = resp[7] << 16 | resp[6]; + } + + if (cmd->error || !host->mrq->data || (host->mrq->cmd != cmd)) { + pxa_sdh_finish_request(host, host->mrq); + } + + return 1; +} + +static void pxa_sdh_pio_data_done(struct pxa_sdh_host *host) +{ + struct mmc_data *data = host->mrq->data; + u16 blk_size = data->blksz; + struct scatterlist *sg = &data->sg[host->sg_index]; + char *sg_va = (char*)sg_virt(sg) + host->pio_sg_pos; + u16 i = 0; + + BUG_ON (host->pio_sg_pos + blk_size > sg_dma_len(sg)); + + if (DATA_DIRECTION_READ(data)) { + for (i = 0; i < blk_size; i += sizeof(u32)) { + *(u32*)(sg_va + i) = readl(host->base + SD_BUF_DPORT_0); + } + } else { + if (host->pio_sg_pos < sg_dma_len(sg)) { + for (i = 0; i < blk_size; i += sizeof(u32)) { + writel(*(u32*)(sg_va + i), host->base + SD_BUF_DPORT_0); + } + } + } + + host->pio_sg_pos += blk_size; + data->bytes_xfered += blk_size; + + if (host->pio_sg_pos >= sg_dma_len(sg)) { + host->sg_index++; + if (host->sg_index < data->sg_len) + pxa_sdh_setup_data(host); + } +} + +static void pxa_sdh_dma_data_done(struct pxa_sdh_host *host) +{ + struct mmc_data *data = host->mrq->data; + + host->sg_index += host->dma_sg_len; + data->bytes_xfered += host->data_len; + if (host->sg_index < data->sg_len) + pxa_sdh_setup_data(host); +} + +static void pxa_sdh_data_done(struct pxa_sdh_host *host) +{ + struct mmc_data *data = host->mrq->data; + + if (!host->mrq || !host->mrq->data) //ignore unexpected XFER_COMP interrupt + return; + + if (host->dma_sg_len) + dma_unmap_sg(mmc_dev(host->mmc), &data->sg[host->sg_index], host->dma_sg_len, + DATA_DIRECTION_READ(host->mrq->data) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + if (!data->error) { + if (host->sg_index < data->sg_len) { + if (!host->dma_sg_len) { + if (host->pio_sg_pos < sg_dma_len(&data->sg[host->sg_index])){ + BUG_ON (!DATA_DIRECTION_READ(data)); + pxa_sdh_pio_data_done(host); + } + BUG_ON (host->pio_sg_pos != sg_dma_len(&data->sg[host->sg_index - 1])); + } else { + pxa_sdh_dma_data_done(host); + } + } + + BUG_ON (host->sg_index != data->sg_len); + BUG_ON (data->bytes_xfered != data->blocks * data->blksz); + } + + /* + * If there was an error on any block, we mark all + * data blocks as being in error. + */ + if (data->error) + data->bytes_xfered = 0; + + if (host->mrq->stop) + pxa_sdh_start_cmd(host, host->mrq->stop); + else + pxa_sdh_finish_request(host, host->mrq); +} + +static inline int query_irq(u16 *irqs, u16 mask) +{ + u16 re = *irqs & mask; + *irqs &= ~mask; + return re; +} + +static int pxa_sdh_err_int_handler(struct pxa_sdh_host *host, u16 err_stat) +{ + u16 err_stat_bk = err_stat; + BUG_ON(err_stat & CPL_TO_ERR); /* CE-ATA mode only, not support yet*/ + if (query_irq(&err_stat, AXI_RESP_ERR)) { + dev_err(mmc_dev(host->mmc), + "AXI Bus Response Error, non-reconverable!\n"); + host->cmd->error = -ENOTRECOVERABLE; + pxa_sdh_finish_request(host, host->mrq); + goto out; + } + BUG_ON(err_stat & SPI_ERR); /* SPI mode only, not support yet*/ + if (query_irq(&err_stat, ADMA_ERR)) { + dev_err(mmc_dev(host->mmc), + "ADMA Error(status: 0x%x)!\n", + GET_REG(host, SD_ADMA_ERR_STAT)); + host->mrq->data->error = -EIO; + pxa_sdh_data_done(host); + goto out; + } + BUG_ON(err_stat & AUTO_CMD12_ERR); /* Auto CMD12 not used yet*/ + BUG_ON(err_stat & CUR_LIMIT_ERR); /* Not support by host Controller*/ + if (query_irq(&err_stat, SD_ERR_INT_CMD_ERR_MASK)) { + dev_dbg(mmc_dev(host->mmc), + "CMD Line Error(status: 0x%04x)!\n", err_stat_bk); + SET_REG(host, \ + GET_REG(host, SD_TO_CTRL_SW_RST) | SW_RST_CMD, \ + SD_TO_CTRL_SW_RST); + if (err_stat & CMD_TO_ERR) + host->cmd->error = -ETIMEDOUT; + else + host->cmd->error = -EIO; + pxa_sdh_cmd_done(host); + goto out; + } + if (query_irq(&err_stat, SD_ERR_INT_DATA_ERR_MASK)) { + dev_err(mmc_dev(host->mmc), + "DATA Line Error(status: 0x%04x)!\n", err_stat_bk); + SET_REG(host, \ + GET_REG(host, SD_TO_CTRL_SW_RST) | SW_RST_DAT, \ + SD_TO_CTRL_SW_RST); + if (err_stat & CRC_STATUS_ERR) { + BUG_ON(!host->mrq->data || (DATA_DIRECTION_READ(host->mrq->data))); + } + if (err_stat & DATA_TO_ERR) + host->mrq->data->error = -ETIMEDOUT; + else + host->mrq->data->error = -EIO; + pxa_sdh_data_done(host); + goto out; + } + +out: + BUG_ON(err_stat); + return 1; +} + +static irqreturn_t pxa_sdh_irq(int irq, void *devid) +{ + struct pxa_sdh_host *host = devid; + u16 nor_stat, err_stat; + + nor_stat = GET_REG(host, SD_NOR_I_STAT); + if (nor_stat == 0 || nor_stat == 0xFFFF) + return IRQ_NONE; + + SET_REG(host, nor_stat & ~ERR_INT & ~CARD_INT, SD_NOR_I_STAT); + err_stat = GET_REG(host, SD_ERR_I_STAT); + SET_REG(host, err_stat, SD_ERR_I_STAT); + + dev_dbg(mmc_dev(host->mmc), + "Card Interrupt, nor_stat: 0x%x, err_stat: 0x%x\n", nor_stat, err_stat); + + if (query_irq(&nor_stat, CARD_INT)) { + dev_dbg(mmc_dev(host->mmc), "CARD_INT Detected!\n"); + mmc_signal_sdio_irq(host->mmc); + } + if (query_irq(&nor_stat, CARD_REM)) { + if (host->pdata->mfp_config) + host->pdata->mfp_config(); + mmc_detect_change(host->mmc, host->pdata->detect_delay); + } + if (query_irq(&nor_stat, CARD_INS)) { + if (host->pdata->mfp_config) + host->pdata->mfp_config(); + mmc_detect_change(host->mmc, host->pdata->detect_delay); + } + + if (!nor_stat) + goto out; + + BUG_ON(!host->mrq); + + if (query_irq(&nor_stat, ERR_INT)) { + BUG_ON(!err_stat); + if (query_irq(&err_stat, CRC_STATUS_ERR)) { + /*a strange error, have no idea when happens but seems like DATA-related.*/ + dev_dbg(mmc_dev(host->mmc), + "CRC Status Error detected, strange!\n"); + SET_REG_BIT(host, SW_RST_DAT | SW_RST_CMD, SD_TO_CTRL_SW_RST); + if (host->mrq->data) + host->mrq->data->error = -ECANCELED; + else + BUG_ON(1); + BUG_ON(nor_stat != XFER_COMP); + nor_stat = 0; + pxa_sdh_finish_request(host, host->mrq); + goto out; + } else { + nor_stat = 0; + pxa_sdh_err_int_handler(host, err_stat); + goto out; + } + } + + BUG_ON(err_stat); + + if (query_irq(&nor_stat, CMD_COMP)) { + pxa_sdh_cmd_done(host); + } + if ((nor_stat & TX_RDY) || (nor_stat & RX_RDY)) { + BUG_ON((nor_stat & (RX_RDY | TX_RDY)) == (RX_RDY | TX_RDY)); + nor_stat &= ~(RX_RDY | TX_RDY); + pxa_sdh_pio_data_done(host); + } + if (query_irq(&nor_stat, DMA_INT)) { + pxa_sdh_dma_data_done(host); + } + if (query_irq(&nor_stat, XFER_COMP)) { + pxa_sdh_data_done(host); + } + +out: + BUG_ON(nor_stat); + + return IRQ_RETVAL(1); +} + +static void pxa_sdh_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct pxa_sdh_host *host = mmc_priv(mmc); + u16 val; + u32 timeout = 1000, i; + + BUG_ON(host->mrq != NULL); + + /* release this constraint when request is finished */ + set_dvfm_constraint(); + + if (!pxa_sdh_get_cd(mmc)) { + mrq->cmd->error = -ENODEV; + pxa_sdh_finish_request(host, mrq); + return; + } + if (pxa_sdh_wait_reset(host)) { + mrq->cmd->error = -EBUSY; + pxa_sdh_finish_request(host, mrq); + return; + } + + do { + val = GET_REG(host, SD_PRESENT_STAT_1); + if (!(val & CMD_INHBT_DAT || val & CMD_INHBT_CMD)) + break; + udelay(1); + } while (timeout--); + if (!timeout) { + dev_err(mmc_dev(mmc), "In busy, unable to start the request.\n"); + mrq->cmd->error = -EBUSY; + pxa_sdh_finish_request(host, mrq); + return; + } + + pxa_sdh_enable_irq(host, ~(u32)CARD_INT & ~(u32)BLK_GAP_EVNT & + ~(u32)TX_RDY & ~(u32)RX_RDY & ~(u32)DMA_INT); + host->mrq = mrq; + + if (mrq->data) { + struct scatterlist *sg; + unsigned int data_len = 0; + if (mrq->data->flags & MMC_DATA_STREAM) { + dev_err(mmc_dev(mmc), "No stream commands support\n"); + goto inval_out; + } + for (i = 0; i < mrq->data->sg_len; i++) { + sg = &mrq->data->sg[i]; + if ((unsigned int)sg_virt(sg) % 4) { + dev_err(mmc_dev(host->mmc), + "sg_addr 0x%x(sg_len=%x) is not 4-byte alined, unsupported!\n", + (unsigned int)sg_virt(sg), sg_dma_len(sg)); + goto inval_out; + } + if (sg_dma_len(sg) % 4) { + dev_err(mmc_dev(host->mmc), + "sg_len 0x%x is not 4-byte alined, unsupported!\n", + sg_dma_len(sg)); + goto inval_out; + } + if (sg_dma_len(sg) % mrq->data->blksz) { + dev_err(mmc_dev(host->mmc), + "sg_len 0x%x is not multiple of blk_size 0x%x, unsupported!\n", + sg_dma_len(sg), mrq->data->blksz); + goto inval_out; + } else { + data_len += sg_dma_len(sg); + } + } + if (data_len / mrq->data->blksz != mrq->data->blocks) { + dev_err(mmc_dev(host->mmc), + "data_len(0x%x) != blk_size(0x%x) * blk_cnt(0x%x), unsupported!\n", + data_len, mrq->data->blksz, mrq->data->blocks); + goto inval_out; + } + + dev_dbg(mmc_dev(host->mmc), "setup data, blk_sz=%d, blk_cnt=0x%x\n", + mrq->data->blksz, mrq->data->blocks); + SET_REG(host, ((u16)HOST_DMA_BDRY_MASK << HOST_DMA_BDRY_OFFSET) | mrq->data->blksz, + SD_BLOCK_SIZE); + SET_REG(host, mrq->data->blocks, SD_BLOCK_COUNT); + + BUG_ON (host->sg_index || host->pio_sg_pos || host->dma_sg_len); + pxa_sdh_setup_data(host); + } + + pxa_sdh_start_cmd(host, mrq->cmd); + return; + +inval_out: + mrq->cmd->error = mrq->data->error = -EINVAL; + pxa_sdh_finish_request(host, host->mrq); + return; +} + +static int pxa_sdh_get_ro(struct mmc_host *mmc) +{ +#if 1 + return 0; +#else + /*not stable on TTC EVB*/ + struct pxa_sdh_host *host = mmc_priv(mmc); + + if (!pxa_sdh_wait_reset(host) && (GET_REG(host, SD_PRESENT_STAT_2) & CARD_PROT)) + return 1; + + return 0; +#endif +} + +static void pxa_sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct pxa_sdh_host *host = mmc_priv(mmc); + + if (pxa_sdh_wait_reset(host)) + return; + + set_dvfm_constraint(); + if (ios->clock) { + unsigned long rate = host->clkrate; + unsigned int clk = rate / ios->clock; + unsigned int shift; + + if (host->clkrt == CLKRT_OFF) + clk_enable(host->clk); + + BUG_ON((ios->clock > mmc->f_max) || (ios->clock < mmc->f_min)); + if (ios->clock >= host->clkrate) { + host->clkrt = 0x00; + } else { + shift = fls(clk); + if (rate / clk > ios->clock) + shift++; + host->clkrt = 1 << (shift - 2); + } + + dev_dbg(mmc_dev(mmc),"set clkrt = %08x\n", host->clkrt); + pxa_sdh_stop_clock(host); + pxa_sdh_start_clock(host); + + if((host->clkrt == 0 && host->clkrate > 25000000) + || (host->clkrt && (host->clkrate/(host->clkrt*2)) > 25000000)) { + //this bit should not be set, or sd8688 cannot pass iperf stress + //SET_REG_BIT(host, HI_SPEED_EN, SD_HOST_CTRL); + dev_dbg(mmc_dev(mmc), "set as HIGH_SPEED.\n"); + } else + CLEAR_REG_BIT(host, HI_SPEED_EN, SD_HOST_CTRL); + + } else { + pxa_sdh_stop_clock(host); + if (host->clkrt != CLKRT_OFF) { + host->clkrt = CLKRT_OFF; + clk_disable(host->clk); + } + } + + SET_REG_BITS(host, SDCLK_SEL_OFFSET, SDCLK_SEL_MASK, SDCLK_SEL_INIT_VAL, SD_CLK_BURST_SET); + + if (host->power_mode != ios->power_mode) { + host->power_mode = ios->power_mode; + + if (host->pdata && host->pdata->setpower) + host->pdata->setpower(mmc_dev(mmc), ios->vdd); + + if (ios->power_mode == MMC_POWER_ON) { + SET_REG_BITS(host, SD_BUS_VLT_OFFSET, SD_BUS_VLT_MASK, + SD_BUS_VLT_18V, SD_HOST_CTRL); + SET_REG_BIT(host, SD_BUS_POWER, SD_HOST_CTRL); + } + } + + if (ios->bus_width == MMC_BUS_WIDTH_8) { + SET_REG_BIT(host, MMC_CARD, SD_CE_ATA_2); + SET_REG_BIT(host, DATA_WIDTH_8BIT, SD_CE_ATA_2); + dev_dbg(mmc_dev(mmc), "set as 8_BIT_MODE.\n"); + } else { + CLEAR_REG_BIT(host, MMC_CARD, SD_CE_ATA_2); + CLEAR_REG_BIT(host, DATA_WIDTH_8BIT, SD_CE_ATA_2); + if (ios->bus_width == MMC_BUS_WIDTH_4) { + SET_REG_BIT(host, DATA_WIDTH_4BIT, SD_HOST_CTRL); + dev_dbg(mmc_dev(mmc), "set as 4_BIT_MODE.\n"); + } else { + CLEAR_REG_BIT(host, DATA_WIDTH_4BIT, SD_HOST_CTRL); + } + } + unset_dvfm_constraint(); +} + +static void pxa_sdh_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct pxa_sdh_host *host = mmc_priv(mmc); + + if (pxa_sdh_wait_reset(host)) + return; + + if (enable) { + pxa_sdh_enable_irq(host, CARD_INT); + SET_REG_BIT(host, DIS_PAD_SD_CLK_GATE, SD_FIFO_PARAM); + } else { + pxa_sdh_disable_irq(host, CARD_INT); + CLEAR_REG_BIT(host, DIS_PAD_SD_CLK_GATE, SD_FIFO_PARAM); + } +} + +static const struct mmc_host_ops pxa_sdh_ops = { + .request = pxa_sdh_request, + .get_ro = pxa_sdh_get_ro, + .set_ios = pxa_sdh_set_ios, + .get_cd = pxa_sdh_get_cd, + .enable_sdio_irq = pxa_sdh_enable_sdio_irq, +}; + +static irqreturn_t pxa_sdh_detect_irq(int irq, void *devid) +{ + struct pxa_sdh_host *host = mmc_priv(devid); + + mmc_detect_change(devid, host->pdata->detect_delay); + return IRQ_HANDLED; +} + +static int pxa_sdh_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct pxa_sdh_host *host = NULL; + struct resource *r; + int ret, irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) + return -ENXIO; + r = request_mem_region(r->start, SZ_256, DRIVER_NAME); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct pxa_sdh_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + mmc->ops = &pxa_sdh_ops; + + mmc->max_phys_segs = ADMA_DESC_TBL_SIZE / sizeof(struct adma_desc_table); + mmc->max_hw_segs = ADMA_DESC_TBL_SIZE / sizeof(struct adma_desc_table); + + /*Determined by the 16-bit Length Field of ADMA2 Descriptor Table line*/ + mmc->max_seg_size = (1 << 16); + + mmc->max_blk_size = BLOCK_SIZE_MAX; + + /*Set as the maximum value of 16-bit SD_BLOCK_COUNT Register*/ + mmc->max_blk_count = (1 << 16) -1; + + mmc->max_req_size = min(mmc->max_blk_size * mmc->max_blk_count, + mmc->max_seg_size * mmc->max_phys_segs); + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdata = pdev->dev.platform_data; + host->clkrt = CLKRT_OFF; + + host->clk = clk_get(&pdev->dev, "PXA-SDHCLK"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + host->clk = NULL; + goto out; + } + clk_enable(host->clk); + + host->clkrate = clk_get_rate(host->clk); + /* + * Calculate minimum clock rate, rounding up. + */ + mmc->f_min = (host->clkrate + SD_FREQ_SEL_MASK) / (SD_FREQ_SEL_MASK + 1); + mmc->f_max = host->clkrate; + if (host->pdata->max_speed) + mmc->f_max = host->pdata->max_speed; + + mmc->ocr_avail = 0xffffffff; /*host->pdata ? + host->pdata->ocr_mask : + MMC_VDD_32_33|MMC_VDD_33_34;*/ + + mmc->caps = MMC_CAP_SDIO_IRQ | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + if (host->pdata->bus_width == 8) + mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA; + else if ((host->pdata->bus_width == 4) || !host->pdata->bus_width) //default as 4-bit bus + mmc->caps |= MMC_CAP_4_BIT_DATA; + else + dev_info(mmc_dev(mmc), "works as 1-bit mode\n"); + + host->desc_tbl_addr = dma_alloc_coherent(mmc_dev(host->mmc), ADMA_DESC_TBL_SIZE, + &host->desc_tbl_dma_addr, GFP_KERNEL); + if (!host->desc_tbl_addr) { + ret = -ENOMEM; + goto out; + } + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + + host->base = ioremap(r->start, SZ_256); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + pxa_sdh_stop_clock(host); + pxa_sdh_disable_irq(host, 0xffffffff); + ret = request_irq(host->irq, pxa_sdh_irq, IRQF_SHARED, DRIVER_NAME, host); + if (ret) + goto out; + + platform_set_drvdata(pdev, mmc); + + if (host->pdata && host->pdata->init) + host->pdata->init(&pdev->dev, pxa_sdh_detect_irq, mmc); + + mmc_add_host(mmc); + pxa_sdh_enable_irq(host, CARD_INS | CARD_REM); + + if (host->pdata->mfp_config) + host->pdata->mfp_config(); + + //SET_REG_BIT(host, DIS_PAD_SD_CLK_GATE, SD_FIFO_PARAM); + return 0; + + out: + if (host) { + if (host->base) + iounmap(host->base); + if (host->clk) + clk_put(host->clk); + } + if (host->desc_tbl_addr) + dma_free_coherent(mmc_dev(host->mmc), PAGE_SIZE, + host->desc_tbl_addr, host->desc_tbl_dma_addr); + if (mmc) + mmc_free_host(mmc); + release_mem_region(r->start, SZ_256); + return ret; +} + +static int pxa_sdh_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct pxa_sdh_host *host; + + platform_set_drvdata(pdev, NULL); + + if (mmc) { + host = mmc_priv(mmc); + + if (host->pdata && host->pdata->exit) + host->pdata->exit(&pdev->dev, mmc); + + mmc_remove_host(mmc); + + pxa_sdh_stop_clock(host); + pxa_sdh_disable_irq(host, 0xffffffff); + + free_irq(host->irq, host); + iounmap(host->base); + dma_free_coherent(mmc_dev(host->mmc), PAGE_SIZE, + host->desc_tbl_addr, host->desc_tbl_dma_addr); + + clk_put(host->clk); + + release_mem_region(host->res->start, SZ_256); + + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +static int pxa_sdh_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int pxa_sdh_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +#else +#define pxa_sdh_suspend NULL +#define pxa_sdh_resume NULL +#endif + +static struct platform_driver pxa_sdh_driver = { + .probe = pxa_sdh_probe, + .remove = pxa_sdh_remove, + .suspend = pxa_sdh_suspend, + .resume = pxa_sdh_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init pxa_sdh_init(void) +{ +#ifdef CONFIG_DVFM + dvfm_register("MMC", &(dvfm_lock.dev_idx)); +#endif + return platform_driver_register(&pxa_sdh_driver); +} + +static void __exit pxa_sdh_exit(void) +{ + platform_driver_unregister(&pxa_sdh_driver); +#ifdef CONFIG_DVFM + dvfm_unregister("MMC", &(dvfm_lock.dev_idx)); +#endif +} + +module_init(pxa_sdh_init); +module_exit(pxa_sdh_exit); + +MODULE_DESCRIPTION("PXA SD Host Controller(MM4) Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/pxa_sdh.h b/drivers/mmc/host/pxa_sdh.h new file mode 100644 index 00000000000000..de454cb2949556 --- /dev/null +++ b/drivers/mmc/host/pxa_sdh.h @@ -0,0 +1,195 @@ +/* register definitions of PXA SD Host Controller*/ +#define SD_SYS_ADDR_LOW 0x0000 /* DMA System Address Low */ +#define SD_SYS_ADDR_HIGH 0x0002 /* DMA System Address High */ +#define SD_BLOCK_SIZE 0x0004 /* Block Size*/ +#define SD_BLOCK_COUNT 0x0006 /* Block Count */ +#define SD_ARG_LOW 0x0008 /* Command Argument Low */ +#define SD_ARG_HIGH 0x000a /* Command Argument High */ +#define SD_TRANS_MODE 0x000c /* Transfer Mode */ +#define SD_COMMAND 0x000e /* Command */ +#define SD_RESP_0 0x0010 /* Command Response 0 */ +#define SD_RESP_1 0x0012 /* Command Response 1 */ +#define SD_RESP_2 0x0014 /* Command Response 2 */ +#define SD_RESP_3 0x0016 /* Command Response 3 */ +#define SD_RESP_4 0x0018 /* Command Response 4 */ +#define SD_RESP_5 0x001a /* Command Response 5 */ +#define SD_RESP_6 0x001c /* Command Response 6 */ +#define SD_RESP_7 0x001e /* Command Response 7 */ +#define SD_BUF_DPORT_0 0x0020 /* Buffer Data Port 0 */ +#define SD_BUF_DPORT_1 0x0022 /* Buffer Data Port 1 */ +#define SD_PRESENT_STAT_1 0x0024 /* Present State 1 */ +#define SD_PRESENT_STAT_2 0x0026 /* Present State 2 */ +#define SD_HOST_CTRL 0x0028 /* Host Control */ +#define SD_BLOCK_GAP_CTRL 0x002a /* Block Gap Control */ +#define SD_CLOCK_CNTL 0x002c /* Clock Control */ +#define SD_TO_CTRL_SW_RST 0x002e /* Timeout Control/SW Reset */ +#define SD_NOR_I_STAT 0x0030 /* Normal Interrupt Status */ +#define SD_ERR_I_STAT 0x0032 /* Error Interrupt Status */ +#define SD_NOR_I_STAT_EN 0x0034 /* Normal Interrupt Status Enable */ +#define SD_ERR_I_STAT_EN 0x0036 /* Error Interrupt Status Enable */ +#define SD_NOR_INT_EN 0x0038 /* Normal Interrupt Generation Enable */ +#define SD_ERR_INT_EN 0x003a /* Error Interrupt Generation Enable */ +#define SD_ACMD12_ERR_STAT 0x003c /* Auto CMD12 Error Status */ +#define SD_CAP_1 0x0040 /* Capabilities 1 */ +#define SD_CAP_3 0x0044 /* Capabilities 3 */ +#define SD_CAP_4 0x0046 /* Capabilities 4 */ +#define SD_MAX_CUR_1 0x0048 /* Maximum Current 1 */ +#define SD_MAX_CUR_2 0x004a /* Maximum Current 2 */ +#define SD_MAX_CUR_3 0x004c /* Maximum Current 3 */ +#define SD_MAX_CUR_4 0x004e /* Maximum Current 4 */ +#define SD_FE_ACMD12_ERR 0x0050 /* Force Event for Auto CMD12 Error */ +#define SD_FE_ERR_STAT 0x0052 /* Force Event for Error Status */ +#define SD_ADMA_ERR_STAT 0x0054 /* ADMA Error Status */ +#define SD_ADMA_SADDR_1 0x0058 /* ADMA System Address[15:0] */ +#define SD_ADMA_SADDR_2 0x005a /* ADMA System Address[31:16] */ +#define SD_ADMA_SADDR_3 0x005c /* ADMA System Address[47:32] */ +#define SD_ADMA_SADDR_4 0x005e /* ADMA System Address[64:48] */ +#define SD_FIFO_PARAM 0x00e0 /* FIRO Parameters */ +#define SD_SPI_MODE 0x00e4 /* SPI Mode */ +#define SD_CLK_BURST_SET 0x00e6 /* Clock and Burst Size Setup */ +#define SD_CE_ATA_1 0x00e8 /* CE-ATA 1 */ +#define SD_CE_ATA_2 0x00ea /* CE-ATA 2 */ +#define SD_PAD_IO_SETUP 0x00ec /* Pad I/O Setup */ +#define SD_SLOT_INT_STAT 0x00fc /* Slot Interrupt Status*/ +#define SD_HOST_CTRL_VER 0x00fe /* Host Controller Version */ + +/* SD_BLOCK_SIZE */ +#define HOST_DMA_BDRY_OFFSET 12 +#define HOST_DMA_BDRY_MASK ((u16)0x7) +#define BLOCK_SIZE_OFFSET 0 +#define BLOCK_SIZE_MASK ((u16)0x0fff) +#define BLOCK_SIZE_MAX ((u16)0x0800) + +/* SD_TRANS_MODE */ +#define MULTI_BLK_SEL ((u16)1 << 5) +#define TO_HOST_DIR ((u16)1 << 4) +#define AUTO_CMD12_EN ((u16)1 << 2) +#define BLK_CNT_EN ((u16)1 << 1) +#define DMA_EN ((u16)1 << 0) + +/* SD_COMMAND */ +#define CMD_IDX_OFFSET 8 +#define CMD_IDX_MASK ((u16)0x3f) +#define CMD_TYPE_OFFSET 6 +#define CMD_TYPE_MASK ((u16)0x3) +#define CMD_TYPE_NORMAL ((u16)0x0) +#define CMD_TYPE_RESUME ((u16)0x1) +#define CMD_TYPE_SUSPEND ((u16)0x2) +#define CMD_TYPE_ABORT ((u16)0x3) +#define DATA_PRESENT ((u16)1 << 5) +#define CMD_IDX_CHK_EN ((u16)1 << 4) +#define CMD_CRC_CHK_EN ((u16)1 << 3) +#define RESP_TYPE_OFFSET 0 +#define RESP_TYPE_MASK ((u16)0x3) +/* RES_TYPE */ +#define CMD_RESP_NONE ((u16)0x0) +#define CMD_RESP_136BIT ((u16)0x1) +#define CMD_RESP_48BIT ((u16)0x2) +#define CMD_RESP_48BITB ((u16)0x3) + +/* SD_PRESENT_STAT_1 */ +#define CMD_INHBT_DAT ((u16)1 << 1) +#define CMD_INHBT_CMD ((u16)1 << 0) + +/* SD_PRESENT_STAT_2 */ +#define CARD_STABLE ((u16)1 << 1) +#define CARD_DETECTED ((u16)1 << 2) +#define CARD_PROT ((u16)1 << 3) +#define DATA_LINE_LEVEL_MASK ((u16)0xf << 4) +#define CMD_LINE_LEVEL_MASK ((u16)1 << 8) + +/* SD_HOST_CTRL */ +#define SD_BUS_VLT_OFFSET 9 +#define SD_BUS_VLT_MASK ((u16)0x7) +#define SD_BUS_VLT_18V ((u16)0x5) +#define SD_BUS_VLT_30V ((u16)0x6) +#define SD_BUS_VLT_33V ((u16)0x7) +#define SD_BUS_POWER ((u16)1 << 8) +#define DMA_SEL_OFFSET 3 +#define DMA_SEL_MASK ((u16)0x3) +#define DMA_SEL_SDMA ((u16)0) +#define DMA_SEL_ADMA1 ((u16)1) +#define DMA_SEL_ADMA2_32 ((u16)2) +#define DMA_SEL_ADMA2_64 ((u16)3) +#define HI_SPEED_EN ((u16)1 << 2) +#define DATA_WIDTH_4BIT ((u16)1 << 1) + +/* SD_BLOCK_GAP_CTRL */ +#define INT_BLK_GAP ((u16)1 << 3) +#define RD_WT_CNTL ((u16)1 << 2) +#define CONT_REQ ((u16)1 << 1) +#define STOP_AT_BLK_GAP_REQ ((u16)1 << 0) + +/* SD_CLOCK_CNTL */ +#define SD_FREQ_SEL_OFFSET 8 +#define SD_FREQ_SEL_MASK ((u16)0xff) +#define EXT_CLK_EN ((u16)1 << 2) +#define INT_CLK_STABLE ((u16)1 << 1) +#define INT_CLK_EN ((u16)1 << 0) + +/* SD_TO_CTRL_SW_RST */ +#define SW_RST_DAT ((u16)1 << 10) +#define SW_RST_CMD ((u16)1 << 9) +#define SW_RST_ALL ((u16)1 << 8) +#define DAT_TO_VAL_OFFSET 0 +#define DAT_TO_MASK ((u16)0xf) + +/* SD_NOR_I_STAT, SD_NOR_I_STAT_EN, SD_NOR_INT_EN */ +#define ERR_INT ((u16)1 << 15) /* Error Interrupt*/ +#define CARD_INT ((u16)1 << 8) /* Card Interrupt */ +#define CARD_REM ((u16)1 << 7) /* Card Removal Interrupt */ +#define CARD_INS ((u16)1 << 6) /* Card Insertion Interrupt */ +#define RX_RDY ((u16)1 << 5) /* Buffer Read Ready */ +#define TX_RDY ((u16)1 << 4) /* Buffer Write Ready */ +#define DMA_INT ((u16)1 << 3) /* DMA Interrupt */ +#define BLK_GAP_EVNT ((u16)1 << 2) /* Block Gap Event */ +#define XFER_COMP ((u16)1 << 1) /* Transfer Complete */ +#define CMD_COMP ((u16)1 << 0) /* Command Complete */ +#define SD_NOR_I_STAT_RVD_MASK ((u16)0x7e00) /* Mask for SD_NOR_I_STAT Reserved Bits[14 :9] */ +#define SD_NOR_INT_EN_RVD_MASK ((u16)0xfe00) /* Mask for SD_NOR_INT_EN/SD_NOR_I_STAT_EN Reserved Bits[15 :9] */ + +/* SD_ERR_I_STAT, SD_ERR_I_STAT_EN, SD_ERR_INT_EN */ +#define CRC_STATUS_ERR ((u16)1 << 15) /* CRC Status Error Returned from Card in Write Transaction*/ +#define CPL_TO_ERR ((u16)1 << 14) /* Command Completion Signal Timeout Error, for CE-ATA mode only*/ +#define AXI_RESP_ERR ((u16)1 << 13) /* AXI Bus Response Error */ +#define SPI_ERR ((u16)1 << 12) /* SPI Mode Error*/ +#define ADMA_ERR ((u16)1 << 9) /* AMDA Error */ +#define AUTO_CMD12_ERR ((u16)1 << 8) /* Auto CMD12 Error*/ +#define CUR_LIMIT_ERR ((u16)1 << 7) /* Current Limit Error*/ +#define RD_DATA_END_ERR ((u16)1 << 6) /* Read Data End Bit Error*/ +#define RD_DATA_CRC_ERR ((u16)1 << 5) /* Read Data CRC Error*/ +#define DATA_TO_ERR ((u16)1 << 4) /* Data Timeout Error*/ +#define CMD_IDX_ERR ((u16)1 << 3) /* Command Index Error*/ +#define CMD_END_BIT_ERR ((u16)1 << 2) /* Command End Bit Error*/ +#define CMD_CRC_ERR ((u16)1 << 1) /* Command CRC Error*/ +#define CMD_TO_ERR ((u16)1 << 0) /* Command Timeout Error*/ +#define SD_ERR_INT_EN_RVD_MASK ((u16)0x0c00) /* Mask for SD_ERR_INT_EN/SD_ERR_I_STAT_EN Reserved Bits[11 :10] */ +#define SD_ERR_INT_DATA_ERR_MASK (DATA_TO_ERR | RD_DATA_CRC_ERR | RD_DATA_END_ERR) /*DATA Line Error*/ +#define SD_ERR_INT_CMD_ERR_MASK (CMD_TO_ERR | CMD_CRC_ERR | CMD_END_BIT_ERR | CMD_IDX_ERR) /* CMD Line Error*/ + +/* SD_FIFO_PARAM */ +#define DIS_PAD_SD_CLK_GATE ((u16)1 << 10) /* Turn on/off Dynamic SD Clock Gating */ + +/* SD_CLK_BURST_SET */ +#define SDCLK_DELAY_OFFSET 10 +#define SDCLK_DELAY_MASK ((u16)0xf) +#define SDCLK_DELAY_MAX ((u16)0xf) +#define SDCLK_SEL_OFFSET 8 +#define SDCLK_SEL_MASK ((u16)0x3) +#define SDCLK_SEL_INIT_VAL ((u16)0x3) +#define DMA_BURST_SIZE ((u16)0) + +/* SD_SLOT_INT_STAT */ +#define SLOT_INT1 ((u16)1<<1) +#define SLOT_INT0 ((u16)1<<0) +#define SlOT_INT_MASK (SLOT_INT0 | SLOT_INT1) + +/* SD_CE_ATA_2 */ +#define DATA_WIDTH_8BIT ((u16)1 << 8) +#define MMC_CARD ((u16)1 << 12) + +/* Bus width setting*/ +#define SDH_BUS_WIDTH_4 4 /* default */ +#define SDH_BUS_WIDTH_1 1 +#define SDH_BUS_WIDTH_8 8 + diff --git a/drivers/mmc/host/sdhci-pxa168.c b/drivers/mmc/host/sdhci-pxa168.c new file mode 100644 index 00000000000000..a0ce98105fcf71 --- /dev/null +++ b/drivers/mmc/host/sdhci-pxa168.c @@ -0,0 +1,763 @@ +/* + * linux/drivers/mmc/host/pxa_sdh.c - PXAxxx SD Host driver + * + * Copyright (C) 2008-2009 Marvell International Ltd. + * Philip Rakity + * + * Based on linux/drivers/mmc/host/sdhci-pci.c - PXA MMC shim driver + * + * + * 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 + + +#define SD_RESP_6 0x1c /* Command Response 6 */ +#define SD_RESP_7 0x1e /* Command Response 7 */ + +#define SD_FIFO_PARAM 0xE0 +#define DIS_PAD_SD_CLK_GATE_BIT (1 << 10) /* Turn on/off Dynamic SD Clock Gating */ + +#define SD_CLOCK_AND_BURST_SIZE_SETUP 0xE6 +#define SDCLK_DELAY_MASK 0xF +#define SDCLK_SEL_MASK 0x3 +#define SDCLK_DELAY_SHIFT 10 +#define SDCLK_SEL_SHIFT 8 + +#define DRIVER_NAME "pxa-sdh" +#define MAX_SLOTS 8 + +/* SD spec says 74 clocks few more is okay */ +#define INIT_CLOCKS 80 + +struct sdhci_mmc_slot { + struct sdhci_mmc_chip *chip; + struct sdhci_host *host; + struct clk *clk; + u32 clkrate; + u32 f_max; + u8 width; + u8 eightBitEnabled; + u8 clockEnabled; +}; + +struct sdhci_mmc_chip { + struct platform_device *pdev; + struct resource *res; + struct sdhci_mmc_fixes *fixes; + unsigned int quirks; + int num_slots; /* Slots on controller */ + struct sdhci_mmc_slot *slots[MAX_SLOTS]; + struct pxasdh_platform_data *pdata; + unsigned int mrvl_quirks; +}; + +struct sdhci_mmc_fixes { + unsigned int quirks; + int (*probe)(struct sdhci_mmc_chip*); + int (*probe_slot)(struct sdhci_mmc_slot*); + void (*remove_slot)(struct sdhci_mmc_slot*, int); + int (*suspend)(struct sdhci_mmc_chip*, pm_message_t); + int (*resume)(struct sdhci_mmc_chip*); +}; + +#define DBG(f, x...) \ + pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + +static inline void set_sd_clock_gating(struct sdhci_host *host, int enable) +{ + unsigned short tmp; + + tmp = readw(host->ioaddr + SD_FIFO_PARAM); + DBG("ENTER %s SD_FIFO_PARAM = %04X\n", mmc_hostname(host->mmc), tmp); + + if (enable) + tmp &= ~DIS_PAD_SD_CLK_GATE_BIT; + else + tmp |= DIS_PAD_SD_CLK_GATE_BIT; + + writew(tmp, host->ioaddr + SD_FIFO_PARAM); + DBG("EXIT: %s SD_FIFO_PARAM = %04X\n", mmc_hostname(host->mmc), tmp); +} + +static int platform_supports_8_bit(struct sdhci_host *host) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + + return slot->width >= 8; +} + +#define SD_CE_ATA_2 0xEA +#define MMC_CARD (1<<12) +#define MMC_WIDTH (1<<8) + +static void platform_clear_8_bit(struct sdhci_host *host) +{ + unsigned short tmp; + struct sdhci_mmc_slot *slot = sdhci_priv(host); + + tmp = readw(host->ioaddr + SD_CE_ATA_2); + tmp &= ~(MMC_CARD | MMC_WIDTH); + writew(tmp, host->ioaddr + SD_CE_ATA_2); + slot->eightBitEnabled = 0; + DBG("EXIT: %s SD_CE_ATA_2 = %04X\n", mmc_hostname(host->mmc), tmp); +} + +static void platform_set_8_bit(struct sdhci_host *host) +{ + unsigned short tmp; + struct sdhci_mmc_slot *slot = sdhci_priv(host); + + tmp = readw(host->ioaddr + SD_CE_ATA_2); + tmp |= MMC_CARD | MMC_WIDTH; + writew(tmp, host->ioaddr + SD_CE_ATA_2); + slot->eightBitEnabled = 1; + DBG("EXIT: %s SD_CE_ATA_2 = %04X\n", mmc_hostname(host->mmc), tmp); +} + +static int platform_init_after_reset (struct sdhci_host *host) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + struct sdhci_mmc_chip *chip = slot->chip; + u16 tmp; + + if (platform_supports_8_bit(host)) { + if (slot->eightBitEnabled) + platform_set_8_bit(host); + else + platform_clear_8_bit(host); + } + if (chip->pdata && chip->pdata->sd_clock) { + tmp = readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP); + tmp &= ~(SDCLK_DELAY_MASK << SDCLK_DELAY_SHIFT); + tmp &= ~(SDCLK_SEL_MASK << SDCLK_SEL_SHIFT); + tmp |= (chip->pdata->sdclk_delay & SDCLK_DELAY_MASK) << + SDCLK_DELAY_SHIFT; + tmp |= (chip->pdata->sdclk_sel & SDCLK_SEL_MASK) << + SDCLK_SEL_SHIFT; + writew(tmp, host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP); + } + + DBG ("SD_CLOCK_AND_BURST_SIZE_SETUP to %04X\n", readw(host->ioaddr + SD_CLOCK_AND_BURST_SIZE_SETUP)); + return 0; +} + + +/* + * we cannot talk to controller for 8 bus cycles according to sdio spec + * at lowest speed this is 100,000 HZ per cycle or 800,000 cycles + * which is quite a LONG TIME on a fast cpu -- so delay if needed + */ +static void platform_specific_delay (struct sdhci_host *host) +{ + unsigned long delay; + + if (host->clock < 3200000) { + if (platform_supports_8_bit(host) == 0) { + delay = 5*(3000000/host->clock) + 9; + mdelay(delay); + } + } +} + +static void platform_specific_sdio (struct sdhci_host *host, int enable) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + struct sdhci_mmc_chip *chip = slot->chip; + if (chip->mrvl_quirks & MRVL_QUIRK_SDIO_ENABLE_DYN_CLOCK_GATING) { + set_sd_clock_gating(host, true); + } else { + set_sd_clock_gating(host, false); + } +} + +static void platform_specific_reset (struct sdhci_host *host, u8 mask) +{ + DBG("ENTER: %s mask == SDHCI_RESET_ALL = %d\n", mmc_hostname (host->mmc), mask == SDHCI_RESET_ALL); + if (mask & SDHCI_RESET_ALL) + platform_init_after_reset (host); +} + +static int platform_specific_get_ro(struct mmc_host *mmc) +{ + struct sdhci_mmc_chip *chip = NULL; + struct sdhci_mmc_slot *slot = NULL; + struct sdhci_host *host; + + host = mmc_priv(mmc); + slot = sdhci_priv(host); + chip = slot->chip; + + if (chip->pdata && chip->pdata->get_ro) + return !!chip->pdata->get_ro(mmc_dev(mmc)); + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; +} + +static int platform_get_cd(struct mmc_host *mmc) +{ + struct sdhci_mmc_chip *chip = NULL; + struct sdhci_mmc_slot *slot = NULL; + struct sdhci_host *host; + + host = mmc_priv(mmc); + slot = sdhci_priv(host); + chip = slot->chip; + + if (chip->pdata && chip->pdata->get_cd) + return (chip->pdata->get_cd(mmc_dev(mmc))); + + return -ENOSYS; +} + +/* + * MMC spec calls for the host to send 74 clocks to the card + * during initialization, right after voltage stabilization. + * the pxa168 controller has no easy way to generate those clocks. + * create the clocks manually right here. + */ + +void generate_init_clocks(struct sdhci_host *host) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + struct sdhci_mmc_chip *chip = slot->chip; + struct pfn_cfg *cfg = chip->pdata->pfn_table; + mfp_cfg_t clk_pin; + int i; + + if (cfg == NULL) { + DBG("Cannot generate init clocks!\n"); + return; + } + + /* CMD/CLK pin to gpio mode. */ + mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CMD), 1); + mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK), 1); + + udelay(3); /* ensure at least 1/2 period stable to prevent runt pulse.*/ + + clk_pin = *(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CLK)); + if (gpio_request(MFP_PIN(clk_pin), "MMC_CLK")) { + printk(KERN_ERR "Cannot obtain MMC_CLK GPIO %ld\n", + MFP_PIN(clk_pin)); + goto err; + } + + DBG("Generating init clocks on pins CLK %ld\n", MFP_PIN(clk_pin)); + + for (i = 0; i < INIT_CLOCKS; i++) { + gpio_direction_output(MFP_PIN(clk_pin), 0); /* low */ + udelay(3); + gpio_direction_output(MFP_PIN(clk_pin), 1); /* high */ + udelay(3); + } + + gpio_free(MFP_PIN(clk_pin)); + +err: + /* CMD/CLK pin back MMC mode. */ + mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CMD), 1); + mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CLK), 1); +} + +static void enable_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + + DBG("ENTER: %s slot->clockEnabled = %d (clock = %u)\n", + mmc_hostname(host->mmc), slot->clockEnabled, clock); + + if (slot->clockEnabled == 0) { + clk_enable(slot->clk); + slot->clockEnabled = 1; + } + +/* + * heuristic: host->clock contains the previous clock state and clock contains + * the new clock state. If the clock state goes from zero to less than 400kHz + * we can safely assume we need to generate the initial 74 clocks. + */ + if (host->clock == 0 && clock <= 400000) { + DBG("%s: supplying initial clocks\n", mmc_hostname(host->mmc)); + generate_init_clocks(host); + } + +} + + +static void disable_clock(struct sdhci_host *host) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + + DBG("ENTER:\n"); + + if (slot->clockEnabled) { + clk_disable(slot->clk); + slot->clockEnabled = 0; + } +} + +static void set_clock(struct sdhci_host *host, unsigned int clock) +{ + DBG("ENTER: clock = %d\n", clock); + + if (clock == 0) + disable_clock(host); + else + enable_clock(host, clock); +} + + +static int handle_shared_mmc_bus (struct sdhci_host *host, u32 intmask) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + + DBG("ENTER: hostname = %s, intmask = %08X\n", mmc_hostname(host->mmc), intmask); + DBG("ENTER: slot = %p, chip = %p\n", slot, slot->chip); + DBG("ENTER: hostname = %s\n", + mmc_hostname(host->mmc)); + + if (intmask & SDHCI_INT_CARD_INSERT) { + if(slot->chip->pdata && slot->chip->pdata->mfp_config) + slot->chip->pdata->mfp_config(); + + /* force clock on */ + clk_enable(slot->clk); + slot->clockEnabled = 1; + set_sd_clock_gating(host, true); + } + else if (intmask & SDHCI_INT_CARD_REMOVE) { + set_sd_clock_gating(host, true); + } + DBG("EXIT: hostname = %s\n", + mmc_hostname(host->mmc)); + + return 0; +} + + + +static unsigned int get_max_clock (struct sdhci_host *host) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + unsigned int clkrate = slot->clkrate; + + DBG("EXIT: slot->clkrate = %u \n", clkrate); + + return clkrate; +} + +static unsigned int get_f_max_clock (struct sdhci_host *host) +{ + struct sdhci_mmc_slot *slot = sdhci_priv(host); + unsigned int f_max; + + f_max = slot->f_max; + + DBG("EXIT: %s f_max = %u\n", mmc_hostname(host->mmc), f_max); + + return f_max; +} + +static inline u16 pxa168_readw(struct sdhci_host *host, int reg) +{ + u32 temp; + if (reg == SDHCI_HOST_VERSION) { + temp = readl (host->ioaddr + SDHCI_HOST_VERSION - 2) >> 16; + return temp & 0xffff; + } + + return readw(host->ioaddr + reg); +} + + +static struct sdhci_ops sdhci_mmc_ops = { + .sd_readw = &pxa168_readw, + .init = &handle_shared_mmc_bus, + .platform_specific_reset = &platform_specific_reset, + .platform_specific_delay = &platform_specific_delay, + .platform_specific_sdio = &platform_specific_sdio, + .get_max_clock = &get_max_clock, + .get_f_max_clock = &get_f_max_clock, + .set_clock = &set_clock, + .platform_supports_8_bit = &platform_supports_8_bit, + .platform_set_8_bit = &platform_set_8_bit, + .platform_clear_8_bit = &platform_clear_8_bit, +}; + + +static void sdhci_mmc_remove_slot(struct sdhci_mmc_slot *slot) +{ + int dead; + u32 scratch; + + DBG("ENTER %s\n", mmc_hostname(slot->host->mmc)); + dead = 0; + scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + sdhci_remove_host(slot->host, dead); + + if (slot->chip->fixes && slot->chip->fixes->remove_slot) + slot->chip->fixes->remove_slot(slot, dead); + + free_irq(slot->host->irq, slot->host); + if (slot->host->ioaddr) + iounmap(slot->host->ioaddr); + release_mem_region(slot->chip->res->start, SZ_256); + sdhci_free_host(slot->host); +} + +static irqreturn_t pxasdh_detect_irq(int irq, void *devid) +{ + struct sdhci_mmc_slot *slot = devid; + + mmc_detect_change(slot->host->mmc, slot->chip->pdata->detect_delay); + return IRQ_HANDLED; +} + +static int pxa_sdh_probe(struct platform_device *pdev) +{ + struct sdhci_host *host = NULL; + struct resource *r; + int ret; + int irq; + struct sdhci_mmc_chip *chip; + struct sdhci_mmc_slot *slot = NULL; + + DBG("ENTER\n"); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if(cpu_is_pxa910_Ax()) + if(r->start == 0xd4281000) + r->start = 0xd4280800; + + irq = platform_get_irq(pdev, 0); + DBG("platform_get_resource = %p\n", r); + if (!r || irq < 0) { + ret = -ENXIO; + goto err; + } + + chip = kzalloc(sizeof(struct sdhci_mmc_chip), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto err; + } + + chip->quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE | + SDHCI_QUIRK_PLATORM_RESET | SDHCI_QUIRK_USE_SUPPLIED_CLOCKS | + SDHCI_QUIRK_NO_BUSY_IRQ; + +#if 1 +#warning FORCE HIGHSPEED BIT LOW IN CONTROL REGISTER + chip->quirks |= SDHCI_QUIRK_BROKEN_HOST_HIGHSPEED; +#endif + +#if 1 +#warning SET BROKEN_TIMEOUT_VAL + chip->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; +#endif + chip->quirks |= SDHCI_QUIRK_BROKEN_ADMA; + + r = request_mem_region(r->start, SZ_256, DRIVER_NAME); + DBG("request_mem_region = %p\n", r); + if (!r) { + ret = -EBUSY; + goto out; + } + + host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_mmc_slot)); + if (IS_ERR(host)) { + ret = PTR_ERR(host); + goto out; + } + + host->ioaddr = ioremap(r->start, SZ_256); + if (!host->ioaddr) { + ret = -ENOMEM; + goto out; + } + + host->irq = irq; + host->quirks = chip->quirks; + host->ops = &sdhci_mmc_ops; + host->hw_name = "MMC"; + + chip->pdev = pdev; + chip->res = r; + chip->pdata = pdev->dev.platform_data; + platform_set_drvdata(pdev, chip); + + host->quirks |= chip->pdata->quirks; + + if (chip->pdata->get_ro) + sdhci_mmc_ops.platform_specific_get_ro = + &platform_specific_get_ro; + if (chip->pdata->get_cd) + sdhci_mmc_ops.platform_get_cd = + &platform_get_cd; + + slot = sdhci_priv(host); + chip->slots[0] = slot; + chip->num_slots = 1; + slot->chip = chip; + slot->host = host; + + chip->mrvl_quirks = chip->pdata->mrvl_quirks; + + slot->eightBitEnabled = 0; + slot->clockEnabled = 0; + slot->clk = clk_get(&pdev->dev, "PXA-SDHCLK"); + if (slot->clk == NULL) { + ret = -ENXIO; + goto out; + } + slot->clkrate = clk_get_rate(slot->clk); + if (chip->pdata->max_speed) + slot->f_max = chip->pdata->max_speed; + else + slot->f_max = slot->clkrate; + + r = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (r) + slot->width = r->start; + else + slot->width = 4; + + DBG("slot->width = %d\n", slot->width); + + enable_clock(host, 0); + + platform_init_after_reset(host); + + ret = sdhci_add_host(host); + if (ret) + goto out; + +/* A side effect of SDHCI_QUIRK_BROKEN_CARD_DETECTION is to turn on + * MMC_CAP_NEEDS_POLL. We should disable it here since we are using + * this for soldered down parts and not sockets. + */ + if ((host->quirks | SDHCI_QUIRK_BROKEN_CARD_DETECTION) && + (host->mmc->caps | MMC_CAP_NEEDS_POLL)) + host->mmc->caps = host->mmc->caps & ~MMC_CAP_NEEDS_POLL; + + if (chip->pdata->mfp_config) { + chip->pdata->mfp_config(); + } else { + /* initialize pin configuration */ + if (chip->pdata->pfn_table) + pfn_config(chip->pdata->pfn_table, PFN_FN); + } + + if(chip->pdata->init) + chip->pdata->init(&pdev->dev, pxasdh_detect_irq, slot); + +#ifdef CONFIG_SD8XXX_RFKILL + if (chip->pdata->pmmc) + *chip->pdata->pmmc = host->mmc; +#endif + + DBG ("Exit %s\n", mmc_hostname(host->mmc)); + return 0; + + out: + DBG ("%s ERROR EXIT\n", __FUNCTION__); + if (host) { + if (host->ioaddr) + iounmap(host->ioaddr); + sdhci_free_host(host); + } + if (r) + release_mem_region(r->start, SZ_256); + + platform_set_drvdata(pdev, NULL); + if (slot && slot->clk) + { + if (slot->clockEnabled) + clk_disable(slot->clk); + slot->clockEnabled = 0; + } + kfree(chip); + +err: + return ret; +} + +static int __devexit pxa_sdh_remove(struct platform_device *pdev) +{ + struct sdhci_mmc_chip *chip; + int i; + + DBG ("ENTER"); + + chip = platform_get_drvdata(pdev); + if (chip) { + for (i = 0;i < chip->num_slots; i++) + sdhci_mmc_remove_slot(chip->slots[i]); + platform_set_drvdata(pdev, NULL); + clk_disable(chip->slots[i]->clk); + kfree(chip); + } + return 0; +} + +#ifdef CONFIG_PM +static int pxa_sdh_suspend(struct platform_device *dev, pm_message_t state) +{ + struct sdhci_mmc_chip *chip; + struct sdhci_mmc_slot *slot; + int i, ret; + + chip = platform_get_drvdata(dev); + if (!chip) + return 0; + + for (i = 0;i < chip->num_slots;i++) { + slot = chip->slots[i]; + if (!slot) + continue; + + ret = sdhci_suspend_host(slot->host, state); + + if (ret) { + for (i--;i >= 0;i--) + sdhci_resume_host(chip->slots[i]->host); + return ret; + } + } + + if (chip->fixes && chip->fixes->suspend) { + ret = chip->fixes->suspend(chip, state); + if (ret) { + for (i = chip->num_slots - 1;i >= 0;i--) + sdhci_resume_host(chip->slots[i]->host); + return ret; + } + } + return 0; +} + +static int pxa_sdh_resume(struct platform_device *dev) +{ + struct sdhci_mmc_chip *chip; + struct sdhci_mmc_slot *slot; + int i, ret; + + chip = platform_get_drvdata(dev); + if (!chip) + return 0; + + if (chip->fixes && chip->fixes->resume) { + ret = chip->fixes->resume(chip); + if (ret) + return ret; + } + + for (i = 0;i < chip->num_slots;i++) { + slot = chip->slots[i]; + if (!slot) + continue; + + ret = sdhci_resume_host(slot->host); + if (ret) + return ret; + } + + return 0; +} +#else +#define pxa_sdh_suspend NULL +#define pxa_sdh_resume NULL +#endif + +/* + * pxa_sdh_startclk: restart SD clocks by sending a command. + * however, hold the command line high during this operation + * so the attached card does not have to see and process this command. + */ + +void pxa_sdh_startclk(struct mmc_host *mmc) +{ + unsigned int status_en_save; + unsigned int interrupt_en_save; + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_mmc_slot *slot = sdhci_priv(host); + struct sdhci_mmc_chip *chip = slot->chip; + struct pfn_cfg *cfg = chip->pdata->pfn_table; + + if (cfg == NULL) + return; + + /* configure CMD pin as GPIO */ + mfp_config(pfn_lookup(cfg, PFN_GPIO, PIN_MMC_CMD), 1); + + /* Send CMD0 and wait */ + status_en_save = sdhci_readl(host, SDHCI_INT_ENABLE); + interrupt_en_save = sdhci_readl(host, SDHCI_SIGNAL_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_ARGUMENT); + sdhci_writew(host, 0, SDHCI_TRANSFER_MODE); + sdhci_writew(host, 0, SDHCI_COMMAND); + + while (!(sdhci_readl(host, SDHCI_INT_STATUS) & SDHCI_INT_RESPONSE)); + + sdhci_writel(host, sdhci_readl(host, SDHCI_INT_STATUS), + SDHCI_INT_STATUS); + + /* restore CMD pin */ + mfp_config(pfn_lookup(cfg, PFN_FN, PIN_MMC_CMD), 1); + + sdhci_writel(host, status_en_save, SDHCI_INT_ENABLE); + sdhci_writel(host, interrupt_en_save, SDHCI_SIGNAL_ENABLE); +} +EXPORT_SYMBOL_GPL(pxa_sdh_startclk); + +static struct platform_driver pxa_sdh_driver = { + .probe = pxa_sdh_probe, + .remove = pxa_sdh_remove, + .suspend = pxa_sdh_suspend, + .resume = pxa_sdh_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init pxa_sdh_init(void) +{ + return platform_driver_register(&pxa_sdh_driver); +} + +static void __exit pxa_sdh_exit(void) +{ + platform_driver_unregister(&pxa_sdh_driver); +} + +module_init(pxa_sdh_init); +module_exit(pxa_sdh_exit); + +MODULE_AUTHOR("Philip Rakity "); +MODULE_DESCRIPTION("PXA SD Host Controller(MMC) Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9d4fdfa685e575..ad09aeb5cacf36 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -23,11 +23,20 @@ #include #include +#include +#include -#include "sdhci.h" +#include +#include -#define DRIVER_NAME "sdhci" +#undef WARNINGS + +#define SUPPORT_8_BIT_MMC +#ifdef WARNINGS +#warning REDEFINED DRIVER NAME to pxa-sdh +#endif +#define DRIVER_NAME "pxa-sdh" #define DBG(f, x...) \ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) @@ -173,6 +182,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); + +#ifdef WARNINGS +#warning RESET NEEED FOR PRIVATE REGISTERS +#endif + if (host->ops->platform_specific_reset) + host->ops->platform_specific_reset(host, mask); } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); @@ -589,6 +604,15 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL) return 0xE; + /* + * A High Capacity SD and Extended Capacity SD indicates TAAC and + * NSAC of CSD as fixed values. Host should use 100ms + * timeout(minimum) for read operations. It's strongly recommended + * for host to use more than 500ms timeout for write operations. + */ + if (!data->timeout_ns && !data->timeout_clks) + return 0xE; + /* timeout in us */ target_timeout = data->timeout_ns / 1000 + data->timeout_clks / host->clock; @@ -896,6 +920,18 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mdelay(1); } + +#ifdef WARNINGS +#warning HACK FOR CPU TOO FAST +#endif + /* + * we cannot talk to controller for 8 bus cycles according to sdio spec + * at lowest speed this is 100,000 HZ per cycle or 800,000 cycles + * which is quite a LONG TIME on a fast cpu -- so delay if needed + */ + if (host->ops->platform_specific_delay) + host->ops->platform_specific_delay(host); + mod_timer(&host->timer, jiffies + 10 * HZ); host->cmd = cmd; @@ -943,8 +979,15 @@ static void sdhci_finish_command(struct sdhci_host *host) if (host->cmd->flags & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ for (i = 0;i < 4;i++) { - host->cmd->resp[i] = sdhci_readl(host, - SDHCI_RESPONSE + (3-i)*4) << 8; + if(cpu_is_pxa910() && (0 == i)){ + /* Workaround PXA910/920 response reading mode limitation */ + u32 resp6, resp7; + resp6 = sdhci_readw(host, SDHCI_RESPONSE + 0xc); + resp7 = sdhci_readb(host, SDHCI_RESPONSE + 0xe); + host->cmd->resp[i] = resp6 << 8 | resp7 << 24; + } else + host->cmd->resp[i] = sdhci_readl(host, + SDHCI_RESPONSE + (3-i)*4) << 8; if (i != 3) host->cmd->resp[i] |= sdhci_readb(host, @@ -1086,7 +1129,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; - bool present; + bool present = false; unsigned long flags; host = mmc_priv(mmc); @@ -1102,11 +1145,18 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; /* If polling, assume that the card is always present. */ - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) { present = true; - else + } + else if (host->ops->platform_get_cd) { + if (host->ops->platform_get_cd(mmc) == 0) { + present = true; + } + } + else { present = sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT; + SDHCI_CARD_PRESENT; + } if (!present || host->flags & SDHCI_DEVICE_DEAD) { host->mrq->cmd->error = -ENOMEDIUM; @@ -1149,12 +1199,38 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); +#ifdef SUPPORT_8_BIT_MMC + +#ifdef WARNINGS +#warning ENABLE 8 bit support +#endif + if (ios->bus_width == MMC_BUS_WIDTH_8) { + if (host->ops->platform_set_8_bit) + host->ops->platform_set_8_bit(host); + } + else if (ios->bus_width == MMC_BUS_WIDTH_4) { + if (host->ops->platform_clear_8_bit) + host->ops->platform_clear_8_bit(host); + ctrl |= SDHCI_CTRL_4BITBUS; + } + else { + if (host->ops->platform_clear_8_bit) + host->ops->platform_clear_8_bit(host); + ctrl &= ~SDHCI_CTRL_4BITBUS; + } +#else if (ios->bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; else ctrl &= ~SDHCI_CTRL_4BITBUS; +#endif - if (ios->timing == MMC_TIMING_SD_HS) +#ifdef WARNINGS +#warning CHECK FOR BROKEN HIGH SPEED BIT +#endif + if (host->quirks & SDHCI_QUIRK_BROKEN_HOST_HIGHSPEED) + ctrl &= ~SDHCI_CTRL_HISPD; + else if (ios->timing == MMC_TIMING_SD_HS) ctrl |= SDHCI_CTRL_HISPD; else ctrl &= ~SDHCI_CTRL_HISPD; @@ -1182,6 +1258,9 @@ static int sdhci_get_ro(struct mmc_host *mmc) host = mmc_priv(mmc); + if (host->ops->platform_specific_get_ro) + return host->ops->platform_specific_get_ro(mmc); + spin_lock_irqsave(&host->lock, flags); if (host->flags & SDHCI_DEVICE_DEAD) @@ -1208,6 +1287,12 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) if (host->flags & SDHCI_DEVICE_DEAD) goto out; +#ifdef WARNINGS +#warning enable sdio private operation (FIFO PARM) +#endif + if (host->ops->platform_specific_sdio) + host->ops->platform_specific_sdio(host, enable); + if (enable) sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); else @@ -1298,6 +1383,8 @@ static void sdhci_tasklet_finish(unsigned long param) controllers do not like that. */ sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); + } else { + sdhci_reset(host, SDHCI_RESET_DATA); } host->mrq = NULL; @@ -1456,16 +1543,24 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (intmask & SDHCI_INT_DATA_TIMEOUT) host->data->error = -ETIMEDOUT; - else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) + else if (intmask & SDHCI_INT_DATA_END_BIT) + host->data->error = -EILSEQ; + else if ((intmask & SDHCI_INT_DATA_CRC) && + SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) != MMC_BUSTEST_R) { host->data->error = -EILSEQ; + printk(KERN_ERR "%s: CRC error\n", mmc_hostname(host->mmc)); + } else if (intmask & SDHCI_INT_ADMA_ERROR) { printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); sdhci_show_adma_error(host); host->data->error = -EIO; } - if (host->data->error) + if (host->data->error) { + printk ("%s: Data Error = %d, intmask = %08X\n",__FUNCTION__,host->data->error, intmask); + sdhci_dumpregs(host); sdhci_finish_data(host); + } else { if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) sdhci_transfer_pio(host); @@ -1516,6 +1611,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); +#ifdef WARNINGS +#warning PLATFORM SPECIFC INIT +#endif + if (host->ops->init) + host->ops->init(host, intmask); + tasklet_schedule(&host->card_tasklet); } @@ -1531,6 +1632,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, SDHCI_INT_STATUS); sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); + + sdhci_writeb(host, SDHCI_RESET_DATA, SDHCI_SOFTWARE_RESET); } intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); @@ -1584,6 +1687,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) { int ret; + printk ("%s: ENTER\n", __FUNCTION__); + sdhci_disable_card_detection(host); sdhci_disable_card_detection(host); @@ -1602,13 +1707,17 @@ int sdhci_resume_host(struct sdhci_host *host) { int ret; + printk ("%s: ENTER\n", __FUNCTION__); if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); } +#ifdef WARNINGS +#warning request IRQ on DRIVER_NAME +#endif ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(host->mmc), host); + DRIVER_NAME, host); if (ret) return ret; @@ -1741,17 +1850,44 @@ int sdhci_add_host(struct sdhci_host *host) mmc_dev(host->mmc)->dma_mask = &host->dma_mask; } - host->max_clk = - (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; - host->max_clk *= 1000000; - if (host->max_clk == 0) { - if (!host->ops->get_max_clock) { - printk(KERN_ERR - "%s: Hardware doesn't specify base clock " - "frequency.\n", mmc_hostname(mmc)); - return -ENODEV; + if (host->quirks & SDHCI_QUIRK_USE_SUPPLIED_CLOCKS) { + if (host->ops->get_max_clock) { + host->max_clk = host->ops->get_max_clock(host); + + if (host->ops->get_f_max_clock) + mmc->f_max = host->ops->get_f_max_clock(host); + else + mmc->f_max = host->max_clk; + + if (host->ops->get_f_min_clock) + mmc->f_min = host->ops->get_f_min_clock(host); + else + mmc->f_min = host->max_clk / 256; + } + else + host->max_clk = 0; // force error + } else { + host->max_clk = + (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + host->max_clk *= 1000000; + if (host->max_clk == 0) { + if (!host->ops->get_max_clock) { + printk(KERN_ERR + "%s: Hardware doesn't specify base clock " + "frequency.\n", mmc_hostname(mmc)); + return -ENODEV; + } + host->max_clk = host->ops->get_max_clock(host); } - host->max_clk = host->ops->get_max_clock(host); + mmc->f_min = host->max_clk / 256; + mmc->f_max = host->max_clk; + } + + if (host->max_clk == 0) { + printk(KERN_ERR + "%s: Hardware doesn't specify base clock " + "frequency.\n", mmc_hostname(mmc)); + return -ENODEV; } host->timeout_clk = @@ -1785,8 +1921,18 @@ int sdhci_add_host(struct sdhci_host *host) if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) mmc->caps |= MMC_CAP_4_BIT_DATA; +#ifdef SUPPORT_8_BIT_MMC +#ifdef WARNINGS +#warning ADD support for 8 BIT +#endif + if (host->ops->platform_supports_8_bit) { + if (host->ops->platform_supports_8_bit(host)) + mmc->caps |= MMC_CAP_8_BIT_DATA; + } +#endif + if (caps & SDHCI_CAN_DO_HISPD) - mmc->caps |= MMC_CAP_SD_HIGHSPEED; + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) mmc->caps |= MMC_CAP_NEEDS_POLL; @@ -1868,8 +2014,11 @@ int sdhci_add_host(struct sdhci_host *host) setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); +#ifdef WARNINGS +#warning request IRQ on DRIVER_NAME +#endif ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - mmc_hostname(mmc), host); + DRIVER_NAME, host); if (ret) goto untasklet; @@ -1903,6 +2052,8 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_enable_card_detection(host); + sdhci_enable_card_detection(host); + return 0; #ifdef SDHCI_USE_LEDS_CLASS diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index ecf90f5c97c2bc..f583d62995cbb4 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -315,6 +315,13 @@ config MTD_OOPS To use, add console=ttyMTDx to the kernel command line, where x is the MTD device number to use. +config PXA3XX_BBM + bool "Marvell PXA3xx Bad Block Management" + depends on MTD && (MTD_ONENAND || MTD_NAND) + help + This enable Marvell Bad Block Management for NAND/ONENAND on + PXA3xx. + source "drivers/mtd/chips/Kconfig" source "drivers/mtd/maps/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4521b1ecce4526..5373d230deb293 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o obj-$(CONFIG_MTD_OOPS) += mtdoops.o +obj-$(CONFIG_PXA3XX_BBM) += pxa3xx_bbm.o nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 81e49a9b017e32..581642b1fe5074 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -59,8 +59,13 @@ #define SR_SRWD 0x80 /* SR write protect */ /* Define max times to check status register before we give up. */ +<<<<<<< HEAD:drivers/mtd/devices/m25p80.c +#define MAX_READY_WAIT_JIFFIES (160 * HZ) /* M25P64 specs 160s max chip erase */ +#define CMD_SIZE 4 +======= #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ #define MAX_CMD_SIZE 4 +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/mtd/devices/m25p80.c #ifdef CONFIG_M25PXX_USE_FAST_READ #define OPCODE_READ OPCODE_FAST_READ @@ -263,6 +268,9 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) /* sanity checks */ if (instr->addr + instr->len > flash->mtd.size) return -EINVAL; + div_u64_rem(instr->addr, mtd->erasesize, &rem); + if (rem) + return -EINVAL; div_u64_rem(instr->len, mtd->erasesize, &rem); if (rem) return -EINVAL; @@ -646,6 +654,11 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, + /* Macronix -- mx25lxxx */ + { "mx25l32", 0xc22016, 0, 64 * 1024, 64, }, + { "mx25l64", 0xc22017, 0, 64 * 1024, 128, }, + { "mx25l128", 0xc22018, 0, 64 * 1024, 256, }, + /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ @@ -670,6 +683,24 @@ static const struct spi_device_id m25p_ids[] = { { "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K) }, /* ST Microelectronics -- newer production may have feature updates */ +<<<<<<< HEAD:drivers/mtd/devices/m25p80.c + { "m25p05", 0x202010, 0, 32 * 1024, 2, }, + { "m25p10", 0x202011, 0, 32 * 1024, 4, }, + { "m25p20", 0x202012, 0, 64 * 1024, 4, }, + { "m25p40", 0x202013, 0, 64 * 1024, 8, }, + { "m25p80", 0, 0, 64 * 1024, 16, }, + { "m25p16", 0x202015, 0, 64 * 1024, 32, }, + { "m25p32", 0x202016, 0, 64 * 1024, 64, }, + { "m25p64", 0x202017, 0, 64 * 1024, 128, }, + { "m25px64", 0x207117, 0, 64 * 1024, 128, }, + { "m25p128", 0x202018, 0, 256 * 1024, 64, }, + + { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, + { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, + + { "m25pe80", 0x208014, 0, 64 * 1024, 16, }, + { "m25pe16", 0x208015, 0, 64 * 1024, 32, SECT_4K, }, +======= { "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) }, { "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) }, { "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) }, @@ -686,6 +717,7 @@ static const struct spi_device_id m25p_ids[] = { { "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) }, { "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) }, +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/mtd/devices/m25p80.c /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) }, @@ -815,7 +847,11 @@ static int __devinit m25p_probe(struct spi_device *spi) flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; +<<<<<<< HEAD:drivers/mtd/devices/m25p80.c + flash->command = kmalloc(CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); +======= flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL); +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/mtd/devices/m25p80.c if (!flash->command) { kfree(flash); return -ENOMEM; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 42e5ea49e97504..fbb5e6c699db75 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -376,6 +376,39 @@ config MTD_NAND_PXA3xx_BUILTIN This enables builtin definitions for some NAND chips. This is deprecated in favor of platform specific data. +choice + prompt "Select MLC NAND Device" + depends on MTD_NAND_PXA3xx + default DEFAULT_NAND + +config SAMSUNG_32G_MLC_NAND + bool "Use 32G MLC NAND (K9LBG08U0D)" + help + This enables the support for Samsung MLC 32Gb NAND (K9LBG08U0D) + +config SAMSUNG_8G_MLC_NAND + bool "Use 8G MLC NAND (K9G8G08U0A)" + help + This enables the support for Samsung MLC 8Gb NAND (K9G8G08U0A) + +config MICRON_32G_MLC_NAND + bool "Use Micron 32G MLC NAND (29F32G08CBABA)" + help + This enables the support for Micron MLC 32Gb NAND (29F32G08CBABA) + +config SAMSUNG_512M_SLC_NAND + bool "Use Samsung Small Block 64Mbyte SLC NAND (K9F1208UOC)" + help + This enables the support for Samsung small block SLC NAND (K9F1208UOC) + + +config DEFAULT_NAND + bool "Use Micron 4G SLC NAND" + help + This enables the support for the default Micron SLC 4Gb NAND (29F4G08AAC) +endchoice + + config MTD_NAND_CM_X270 tristate "Support for NAND Flash on CM-X270 modules" depends on MTD_NAND && MACH_ARMCORE diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8f2958fe2148a1..0ac9834de1c48f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -370,7 +370,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* Do we have a flash based bad block table ? */ if (chip->options & NAND_USE_FLASH_BBT) - ret = nand_update_bbt(mtd, ofs); + ret = chip->update_bbt(mtd, ofs); else { /* We write two bytes, so we dont have to mess with 16 bit * access @@ -2429,6 +2429,12 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); + if (ret && (chip->options & BBT_RELOCATION_IFBAD)) { + chip->block_markbad(mtd, + (loff_t)(page & chip->pagemask) << chip->page_shift); + instr->state = MTD_ERASE_DONE; + ret = 0; + } /* Do call back function */ if (!ret) @@ -2445,10 +2451,10 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, if (!rewrite_bbt[chipnr]) continue; /* update the BBT for chip */ - DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt " - "(%d:0x%0llx 0x%0x)\n", __func__, chipnr, - rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]); - nand_update_bbt(mtd, rewrite_bbt[chipnr]); + DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt " + "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr], + chip->bbt_td->pages[chipnr]); + chip->update_bbt(mtd, rewrite_bbt[chipnr]); } /* Return more or less happy */ @@ -2550,6 +2556,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (chip->waitfunc == NULL) chip->waitfunc = nand_wait; + if (!chip->scan_ident) + chip->scan_ident = nand_scan_ident; if (!chip->select_chip) chip->select_chip = nand_select_chip; if (!chip->read_byte) @@ -2568,6 +2576,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; + if (!chip->update_bbt) + chip->update_bbt = nand_update_bbt; if (!chip->controller) { chip->controller = &chip->hwcontrol; @@ -2755,8 +2765,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; - /* Set the default functions */ - nand_set_defaults(chip, busw); /* Read the flash type */ type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); @@ -3041,7 +3049,8 @@ int nand_scan_tail(struct mtd_info *mtd) */ int nand_scan(struct mtd_info *mtd, int maxchips) { - int ret; + struct nand_chip *chip = mtd->priv; + int ret, busw; /* Many callers got this wrong, so check for it for a while... */ if (!mtd->owner && caller_is_module()) { @@ -3050,7 +3059,12 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } - ret = nand_scan_ident(mtd, maxchips); + /* Get buswidth to select the correct functions */ + busw = chip->options & NAND_BUSWIDTH_16; + /* Set the default functions */ + nand_set_defaults(chip, busw); + + ret = chip->scan_ident(mtd, maxchips); if (!ret) ret = nand_scan_tail(mtd); return ret; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 5d55152162cf5c..91987a19eae028 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -8,6 +8,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#ifdef __KERNEL__ #include #include @@ -24,56 +25,218 @@ #include #include -#include +#include +#include +#include +#endif -#define CHIP_DELAY_TIMEOUT (2 * HZ/10) +#define NAND_STOP_DELAY (100) +#undef PXA3XX_NAND_DEBUG +#ifdef PXA3XX_NAND_DEBUG +#ifdef PXA3XX_NAND_DEBUG_MORE_CONTROL +static int debug_nand = 0; +#define DBG_NAND(x) do {if (debug_nand) {x;}}while(0) +#else +#define DBG_NAND(x) do{x;}while(0) +#endif +#else +#define DBG_NAND(x) +#endif +static int no_error_dump = 0; -/* registers and bit definitions */ -#define NDCR (0x00) /* Control register */ -#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ -#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ -#define NDSR (0x14) /* Status Register */ -#define NDPCR (0x18) /* Page Count Register */ -#define NDBDR0 (0x1C) /* Bad Block Register 0 */ -#define NDBDR1 (0x20) /* Bad Block Register 1 */ -#define NDDB (0x40) /* Data Buffer */ -#define NDCB0 (0x48) /* Command Buffer0 */ -#define NDCB1 (0x4C) /* Command Buffer1 */ -#define NDCB2 (0x50) /* Command Buffer2 */ +#if defined(CONFIG_DVFM) +#include +static int dvfm_dev_idx; + +static void set_dvfm_constraint(void) +{ + /* Disable Low power mode */ + dvfm_disable_op_name("apps_idle", dvfm_dev_idx); + dvfm_disable_op_name("apps_sleep", dvfm_dev_idx); + dvfm_disable_op_name("sys_sleep", dvfm_dev_idx); +} + +static void unset_dvfm_constraint(void) +{ + /* Enable Low power mode */ + dvfm_enable_op_name("apps_idle", dvfm_dev_idx); + dvfm_enable_op_name("apps_sleep", dvfm_dev_idx); + dvfm_enable_op_name("sys_sleep", dvfm_dev_idx); +} +#else +static void set_dvfm_constraint(void) {} +static void unset_dvfm_constraint(void) {} +#endif + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static const char *part_probes[] = { "cmdlinepart", NULL }; +static const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL}; +#endif + +/* convert nano-seconds to nand flash controller clock cycles */ +#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) +#define cycle2ns(cycle, clk) (cycle * 1000 / (clk / 1000000)) +#define CHIP_DELAY_TIMEOUT (500) +#define BCH_THRESHOLD (8) +#define PAGE_CHUNK_SIZE (2048) +#define OOB_CHUNK_SIZE (64) + +/* registers and bit definitions */ +#define NDCR (0x00) /* Control register */ +#define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ +#define NDTR1CS0 (0x0C) /* Timing Parameter 1 for CS0 */ +#define NDSR (0x14) /* Status Register */ +#define NDPCR (0x18) /* Page Count Register */ +#define NDBBR0 (0x1C) /* Bad Block Register 0 */ +#define NDBBR1 (0x20) /* Bad Block Register 1 */ +#define NDREDEL (0x24) /* Read Enable Return Delay Register */ +#define NDECCCTRL (0x28) /* ECC Control Register */ +#define NDBZCNT (0x2C) /* Timer for NDRnB0 and NDRnB1 */ +#define NDMUTEX (0x30) /* Mutex Lock Register */ +#define NDCMDMAT0 (0x34) /* Partition Command Match Register 0 */ +#define NDCMDMAT1 (0x38) /* Partition Command Match Register 1 */ +#define NDCMDMAT2 (0x3C) /* Partition Command Match Register 2 */ +#define NDDB (0x40) /* Data Buffer */ +#define NDCB0 (0x48) /* Command Buffer0 */ +#define NDCB1 (0x4C) /* Command Buffer1 */ +#define NDCB2 (0x50) /* Command Buffer2 */ +#define NDCB3 (0x50) /* Command Buffer3 */ +#define NDARBCR (0x5C) /* DFI Arbitration Control Register */ +#define NDPTXCS0 (0x60) /* Partition Region Control Register 0 */ +#define NDPTXCS1 (0x64) /* Partition Region Control Register 1 */ +#define NDPTXCS2 (0x68) /* Partition Region Control Register 2 */ +#define NDPTXCS3 (0x6C) /* Partition Region Control Register 3 */ +#define NDPTXCS4 (0x70) /* Partition Region Control Register 4 */ +#define NDPTXCS5 (0x74) /* Partition Region Control Register 5 */ +#define NDPTXCS6 (0x78) /* Partition Region Control Register 6 */ +#define NDPTXCS7 (0x7C) /* Partition Region Control Register 7 */ + +/* NDCR Register */ #define NDCR_SPARE_EN (0x1 << 31) #define NDCR_ECC_EN (0x1 << 30) #define NDCR_DMA_EN (0x1 << 29) #define NDCR_ND_RUN (0x1 << 28) #define NDCR_DWIDTH_C (0x1 << 27) #define NDCR_DWIDTH_M (0x1 << 26) -#define NDCR_PAGE_SZ (0x1 << 24) -#define NDCR_NCSX (0x1 << 23) -#define NDCR_ND_MODE (0x3 << 21) -#define NDCR_NAND_MODE (0x0) +#define NDCR_PAGE_SZ_MASK (0x3 << 24) +#define NDCR_PAGE_SZ(x) (((x) << 24) & NDCR_PAGE_SZ_MASK) +#define NDCR_SEQ_DIS (0x1 << 23) +#define NDCR_ND_STOP (0x1 << 22) +#define NDCR_FORCE_CSX (0x1 << 21) #define NDCR_CLR_PG_CNT (0x1 << 20) -#define NDCR_CLR_ECC (0x1 << 19) +#define NDCR_STOP_ON_UNCOR (0x1 << 19) #define NDCR_RD_ID_CNT_MASK (0x7 << 16) #define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK) #define NDCR_RA_START (0x1 << 15) -#define NDCR_PG_PER_BLK (0x1 << 14) +#define NDCR_PG_PER_BLK_MASK (0x3 << 13) +#define NDCR_PG_PER_BLK(x) (((x) << 13) & NDCR_PG_PER_BLK_MASK) #define NDCR_ND_ARB_EN (0x1 << 12) - -#define NDSR_MASK (0xfff) -#define NDSR_RDY (0x1 << 11) +#define NDCR_RDYM (0x1 << 11) +#define NDCR_CS0_PAGEDM (0x1 << 10) +#define NDCR_CS1_PAGEDM (0x1 << 9) +#define NDCR_CS0_CMDDM (0x1 << 8) +#define NDCR_CS1_CMDDM (0x1 << 7) +#define NDCR_CS0_BBDM (0x1 << 6) +#define NDCR_CS1_BBDM (0x1 << 5) +#define NDCR_UNCERRM (0x1 << 4) +#define NDCR_CORERRM (0x1 << 3) +#define NDCR_WRDREQM (0x1 << 2) +#define NDCR_RDDREQM (0x1 << 1) +#define NDCR_WRCMDREQM (0x1) +#define NDCR_INT_MASK (0xFFF) + +/* Data Controller Timing Paramter x Register For CSx */ +#define NDTR0_tADL(c) (min_t(uint32_t, (c), 31) << 27) +#define NDTR0_SELCNTR (0x1 << 26) +#define NDTR0_RD_CNT_DEL_MASK (0xF << 22) +#define NDTR0_RD_CNT_DEL(x) (((x) << 22) & NDTR0_RD_CNT_DEL_MASK) +#define NDTR0_tCH(c) (min_t(uint32_t, (c), 7) << 19) +#define NDTR0_tCS(c) (min_t(uint32_t, (c), 7) << 16) +#define NDTR0_tWH(c) (min_t(uint32_t, (c), 7) << 11) +#define NDTR0_tWP(c) (min_t(uint32_t, (c), 7) << 8) +#define NDTR0_sel_NRE_EDGE (0x1 << 7) +#define NDTR0_ETRP (0x1 << 6) +#define NDTR0_tRH(c) (min_t(uint32_t, (c), 7) << 3) +#define NDTR0_tRP(c) (min_t(uint32_t, (c), 7) << 0) + +#define NDTR1_tR(c) (min_t(uint32_t, (c), 65535) << 16) +#define NDTR1_WAIT_MODE (0x1 << 15) +#define NDTR1_PRESCALE (0x1 << 14) +#define NDTR1_tRHW(c) (min_t(uint32_t, (c), 3) << 8) +#define NDTR1_tWHR(c) (min_t(uint32_t, (c), 15) << 4) +#define NDTR1_tAR(c) (min_t(uint32_t, (c), 15) << 0) + +/* NDSR Register */ +#define NDSR_ERR_CNT_MASK (0x1F << 16) +#define NDSR_ERR_CNT(x) (((x) << 16) & NDSR_ERR_CNT_MASK) +#define NDSR_TRUSTVIO (0x1 << 15) +#define NDSR_MASK (0xFFFF) +#define NDSR_RDY (0x1 << 12) +#define NDSR_FLASH_RDY (0x1 << 11) #define NDSR_CS0_PAGED (0x1 << 10) #define NDSR_CS1_PAGED (0x1 << 9) #define NDSR_CS0_CMDD (0x1 << 8) #define NDSR_CS1_CMDD (0x1 << 7) #define NDSR_CS0_BBD (0x1 << 6) #define NDSR_CS1_BBD (0x1 << 5) -#define NDSR_DBERR (0x1 << 4) -#define NDSR_SBERR (0x1 << 3) +#define NDSR_UNCERR (0x1 << 4) +#define NDSR_CORERR (0x1 << 3) #define NDSR_WRDREQ (0x1 << 2) #define NDSR_RDDREQ (0x1 << 1) #define NDSR_WRCMDREQ (0x1) +/* NDPCR Register */ +#define NDPCR_PG_CNT_1_MASK (0xFF << 16) +#define NDPCR_PG_CNT_1(x) (((x) << 16) & NDPCR_PG_CNT_1_MASK) +#define NDPCR_PG_CNT_0_MASK (0xFF) +#define NDPCR_PG_CNT_0(x) ((x) & NDPCR_PG_CNT_0_MASK) + +/* READ Enable Return Delay Register */ +#define NDREDEL_ND_DIN_SEL (0x1 << 25) +#define NDREDEL_ND_DATA_D_MASK (0x3 << 8) +#define NDREDEL_ND_DATA_DLY(x) (((x) << 8) & NDREDEL_ND_DATA_D_MASK) +#define NDREDEL_ND_RECLK_D_MASK (0xF << 4) +#define NDREDEL_ND_RECLK_DLY(x) (((x) << 4) & NDREDEL_ND_RECLK_D_MASK) +#define NDREDEL_ND_RE_D_MASK (0xF) +#define NDREDEL_ND_RE_DLY(x) ((x) & NDREDEL_ND_RE_D_MASK) + +/* ECC Control Register */ +#define NDECCCTRL_ECC_SPARE_MSK (0xFF << 7) +#define NDECCCTRL_ECC_SPARE(x) (((x) << 7) & NDECCCTRL_ECC_SPARE_MSK) +#define NDECCCTRL_ECC_THR_MSK (0x3F << 1) +#define NDECCCTRL_ECC_THRESH(x) (((x) << 1) & NDECCCTRL_ECC_THR_MSK) +#define NDECCCTRL_BCH_EN (0x1) + +/* Timer for ND_RnBx */ +#define NDBZCNT_MASK (0xFFFF) +#define NDBZCNT_ND_RNB_CNT1(x) (((x & NDBZCNT_MASK) << 16) +#define NDBZCNT_ND_RNB_CNT0(x) (x & NDBZCNT_MASK) + + /* NAND Controller MUTEX Lock Register */ +#define NDMUTEX_MUTEX (0x1) + + /* Partition Command Match Registers */ +#define NDCMDMAT_VALIDCNT_MASK (0x3) +#define NDCMDMAT_CMD_MASK (0xFF) +#define NDCMDMAT_VALIDCNT ((x & NDCMDMAT_VALIDCNT_MASK) << 30) +#define NDCMDMAT_NAKEDDIS2 (0x1 << 29) +#define NDCMDMAT_ROWADD2 (0x1 << 28) +#define NDCMDMAT_CMD2(x) ((x & NDCMDMAT) << 20) +#define NDCMDMAT_NAKEDDIS1 (0x1 << 29) +#define NDCMDMAT_ROWADD1 (0x1 << 28) +#define NDCMDMAT_CMD1(x) ((x & NDCMDMAT) << 20) +#define NDCMDMAT_NAKEDDIS0 (0x1 << 29) +#define NDCMDMAT_ROWADD0 (0x1 << 28) +#define NDCMDMAT_CMD0(x) ((x & NDCMDMAT) << 20) + + /* NAND Controller Command Buffers */ +#define NDCB0_CMD_XTYPE_MASK (0x7 << 29) +#define NDCB0_CMD_XTYPE(x) (((x) << 29) & NDCB0_CMD_XTYPE_MASK) +#define NDCB0_LEN_OVRD (0x1 << 28) +#define NDCB0_RDY_BYP (0x1 << 27) +#define NDCB0_ST_ROW_EN (0x1 << 26) #define NDCB0_AUTO_RS (0x1 << 25) #define NDCB0_CSEL (0x1 << 24) #define NDCB0_CMD_TYPE_MASK (0x7 << 21) @@ -84,738 +247,1076 @@ #define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK) #define NDCB0_CMD2_MASK (0xff << 8) #define NDCB0_CMD1_MASK (0xff) -#define NDCB0_ADDR_CYC_SHIFT (16) + +#define NDCB_MASK (0xFF) +#define NDCB1_ADDR4(x) ((x & NDCB_MASK) << 24) +#define NDCB1_ADDR3(x) ((x & NDCB_MASK) << 16) +#define NDCB1_ADDR2(x) ((x & NDCB_MASK) << 8) +#define NDCB1_ADDR1(x) (x & NDCB_MASK) + +#define NDCB2_ST_MASK(x) ((x & NDCB_MASK) << 24) +#define NDCB2_ST_CMD(x) ((x & NDCB_MASK) << 16) +#define NDCB2_PAGE_COUNT(x) ((x & NDCB_MASK) << 8) +#define NDCB2_ADDR5(x) (x & NDCB_MASK) + +#define NDCB3_ADDR7(x) ((x & NDCB_MASK) << 24) +#define NDCB3_ADDR6(x) ((x & NDCB_MASK) << 16) +#define NDCB3_NDLENCNT_MASK (0xFFFF) +#define NDCB3_NDLENCNT(x) (x & NDCB3_NDLENCNT_MASK) + +/* DFI Arbitration Control Register */ +#define NDARBCR_MASK (0xFFFF) +#define NDARBCR_ARB_CNT(x) (x & NDARBCR_MASK) + +/* Partition Region Control Registers for CSx */ +#define NDPTXCS_VALID (0x1 << 31) +#define NDPTXCS_LOCK (0x1 << 30) +#define NDPTXCS_TRUSTED (0x1 << 29) +#define NDPTXCS_BLOCKADD_MASK (0xFFFFFF) +#define NDPTXCS_BLOCKADD(x) ((x) & NDPTXCS_BLOCKADD_MASK) + +/* dma-able I/O address for the NAND data and commands */ +#define NDCB0_DMA_ADDR (0xd4283048) +#define NDDB_DMA_ADDR (0xd4283040) /* macros for registers read/write */ -#define nand_writel(info, off, val) \ - __raw_writel((val), (info)->mmio_base + (off)) +#define nand_writel(nand, off, val) \ + __raw_writel((val), (nand)->mmio_base + (off)) -#define nand_readl(info, off) \ - __raw_readl((info)->mmio_base + (off)) +#define nand_readl(nand, off) \ + __raw_readl((nand)->mmio_base + (off)) -/* error code and state */ enum { ERR_NONE = 0, - ERR_DMABUSERR = -1, - ERR_SENDCMD = -2, - ERR_DBERR = -3, - ERR_BBERR = -4, - ERR_SBERR = -5, + ERR_DMABUSERR = 1, + ERR_SENDCMD = (1 << 1), + ERR_DBERR = (1 << 2), + ERR_BBERR = (1 << 3), + ERR_CORERR = (1 << 4), + ERR_TRUSTVIO = (1 << 5), }; enum { - STATE_READY = 0, - STATE_CMD_HANDLE, - STATE_DMA_READING, - STATE_DMA_WRITING, - STATE_DMA_DONE, - STATE_PIO_READING, - STATE_PIO_WRITING, + STATE_CMD_WAIT_DONE = 1, + STATE_DATA_PROCESSING = (1 << 1), + STATE_DATA_DONE = (1 << 2), + STATE_PAGE_DONE = (1 << 3), + STATE_CMD_DONE = (1 << 4), + STATE_READY = (1 << 5), + STATE_CMD_PREPARED = (1 << 6), +}; + +static struct nand_ecclayout hw_smallpage_ecclayout = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 11, 12, 13 }, + .oobfree = { {2, 6} } +}; + +static struct nand_ecclayout hw_largepage_ecclayout = { + .eccbytes = 24, + .eccpos = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63}, + .oobfree = { {2, 38} } }; struct pxa3xx_nand_info { struct nand_chip nand_chip; - - struct platform_device *pdev; + struct pxa3xx_nand *nand_data; const struct pxa3xx_nand_flash *flash_info; - struct clk *clk; - void __iomem *mmio_base; - unsigned long mmio_phys; - + size_t data_size; /* data size in FIFO */ + unsigned char *data_buff; + unsigned char *oob_buff; unsigned int buf_start; unsigned int buf_count; - /* DMA information */ - int drcmr_dat; - int drcmr_cmd; - - unsigned char *data_buff; + /* dma related */ dma_addr_t data_buff_phys; - size_t data_buff_size; - int data_dma_ch; - struct pxa_dma_desc *data_desc; dma_addr_t data_desc_addr; + struct pxa_dma_desc *data_desc; - uint32_t reg_ndcr; - - /* saved column/page_addr during CMD_SEQIN */ - int seqin_column; - int seqin_page_addr; - - /* relate to the command */ - unsigned int state; - - int use_ecc; /* use HW ECC ? */ - int use_dma; /* use DMA ? */ - - size_t data_size; /* data size in FIFO */ - int retcode; - struct completion cmd_complete; + uint16_t chip_select; + uint16_t data_column; + uint16_t oob_column; - /* generated NDCBx register values */ - uint32_t ndcb0; + /* command poll */ + uint32_t current_cmd_seqs; + uint32_t total_cmds; + uint32_t need_additional_addressing; + uint32_t need_wait_ready; + uint32_t ndcb0[CMD_POOL_SIZE]; uint32_t ndcb1; uint32_t ndcb2; + uint32_t ndcb3[CMD_POOL_SIZE]; - /* calculated from pxa3xx_nand_flash data */ - size_t oob_size; - size_t read_id_bytes; - - unsigned int col_addr_cycles; - unsigned int row_addr_cycles; -}; - -static int use_dma = 1; -module_param(use_dma, bool, 0444); -MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); - -/* - * Default NAND flash controller configuration setup by the - * bootloader. This configuration is used only when pdata->keep_config is set - */ -static struct pxa3xx_nand_timing default_timing; -static struct pxa3xx_nand_flash default_flash; - -static struct pxa3xx_nand_cmdset smallpage_cmdset = { - .read1 = 0x0000, - .read2 = 0x0050, - .program = 0x1080, - .read_status = 0x0070, - .read_id = 0x0090, - .erase = 0xD060, - .reset = 0x00FF, - .lock = 0x002A, - .unlock = 0x2423, - .lock_status = 0x007A, -}; - -static struct pxa3xx_nand_cmdset largepage_cmdset = { - .read1 = 0x3000, - .read2 = 0x0050, - .program = 0x1080, - .read_status = 0x0070, - .read_id = 0x0090, - .erase = 0xD060, - .reset = 0x00FF, - .lock = 0x002A, - .unlock = 0x2423, - .lock_status = 0x007A, -}; - -#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN -static struct pxa3xx_nand_timing samsung512MbX16_timing = { - .tCH = 10, - .tCS = 0, - .tWH = 20, - .tWP = 40, - .tRH = 30, - .tRP = 40, - .tR = 11123, - .tWHR = 110, - .tAR = 10, -}; - -static struct pxa3xx_nand_flash samsung512MbX16 = { - .timing = &samsung512MbX16_timing, - .cmdset = &smallpage_cmdset, - .page_per_block = 32, - .page_size = 512, - .flash_width = 16, - .dfc_width = 16, - .num_blocks = 4096, - .chip_id = 0x46ec, -}; - -static struct pxa3xx_nand_flash samsung2GbX8 = { - .timing = &samsung512MbX16_timing, - .cmdset = &smallpage_cmdset, - .page_per_block = 64, - .page_size = 2048, - .flash_width = 8, - .dfc_width = 8, - .num_blocks = 2048, - .chip_id = 0xdaec, -}; - -static struct pxa3xx_nand_flash samsung32GbX8 = { - .timing = &samsung512MbX16_timing, - .cmdset = &smallpage_cmdset, - .page_per_block = 128, - .page_size = 4096, - .flash_width = 8, - .dfc_width = 8, - .num_blocks = 8192, - .chip_id = 0xd7ec, -}; - -static struct pxa3xx_nand_timing micron_timing = { - .tCH = 10, - .tCS = 25, - .tWH = 15, - .tWP = 25, - .tRH = 15, - .tRP = 30, - .tR = 25000, - .tWHR = 60, - .tAR = 10, -}; - -static struct pxa3xx_nand_flash micron1GbX8 = { - .timing = µn_timing, - .cmdset = &largepage_cmdset, - .page_per_block = 64, - .page_size = 2048, - .flash_width = 8, - .dfc_width = 8, - .num_blocks = 1024, - .chip_id = 0xa12c, -}; + uint32_t reg_ndcr; + uint32_t timing0; + uint32_t timing1; + uint32_t col_addr_cycles; + uint32_t row_addr_cycles; -static struct pxa3xx_nand_flash micron1GbX16 = { - .timing = µn_timing, - .cmdset = &largepage_cmdset, - .page_per_block = 64, - .page_size = 2048, - .flash_width = 16, - .dfc_width = 16, - .num_blocks = 1024, - .chip_id = 0xb12c, -}; + /* calculated from pxa3xx_nand_flash data */ + size_t oob_size; + size_t read_id_bytes; -static struct pxa3xx_nand_flash micron4GbX8 = { - .timing = µn_timing, - .cmdset = &largepage_cmdset, - .page_per_block = 64, - .page_size = 2048, - .flash_width = 8, - .dfc_width = 8, - .num_blocks = 4096, - .chip_id = 0xdc2c, + /* use HW ECC ? */ + /* 0:off, 1:Hammin ECC 2: BCH ECC */ + uint16_t use_ecc; }; -static struct pxa3xx_nand_flash micron4GbX16 = { - .timing = µn_timing, - .cmdset = &largepage_cmdset, - .page_per_block = 64, - .page_size = 2048, - .flash_width = 16, - .dfc_width = 16, - .num_blocks = 4096, - .chip_id = 0xcc2c, -}; +struct pxa3xx_nand { + struct clk *clk; + void __iomem *mmio_base; + struct nand_hw_control controller; -static struct pxa3xx_nand_timing stm2GbX16_timing = { - .tCH = 10, - .tCS = 35, - .tWH = 15, - .tWP = 25, - .tRH = 15, - .tRP = 25, - .tR = 25000, - .tWHR = 60, - .tAR = 10, -}; + /* 2 chipselects supported for the moment */ + int chip_select; + int enable_arbiter; + int RD_CNT_DEL; + int wait_mode; + struct mtd_info *mtd[NUM_CHIP_SELECT]; -static struct pxa3xx_nand_flash stm2GbX16 = { - .timing = &stm2GbX16_timing, - .cmdset = &largepage_cmdset, - .page_per_block = 64, - .page_size = 2048, - .flash_width = 16, - .dfc_width = 16, - .num_blocks = 2048, - .chip_id = 0xba20, -}; + /* relate to the command */ + unsigned int state; + unsigned int command; + unsigned int is_write; + unsigned int is_ready; + uint16_t use_ecc; + unsigned int bad_count; + unsigned int errcode; + struct completion cmd_complete; -static struct pxa3xx_nand_flash *builtin_flash_types[] = { - &samsung512MbX16, - &samsung2GbX8, - &samsung32GbX8, - µn1GbX8, - µn1GbX16, - µn4GbX8, - µn4GbX16, - &stm2GbX16, + /* DMA information */ + int use_dma; + int drcmr_dat; + int drcmr_cmd; + int data_dma_ch; + size_t data_buff_size; }; -#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */ - -#define NDTR0_tCH(c) (min((c), 7) << 19) -#define NDTR0_tCS(c) (min((c), 7) << 16) -#define NDTR0_tWH(c) (min((c), 7) << 11) -#define NDTR0_tWP(c) (min((c), 7) << 8) -#define NDTR0_tRH(c) (min((c), 7) << 3) -#define NDTR0_tRP(c) (min((c), 7) << 0) - -#define NDTR1_tR(c) (min((c), 65535) << 16) -#define NDTR1_tWHR(c) (min((c), 15) << 4) -#define NDTR1_tAR(c) (min((c), 15) << 0) - -#define tCH_NDTR0(r) (((r) >> 19) & 0x7) -#define tCS_NDTR0(r) (((r) >> 16) & 0x7) -#define tWH_NDTR0(r) (((r) >> 11) & 0x7) -#define tWP_NDTR0(r) (((r) >> 8) & 0x7) -#define tRH_NDTR0(r) (((r) >> 3) & 0x7) -#define tRP_NDTR0(r) (((r) >> 0) & 0x7) - -#define tR_NDTR1(r) (((r) >> 16) & 0xffff) -#define tWHR_NDTR1(r) (((r) >> 4) & 0xf) -#define tAR_NDTR1(r) (((r) >> 0) & 0xf) - -/* convert nano-seconds to nand flash controller clock cycles */ -#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) -/* convert nand flash controller clock cycles to nano-seconds */ -#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000)) - -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_timing *t) +static inline int is_buf_blank(uint8_t *buf, size_t len) { - unsigned long nand_clk = clk_get_rate(info->clk); - uint32_t ndtr0, ndtr1; - - ndtr0 = NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) | - NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) | - NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) | - NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) | - NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) | - NDTR0_tRP(ns2cycle(t->tRP, nand_clk)); - - ndtr1 = NDTR1_tR(ns2cycle(t->tR, nand_clk)) | - NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | - NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - - nand_writel(info, NDTR0CS0, ndtr0); - nand_writel(info, NDTR1CS0, ndtr1); + for (; len > 0; len--) + if (*buf++ != 0xff) + return 0; + return 1; } -#define WAIT_EVENT_TIMEOUT 10 - -static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) +static void disable_int(struct pxa3xx_nand *nand, uint32_t int_mask) { - int timeout = WAIT_EVENT_TIMEOUT; - uint32_t ndsr; - - while (timeout--) { - ndsr = nand_readl(info, NDSR) & NDSR_MASK; - if (ndsr & event) { - nand_writel(info, NDSR, ndsr); - return 0; - } - udelay(10); - } + uint32_t ndcr; - return -ETIMEDOUT; + ndcr = nand_readl(nand, NDCR); + nand_writel(nand, NDCR, ndcr | int_mask); } -static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, - uint16_t cmd, int column, int page_addr) +static void nand_error_dump(struct pxa3xx_nand *nand) { - const struct pxa3xx_nand_flash *f = info->flash_info; - const struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + struct mtd_info *mtd = nand->mtd[nand->chip_select]; + struct pxa3xx_nand_info *info = mtd->priv; + int i; - /* calculate data size */ - switch (f->page_size) { - case 2048: - info->data_size = (info->use_ecc) ? 2088 : 2112; + if (no_error_dump) + return; + + printk(KERN_ERR "NAND controller state wrong!!!\n"); + printk(KERN_ERR "state %x, current seqs %d, errcode %x, bad count %d\n", + nand->state, info->current_cmd_seqs, + nand->errcode, nand->bad_count); + printk(KERN_ERR "Totally %d command for sending\n", + info->total_cmds); + for (i = 0; i < info->total_cmds; i ++) + printk(KERN_ERR "NDCB0:%d: %x; NDCB3:%d, %x\n", + i, info->ndcb0[i], i, info->ndcb3[i]); + printk(KERN_ERR "NDCB1: %x; NDCB2 %x\n", info->ndcb1, info->ndcb2); + + printk(KERN_ERR "\nRegister DUMPing ##############\n"); + printk(KERN_ERR "NDCR %x\n" + "NDSR %x\n" + "NDCB0 %x\n" + "NDCB1 %x\n" + "NDCB2 %x\n" + "NDTR0CS0 %x\n" + "NDTR1CS0 %x\n" + "NDBBR0 %x\n" + "NDBBR1 %x\n" + "NDREDEL %x\n" + "NDECCCTRL %x\n" + "NDBZCNT %x\n\n", + nand_readl(nand, NDCR), + nand_readl(nand, NDSR), + nand_readl(nand, NDCB0), + nand_readl(nand, NDCB1), + nand_readl(nand, NDCB2), + nand_readl(nand, NDTR0CS0), + nand_readl(nand, NDTR1CS0), + nand_readl(nand, NDBBR0), + nand_readl(nand, NDBBR1), + nand_readl(nand, NDREDEL), + nand_readl(nand, NDECCCTRL), + nand_readl(nand, NDBZCNT)); +} + +/* + * This function shows the real timing when NAND controller + * send signal to the NAND chip. + */ +static void show_real_timing(uint32_t ndtr0, uint32_t ndtr1, unsigned long nand_clk) +{ + uint32_t rtADL, rtCH, rtCS, rtWH, rtWP, rtRH, rtRP; + uint32_t rtR, rtRHW, rtWHR, rtAR, tmp; + + rtCH = ((ndtr0 >> 19) & 0x7) + 1; + rtCS = ((ndtr0 >> 16) & 0x7) + 1; + rtWH = ((ndtr0 >> 11) & 0x7) + 1; + rtWP = ((ndtr0 >> 8) & 0x7) + 1; + rtADL= (ndtr0 >> 27) & 0x1f; + rtRH = ((ndtr0 >> 3) & 0x7) + 1; + rtRP = (ndtr0 & NDTR0_ETRP) ? ((0x8 | (ndtr0 & 0x7)) + 1) + : ((ndtr0 & 0x7) + 1); + rtRHW = (ndtr1 >> 8) & 0x3; + rtWHR = (ndtr1 >> 4) & 0xf; + rtAR = ndtr1 & 0xf; + + if (rtADL != 0) + rtADL -= 3 + rtWP; + rtR = (ndtr1 >> 16) & 0xffff; + if (ndtr1 & NDTR1_PRESCALE) + rtR *= 16; + + rtR += rtCH + 2; + switch(rtRHW) { + case 0: + rtRHW = 0; break; - case 512: - info->data_size = (info->use_ecc) ? 520 : 528; + case 1: + rtRHW = 16; + break; + case 2: + rtRHW = 32; + break; + case 3: + rtRHW = 48; break; - default: - return -EINVAL; } - /* generate values for NDCBx registers */ - info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); - info->ndcb1 = 0; - info->ndcb2 = 0; - info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles + info->col_addr_cycles); - - if (info->col_addr_cycles == 2) { - /* large block, 2 cycles for column address - * row address starts from 3rd cycle - */ - info->ndcb1 |= page_addr << 16; - if (info->row_addr_cycles == 3) - info->ndcb2 = (page_addr >> 16) & 0xff; - } else - /* small block, 1 cycles for column address - * row address starts from 2nd cycle - */ - info->ndcb1 = page_addr << 8; - - if (cmd == cmdset->program) - info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; - - return 0; -} + /* + * TWHR delay=max(tAR, max(0, tWHR-max(tWH, tCH))) + * TAR delay=max(tAR, max(0, tWHR-max(tWH, tCH))) + 2 + */ + if (rtWH > rtCH) + tmp = rtWH - 1; + else + tmp = rtCH - 1; + if (rtWHR < tmp) + rtWHR = rtAR; + else { + if (rtAR > (rtWHR - tmp)) + rtWHR = rtAR; + else + rtWHR = rtWHR - tmp; + } + rtAR = rtWHR + 2; + printk("Shows real timing(ns):\n"); + if (ndtr0 & NDTR0_SELCNTR) + printk("NDTR0 SELCNTR is set\n"); + else + printk("NDTR0 SELCNTR is not set\n"); + if (ndtr0 & NDTR0_RD_CNT_DEL_MASK) + printk("Read Strobe delay is %d\n", + (ndtr0 & NDTR0_RD_CNT_DEL_MASK) >> 22); + else + printk("No Read Stobe delay\n"); + if (ndtr0 & NDTR0_sel_NRE_EDGE) + printk("Controller is using rising edge to detect RE\n"); + else + printk("Controller is using falling edge to detect RE\n"); -static int prepare_erase_cmd(struct pxa3xx_nand_info *info, - uint16_t cmd, int page_addr) -{ - info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); - info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); - info->ndcb1 = page_addr; - info->ndcb2 = 0; - return 0; + if (ndtr1 & NDTR1_WAIT_MODE) + printk("NDTR1 wait mode is set\n"); + else + printk("NDTR1 wait mode is not set\n"); + + printk("TADL is %ld TCH is %ld TCS is %ld TWH is %ld TWP is %ld TRH is %ld " + "TRP is %ld TR is %ld TRHW is %ld TWHR is %ld TAR is %ld\n", + cycle2ns(rtADL, nand_clk), cycle2ns(rtCH, nand_clk), + cycle2ns(rtCS, nand_clk), cycle2ns(rtWH, nand_clk), + cycle2ns(rtWP, nand_clk), cycle2ns(rtRH, nand_clk), + cycle2ns(rtRP, nand_clk), cycle2ns(rtR, nand_clk), + cycle2ns(rtRHW, nand_clk), cycle2ns(rtWHR, nand_clk), + cycle2ns(rtAR, nand_clk)); } -static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, + const struct pxa3xx_nand_timing *t, int show_timing) { - const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; - - info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); - info->ndcb1 = 0; - info->ndcb2 = 0; - - if (cmd == cmdset->read_id) { - info->ndcb0 |= NDCB0_CMD_TYPE(3); - info->data_size = 8; - } else if (cmd == cmdset->read_status) { - info->ndcb0 |= NDCB0_CMD_TYPE(4); - info->data_size = 8; - } else if (cmd == cmdset->reset || cmd == cmdset->lock || - cmd == cmdset->unlock) { - info->ndcb0 |= NDCB0_CMD_TYPE(5); - } else - return -EINVAL; + struct pxa3xx_nand *nand = info->nand_data; + unsigned long nand_clk = clk_get_rate(nand->clk); + uint32_t ndtr0, ndtr1, tRP, tR, tRHW, tADL; + + if (!info->timing0 && !info->timing1) { + ndtr0 = ndtr1 = 0; + tRP = ns2cycle(t->tRP, nand_clk); + tRP = (tRP > 0xf) ? 0xf : tRP; + if (tRP > 0x7) { + ndtr0 |= NDTR0_ETRP; + tRP -= 0x7; + } + tR = ns2cycle(t->tR, nand_clk); + if (tR > 0xffff) { + ndtr1 |= NDTR1_PRESCALE; + tR /= 16; + } + if (t->tRHW > 0) { + tRHW = ns2cycle(t->tRHW, nand_clk); + if (tRHW < 16) + tRHW = 1; + else { + if (tRHW < 32) + tRHW = 2; + else + tRHW = 3; + } + } + else + tRHW = 0; + tADL = (t->tADL > 0) ? ns2cycle(t->tADL, nand_clk) : 0; + + if (nand->RD_CNT_DEL > 0) + ndtr0 |= NDTR0_SELCNTR + | (NDTR0_RD_CNT_DEL(nand->RD_CNT_DEL - 1)); + + ndtr0 |= NDTR0_tADL(tADL) + | NDTR0_tCH(ns2cycle(t->tCH, nand_clk)) + | NDTR0_tCS(ns2cycle(t->tCS, nand_clk)) + | NDTR0_tWH(ns2cycle(t->tWH, nand_clk)) + | NDTR0_tWP(ns2cycle(t->tWP, nand_clk)) + | NDTR0_tRH(ns2cycle(t->tRH, nand_clk)) + | NDTR0_tRP(tRP) + | NDTR0_SELCNTR; + + if (nand->wait_mode) + ndtr1 |= NDTR1_WAIT_MODE; + + ndtr1 |= NDTR1_tR(tR) + | NDTR1_tRHW(tRHW) + | NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) + | NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); + + info->timing0 = ndtr0; + info->timing1 = ndtr1; + if (show_timing) + show_real_timing(ndtr0, ndtr1, nand_clk); + } - return 0; + nand_writel(nand, NDTR0CS0, info->timing0); + nand_writel(nand, NDTR1CS0, info->timing1); + nand_writel(nand, NDREDEL, 0x0); } -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info, int oob_enable) { - uint32_t ndcr; + const struct pxa3xx_nand_flash *flash_info = info->flash_info; - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~int_mask); -} + if (likely(flash_info->page_size >= PAGE_CHUNK_SIZE)) { + info->data_size = 2048; + if (!oob_enable) { + info->oob_size = 0; + return; + } -static void disable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) -{ - uint32_t ndcr; + switch (info->use_ecc) { + case ECC_HAMMIN: + info->oob_size = 40; + break; + case ECC_BCH: + info->oob_size = 32; + break; + default: + info->oob_size = 64; + break; + } + } + else { + info->data_size = 512; + if (!oob_enable) { + info->oob_size = 0; + return; + } - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr | int_mask); + switch (info->use_ecc) { + case ECC_HAMMIN: + info->oob_size = 8; + break; + case ECC_BCH: + printk("Don't support BCH on small" + " page device!!!\n"); + break; + default: + info->oob_size = 16; + break; + } + } } -/* NOTE: it is a must to set ND_RUN firstly, then write command buffer - * otherwise, it does not work +/* NOTE: it is a must to set ND_RUN firstly, then write + * command buffer, otherwise, it does not work */ -static int write_cmd(struct pxa3xx_nand_info *info) +static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) { - uint32_t ndcr; - - /* clear status bits and run */ - nand_writel(info, NDSR, NDSR_MASK); + uint32_t ndcr, ndeccctrl; + struct pxa3xx_nand *nand = info->nand_data; ndcr = info->reg_ndcr; + ndeccctrl = 0; + + switch (info->use_ecc) { + case ECC_BCH: + ndeccctrl |= NDECCCTRL_BCH_EN; + ndeccctrl |= NDECCCTRL_ECC_THRESH(BCH_THRESHOLD); + case ECC_HAMMIN: + ndcr |= NDCR_ECC_EN; + break; + default: + break; + } - ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; - ndcr |= info->use_dma ? NDCR_DMA_EN : 0; + ndcr |= nand->use_dma ? NDCR_DMA_EN : NDCR_STOP_ON_UNCOR; ndcr |= NDCR_ND_RUN; - nand_writel(info, NDCR, ndcr); + DBG_NAND(printk("@@@ndcr set: %x, ndeccctrl set %x\n", + ndcr, ndeccctrl)); + /* clear status bits and run */ + nand_writel(nand, NDCR, 0); + nand_writel(nand, NDECCCTRL, ndeccctrl); + nand_writel(nand, NDSR, NDSR_MASK); + nand_writel(nand, NDCR, ndcr); +} - if (wait_for_event(info, NDSR_WRCMDREQ)) { - printk(KERN_ERR "timed out writing command\n"); - return -ETIMEDOUT; +static void pxa3xx_nand_stop(struct pxa3xx_nand* nand) +{ + uint32_t ndcr; + int timeout = NAND_STOP_DELAY; + + /* wait RUN bit in NDCR become 0 */ + do { + /* clear status bits */ + nand_writel(nand, NDSR, NDSR_MASK); + ndcr = nand_readl(nand, NDCR); + udelay(1); + } while ((ndcr & NDCR_ND_RUN) && (timeout -- > 0)); + + if (timeout <= 0) { + if (!no_error_dump) + printk(KERN_ERR "NAND controller unable to stop," + "please reconfigure your timing!!!\n"); + nand_error_dump(nand); + ndcr &= ~(NDCR_ND_RUN); + nand_writel(nand, NDCR, ndcr); } - - nand_writel(info, NDCB0, info->ndcb0); - nand_writel(info, NDCB0, info->ndcb1); - nand_writel(info, NDCB0, info->ndcb2); - return 0; } -static int handle_data_pio(struct pxa3xx_nand_info *info) +static void start_data_dma(struct pxa3xx_nand *nand, int dir_out, int cmd_seqs) { - int ret, timeout = CHIP_DELAY_TIMEOUT; + struct mtd_info *mtd = nand->mtd[nand->chip_select]; + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa_dma_desc *desc = info->data_desc; + int dma_len = ALIGN(info->ndcb3[cmd_seqs] + info->oob_size, 32); - switch (info->state) { - case STATE_PIO_WRITING: - __raw_writesl(info->mmio_base + NDDB, info->data_buff, - DIV_ROUND_UP(info->data_size, 4)); + desc->ddadr = DDADR_STOP; + desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 + | DCMD_BURST32 | dma_len; - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); + if (dir_out) { + desc->dsadr = info->data_buff_phys + info->data_column; + desc->dtadr = NDDB_DMA_ADDR; - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); - if (!ret) { - printk(KERN_ERR "program command time out\n"); - return -1; - } - break; - case STATE_PIO_READING: - __raw_readsl(info->mmio_base + NDDB, info->data_buff, - DIV_ROUND_UP(info->data_size, 4)); - break; - default: - printk(KERN_ERR "%s: invalid state %d\n", __func__, - info->state); - return -EINVAL; + desc->dcmd |= DCMD_INCSRCADDR + | DCMD_FLOWTRG; + } else { + desc->dtadr = info->data_buff_phys + info->data_column; + desc->dsadr = NDDB_DMA_ADDR; + + desc->dcmd |= DCMD_INCTRGADDR + | DCMD_FLOWSRC; } - info->state = STATE_READY; - return 0; + DBG_NAND(printk("DMA START:DMA dcmd %x, dsadr %x, dtadr %x, len %x\n", + desc->dcmd, desc->dsadr, desc->dtadr, dma_len)); + DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch; + DDADR(nand->data_dma_ch) = info->data_desc_addr; + DCSR(nand->data_dma_ch) |= DCSR_RUN; + + return; } -static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) +static void handle_data_pio(struct pxa3xx_nand *nand, int cmd_seqs) { - struct pxa_dma_desc *desc = info->data_desc; - int dma_len = ALIGN(info->data_size, 32); + void *mmio_base = nand->mmio_base; + struct mtd_info *mtd = nand->mtd[nand->chip_select]; + struct pxa3xx_nand_info *info = mtd->priv; - desc->ddadr = DDADR_STOP; - desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; + DBG_NAND(printk("data col %x, size %x, oob col %x, size %x\n", + info->data_column, info->ndcb3[cmd_seqs], + info->oob_column, info->oob_size)); + if (nand->is_write) { + if (info->oob_size > 0) { + /* write data part */ + __raw_writesl(mmio_base + NDDB, \ + info->data_buff + info->data_column, \ + info->ndcb3[cmd_seqs] >> 2); + + /* write oob part */ + __raw_writesl(mmio_base + NDDB, \ + info->oob_buff + info->oob_column, \ + info->oob_size >> 2); + } + else + __raw_writesl(mmio_base + NDDB, \ + info->data_buff + info->data_column, \ + info->ndcb3[cmd_seqs] >> 2); + } + else { + if (info->oob_size > 0) { + /* read data part */ + __raw_readsl(mmio_base + NDDB, \ + info->data_buff + info->data_column, \ + info->ndcb3[cmd_seqs] >> 2); + + /* read oob part */ + __raw_readsl(mmio_base + NDDB, \ + info->oob_buff + info->oob_column, \ + info->oob_size >> 2); + } + else + __raw_readsl(mmio_base + NDDB, \ + info->data_buff + info->data_column, \ + info->ndcb3[cmd_seqs] >> 2); - if (dir_out) { - desc->dsadr = info->data_buff_phys; - desc->dtadr = info->mmio_phys + NDDB; - desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; - } else { - desc->dtadr = info->data_buff_phys; - desc->dsadr = info->mmio_phys + NDDB; - desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; } - DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; - DDADR(info->data_dma_ch) = info->data_desc_addr; - DCSR(info->data_dma_ch) |= DCSR_RUN; + info->data_column += info->ndcb3[cmd_seqs]; + info->oob_column += info->oob_size; } static void pxa3xx_nand_data_dma_irq(int channel, void *data) { - struct pxa3xx_nand_info *info = data; - uint32_t dcsr; + struct pxa3xx_nand *nand= data; + struct mtd_info *mtd = nand->mtd[nand->chip_select]; + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand_flash *flash_info = info->flash_info; + uint32_t dcsr, ndcr; + int i, tmp; dcsr = DCSR(channel); DCSR(channel) = dcsr; + DBG_NAND(printk("DMA IRQ: dcsr %x\n", dcsr)); if (dcsr & DCSR_BUSERR) { - info->retcode = ERR_DMABUSERR; - complete(&info->cmd_complete); + nand->errcode |= ERR_DMABUSERR; } - if (info->state == STATE_DMA_WRITING) { - info->state = STATE_DMA_DONE; - enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - } else { - info->state = STATE_READY; - complete(&info->cmd_complete); + /* + * Here we need a workaround as flash layout is not our need + * Should notice this is only applied to 4K page size NAND + * In the first step, copy the first oob to the last part + * then kick the second chunk data next to the first data. + * After this, the two oob chunk would be at wrong order, + * that is also why we need to swap those two parts. + */ + if (flash_info->page_size > PAGE_CHUNK_SIZE && info->oob_size > 0) { + if (info->data_column == 0) + memcpy(info->oob_buff + info->oob_size, info->data_buff + + info->oob_size, info->oob_size); + else + for (i = 0; i < info->oob_size; i ++) { + tmp = info->oob_buff[i]; + info->oob_buff[i] = info->oob_buff[info->oob_size + i]; + info->oob_buff[info->oob_size + i] = tmp; + } } + + info->data_column += info->ndcb3[info->current_cmd_seqs - 1]; + ndcr = nand_readl(nand, NDCR); + ndcr &= ~NDCR_INT_MASK; + nand_writel(nand, NDCR, ndcr); + nand_writel(nand, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); } static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { - struct pxa3xx_nand_info *info = devid; + struct pxa3xx_nand *nand = devid; + struct pxa3xx_nand_info *info; + struct mtd_info *mtd; unsigned int status; + int chip_select, cmd_done, ready, page_done, badblock_detect; + int cmd_seqs, ndcb1, ndcb2, ndcr, is_completed = 0; + + chip_select = nand->chip_select; + ready = (chip_select) ? NDSR_RDY : NDSR_FLASH_RDY; + cmd_done = (chip_select) ? NDSR_CS1_CMDD : NDSR_CS0_CMDD; + page_done = (chip_select) ? NDSR_CS1_PAGED : NDSR_CS0_PAGED; + badblock_detect = (chip_select) ? NDSR_CS1_BBD : NDSR_CS0_BBD; + mtd = nand->mtd[chip_select]; + info = (struct pxa3xx_nand_info *)(mtd->priv); + cmd_seqs = info->current_cmd_seqs; + + status = nand_readl(nand, NDSR); + nand->bad_count = (status & NDSR_ERR_CNT_MASK) >> 16; + DBG_NAND(if (status != 0) + printk("\t\tcmd seqs %d, status %x\n", cmd_seqs, status)); + if (no_error_dump) + goto ERR_IRQ_EXIT; + + if (status & NDSR_TRUSTVIO) + nand->errcode |= ERR_TRUSTVIO; + + if (status & NDSR_CORERR) + nand->errcode |= ERR_CORERR; + + if (status & NDSR_UNCERR) + nand->errcode |= ERR_DBERR; + + if (status & badblock_detect) + nand->errcode |= ERR_BBERR; + + if ((status & NDSR_WRDREQ) || (status & NDSR_RDDREQ)) { + + nand->state |= STATE_DATA_PROCESSING; + /* whether use dma to transfer data */ + if (nand->use_dma) { + ndcr = nand_readl(nand, NDCR); + ndcr |= NDCR_INT_MASK; + nand_writel(nand, NDCR, ndcr); + start_data_dma(nand, nand->is_write, cmd_seqs - 1); + goto NORMAL_IRQ_EXIT; + } + else + handle_data_pio(nand, cmd_seqs - 1); - status = nand_readl(info, NDSR); + nand->state |= STATE_DATA_DONE; + } - if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { - if (status & NDSR_DBERR) - info->retcode = ERR_DBERR; - else if (status & NDSR_SBERR) - info->retcode = ERR_SBERR; + if (status & cmd_done) { + nand->state |= STATE_CMD_DONE; - disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); + /* complete the command cycle when all command + * done, and don't wait for ready signal + */ + if ((cmd_seqs == info->total_cmds) \ + && !(cmd_seqs == info->need_wait_ready)) { - if (info->use_dma) { - info->state = STATE_DMA_READING; - start_data_dma(info, 0); - } else { - info->state = STATE_PIO_READING; - complete(&info->cmd_complete); + is_completed = 1; } - } else if (status & NDSR_WRDREQ) { - disable_int(info, NDSR_WRDREQ); - if (info->use_dma) { - info->state = STATE_DMA_WRITING; - start_data_dma(info, 1); - } else { - info->state = STATE_PIO_WRITING; - complete(&info->cmd_complete); + } + + if (status & ready) { + nand->state |= STATE_READY; + /* + * wait for the ready signal, + * then leavl the command cycle + */ + if ((cmd_seqs == info->total_cmds) \ + && (cmd_seqs == info->need_wait_ready)) { + + is_completed = 1; } - } else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { - if (status & NDSR_CS0_BBD) - info->retcode = ERR_BBERR; - disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - info->state = STATE_READY; - complete(&info->cmd_complete); + nand->is_ready = 1; } - nand_writel(info, NDSR, status); - return IRQ_HANDLED; -} -static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) -{ - uint32_t ndcr; - int ret, timeout = CHIP_DELAY_TIMEOUT; + if (status & page_done) + nand->state |= STATE_PAGE_DONE; - if (write_cmd(info)) { - info->retcode = ERR_SENDCMD; - goto fail_stop; + if (nand->errcode != ERR_NONE && !nand->use_dma) { + is_completed = 1; + goto ERR_IRQ_EXIT; } - info->state = STATE_CMD_HANDLE; + if (status & NDSR_WRCMDREQ) { + nand_writel(nand, NDSR, NDSR_WRCMDREQ); + status &= ~NDSR_WRCMDREQ; + if (cmd_seqs < info->total_cmds) { - enable_int(info, event); + info->current_cmd_seqs ++; + if (cmd_seqs == 0) { + ndcb1 = info->ndcb1; + ndcb2 = info->ndcb2; + } + else { + ndcb1 = 0; + ndcb2 = 0; + } - ret = wait_for_completion_timeout(&info->cmd_complete, timeout); - if (!ret) { - printk(KERN_ERR "command execution timed out\n"); - info->retcode = ERR_SENDCMD; - goto fail_stop; - } + nand->state = STATE_CMD_WAIT_DONE; + nand_writel(nand, NDCB0, info->ndcb0[cmd_seqs]); + nand_writel(nand, NDCB0, ndcb1); + nand_writel(nand, NDCB0, ndcb2); + if (info->need_additional_addressing) { + nand_writel(nand, NDCB0, info->ndcb3[cmd_seqs]); + DBG_NAND(printk("\tndcb0 %x ndcb1 %x, " + "ndcb2 %x, ndcb3 %x\n", + info->ndcb0[cmd_seqs], + ndcb1, ndcb2, info->ndcb3[cmd_seqs])); + } + else { + DBG_NAND(printk("\tndcb0 %x ndcb1 %x ndcb2 %x\n", + info->ndcb0[cmd_seqs], + ndcb1, ndcb2)); + } + } + else + is_completed = 1; - if (info->use_dma == 0 && info->data_size > 0) - if (handle_data_pio(info)) - goto fail_stop; + } - return 0; +ERR_IRQ_EXIT: + /* clear NDSR to let the controller exit the IRQ */ + nand_writel(nand, NDSR, status); + if (is_completed) + complete(&nand->cmd_complete); -fail_stop: - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - udelay(10); - return -ETIMEDOUT; +NORMAL_IRQ_EXIT: + return IRQ_HANDLED; } static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) { struct pxa3xx_nand_info *info = mtd->priv; - return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0; -} + struct pxa3xx_nand *nand = info->nand_data; + int ready_mask = (nand->chip_select) + ? NDSR_RDY : NDSR_FLASH_RDY; -static inline int is_buf_blank(uint8_t *buf, size_t len) -{ - for (; len > 0; len--) - if (*buf++ != 0xff) - return 0; - return 1; + return (nand_readl(nand, NDSR) & ready_mask) ? 1 : 0; } -static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, - int column, int page_addr) +static int prepare_command_pool(struct pxa3xx_nand *nand, int command, + uint16_t column, int page_addr) { + uint16_t cmd; + int addr_cycle, exec_cmd, ndcb0, ndcb3 = 0, i, chunks = 0, ecc_strength; + struct mtd_info *mtd = nand->mtd[nand->chip_select]; struct pxa3xx_nand_info *info = mtd->priv; + struct nand_chip *chip = mtd->priv; const struct pxa3xx_nand_flash *flash_info = info->flash_info; - const struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset; - int ret; - - info->use_dma = (use_dma) ? 1 : 0; - info->use_ecc = 0; - info->data_size = 0; - info->state = STATE_READY; - init_completion(&info->cmd_complete); + ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0; + addr_cycle = 0; + exec_cmd = 1; + + /* reset data and oob column point to handle data */ + info->data_column = 0; + info->oob_column = 0; + info->buf_start = 0; + info->buf_count = 0; + info->current_cmd_seqs = 0; + info->need_wait_ready = -1; + info->oob_size = 0; + info->use_ecc = ECC_NONE; + + nand->state = 0; + nand->is_write = 0; + nand->is_ready = 0; + nand->errcode = ERR_NONE; + nand->bad_count = 0; + nand->command = command; switch (command) { + case NAND_CMD_READ0: + case NAND_CMD_PAGEPROG: + if (chip->ecc.mode == NAND_ECC_HW) + info->use_ecc = flash_info->ecc_type; + case NAND_CMD_READOOB: - /* disable HW ECC to get all the OOB data */ - info->buf_count = mtd->writesize + mtd->oobsize; - info->buf_start = mtd->writesize + column; - memset(info->data_buff, 0xFF, info->buf_count); + ecc_strength = (command == NAND_CMD_READOOB) ? 1 + : flash_info->ecc_strength; + if (ecc_strength > 1) { + info->reg_ndcr &= ~NDCR_SPARE_EN; + ndcb0 |= NDCB0_LEN_OVRD; + } + else + info->reg_ndcr |= NDCR_SPARE_EN; - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) - break; + pxa3xx_set_datasize(info, info->reg_ndcr & NDCR_SPARE_EN); + chunks = flash_info->page_size / info->data_size; + chunks = chunks * ecc_strength; + ndcb3 = info->data_size / ecc_strength; + break; + case NAND_CMD_SEQIN: + exec_cmd = 0; + break; + default: + info->ndcb1 = 0; + info->ndcb2 = 0; + break; + } - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); + /* clear the command buffer */ + for (i = 0; i < CMD_POOL_SIZE; i ++) + info->ndcb0[i] = ndcb0; + addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles + + info->col_addr_cycles); - /* We only are OOB, so if the data has error, does not matter */ - if (info->retcode == ERR_DBERR) - info->retcode = ERR_NONE; - break; + if ((ndcb0 & NDCB0_LEN_OVRD) + || ((info->row_addr_cycles + info->col_addr_cycles) > 5)) { + info->need_additional_addressing = 1; + } + else + info->need_additional_addressing = 0; + switch (command) { + case NAND_CMD_READOOB: case NAND_CMD_READ0: - info->use_ecc = 1; - info->retcode = ERR_NONE; - info->buf_start = column; - info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xFF, info->buf_count); - if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) - break; + cmd = flash_info->cmdset->read1; - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); + if (command == NAND_CMD_READOOB) { + if (!(info->reg_ndcr & NDCR_SPARE_EN)) + return 0; - if (info->retcode == ERR_DBERR) { - /* for blank page (all 0xff), HW will calculate its ECC as - * 0, which is different from the ECC information within - * OOB, ignore such double bit errors - */ - if (is_buf_blank(info->data_buff, mtd->writesize)) - info->retcode = ERR_NONE; + info->buf_start = mtd->writesize + column; + for (i = 1; i <= chunks; i ++) + info->ndcb3[i] = info->data_size; } - break; + else { + info->ndcb3[0] = info->data_size; + for (i = 1; i <= chunks; i ++) + info->ndcb3[i] = ndcb3; + + info->buf_start = column; + } + + if (unlikely(flash_info->page_size < PAGE_CHUNK_SIZE)) { + info->total_cmds = 1; + + info->ndcb0[0] |= NDCB0_CMD_TYPE(0) + | addr_cycle + | cmd; + } + else { + info->total_cmds = chunks + 1; + + info->ndcb0[0] |= NDCB0_CMD_XTYPE(0x6) + | NDCB0_CMD_TYPE(0) + | NDCB0_DBC + | NDCB0_NC + | addr_cycle + | cmd; + + info->ndcb0[1] |= NDCB0_CMD_XTYPE(0x5) + | NDCB0_NC + | addr_cycle; + + for (i = 2; i <= chunks; i ++) + info->ndcb0[i] = info->ndcb0[1]; + + info->ndcb0[chunks] &= ~NDCB0_NC; + } + case NAND_CMD_SEQIN: - info->buf_start = column; + /* small page addr setting */ + if (unlikely(flash_info->page_size < PAGE_CHUNK_SIZE)) { + info->ndcb1 = ((page_addr & 0xFFFFFF) << 8) + | (column & 0xFF); + + info->ndcb2 = 0; + } + else { + info->ndcb1 = ((page_addr & 0xFFFF) << 16) + | (column & 0xFFFF); + + if (page_addr & 0xFF0000) + info->ndcb2 = (page_addr & 0xFF0000) >> 16; + else + info->ndcb2 = 0; + } + info->buf_count = mtd->writesize + mtd->oobsize; - memset(info->data_buff, 0xff, info->buf_count); + memset(info->data_buff, 0xFF, info->buf_count); - /* save column/page_addr for next CMD_PAGEPROG */ - info->seqin_column = column; - info->seqin_page_addr = page_addr; break; - case NAND_CMD_PAGEPROG: - info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; - if (prepare_read_prog_cmd(info, cmdset->program, - info->seqin_column, info->seqin_page_addr)) + case NAND_CMD_PAGEPROG: + if (is_buf_blank(info->data_buff, + (mtd->writesize + mtd->oobsize))) { + exec_cmd = 0; break; + } - pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); - break; - case NAND_CMD_ERASE1: - if (prepare_erase_cmd(info, cmdset->erase, page_addr)) - break; + cmd = flash_info->cmdset->program; + + nand->is_write = 1; + info->need_wait_ready = chunks + 1; + + if (unlikely(flash_info->page_size < PAGE_CHUNK_SIZE)) { + info->total_cmds = 1; + info->ndcb0[0] |= NDCB0_CMD_TYPE(0x1) + | NDCB0_AUTO_RS + | NDCB0_ST_ROW_EN + | NDCB0_DBC + | cmd + | addr_cycle; + } + else { + info->total_cmds = chunks + 1; + info->ndcb0[0] |= NDCB0_CMD_XTYPE(0x4) + | NDCB0_CMD_TYPE(0x1) + | NDCB0_NC + | NDCB0_AUTO_RS + | (cmd & NDCB0_CMD1_MASK) + | addr_cycle; + + for (i = 1; i < chunks; i ++) + info->ndcb0[i] |= NDCB0_CMD_XTYPE(0x5) + | NDCB0_NC + | NDCB0_AUTO_RS + | NDCB0_CMD_TYPE(0x1) + | addr_cycle; + + info->ndcb0[chunks] |= NDCB0_CMD_XTYPE(0x3) + | NDCB0_CMD_TYPE(0x1) + | NDCB0_ST_ROW_EN + | NDCB0_DBC + | (cmd & NDCB0_CMD2_MASK) + | NDCB0_CMD1_MASK + | addr_cycle; + } + + if (ndcb3) { + for (i = 0; i < chunks; i ++) + info->ndcb3[i] = ndcb3; + info->ndcb3[i] = info->data_size; + } - pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); - break; - case NAND_CMD_ERASE2: break; + case NAND_CMD_READID: + cmd = flash_info->cmdset->read_id; + nand->use_dma = 0; + info->total_cmds = 1; + info->buf_count = info->read_id_bytes; + + info->ndcb0[0] |= NDCB0_CMD_TYPE(3) + | NDCB0_ADDR_CYC(1) + | cmd; + info->ndcb3[0] = 8; + + break; + case NAND_CMD_STATUS: - info->use_dma = 0; /* force PIO read */ - info->buf_start = 0; - info->buf_count = (command == NAND_CMD_READID) ? - info->read_id_bytes : 1; + cmd = flash_info->cmdset->read_status; + nand->use_dma = 0; + info->total_cmds = 1; + info->buf_count = 1; + info->ndcb0[0] |= NDCB0_CMD_TYPE(4) + | NDCB0_ADDR_CYC(1) + | cmd; + info->ndcb3[0] = 8; - if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? - cmdset->read_id : cmdset->read_status)) - break; + break; + + case NAND_CMD_ERASE1: + cmd = flash_info->cmdset->erase; + info->total_cmds = 1; + info->ndcb0[0] |= NDCB0_CMD_TYPE(2) + | NDCB0_AUTO_RS + | NDCB0_ADDR_CYC(3) + | NDCB0_DBC + | cmd; + info->ndcb1 = page_addr; + info->ndcb2 = 0; - pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); break; case NAND_CMD_RESET: - if (prepare_other_cmd(info, cmdset->reset)) - break; + cmd = flash_info->cmdset->reset; + info->total_cmds = 1; + info->ndcb0[0] |= NDCB0_CMD_TYPE(5) + | cmd; - ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); - if (ret == 0) { - int timeout = 2; - uint32_t ndcr; - - while (timeout--) { - if (nand_readl(info, NDSR) & NDSR_RDY) - break; - msleep(10); - } + break; - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - } + case NAND_CMD_ERASE2: + exec_cmd = 0; break; + default: + exec_cmd = 0; printk(KERN_ERR "non-supported command.\n"); break; } - if (info->retcode == ERR_DBERR) { - printk(KERN_ERR "double bit error @ page %08x\n", page_addr); - info->retcode = ERR_NONE; + nand->use_ecc = info->use_ecc; + return exec_cmd; +} + +static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand *nand = info->nand_data; + const struct pxa3xx_nand_flash *flash_info = info->flash_info; + int ret, exec_cmd, use_dma; + loff_t addr; +#ifdef CONFIG_PXA3XX_BBM + struct pxa3xx_bbm *pxa3xx_bbm = mtd->bbm; + + DBG_NAND(printk("command %x, page %x, ", command, page_addr);); + if (pxa3xx_bbm && (command == NAND_CMD_READOOB + || command == NAND_CMD_READ0 + || command == NAND_CMD_SEQIN + || command == NAND_CMD_ERASE1)) { + + addr = (loff_t)page_addr << mtd->writesize_shift; + addr = pxa3xx_bbm->search(mtd, addr); + page_addr = addr >> mtd->writesize_shift; + } + DBG_NAND(printk("post page %x\n", page_addr)); +#else + DBG_NAND(printk("command %x, page %x\n", command, page_addr);); +#endif + + set_dvfm_constraint(); + + /* reset timing */ + if (nand->chip_select != info->chip_select) { + pxa3xx_nand_set_timing(info, flash_info->timing, 0); + nand->chip_select = info->chip_select; + } + + /* if this is a x16 device ,then convert the input + * "byte" address into a "word" address appropriate + * for indexing a word-oriented device + */ + if (flash_info->flash_width == 16) + column /= 2; + + use_dma = nand->use_dma; + exec_cmd = prepare_command_pool(nand, command, column, page_addr); + if (exec_cmd) { + /* prepare for the first command */ + init_completion(&nand->cmd_complete); + + nand->state |= STATE_CMD_PREPARED; + pxa3xx_nand_start(info); + + ret = wait_for_completion_timeout(&nand->cmd_complete, + CHIP_DELAY_TIMEOUT); + if (!ret) { + printk(KERN_ERR "Wait time out!!!\n"); + nand_error_dump(nand); + nand->errcode |= ERR_SENDCMD; + } + + /* Stop State Machine for next command cycle */ + pxa3xx_nand_stop(nand); + disable_int(nand, NDCR_INT_MASK); + nand->state &= ~STATE_CMD_PREPARED; } + + nand->use_dma = use_dma; + unset_dvfm_constraint(); } static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) @@ -835,7 +1336,9 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd) struct pxa3xx_nand_info *info = mtd->priv; u16 retval = 0xFFFF; - if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { + if (!(info->buf_start & 0x01) \ + && info->buf_start < info->buf_count) { + retval = *((u16 *)(info->data_buff+info->buf_start)); info->buf_start += 2; } @@ -858,330 +1361,452 @@ static void pxa3xx_nand_write_buf(struct mtd_info *mtd, int real_len = min_t(size_t, len, info->buf_count - info->buf_start); memcpy(info->data_buff + info->buf_start, buf, real_len); - info->buf_start += real_len; -} - -static int pxa3xx_nand_verify_buf(struct mtd_info *mtd, - const uint8_t *buf, int len) -{ - return 0; -} - -static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) -{ - return; -} - -static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) -{ - struct pxa3xx_nand_info *info = mtd->priv; - - /* pxa3xx_nand_send_command has waited for command complete */ - if (this->state == FL_WRITING || this->state == FL_ERASING) { - if (info->retcode == ERR_NONE) - return 0; - else { - /* - * any error make it return 0x01 which will tell - * the caller the erase and write fail - */ - return 0x01; - } - } - - return 0; + info->buf_start += real_len; } -static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode) +static int pxa3xx_nand_verify_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) { - return; + return 0; } -static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd, - const uint8_t *dat, uint8_t *ecc_code) +static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip) { - return 0; + return; } -static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, - uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) +/* Error handling expose to MTD level */ +static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) { struct pxa3xx_nand_info *info = mtd->priv; - /* - * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we - * consider it as a ecc error which will tell the caller the - * read fail We have distinguish all the errors, but the - * nand_read_ecc only check this function return value - * - * Corrected (single-bit) errors must also be noted. - */ - if (info->retcode == ERR_SBERR) - return 1; - else if (info->retcode != ERR_NONE) - return -1; + struct pxa3xx_nand *nand = info->nand_data; - return 0; + if (nand->errcode & ERR_TRUSTVIO) { + printk(KERN_ERR "Trust violation!!!\n"); + nand_error_dump(nand); + return NAND_STATUS_FAIL; + } + + if (nand->errcode & (ERR_BBERR | ERR_SENDCMD)) + return NAND_STATUS_FAIL; + else + return 0; } -static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) +static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf) { - const struct pxa3xx_nand_flash *f = info->flash_info; - const struct pxa3xx_nand_cmdset *cmdset = f->cmdset; - uint32_t ndcr; - uint8_t id_buff[8]; + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand *nand = info->nand_data; + + chip->read_buf(mtd, buf, mtd->writesize); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (nand->errcode & ERR_CORERR) { + DBG_NAND(printk("###correctable error detected\n");); + switch (nand->use_ecc) { + case ECC_BCH: + if (nand->bad_count > BCH_THRESHOLD) + mtd->ecc_stats.corrected += + (nand->bad_count - BCH_THRESHOLD); + break; - if (prepare_other_cmd(info, cmdset->read_id)) { - printk(KERN_ERR "failed to prepare command\n"); - return -EINVAL; + case ECC_HAMMIN: + mtd->ecc_stats.corrected ++; + break; + + case ECC_NONE: + default: + break; + } } + else if (nand->errcode & ERR_DBERR) { + int buf_blank; - /* Send command */ - if (write_cmd(info)) - goto fail_timeout; + buf_blank = is_buf_blank(buf, mtd->writesize); - /* Wait for CMDDM(command done successfully) */ - if (wait_for_event(info, NDSR_RDDREQ)) - goto fail_timeout; + if (!buf_blank) { + DBG_NAND(printk("###uncorrectable error!!!\n")); + mtd->ecc_stats.failed++; + } + } - __raw_readsl(info->mmio_base + NDDB, id_buff, 2); - *id = id_buff[0] | (id_buff[1] << 8); return 0; +} -fail_timeout: - ndcr = nand_readl(info, NDCR); - nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); - udelay(10); - return -ETIMEDOUT; +static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, + struct nand_chip *chip, const uint8_t *buf) +{ + chip->write_buf(mtd, buf, mtd->writesize); + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_flash *f) + const struct pxa3xx_nand_flash *f, int show_timing) { - struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; - uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ - - if (f->page_size != 2048 && f->page_size != 512) - return -EINVAL; - - if (f->flash_width != 16 && f->flash_width != 8) - return -EINVAL; + /* enable all interrupts */ + uint32_t ndcr = 0; + struct pxa3xx_nand *nand = info->nand_data; /* calculate flash information */ - info->oob_size = (f->page_size == 2048) ? 64 : 16; - info->read_id_bytes = (f->page_size == 2048) ? 4 : 2; + info->oob_buff = info->data_buff + f->page_size; + info->read_id_bytes = (f->page_size >= 2048) ? 4 : 2; /* calculate addressing information */ - info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; + info->col_addr_cycles = (f->page_size >= 2048) ? 2 : 1; if (f->num_blocks * f->page_per_block > 65536) info->row_addr_cycles = 3; else info->row_addr_cycles = 2; - ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; + ndcr |= (nand->enable_arbiter) ? NDCR_ND_ARB_EN : 0; ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0; - ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0; - ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0; ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; - ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes); - ndcr |= NDCR_SPARE_EN; /* enable spare by default */ + switch (f->page_per_block) { + case 32: + ndcr |= NDCR_PG_PER_BLK(0x0); + break; + case 128: + ndcr |= NDCR_PG_PER_BLK(0x1); + break; + case 256: + ndcr |= NDCR_PG_PER_BLK(0x3); + break; + case 64: + default: + ndcr |= NDCR_PG_PER_BLK(0x2); + break; + } + + switch (f->page_size) { + case 512: + ndcr |= NDCR_PAGE_SZ(0x0); + break; + case 2048: + default: + ndcr |= NDCR_PAGE_SZ(0x1); + ndcr |= NDCR_FORCE_CSX; + break; + + } + ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes); info->reg_ndcr = ndcr; - pxa3xx_nand_set_timing(info, f->timing); + info->timing0 = info->timing1 = 0; + pxa3xx_nand_set_timing(info, f->timing, show_timing); info->flash_info = f; return 0; } -static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info, - struct pxa3xx_nand_timing *t) +static void pxa3xx_erase_cmd(struct mtd_info *mtd, int page) { - unsigned long nand_clk = clk_get_rate(info->clk); - uint32_t ndtr0 = nand_readl(info, NDTR0CS0); - uint32_t ndtr1 = nand_readl(info, NDTR1CS0); - - t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk); - t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk); - t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk); - t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk); - t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk); - t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk); - - t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk); - t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk); - t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk); + struct nand_chip *chip = mtd->priv; + /* Send commands to erase a block */ + chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page); } -static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info, int cs) { - uint32_t ndcr = nand_readl(info, NDCR); - struct nand_flash_dev *type = NULL; - uint32_t id = -1; - int i; + struct pxa3xx_nand *nand = info->nand_data; + const struct pxa3xx_nand_flash *f = &nand_common; + struct mtd_info *mtd = nand->mtd[cs]; - default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32; - default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; - default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8; - default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8; + pxa3xx_nand_config_flash(info, f, 0); + pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0); - if (default_flash.page_size == 2048) - default_flash.cmdset = &largepage_cmdset; + if (nand->is_ready) + return 1; else - default_flash.cmdset = &smallpage_cmdset; + return 0; +} - /* set info fields needed to __readid */ - info->flash_info = &default_flash; - info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2; - info->reg_ndcr = ndcr; +static int pxa3xx_nand_scan_ident(struct mtd_info *mtd, int maxchips) +{ + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand *nand = info->nand_data; + struct pxa3xx_nand_flash *f; + struct nand_chip *chip; + uint32_t id = -1; + int i, ret, chip_select; - if (__readid(info, &id)) - return -ENODEV; + f = builtin_flash_types[0]; + chip_select = info->chip_select; + chip = mtd->priv; + ret = pxa3xx_nand_sensing(info, chip_select); + if (!ret) { + kfree (mtd); + nand->mtd[chip_select] = NULL; + printk(KERN_INFO "There is no nand chip on cs %d!\n", chip_select); + + return -EINVAL; + } + + pxa3xx_nand_cmdfunc(mtd, NAND_CMD_READID, 0, 0); + + id = *((uint16_t *)(info->data_buff)); + + if (id != 0) + printk(KERN_INFO "Detect a flash id %x, cs %x\n", id, chip_select); + else { + kfree(mtd); + nand->mtd[chip_select] = NULL; + printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n"); + + return -EINVAL; + } + + for (i = 1; i < ARRAY_SIZE(builtin_flash_types); i++) { + + f = builtin_flash_types[i]; - /* Lookup the flash id */ - id = (id >> 8) & 0xff; /* device id is byte 2 */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (id == nand_flash_ids[i].id) { - type = &nand_flash_ids[i]; + /* find the chip in default list */ + if (f->chip_id == (id & f->chip_id_mask)) { + pxa3xx_nand_config_flash(info, f, 1); + chip->cellinfo = info->data_buff[2]; + mtd->writesize = f->page_size; + mtd->writesize_shift = ffs(mtd->writesize) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + mtd->oobsize = mtd->writesize / 32; + mtd->erasesize = f->page_size * f->page_per_block; + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + + mtd->name = f->name; break; } } - if (!type) - return -ENODEV; - - /* fill the missing flash information */ - i = __ffs(default_flash.page_per_block * default_flash.page_size); - default_flash.num_blocks = type->chipsize << (20 - i); + if (i == ARRAY_SIZE(builtin_flash_types)) { + kfree(mtd); + nand->mtd[chip_select] = NULL; + printk(KERN_ERR "ERROR!! flash not defined!!!\n"); - info->oob_size = (default_flash.page_size == 2048) ? 64 : 16; + return -EINVAL; + } - /* calculate addressing information */ - info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = f->page_size; + chip->ecc.read_page = pxa3xx_nand_read_page_hwecc; + chip->ecc.write_page = pxa3xx_nand_write_page_hwecc; - if (default_flash.num_blocks * default_flash.page_per_block > 65536) - info->row_addr_cycles = 3; + if (f->page_size == 2048) + chip->ecc.layout = &hw_largepage_ecclayout; else - info->row_addr_cycles = 2; + chip->ecc.layout = &hw_smallpage_ecclayout; + + chip->chipsize = (uint64_t)f->num_blocks * \ + f->page_per_block * \ + f->page_size; + + chip->chip_shift = ffs(chip->chipsize) - 1; + mtd->size = chip->chipsize; + + /* Calculate the address shift from the page size */ + chip->page_shift = ffs(mtd->writesize) - 1; + chip->pagemask = mtd_div_by_ws(chip->chipsize, mtd) - 1; - pxa3xx_nand_detect_timing(info, &default_timing); - default_flash.timing = &default_timing; + chip->numchips = 1; + chip->chip_delay = 25; + chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; + + /* Set the bad block position */ + chip->badblockpos = mtd->writesize > 512 ? + NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + + /* + * Set chip as a default. Board drivers can override it, + * if necessary + */ + chip->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0; + chip->options |= NAND_NO_AUTOINCR; + chip->options |= NAND_NO_READRDY; + chip->options |= NAND_USE_FLASH_BBT; + chip->options |= BBT_RELOCATION_IFBAD; return 0; } -static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, - const struct pxa3xx_nand_platform_data *pdata) +/* the max buff size should be large than + * the largest size of page of NAND flash + * that currently controller support + */ +#define MAX_BUFF_SIZE ((PAGE_CHUNK_SIZE + OOB_CHUNK_SIZE) * 2) + sizeof(struct pxa_dma_desc) + +static struct pxa3xx_nand *alloc_nand_resource(struct platform_device *pdev, + int use_dma) { - const struct pxa3xx_nand_flash *f; - uint32_t id = -1; - int i; + struct pxa3xx_nand_info *info; + struct pxa3xx_nand *nand; + struct mtd_info *mtd; + struct resource *r; + int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc); + int ret, irq, i, chip_select; - if (pdata->keep_config) - if (pxa3xx_nand_detect_config(info) == 0) - return 0; + nand = kzalloc(sizeof(struct pxa3xx_nand), GFP_KERNEL); + if (!nand) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return NULL; + } - for (i = 0; inum_flash; ++i) { - f = pdata->flash + i; + platform_set_drvdata(pdev, nand); + nand->clk = clk_get(&pdev->dev, "NANDCLK"); + if (IS_ERR(nand->clk)) { + dev_err(&pdev->dev, "failed to get nand clock\n"); + goto fail_end; + } + clk_enable(nand->clk); - if (pxa3xx_nand_config_flash(info, f)) - continue; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no IO memory resource defined\n"); + goto fail_put_clk; + } - if (__readid(info, &id)) - continue; + r = request_mem_region(r->start, resource_size(r), pdev->name); + if (r == NULL) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + goto fail_put_clk; + } - if (id == f->chip_id) - return 0; + nand->mmio_base = ioremap(r->start, r->end - r->start + 1); + if (nand->mmio_base == NULL) { + dev_err(&pdev->dev, "ioremap() failed\n"); + goto fail_free_res; } -#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN - for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) { + /* disable all irq before structure initialized */ + disable_int(nand, NDCR_INT_MASK); + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no IRQ resource defined\n"); + goto fail_free_res; + } - f = builtin_flash_types[i]; + ret = request_irq(IRQ_PXA168_NAND, pxa3xx_nand_irq, IRQF_DISABLED, + pdev->name, nand); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + goto fail_free_res; + } - if (pxa3xx_nand_config_flash(info, f)) - continue; + for (chip_select = 0; chip_select < NUM_CHIP_SELECT; chip_select ++) { + mtd = kzalloc(sizeof(struct mtd_info) \ + + sizeof(struct pxa3xx_nand_info), \ + GFP_KERNEL); - if (__readid(info, &id)) - continue; + if (!mtd) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + break; + } - if (id == f->chip_id) - return 0; - } -#endif + info = (struct pxa3xx_nand_info *)(&mtd[1]); + info->chip_select = chip_select; + info->nand_data = nand; + mtd->priv = info; + mtd->owner = THIS_MODULE; + nand->mtd[chip_select] = mtd; - dev_warn(&info->pdev->dev, - "failed to detect configured nand flash; found %04x instead of\n", - id); - return -ENODEV; -} + if (use_dma == 0) { -/* the maximum possible buffer size for large page with OOB data - * is: 2048 + 64 = 2112 bytes, allocate a page here for both the - * data buffer and the DMA descriptor - */ -#define MAX_BUFF_SIZE PAGE_SIZE + info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); + if (info->data_buff == NULL) { + break; + } + } + else { + info->data_buff = dma_alloc_coherent(&pdev->dev, \ + MAX_BUFF_SIZE, \ + &info->data_buff_phys, \ + GFP_KERNEL); -static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) -{ - struct platform_device *pdev = info->pdev; - int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc); + if (info->data_buff == NULL) { + dev_err(&pdev->dev, "failed to allocate dma \ + buffer\n"); - if (use_dma == 0) { - info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); - if (info->data_buff == NULL) - return -ENOMEM; - return 0; - } + break; + } - info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE, - &info->data_buff_phys, GFP_KERNEL); - if (info->data_buff == NULL) { - dev_err(&pdev->dev, "failed to allocate dma buffer\n"); - return -ENOMEM; - } + info->data_desc = (void *)info->data_buff \ + + data_desc_offset; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, "no resource defined \ + for data DMA\n"); + + goto fail_free_buf; + } + nand->drcmr_dat = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r == NULL) { + dev_err(&pdev->dev, "no resource defined \ + for command DMA\n"); - info->data_buff_size = MAX_BUFF_SIZE; - info->data_desc = (void *)info->data_buff + data_desc_offset; - info->data_desc_addr = info->data_buff_phys + data_desc_offset; - - info->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW, - pxa3xx_nand_data_dma_irq, info); - if (info->data_dma_ch < 0) { - dev_err(&pdev->dev, "failed to request data dma\n"); - dma_free_coherent(&pdev->dev, info->data_buff_size, - info->data_buff, info->data_buff_phys); - return info->data_dma_ch; + goto fail_free_buf; + } + nand->drcmr_cmd = r->start; + info->data_desc_addr = info->data_buff_phys \ + + data_desc_offset; + + nand->data_buff_size = MAX_BUFF_SIZE; + nand->data_dma_ch = pxa_request_dma("nand-data", \ + DMA_PRIO_LOW, \ + pxa3xx_nand_data_dma_irq, nand); + + if (nand->data_dma_ch < 0) { + dev_err(&pdev->dev, "failed to request data dma\n"); + goto fail_free_dma; + } + } } - return 0; -} + return nand; +fail_free_dma: + if (use_dma) + pxa_free_dma(nand->data_dma_ch); +fail_free_buf: + for (i = 0; i < NUM_CHIP_SELECT; i ++) { + mtd = nand->mtd[i]; + info = mtd->priv; + + if (info->data_buff) { + if (use_dma) + dma_free_coherent(&pdev->dev, \ + nand->data_buff_size, \ + info->data_buff, \ + info->data_buff_phys); + else + kfree(info->data_buff); + } -static struct nand_ecclayout hw_smallpage_ecclayout = { - .eccbytes = 6, - .eccpos = {8, 9, 10, 11, 12, 13 }, - .oobfree = { {2, 6} } -}; + if (mtd) + kfree(mtd); + } -static struct nand_ecclayout hw_largepage_ecclayout = { - .eccbytes = 24, - .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { {2, 38} } -}; + iounmap(nand->mmio_base); + free_irq(irq, nand); +fail_free_res: + release_mem_region(r->start, resource_size(r)); +fail_put_clk: + clk_disable(nand->clk); + clk_put(nand->clk); +fail_end: + kfree(nand); + return NULL; +} -static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, - struct pxa3xx_nand_info *info) +static void pxa3xx_nand_init_mtd(struct mtd_info *mtd) { - const struct pxa3xx_nand_flash *f = info->flash_info; + struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand *nand = info->nand_data; struct nand_chip *this = &info->nand_chip; - this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0; - + this->controller = &nand->controller; + this->scan_ident = pxa3xx_nand_scan_ident; this->waitfunc = pxa3xx_nand_waitfunc; this->select_chip = pxa3xx_nand_select_chip; this->dev_ready = pxa3xx_nand_dev_ready; @@ -1191,212 +1816,177 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, this->read_buf = pxa3xx_nand_read_buf; this->write_buf = pxa3xx_nand_write_buf; this->verify_buf = pxa3xx_nand_verify_buf; - - this->ecc.mode = NAND_ECC_HW; - this->ecc.hwctl = pxa3xx_nand_ecc_hwctl; - this->ecc.calculate = pxa3xx_nand_ecc_calculate; - this->ecc.correct = pxa3xx_nand_ecc_correct; - this->ecc.size = f->page_size; - - if (f->page_size == 2048) - this->ecc.layout = &hw_largepage_ecclayout; - else - this->ecc.layout = &hw_smallpage_ecclayout; - - this->chip_delay = 25; + this->erase_cmd = pxa3xx_erase_cmd; + this->write_page = NULL; + this->bbt = NULL; +#ifdef CONFIG_PXA3XX_BBM + this->scan_bbt = pxa3xx_scan_bbt; + this->update_bbt = pxa3xx_update_bbt; + this->block_markbad = pxa3xx_block_markbad; + this->block_bad = pxa3xx_block_bad; +#else + this->scan_bbt = NULL; + this->update_bbt = NULL; + this->block_markbad = NULL; + this->block_bad = NULL; +#endif } static int pxa3xx_nand_probe(struct platform_device *pdev) { struct pxa3xx_nand_platform_data *pdata; - struct pxa3xx_nand_info *info; - struct nand_chip *this; - struct mtd_info *mtd; - struct resource *r; - int ret = 0, irq; + struct pxa3xx_nand *nand; + struct mtd_info *mtd; + int i, ret = -1; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL, *parts = NULL; + int num_part = 0; +#endif pdata = pdev->dev.platform_data; - if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); return -ENODEV; } - mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info), - GFP_KERNEL); - if (!mtd) { - dev_err(&pdev->dev, "failed to allocate memory\n"); - return -ENOMEM; - } - - info = (struct pxa3xx_nand_info *)(&mtd[1]); - info->pdev = pdev; - - this = &info->nand_chip; - mtd->priv = info; - mtd->owner = THIS_MODULE; - - info->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(info->clk)) { - dev_err(&pdev->dev, "failed to get nand clock\n"); - ret = PTR_ERR(info->clk); - goto fail_free_mtd; - } - clk_enable(info->clk); - - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for data DMA\n"); - ret = -ENXIO; - goto fail_put_clk; - } - info->drcmr_dat = r->start; - - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for command DMA\n"); - ret = -ENXIO; - goto fail_put_clk; - } - info->drcmr_cmd = r->start; - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no IRQ resource defined\n"); - ret = -ENXIO; - goto fail_put_clk; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IO memory resource defined\n"); - ret = -ENODEV; - goto fail_put_clk; - } - - r = request_mem_region(r->start, resource_size(r), pdev->name); - if (r == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto fail_put_clk; - } - - info->mmio_base = ioremap(r->start, resource_size(r)); - if (info->mmio_base == NULL) { - dev_err(&pdev->dev, "ioremap() failed\n"); - ret = -ENODEV; - goto fail_free_res; - } - info->mmio_phys = r->start; - - ret = pxa3xx_nand_init_buff(info); - if (ret) - goto fail_free_io; - - /* initialize all interrupts to be disabled */ - disable_int(info, NDSR_MASK); + nand = alloc_nand_resource(pdev, pdata->use_dma); + if (!nand) + return -ENODEV; - ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED, - pdev->name, info); - if (ret < 0) { - dev_err(&pdev->dev, "failed to request IRQ\n"); - goto fail_free_buf; - } + nand->enable_arbiter = pdata->enable_arbiter; + nand->use_dma = pdata->use_dma; + nand->RD_CNT_DEL = pdata->RD_CNT_DEL; + spin_lock_init(&nand->controller.lock); + init_waitqueue_head(&nand->controller.wq); - ret = pxa3xx_nand_detect_flash(info, pdata); - if (ret) { - dev_err(&pdev->dev, "failed to detect flash\n"); - ret = -ENODEV; - goto fail_free_irq; - } + for (i = 0; i < NUM_CHIP_SELECT; i ++) { + mtd = nand->mtd[i]; + pxa3xx_nand_init_mtd(mtd); + if (nand_scan(mtd, 1)) + continue; - pxa3xx_nand_init_mtd(mtd, info); +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd->name = mtd_names[i]; + num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0); +#endif + if (num_part <= 0) { + num_part = pdata->nr_parts[i]; + partitions = pdata->parts[i]; + } - platform_set_drvdata(pdev, mtd); + if (partitions && num_part > 0) { +#ifdef CONFIG_PXA3XX_BBM + struct pxa3xx_bbm *pxa3xx_bbm = mtd->bbm; + parts = pxa3xx_bbm->check_partition(mtd, partitions, &num_part); + if (!parts) + return -EINVAL; +#else + parts = partitions; +#endif - if (nand_scan(mtd, 1)) { - dev_err(&pdev->dev, "failed to scan nand\n"); - ret = -ENXIO; - goto fail_free_irq; + ret = add_mtd_partitions(mtd, parts, num_part); +#ifdef CONFIG_PXA3XX_BBM + kfree(parts); +#endif + } + else + ret = add_mtd_device(mtd); +#else + ret = add_mtd_device(mtd); +#endif } - return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); - -fail_free_irq: - free_irq(irq, info); -fail_free_buf: - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_coherent(&pdev->dev, info->data_buff_size, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); -fail_free_io: - iounmap(info->mmio_base); -fail_free_res: - release_mem_region(r->start, resource_size(r)); -fail_put_clk: - clk_disable(info->clk); - clk_put(info->clk); -fail_free_mtd: - kfree(mtd); return ret; } static int pxa3xx_nand_remove(struct platform_device *pdev) { - struct mtd_info *mtd = platform_get_drvdata(pdev); - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand *nand = platform_get_drvdata(pdev); + struct mtd_info *mtd; + struct pxa3xx_nand_info *info; struct resource *r; - int irq; + int i; +#ifdef CONFIG_PXA3XX_BBM + struct pxa3xx_bbm *pxa3xx_bbm; +#endif + pxa3xx_nand_stop(nand); platform_set_drvdata(pdev, NULL); + free_irq(IRQ_PXA168_NAND, nand); + if (nand->use_dma) + pxa_free_dma(nand->data_dma_ch); - del_mtd_device(mtd); - del_mtd_partitions(mtd); - irq = platform_get_irq(pdev, 0); - if (irq >= 0) - free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_writecombine(&pdev->dev, info->data_buff_size, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); - - iounmap(info->mmio_base); + iounmap(nand->mmio_base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(r->start, resource_size(r)); - clk_disable(info->clk); - clk_put(info->clk); + clk_disable(nand->clk); + clk_put(nand->clk); + + for (i = 0; i < NUM_CHIP_SELECT; i ++) { + mtd = nand->mtd[i]; + if (!mtd) + continue; + info = mtd->priv; +#ifdef CONFIG_PXA3XX_BBM + pxa3xx_bbm = mtd->bbm; + pxa3xx_bbm->uninit(mtd); +#endif + if (nand->use_dma) { + dma_free_writecombine(&pdev->dev, nand->data_buff_size, + info->data_buff, info->data_buff_phys); + } else + kfree(info->data_buff); + del_mtd_device(mtd); + del_mtd_partitions(mtd); + kfree(mtd); + } - kfree(mtd); return 0; } #ifdef CONFIG_PM -static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) +static unsigned int ndtr0cs0, ndtr1cs0; + +static int pxa3xx_nand_suspend(struct platform_device *pdev, + pm_message_t state) { - struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); - struct pxa3xx_nand_info *info = mtd->priv; + struct pxa3xx_nand *nand = platform_get_drvdata(pdev); + struct mtd_info *mtd = nand->mtd[nand->chip_select]; + int ret = 0; - if (info->state != STATE_READY) { - dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); + if (nand->state & STATE_CMD_PREPARED) { + dev_err(&pdev->dev, "driver busy, state = %d\n", nand->state); return -EAGAIN; } - return 0; + if (mtd) + ret = mtd->suspend(mtd); + + ndtr0cs0 = nand_readl(nand, NDTR0CS0); + ndtr1cs0 = nand_readl(nand, NDTR1CS0); + + return ret; } static int pxa3xx_nand_resume(struct platform_device *pdev) { - struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); + struct pxa3xx_nand *nand = platform_get_drvdata(pdev); + struct mtd_info *mtd = nand->mtd[nand->chip_select]; struct pxa3xx_nand_info *info = mtd->priv; - clk_enable(info->clk); + nand_writel(nand, NDTR0CS0, ndtr0cs0); + nand_writel(nand, NDTR1CS0, ndtr1cs0); + nand_writel(nand, NDREDEL, 0x0); - return pxa3xx_nand_config_flash(info, info->flash_info); + no_error_dump = 1; + pxa3xx_nand_start(info); + pxa3xx_nand_stop(nand); + no_error_dump = 0; + if (mtd) + mtd->resume(mtd); + return 0; } #else #define pxa3xx_nand_suspend NULL @@ -1415,12 +2005,18 @@ static struct platform_driver pxa3xx_nand_driver = { static int __init pxa3xx_nand_init(void) { +#if defined(CONFIG_DVFM) + dvfm_register("NAND", &dvfm_dev_idx); +#endif return platform_driver_register(&pxa3xx_nand_driver); } module_init(pxa3xx_nand_init); static void __exit pxa3xx_nand_exit(void) { +#if defined(CONFIG_DVFM) + dvfm_unregister("NAND", &dvfm_dev_idx); +#endif platform_driver_unregister(&pxa3xx_nand_driver); } module_exit(pxa3xx_nand_exit); diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index e78914938c5c69..85bd8186bd2d46 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -19,6 +19,8 @@ #include #include #include +#include + #include /* @@ -43,7 +45,13 @@ struct onenand_info { static int __devinit generic_onenand_probe(struct platform_device *pdev) { struct onenand_info *info; - struct onenand_platform_data *pdata = pdev->dev.platform_data; + struct flash_platform_data *pdata = pdev->dev.platform_data; + struct resource *res = pdev->resource; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL, *parts = NULL; + int part_num; +#endif + unsigned long size = res->end - res->start + 1; struct resource *res = pdev->resource; unsigned long size = resource_size(res); int err; @@ -52,7 +60,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) if (!info) return -ENOMEM; - if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) { + if (!request_mem_region(res->start, size, pdev->name)) { err = -EBUSY; goto out_free_info; } @@ -70,6 +78,16 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) info->mtd.priv = &info->onenand; info->mtd.owner = THIS_MODULE; +#ifdef CONFIG_PXA3XX_BBM + info->onenand.scan_bbt = pxa3xx_scan_bbt; + info->onenand.block_bad = pxa3xx_block_bad; + info->onenand.block_markbad = pxa3xx_block_markbad; +#else + info->onenand.scan_bbt = NULL; + info->onenand.block_bad = NULL; + info->onenand.block_markbad = NULL; +#endif + if (onenand_scan(&info->mtd, 1)) { err = -ENXIO; goto out_iounmap; @@ -77,6 +95,29 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) #ifdef CONFIG_MTD_PARTITIONS err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); + if (err > 0) { + partitions = info->parts; + part_num = err; + } + else if (err <= 0 && pdata->parts) { + partitions = pdata->parts; + part_num = pdata->nr_parts; + } + + if (part_num > 0 && partitions) { +#ifdef CONFIG_PXA3XX_BBM + struct pxa3xx_bbm *pxa3xx_bbm = info->mtd.bbm; + parts = pxa3xx_bbm->check_partition(&info->mtd, partitions, &part_num); + if (!parts) + return -EINVAL; +#else + parts = partitions; +#endif + add_mtd_partitions(&info->mtd, parts, part_num); +#ifdef CONFIG_PXA3XX_BBM + kfree(parts); +#endif + } if (err > 0) add_mtd_partitions(&info->mtd, info->parts, err); else if (err <= 0 && pdata && pdata->parts) @@ -101,7 +142,8 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev) static int __devexit generic_onenand_remove(struct platform_device *pdev) { - struct onenand_info *info = platform_get_drvdata(pdev); + struct onenand_info *info = dev_get_drvdata(&pdev->dev); + //struct onenand_info *info = platform_get_drvdata(pdev); struct resource *res = pdev->resource; unsigned long size = resource_size(res); @@ -129,8 +171,11 @@ static struct platform_driver generic_onenand_driver = { }, .probe = generic_onenand_probe, .remove = __devexit_p(generic_onenand_remove), +#ifdef CONFIG_PM + .suspend = NULL, + .resume = NULL, +#endif }; - MODULE_ALIAS(DRIVER_NAME); static int __init generic_onenand_init(void) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 32f0ed33afe09b..f0d4b9a414d6b8 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -32,9 +32,50 @@ #include #include #include +#include #include +#if defined(CONFIG_DVFM) +#include + +static struct dvfm_lock dvfm_lock = { + .lock = SPIN_LOCK_UNLOCKED, + .dev_idx = -1, + .count = 0, +}; + +static void set_dvfm_constraint(void) +{ + spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags); + if (dvfm_lock.count++ == 0) { + /* Disable Low power mode */ + dvfm_disable_op_name("apps_idle", dvfm_lock.dev_idx); + dvfm_disable_op_name("apps_sleep", dvfm_lock.dev_idx); + dvfm_disable_op_name("sys_sleep", dvfm_lock.dev_idx); + } + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); +} + +static void unset_dvfm_constraint(void) +{ + spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags); + if (dvfm_lock.count == 0) { + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); + return; + } + if (--dvfm_lock.count == 0) { + /* Enable Low power mode */ + dvfm_enable_op_name("apps_idle", dvfm_lock.dev_idx); + dvfm_enable_op_name("apps_sleep", dvfm_lock.dev_idx); + dvfm_enable_op_name("sys_sleep", dvfm_lock.dev_idx); + } + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); +} +#else +static void set_dvfm_constraint(void) {} +static void unset_dvfm_constraint(void) {} +#endif /* * Multiblock erase if number of blocks to erase is 2 or more. * Maximum number of blocks for simultaneous erase is 64. @@ -342,8 +383,18 @@ EXPORT_SYMBOL(flexonenand_region); static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; + struct pxa3xx_bbm *bbm = mtd->bbm; int value, block, page; + if (cmd != ONENAND_CMD_UNLOCK + && cmd != ONENAND_CMD_LOCK + && cmd != ONENAND_CMD_LOCK_TIGHT + && cmd != ONENAND_CMD_UNLOCK_ALL) + + if (bbm && bbm->is_init) + addr = bbm->search(mtd, addr); + + /* Address translation */ switch (cmd) { case ONENAND_CMD_UNLOCK: @@ -524,6 +575,11 @@ static int onenand_wait(struct mtd_info *mtd, int state) int ecc = onenand_read_ecc(this); if (ecc) { if (ecc & ONENAND_ECC_2BIT_ALL) { + //printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } else if (ecc & ONENAND_ECC_1BIT_ALL) { + //printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); printk(KERN_ERR "%s: ECC error = 0x%04x\n", __func__, ecc); mtd->ecc_stats.failed++; @@ -958,6 +1014,9 @@ static int onenand_get_device(struct mtd_info *mtd, int new_state) remove_wait_queue(&this->wq, &wait); } + /* If get onenand device, set dvfm constraint */ + set_dvfm_constraint(); + return 0; } @@ -976,6 +1035,9 @@ static void onenand_release_device(struct mtd_info *mtd) this->state = FL_READY; wake_up(&this->wq); spin_unlock(&this->chip_lock); + + /* unset dvfm constraint when release onenand device */ + unset_dvfm_constraint(); } /** @@ -2198,10 +2260,9 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) { struct onenand_chip *this = mtd->priv; - struct bbm_info *bbm = this->bbm; /* Return info from the table */ - return bbm->isbad_bbt(mtd, ofs, allowbbt); + return this->block_bad(mtd, ofs, allowbbt); } @@ -2490,6 +2551,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); + if (ret && (this->options & ONENAND_RELOC_IFBAD)) { + this->block_markbad(mtd, addr); + instr->state = MTD_ERASE_DONE; + ret = 0; + } + /* Do call back function */ if (!ret) { instr->state = MTD_ERASE_DONE; @@ -3821,6 +3888,8 @@ static int onenand_probe(struct mtd_info *mtd) this->density_mask = this->chipsize >> (this->erase_shift + 1); /* It's real page size */ this->writesize = mtd->writesize; + mtd->writesize_shift = this->page_shift; + mtd->erasesize_shift = this->erase_shift; /* REVISIT: Multichip handling */ @@ -3843,6 +3912,9 @@ static int onenand_probe(struct mtd_info *mtd) mtd->erasesize <<= 1; } +#if defined(CONFIG_DVFM) + dvfm_register("OneNAND", &dvfm_lock.dev_idx); +#endif return 0; } @@ -3908,6 +3980,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) this->block_markbad = onenand_default_block_markbad; if (!this->scan_bbt) this->scan_bbt = onenand_default_bbt; + if (!this->block_bad) + this->block_bad = onenand_isbad_bbt; if (onenand_probe(mtd)) return -ENXIO; @@ -4038,6 +4112,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) void onenand_release(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; +#ifdef CONFIG_PXA3XX_BBM + struct pxa3xx_bbm *bbm = mtd->bbm; + + bbm->uninit(mtd); +#else + /* Free bad block table memory, if allocated */ + if (this->bbm) { + struct bbm_info *bbm = this->bbm; + kfree(bbm->bbt); + kfree(this->bbm); + } +#endif #ifdef CONFIG_MTD_PARTITIONS /* Deregister partitions */ @@ -4046,12 +4132,6 @@ void onenand_release(struct mtd_info *mtd) /* Deregister the device */ del_mtd_device (mtd); - /* Free bad block table memory, if allocated */ - if (this->bbm) { - struct bbm_info *bbm = this->bbm; - kfree(bbm->bbt); - kfree(this->bbm); - } /* Buffers allocated by onenand_scan */ if (this->options & ONENAND_PAGEBUF_ALLOC) kfree(this->page_buf); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index a91fcac1af01c6..07eb9aa0b8c9da 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -141,7 +141,7 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc * @param offs offset in the device * @param allowbbt allow access to bad block table region */ -static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) +int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct onenand_chip *this = mtd->priv; struct bbm_info *bbm = this->bbm; @@ -198,9 +198,6 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) /* Set erase shift */ bbm->bbt_erase_shift = this->erase_shift; - if (!bbm->isbad_bbt) - bbm->isbad_bbt = onenand_isbad_bbt; - /* Scan the device to build a memory based bad block table */ if ((ret = onenand_memory_bbt(mtd, bd))) { printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n"); diff --git a/drivers/mtd/pxa3xx_bbm.c b/drivers/mtd/pxa3xx_bbm.c new file mode 100644 index 00000000000000..1a442fb67d4b83 --- /dev/null +++ b/drivers/mtd/pxa3xx_bbm.c @@ -0,0 +1,1371 @@ +/* + * Bad Block Management support for PXA3XX. + * Copyright (C) 2009 Marvell International Ltd. + * Lei Wen + * + * 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 + +#define NEW_BBM_RELOC_PERCENTAGE (2) +#define MAX_SUPPRTED_PARTNUM (3) +static struct mtd_partition *pxa3xx_check_partition(struct mtd_info *mtd, + struct mtd_partition *part, int *num); + +static int erase_success; +static int should_reloc = 1; +static void pxa3xx_bbm_callback(struct erase_info *instr) +{ + if (instr->fail_addr == MTD_FAIL_ADDR_UNKNOWN) + erase_success = 1; + else + erase_success = 0; +} + +static void dump_reloc_table(struct reloc_item *item, int entry_num) +{ + int i; + + if (entry_num == 0) { + printk(KERN_INFO "The reloc table is empty now\n"); + return; + } + + for (i = 0; i < entry_num; i++) + printk(KERN_INFO "block: %8d is relocated to block: %d\n", + item[i].from, item[i].to); +} + +static void dump_fact_bads(struct pxa3xx_bbt *fbbt) +{ + uint32_t *fact_bad = (uint32_t *)&fbbt->fact_bad; + int i; + + if (fbbt->entry_num == 0) { + printk(KERN_INFO "There is no factory bad block!!\n"); + return; + } + + for (i = 0; i < fbbt->entry_num; i ++) + printk(KERN_INFO "block %d is bad.\n", fact_bad[i]); +} + +static void dump_part_info(struct mtd_info *mtd) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_new_bbm *new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + struct pxa3xx_part *part = new_bbm->part; + struct pxa3xx_partinfo *partinfo; + struct pxa3xx_bbt *rbbt; + struct reloc_item *item; + char tmp[9]; + int i; + uint32_t swap_temp; + + printk(KERN_INFO "\nThere are totally %d parts", part->part_num); + for (i = 0; i < part->part_num; i ++) { + printk(KERN_INFO "\n===The part %d info:===\n", i); + partinfo = &new_bbm->partinfo[i]; + if (partinfo->type == PART_LOGI) + printk(KERN_INFO "This part is Logi\n"); + else + printk(KERN_INFO "This part is Phys\n"); + if (partinfo->usage && partinfo->usage != 0xffffffff) { + memcpy(tmp, &partinfo->usage, 4); + tmp[4] = '\0'; + printk(KERN_INFO "Part name %s\n", tmp); + } + if (partinfo->identifier && partinfo->identifier != 0xffffffff) { + memcpy(tmp, &partinfo->identifier, 4); + tmp[4] = '\0'; + printk(KERN_INFO "identifier %s\n", tmp); + } + printk(KERN_INFO "Attr %16x\n", partinfo->attrs); + printk(KERN_INFO "This part start from %llx to %llx\n", + partinfo->start_addr, partinfo->end_addr); + printk(KERN_INFO "Reserved pool start from %llx, size %llx\n", + partinfo->rp_start, partinfo->rp_size); + if (partinfo->rp_algo == RP_UPWD) + printk(KERN_INFO "Reserved pool grow upwards\n"); + else + printk(KERN_INFO "Reserved pool grow downwards\n"); + + swap_temp = partinfo->rbbt_type; + swab32s(&swap_temp); + memcpy(tmp, &swap_temp, 4); + tmp[4] = '\0'; + printk(KERN_INFO "\nRBBT type %s\n", tmp); + printk(KERN_INFO "RBBT start at %llx, its back at %llx\n", + partinfo->rbbt_start, partinfo->rbbt_start_back); + rbbt = &new_bbm->rbbt[i]; + printk(KERN_INFO "RBBT could max reloc %d blocks\n", + new_bbm->max_reloc_entry[i]); + printk(KERN_INFO "Current slot is at 0x%llx\n", + new_bbm->rbbt_offset[i] << mtd->writesize_shift); + item = (struct reloc_item *)&rbbt->reloc; + dump_reloc_table(item, new_bbm->rbbt->entry_num); + } +} + +static void pxa3xx_uninit_reloc_tb(struct mtd_info *mtd) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct pxa3xx_new_bbm *new_bbm; + + if (bbm) { + switch (bbm->bbm_type) { + case BBM_LEGACY: + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + kfree(legacy_bbm->table); + break; + + case BBM_NEW: + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + kfree(new_bbm->rbbt); + kfree(new_bbm->fbbt); + kfree(new_bbm->part); + default: + break; + } + + if (bbm->data_buf) + kfree(bbm->data_buf); + kfree(bbm); + mtd->bbm = NULL; + } +} + +/* + * Found the block belong to which partition + */ +static int find_part(struct mtd_info *mtd, uint64_t offset) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_new_bbm *new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + struct pxa3xx_part *part = new_bbm->part; + struct pxa3xx_partinfo *partinfo; + int i, found_part = -EINVAL; + + for (i = 0; i < part->part_num; i ++) { + partinfo = &(new_bbm->partinfo[i]); + if (offset < partinfo->start_addr) + break; + + if (offset < partinfo->end_addr) { + found_part = i; + break; + } + } + + return found_part; +} + +/* + * start_page and end_page should be in one block boundary + * direction: 1 for positive page grow order, 0 for the reversed order + * indicator should be meaningful bit order stand for BBT + */ +int page_search(struct mtd_info *mtd, int start_page, int end_page, + int direction, unsigned int indicator, void *buf, unsigned int mask) +{ + int found_page = -EINVAL, cur_page, ret; + unsigned int header; + size_t retlen; + + cur_page = (direction == ORDER_POSITIVE) ? end_page : start_page; + while (start_page <= end_page) { + ret = mtd->read(mtd, cur_page << mtd->writesize_shift, + mtd->writesize, &retlen, buf); + header = *(unsigned int *)buf & mask; + if (!ret && header == indicator) { + found_page = cur_page; + break; + } + + if (direction == ORDER_POSITIVE) { + cur_page --; + if (cur_page < start_page) + break; + } + else { + cur_page ++; + if (cur_page > end_page) + break; + } + } + + return found_page; +} + +/* add the relocation entry into the relocation table + * It's valid on MOBM V3. + * If the relocated block is bad, an new entry will be added into the + * bottom of the relocation table. + */ +static int sync_pxa3xx_bbt(struct mtd_info *mtd, loff_t ofs) +{ + struct pxa3xx_bbm *bbm = mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm = NULL; + struct pxa3xx_new_bbm *new_bbm; + struct pxa3xx_partinfo *partinfo; + struct pxa3xx_bbt *bbt = NULL; + struct reloc_item *item; + struct erase_info instr; + int reloc_block, entry_num = -1; + char *rel_dist; + int i, block, _rel, max_reloc_entry, reloc_boundary, total, part; + + printk(KERN_INFO "ready to put %llx into the bbt\n", ofs); + if (bbm->bbm_type == BBM_LEGACY) { + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + item = legacy_bbm->reloc; + reloc_boundary = mtd_div_by_eb(mtd->size, mtd) + - legacy_bbm->max_reloc_entry; + max_reloc_entry = legacy_bbm->max_reloc_entry; + total = legacy_bbm->table->total; + } + else { + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + part = find_part(mtd, ofs); + if (part < 0) + return -EINVAL; + new_bbm->update_indicator |= 1 << part; + max_reloc_entry = new_bbm->max_reloc_entry[part]; + bbt = &new_bbm->rbbt[part]; + partinfo = &new_bbm->partinfo[part]; + item = (struct reloc_item *)&bbt->reloc; + reloc_boundary = mtd_div_by_eb(partinfo->rp_start, mtd); + total = bbt->entry_num; + } + + block = (int)(ofs >> mtd->erasesize_shift); + if (total >= max_reloc_entry) { + printk(KERN_WARNING "Relocation table currently have %d\n" + "Exceed max num %d, cannot relocate block %d!!\n", + total, max_reloc_entry, block); + return -ENOSPC; + } + + if (block >= reloc_boundary) + return -EINVAL; + + //identify whether the block has been relocated + for(i = total - 1; i >= 0; i --) { + if(block == item[i].from) + entry_num = i; + } + + rel_dist = bbm->rel_dist; + if (!rel_dist) { + rel_dist = kzalloc(max_reloc_entry, GFP_KERNEL); + /* need to save this */ + bbm->rel_dist = rel_dist; + } + else + memset(rel_dist, 0, max_reloc_entry); + //find the available block with the largest number in reservered area + for (i = 0; i < total; i ++) { + _rel = (item[i].to != 65535) ? item[i].to : item[i].from; + rel_dist[_rel - reloc_boundary] = 1; + } + + while (1) { + /* Make sure that reloc_block is pointing to a valid block */ + for (reloc_block = max_reloc_entry - 1; + reloc_block >= 0; reloc_block --) { + if (rel_dist[reloc_block] == 0) { + break; + } + } + + if (reloc_block < 0) { + if (entry_num >= 0) { + item[entry_num].from = item[entry_num].to; + item[entry_num].to = 65535; + } + return -ENOSPC; + } + + reloc_block = reloc_block + reloc_boundary; + memset(&instr, 0, sizeof(struct erase_info)); + instr.mtd = mtd; + instr.addr = (uint64_t)reloc_block << mtd->erasesize_shift; + instr.len = mtd->erasesize; + instr.callback = pxa3xx_bbm_callback; + + should_reloc = 0; + mtd->erase(mtd, &instr); + should_reloc = 1; + if (erase_success) + break; + else { + /* skip it if the reloc_block is also a + * bad block + */ + if (instr.fail_addr == instr.addr) { + item[total].from = reloc_block; + + item[total].to = 65535; + total ++; + rel_dist[reloc_block - reloc_boundary] = 1;; + continue; + } else + return -EINVAL; + } + } + + /* + * Create the relocated block information in the table + * when the block is relocated before, blob should modify + * the original entry to new relocated block and the old + * relocated block point to 65535. If not the situation, + * create a new entry + */ + if (entry_num != -1) { + item[total].from = item[entry_num].to; + item[total].to = 65535; + total ++; + item[entry_num].to = reloc_block; + } else { + item[total].from = block; + item[total].to = reloc_block; + total ++; + } + + if (bbm->bbm_type == BBM_LEGACY) + legacy_bbm->table->total = total; + else + bbt->entry_num = total; + + return 0; +} + +/* Write the relocation table back to device, if there's room. */ +int pxa3xx_update_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct pxa3xx_new_bbm *new_bbm; + size_t retlen; + loff_t offset = 0; + void *buf; + int ret = 1, part = 0, pages, is_continue = 1; + + while (is_continue) { + switch (bbm->bbm_type) { + case BBM_LEGACY: + if (!ret) { + printk(KERN_INFO "update legacy bbt" + " at %llx\n", offset); + return 0; + } + + pages = mtd->erasesize >> mtd->writesize_shift; + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + if (legacy_bbm->current_slot <= PXA_BEGIN_SLOT + || legacy_bbm->current_slot > pages) + goto ERR_EXIT; + + /* should write to the next slot */ + legacy_bbm->current_slot --; + buf = legacy_bbm->table; + offset = legacy_bbm->current_slot + << mtd->writesize_shift; + break; + + case BBM_NEW: + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + if (!ret) { + printk(KERN_INFO "update new bbm bbt" + " at %llx\n", offset); + new_bbm->update_indicator &= ~(1 << part); + } + for (; part < MAX_SUPPRTED_PARTNUM; part ++) + if (new_bbm->update_indicator & (1 << part)) + break; + + if (part >= MAX_SUPPRTED_PARTNUM) + return 0; + + offset = (new_bbm->rbbt_offset[part] + 1) + << mtd->writesize_shift; + if (!(unsigned int)(offset & mtd->erasesize_mask)) + goto ERR_EXIT; + + new_bbm->rbbt_offset[part] ++; + buf = new_bbm->rbbt; + break; + + default: + return 0; + } + + ret = mtd->write(mtd, offset, mtd->writesize, &retlen, buf); + } + + return 0; + +ERR_EXIT: + printk(KERN_ERR "Can't write relocation table to device any more.\n"); + return -EINVAL; +} + +/* Find the relocated block of the bad one. + * If it's a good block, return 0. Otherwise, return a relocated one. + * idx points to the next relocation entry + * If the relocated block is bad, an new entry will be added into the + * bottom of the relocation table. + */ +static loff_t pxa3xx_search_reloc_tb(struct mtd_info *mtd, loff_t ofs) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct pxa3xx_new_bbm *new_bbm; + struct reloc_item *item; + int i, block, max_allow_relocated, entry_num, part; + + if (!bbm || (bbm->is_init == BBT_NOINIT)) + return ofs; + + block = ofs >> mtd->erasesize_shift; + switch (bbm->bbm_type) { + case BBM_LEGACY: + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + if (legacy_bbm->current_slot < 0) + return ofs; + + max_allow_relocated = mtd_div_by_eb(mtd->size, mtd) + - legacy_bbm->max_reloc_entry; + item = legacy_bbm->reloc; + entry_num = legacy_bbm->table->total; + break; + + case BBM_NEW: + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + part = find_part(mtd, ofs); + if (part < 0) + return ofs; + item = (struct reloc_item *)&new_bbm->rbbt[part].reloc; + entry_num = new_bbm->rbbt[part].entry_num; + max_allow_relocated = + mtd_div_by_eb(new_bbm->partinfo[part].end_addr, mtd); + break; + + default: + return ofs; + } + + if (block >= max_allow_relocated || entry_num == 0) + return ofs; + + ofs -= block * mtd->erasesize; + for (i = entry_num - 1; i >= 0; i --) { + if (block == item[i].from) { + block = item[i].to; + break; + } + } + + ofs += block * mtd->erasesize; + + return ofs; +} + +static int pxa3xx_init_bbm(struct mtd_info *mtd, int bbm_type) +{ + struct pxa3xx_bbm *bbm = mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct pxa3xx_new_bbm *new_bbm; + int size, ret, entrys, max_relcs; + + if (bbm_type != BBM_NEW && bbm_type != BBM_LEGACY) + return -EFAULT; + + bbm = kzalloc(sizeof(struct pxa3xx_bbm), GFP_KERNEL); + if (!bbm) + return -ENOMEM; + + bbm->search = pxa3xx_search_reloc_tb; + bbm->uninit = pxa3xx_uninit_reloc_tb; + bbm->check_partition = pxa3xx_check_partition; + mtd->bbm = bbm; + size = (bbm_type == BBM_NEW) ? sizeof(struct pxa3xx_new_bbm) : + sizeof(struct pxa3xx_legacy_bbm); + bbm->is_init = BBT_NOINIT; + bbm->no_sync = 0; + bbm->data_buf = kzalloc(size, GFP_KERNEL); + if (!bbm->data_buf) { + ret = -ENOMEM; + goto ERR_EXIT; + } + + if (bbm_type == BBM_NEW) { + bbm->bbm_type = BBM_NEW; + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + new_bbm->main_block = -1; + new_bbm->back_block = -1; + new_bbm->fbbt = kzalloc(mtd->writesize, GFP_KERNEL); + new_bbm->part = kzalloc(mtd->writesize, GFP_KERNEL); + new_bbm->rbbt = + kzalloc(mtd->writesize * MAX_SUPPRTED_PARTNUM, GFP_KERNEL); + new_bbm->rbbt_offset = + kzalloc(sizeof(loff_t) * MAX_SUPPRTED_PARTNUM, GFP_KERNEL); + new_bbm->max_reloc_entry = + kzalloc(sizeof(int) * MAX_SUPPRTED_PARTNUM, GFP_KERNEL); + if (!new_bbm->rbbt + || !new_bbm->rbbt_offset + || !new_bbm->max_reloc_entry + || !new_bbm->fbbt + || !new_bbm->part) { + kfree(bbm->data_buf); + ret = -ENOMEM; + goto ERR_EXIT; + } + + new_bbm->partinfo = + (struct pxa3xx_partinfo *)&new_bbm->part[1]; + memset(new_bbm->fbbt, 0xff, mtd->writesize); + memset(new_bbm->part, 0xff, mtd->writesize); + } + else { + bbm->bbm_type = BBM_LEGACY; + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + entrys = mtd_div_by_eb(mtd->size, mtd); + entrys = (entrys / 100) * 2; + max_relcs = (mtd->writesize - sizeof(struct reloc_table)) + / sizeof(struct reloc_item); + + legacy_bbm->max_reloc_entry = (entrys < max_relcs) ? + entrys : max_relcs; + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + legacy_bbm->table = kzalloc(mtd->writesize, GFP_KERNEL); + if (!legacy_bbm->table) { + kfree(bbm->data_buf); + ret = -ENOMEM; + goto ERR_EXIT; + } + + memset(legacy_bbm->table, 0xff, mtd->writesize); + legacy_bbm->reloc = (struct reloc_item *)&legacy_bbm->table[1]; + legacy_bbm->current_slot = -1; + } + + return 0; + +ERR_EXIT: + kfree(bbm); + mtd->bbm = NULL; + return ret; +} + +static int legacy_bbm_scan(struct mtd_info *mtd) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct reloc_table *table; + + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + table = legacy_bbm->table; + legacy_bbm->current_slot = page_search(mtd, PXA_BEGIN_SLOT, + (mtd->erasesize >> mtd->writesize_shift) - 1, + ORDER_REVERSE, PXA_RELOC_HEADER, table, BBM_HALF_MASK); + + printk(KERN_INFO "Max capacity of BBM is %d blocks!!\n", + legacy_bbm->max_reloc_entry); + if (legacy_bbm->current_slot >= 0) { + printk(KERN_INFO "relocation table at page:%d\n", + legacy_bbm->current_slot); + dump_reloc_table(legacy_bbm->reloc, table->total); + + return 0; + } + + /* There should be a valid relocation table slot at least. */ + printk(KERN_ERR "NO VALID reloc table can be recognized\n"); + printk(KERN_ERR "CAUTION: It may cause unpredicated error\n"); + printk(KERN_ERR "Please re-initialize the flash.\n"); + kfree(bbm->data_buf); + + return -EINVAL; +} + +#define FOUND_FBBT 0x1 +#define FOUND_PART 0x2 +#define BBM_NOCOPY 0x1 +static int scan_fbbt_part(struct mtd_info *mtd, int block, void *buf, int flag) +{ + /* + * NTIM header at least occupy by one page, + * so search the FBBT or part from second page, + * and this search should be ended at the fifth page + */ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_new_bbm *new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + struct pxa3xx_part *part; + struct pxa3xx_partinfo *partinfo; + int page, ret, part_num, found = 0, i, max_reloc_entry, rp_num; + int start_page, end_page; + loff_t offset; + size_t retlen; + + max_reloc_entry = (mtd->writesize - 40) / sizeof(struct reloc_item); + for (page = 1; page < 5; page ++) { + if (found == (FOUND_PART | FOUND_FBBT)) + break; + + offset = ((uint64_t)block << mtd->erasesize_shift) + + (page << mtd->writesize_shift); + ret = mtd->read(mtd, offset, mtd->writesize, &retlen, buf); + + /* found FBBT */ + if (!ret && *(unsigned int *)buf == PXA_NEW_BBM_HEADER) { + if (flag == BBM_NOCOPY) + return 1; + + found |= FOUND_FBBT; + memcpy(new_bbm->fbbt, buf, retlen); + } + + /* found partition table */ + if (!ret && *(unsigned int *)buf == PXA_PART_IDET_1) { + if (*((unsigned int *)buf + 1) != PXA_PART_IDET_2) + continue; + + if (flag == BBM_NOCOPY) + return 1; + + found |= FOUND_PART; + memcpy(new_bbm->part, buf, retlen); + part = new_bbm->part; + part_num = part->part_num; + + for (i = 0; i < part_num; i ++) { + partinfo = &new_bbm->partinfo[i]; + start_page = + mtd_div_by_ws(partinfo->rbbt_start, mtd); + end_page = start_page - 1 + + (mtd->erasesize >> mtd->writesize_shift); + new_bbm->rbbt_offset[i] = + page_search(mtd, start_page, end_page, + ORDER_POSITIVE, PXA_NEW_BBM_HEADER, + &new_bbm->rbbt[i], BBM_FULL_MASK); + rp_num = mtd_div_by_eb(partinfo->rp_size, mtd); + new_bbm->max_reloc_entry[i] = + (max_reloc_entry < rp_num) ? + max_reloc_entry : rp_num; + } + } + } + + return found == (FOUND_PART | FOUND_FBBT); +} + +static int new_bbm_scan(struct mtd_info *mtd) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_new_bbm *new_bbm; + int block, ret, flag; + void *buf; + + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + buf = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + flag = 0; + for (block = 0; block < 10; block ++) { + ret = scan_fbbt_part(mtd, block, buf, flag); + if (ret) { + flag = BBM_NOCOPY; + if (new_bbm->main_block == -1) + new_bbm->main_block = block; + else if (new_bbm->back_block == -1) { + new_bbm->back_block = block; + break; + } + } + } + kfree(buf); + + if (new_bbm->main_block == -1 && new_bbm->back_block == -1) { + printk(KERN_ERR "New BBM initilization failed!!!!!!\n"); + return -EINVAL; + } + + printk(KERN_INFO "Found main block at %d, back at %d\n", + new_bbm->main_block, new_bbm->back_block); + new_bbm->update_indicator = 0; + printk(KERN_INFO "Factory marked bad blocks:\n"); + dump_fact_bads(new_bbm->fbbt); + dump_part_info(mtd); + return 0; +} + +int pxa3xx_scan_bbt(struct mtd_info *mtd) +{ + struct pxa3xx_bbm *bbm; + size_t retlen; + int ret, bbm_type; + void *buf; + + if (!mtd->bbm) { + /* + * Legacy BBM only use the block 0, while new BBM scheme use + * the block 0 to block 9. So first read the last page in + * block 0 to see if it is legacy BBM, or change the + * scan strategy to new BBM scheme. + */ + buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = mtd->read(mtd, mtd->erasesize - mtd->writesize , mtd->writesize, + &retlen, buf); + + /* This flash chip is using legacy BBM */ + if (!ret && *(unsigned short *)buf == PXA_RELOC_HEADER) + bbm_type = BBM_LEGACY; + else + bbm_type = BBM_NEW; + + kfree(buf); + ret = pxa3xx_init_bbm(mtd, bbm_type); + if (ret) + return ret; + bbm = (struct pxa3xx_bbm *)mtd->bbm; + } + else { + bbm = (struct pxa3xx_bbm *)mtd->bbm; + bbm_type = bbm->bbm_type; + } + + if (bbm->is_init != BBT_NOINIT) + return 0; + + if (bbm_type == BBM_LEGACY) + ret = legacy_bbm_scan(mtd); + else + ret = new_bbm_scan(mtd); + + if (!ret) + bbm->is_init = BBT_INITED; + else { + printk(KERN_ERR "BBM NOT Initialized, " + "Please re-init the flash!!!\n\n"); + bbm->is_init = BBT_NOINIT; + } + + return ret; +} + +static int checkbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + uint32_t bad_mark; + + ops.ooboffs = 0; + ops.ooblen = 2; + ops.len = 2; + ops.datbuf = NULL; + ops.oobbuf = (uint8_t *)&bad_mark; + ops.mode = (mtd_oob_mode_t)MTD_OOB_PLACE; + + mtd->read_oob(mtd, ofs, &ops); + if ((bad_mark & 0xFF) != 0xFF) + return 1; + else + return 0; +} + +static int boot_part_bad(struct mtd_info *mtd, loff_t ofs) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_new_bbm *new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + struct pxa3xx_bbt *fbbt = new_bbm->fbbt; + int block = ofs >> mtd->erasesize_shift, i; + uint32_t *fact_bad = (uint32_t *)&fbbt->fact_bad; + + for (i = 0; i < fbbt->entry_num; i ++) + if (fact_bad[i] == block) + return 1; + + return 0; +} + +int pxa3xx_block_bad(struct mtd_info *mtd, loff_t ofs, int allowbbt) +{ + struct pxa3xx_bbm *bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct pxa3xx_new_bbm *new_bbm; + struct reloc_table *table; + int part; + + bbm = (struct pxa3xx_bbm *)mtd->bbm; + if (bbm && (bbm->is_init != BBT_NOINIT)) { + if (bbm->is_init == BBT_FORCE_NOINIT) + return 0; + + bbm = (struct pxa3xx_bbm *)mtd->bbm; + switch (bbm->bbm_type) { + case BBM_LEGACY: + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + table = legacy_bbm->table; + /* + * If relocation table is not yet full, then any block + * in the flash should be good + */ + if (legacy_bbm->current_slot >= PXA_BEGIN_SLOT + && table->total <= legacy_bbm->max_reloc_entry) + return 0; + else + return 1; + break; + + case BBM_NEW: + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + part = find_part(mtd, ofs); + if (part >= 0) { + if (new_bbm->rbbt[part].entry_num + < new_bbm->max_reloc_entry[part]) + return 0; + else + return 1; + } + default: + break; + } + } + + return 0; +} + +int pxa3xx_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct pxa3xx_bbm *bbm = mtd->bbm; + int ret; + + if (!should_reloc) + return 0; + + if (bbm) { + if (bbm->bbm_type != BBM_LEGACY && bbm->bbm_type != BBM_NEW) { + printk(KERN_WARNING "There is no way" + " to mark bad at %llx", ofs); + return 0; + } + + if (bbm->is_init == BBT_NOINIT) { + printk(KERN_WARNING "You should scan bbm first!!\n"); + return 0; + } + + ret = sync_pxa3xx_bbt(mtd, ofs); + if (!ret && !bbm->no_sync) + ret = pxa3xx_update_bbt(mtd, 0); + + return ret; + } + else { + printk(KERN_ERR "Unable to mark bad block at %llx\n", ofs); + return -EFAULT; + } +} + +static int recover_legacy_bbm(struct mtd_info *mtd, int backup) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct reloc_table *table; + struct erase_info instr = { + .callback = NULL, + }; + int backup_size, ret = 0; + loff_t ofs; + void *buf; + size_t retlen; + + backup_size = mtd->writesize * PXA_BEGIN_SLOT; + bbm->is_init = BBT_FORCE_NOINIT; + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + legacy_bbm->current_slot = mtd->erasesize >> mtd->writesize_shift; + table = legacy_bbm->table; + table->header = PXA_RELOC_HEADER; + table->total = 0; + + if (backup) { + buf = kzalloc(backup_size, GFP_KERNEL); + if (!buf) { + printk(KERN_ERR "MEM alloc failed!!\n"); + return -ENOMEM; + } + printk(KERN_INFO "Ready to read.."); + mtd->read(mtd, 0, backup_size, &retlen, buf); + } + + instr.addr = 0; + instr.len = mtd->erasesize; + instr.callback = pxa3xx_bbm_callback; + printk(KERN_INFO "erasing.."); + + should_reloc = 0; + mtd->erase(mtd, &instr); + should_reloc = 1; + if (!erase_success) { + printk(KERN_ERR "erase block 0 failed!!!\n"); + return -EFAULT; + } + + if (backup) { + printk(KERN_INFO "write back.."); + mtd->write(mtd, 0, backup_size, &retlen, buf); + kfree(buf); + } + + printk(KERN_INFO "collect bad info.."); + for (ofs = mtd->erasesize; ofs < mtd->size; ofs += mtd->erasesize) + if (checkbad(mtd, ofs)) { + printk(KERN_INFO "\nmark %llx as bad in bbt\n", ofs); + sync_pxa3xx_bbt(mtd, ofs); + } + + if (!bbm->no_sync) { + printk(KERN_INFO "update bbt.."); + ret = pxa3xx_update_bbt(mtd, 0); + } + printk(KERN_INFO "done\n"); + + return ret; +} + +static int update_fbbt(struct pxa3xx_bbt *fbbt, int block) +{ + uint32_t *fact_bad = (uint32_t *)&fbbt->fact_bad; + int i; + + for (i = 0; i < fbbt->entry_num; i ++) + if (fact_bad[i] == block) + return 0; + + fact_bad[i] = block; + fbbt->entry_num ++; + + return fbbt->entry_num; +} + +/* + * recover_new_bbm only try to rebuild the fbbt and use the + * default partition table to build the pt + */ +static int recover_new_bbm(struct mtd_info *mtd, struct reloc_item * item, + int num, int reserve_last_page) +{ + struct pxa3xx_bbm *bbm = (struct pxa3xx_bbm *)mtd->bbm; + struct pxa3xx_new_bbm *new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + struct pxa3xx_bbt *fbbt = new_bbm->fbbt; + struct pxa3xx_part *part = new_bbm->part; + struct erase_info instr = { + .callback = NULL, + }; + int boot_block, block, total_block, reserved_block, ret; + int rbbt, rbbt_back, max_reloc_entry, len, failed = 0; + loff_t ofs; + size_t retlen; + u_char *backup_buf = NULL; + + /* + * This should be the most init state + * should try to find two good blocks without the fbbt's help + * then build up a new fbbt + */ + backup_buf = kmalloc(mtd->erasesize, GFP_KERNEL); + if (!backup_buf) { + printk(KERN_ERR "Fail to allocate recovery memory!!\n"); + return -ENOMEM; + } + bbm->is_init = BBT_FORCE_NOINIT; + if (new_bbm->main_block == -1) { + memset(new_bbm->rbbt, 0xff, mtd->writesize); + new_bbm->rbbt->ident = PXA_NEW_BBM_HEADER; + new_bbm->rbbt->type = BBT_TYPE_RUNT; + if (item != NULL && num > 0) { + memcpy(&(new_bbm->rbbt->reloc), (void *)item, + sizeof(struct reloc_item) * num); + new_bbm->rbbt->entry_num = num; + } + else + new_bbm->rbbt->entry_num = 0; + max_reloc_entry = (mtd->writesize - sizeof(struct pxa3xx_bbt)) + / sizeof(struct reloc_item) + 1; + + fbbt->ident = PXA_NEW_BBM_HEADER; + fbbt->type = BBT_TYPE_FACT; + fbbt->entry_num = 0; + instr.len = mtd->erasesize; + instr.callback = pxa3xx_bbm_callback; + printk(KERN_INFO "Rebuild new bbm as init state..\n"); + for (boot_block = 0; boot_block < BOOT_PRAT_MAX; boot_block ++) { + if (failed) { + ofs = (uint64_t)(boot_block - 1) << mtd->erasesize_shift; + new_bbm->main_block = -1; + update_fbbt(fbbt, boot_block - 1); + failed = 0; + } + instr.addr = (uint64_t)boot_block << mtd->erasesize_shift; + ret = mtd->read(mtd, instr.addr, mtd->erasesize, + &retlen, backup_buf); + if (ret) { + printk(KERN_ERR "Cannot backup block %d!!\n", boot_block); + failed = 1; + continue; + } + if (!reserve_last_page) + memset(backup_buf + mtd->erasesize - mtd->writesize, 0xff, + mtd->writesize); + + should_reloc = 0; + mtd->erase(mtd, &instr); + should_reloc = 1; + if (!erase_success) { + printk(KERN_ERR "erase %llx failed!!\n", instr.addr); + failed = 1; + continue; + } + else { + ret = mtd->write(mtd, instr.addr, + mtd->writesize * PXA_BEGIN_SLOT, + &retlen, backup_buf); + if (ret) { + printk(KERN_ERR "restore backup two page failed!!\n"); + failed = 1; + continue; + } + new_bbm->main_block = boot_block; + } + + printk(KERN_INFO "Get main block at %d\n", new_bbm->main_block); + part->identifier = (uint64_t)PXA_PART_IDET_2 << 32 | PXA_PART_IDET_1; + + // calculate part range under defaut setting of only one part + // The first BOOT_PRAT_MAX block should be used as boot partition + // and next two block should be reversed as run time bbt + part->part_num = 1; + new_bbm->partinfo->type = PART_LOGI; + total_block = mtd_div_by_eb(mtd->size, mtd); + rbbt = rbbt_back = -1; + instr.callback = pxa3xx_bbm_callback; + instr.len = mtd->erasesize; + for (block = BOOT_PRAT_MAX; block < total_block; block ++) { + instr.addr = (uint64_t)block << mtd->erasesize_shift; + should_reloc = 0; + mtd->erase(mtd, &instr); + should_reloc = 1; + if (!erase_success) { + printk(KERN_ERR "Erase %llx failed!!\n", instr.addr); + sync_pxa3xx_bbt(mtd, instr.addr); + update_fbbt(fbbt, block); + } + else { + ofs = (uint64_t)block << mtd->erasesize_shift; + if (rbbt == -1) { + ret = mtd->write(mtd, ofs, mtd->writesize, + &retlen, (void *)new_bbm->rbbt); + if (ret) + continue; + rbbt = block; + } + else if (rbbt_back == -1) { + ret = mtd->write(mtd, ofs, mtd->writesize, + &retlen, (void *)new_bbm->rbbt); + if (ret) + continue; + rbbt_back = block ++; + break; + } + } + } + + printk(KERN_INFO "\nGet RBBT at block %d, its back at %d\n", + rbbt, rbbt_back); + reserved_block = ((total_block - block) / 100) + * NEW_BBM_RELOC_PERCENTAGE; + new_bbm->partinfo->start_addr = (uint64_t)block << mtd->erasesize_shift; + new_bbm->partinfo->end_addr = ((uint64_t)(total_block - reserved_block) + << mtd->erasesize_shift) - 1; + new_bbm->partinfo->rp_start = (uint64_t)(total_block - reserved_block) + << mtd->erasesize_shift; + new_bbm->partinfo->rp_size = (uint64_t)reserved_block << mtd->erasesize_shift; + new_bbm->partinfo->rp_algo = RP_UPWD; + new_bbm->partinfo->rbbt_type = PXA_NEW_BBM_HEADER; + new_bbm->partinfo->rbbt_start = (uint64_t)rbbt + << mtd->erasesize_shift; + new_bbm->partinfo->rbbt_start_back = (uint64_t)rbbt_back + << mtd->erasesize_shift; + + new_bbm->rbbt_offset[0] = mtd_div_by_ws(new_bbm->partinfo->rbbt_start, mtd); + new_bbm->max_reloc_entry[0] = (max_reloc_entry < reserved_block) ? + max_reloc_entry : reserved_block; + + ofs = (PXA_BEGIN_SLOT << mtd->writesize_shift) + + ((uint64_t)new_bbm->main_block << mtd->erasesize_shift); + + printk(KERN_INFO "\nBegin to write main block..\n"); + ret = mtd->write(mtd, ofs, mtd->writesize, &retlen, (void *)fbbt); + if (ret) { + printk(KERN_ERR "Write fbbt failed at %llx\n", ofs); + failed = 1; + continue; + } + + ofs = ((PXA_BEGIN_SLOT + 1) << mtd->writesize_shift) + + ((uint64_t)new_bbm->main_block << mtd->erasesize_shift); + ret = mtd->write(mtd, ofs, mtd->writesize, &retlen, (void *)part); + if (ret) { + printk(KERN_ERR "Write part failed at %llx\n", ofs); + failed = 1; + continue; + } + + ofs = ((PXA_BEGIN_SLOT + 2) << mtd->writesize_shift) + + ((uint64_t)new_bbm->main_block << mtd->erasesize_shift); + len = mtd->erasesize - (mtd->writesize * (PXA_BEGIN_SLOT + 2)); + ret = mtd->write(mtd, ofs, len, &retlen, backup_buf + + (mtd->writesize * (PXA_BEGIN_SLOT + 2))); + if (ret) { + printk(KERN_ERR "restore obm part failed!!\n"); + failed = 1; + } + else + break; + } + + if (boot_block >= BOOT_PRAT_MAX) { + new_bbm->main_block = -1; + printk(KERN_ERR "There is no good blocks in first %d" + " blocks!\n You should use another" + " flash now!!\n", BOOT_PRAT_MAX); + return -EFAULT; + } + } + + /* + * try to find a good block with fbbt's help + * and back the main block to back block + */ + if (new_bbm->back_block == -1) { + ofs = (uint64_t)new_bbm->main_block << mtd->erasesize_shift; + ret = mtd->read(mtd, ofs, mtd->erasesize, &retlen, backup_buf); + if (ret) { + printk(KERN_ERR "Cannot load main boot block!!\n"); + return -EFAULT; + } + + instr.callback = pxa3xx_bbm_callback; + instr.len = mtd->erasesize; + instr.addr = 0; + for (block = 0; block < BOOT_PRAT_MAX; block ++, + instr.addr += mtd->erasesize) { + if (block == new_bbm->main_block + || boot_part_bad(mtd, instr.addr)) + continue; + + ret = mtd->erase(mtd, &instr); + if (!ret) { + printk(KERN_INFO "Got backup block at block %d\n", block); + printk(KERN_INFO "\nBegin to write backup block..\n"); + ret = mtd->write(mtd, instr.addr, mtd->erasesize, + &retlen, backup_buf); + if (ret) { + printk("Failed to backup to %llx\n", instr.addr); + continue; + } + + new_bbm->back_block = block; + break; + } + } + + if (new_bbm->back_block == -1) + printk(KERN_WARNING "Unable to recover backup boot block!!\n"); + } + + if (backup_buf) + kfree(backup_buf); + + printk(KERN_INFO "done!!\n"); + return 0; +} + +/* + * bbm_type: + * BBM_NONE: recover the bbm according to original setting + * BBM_LEGACY: recover bbm as legacy bbm + * BBM_NEW: recover bbm as new bbm + */ +int pxa3xx_bbm_recovery(struct mtd_info *mtd, int bbm_type, struct reloc_item *item, + int num, int reserve_last_page) +{ + struct pxa3xx_bbm *bbm = mtd->bbm; + int ret; + + if (bbm && bbm->bbm_type != bbm_type) { + pxa3xx_uninit_reloc_tb(mtd); + bbm = mtd->bbm; + } + + if (!bbm) { + ret = pxa3xx_init_bbm(mtd, bbm_type); + if (ret) { + printk(KERN_ERR "Init failed!!!\n"); + return -EFAULT; + } + } + + if (bbm_type == BBM_NONE) + bbm_type = bbm->bbm_type; + + switch (bbm_type) { + case BBM_LEGACY: + printk(KERN_INFO "Ready to recover bbm as legacy!\n"); + ret = recover_legacy_bbm(mtd, 1); + break; + + case BBM_NEW: + printk(KERN_INFO "Ready to recover bbm as new!\n"); + ret = recover_new_bbm(mtd, item, num, reserve_last_page); + break; + + case BBM_NONE: + default: + printk(KERN_ERR "Cannot fulfill recovery bbm task!!!\n"); + ret = -EFAULT; + } + + return ret; +} + +static char *bbm_name = "MRVL_BBM"; +static int do_check_part(struct mtd_info *mtd, struct mtd_partition *part_orig, + struct mtd_partition *part, int *num) +{ + struct pxa3xx_bbm *bbm = mtd->bbm; + struct pxa3xx_new_bbm *new_bbm; + struct pxa3xx_legacy_bbm *legacy_bbm; + struct pxa3xx_partinfo *partinfo; + uint64_t boundary_offset, orig_size; + int reloc_boundary, i, j, err, last_add, last_add_orig; + + if (bbm->bbm_type == BBM_LEGACY) { + legacy_bbm = (struct pxa3xx_legacy_bbm *)bbm->data_buf; + reloc_boundary = mtd_div_by_eb(mtd->size, mtd) + - legacy_bbm->max_reloc_entry; + boundary_offset = (uint64_t)reloc_boundary << mtd->erasesize_shift; + + if (boundary_offset < part[*num - 1].offset) { + printk(KERN_ERR "The last part overlay with the reserved area!!\n"); + return -EFAULT; + } + + memcpy(part, part_orig, *num * sizeof(struct mtd_partition)); + if (part[*num - 1].size == MTDPART_SIZ_FULL || + (boundary_offset > part[*num - 1].size + + part[*num - 1].offset)) { + + part[*num - 1].size = boundary_offset - part[*num - 1].offset; + part[*num].name = bbm_name; + part[*num].offset = boundary_offset; + part[*num].size = MTDPART_SIZ_FULL; + part[*num].mask_flags = MTD_WRITEABLE; + *num = *num + 1; + } + return 0; + } + + /* + * The following is for new BBM scheme + * reserved pool should be included in one of defined partition, + * or would cause chech fail + */ + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + last_add_orig = last_add = err = 0; + for (i = 0, j = 0; i < new_bbm->part->part_num && j < *num && !err; i ++) { + partinfo = &new_bbm->partinfo[i]; + for (; j < *num; j ++) { + if (part_orig[j].size == MTDPART_SIZ_FULL) + orig_size = mtd->size - part_orig[j].offset; + else + orig_size = part_orig[j].size; + + if ((orig_size + part_orig[j].offset) + < partinfo->rp_start) + continue; + if (part_orig[j].offset > partinfo->rp_start) { + err = 1; + break; + } + if ((orig_size + part_orig[j].offset) + != (partinfo->rp_start + partinfo->rp_size)) { + err = 1; + break; + } + else { + memcpy(&part[last_add], &part_orig[last_add_orig], + (j - last_add + 1) * sizeof(struct mtd_partition)); + last_add += (j - last_add_orig) + 1; + last_add_orig = j; + part[last_add - 1].size = partinfo->rp_start + - part[last_add -1].offset; + part[last_add].name = bbm_name; + part[last_add].offset = partinfo->rp_start; + part[last_add].size = partinfo->rp_size; + part[last_add].mask_flags = MTD_WRITEABLE; + last_add += 1; + } + } + } + + if (!err) + *num += (last_add - last_add_orig - 1); + + return err; +} + +static struct mtd_partition *pxa3xx_check_partition(struct mtd_info *mtd, + struct mtd_partition *part, int *num) +{ + struct pxa3xx_bbm *bbm = mtd->bbm; + struct pxa3xx_new_bbm *new_bbm; + struct mtd_partition *new_part; + int part_num, alloc_size; + + if (bbm->bbm_type == BBM_LEGACY) + part_num = 1; + else { + new_bbm = (struct pxa3xx_new_bbm *)bbm->data_buf; + part_num = new_bbm->part->part_num; + } + + alloc_size = (*num + part_num) * sizeof(struct mtd_partition); + new_part = kzalloc(alloc_size, GFP_KERNEL); + if (!new_part) { + printk(KERN_ERR "OUT of memory!!\n"); + return NULL; + } + + if (!do_check_part(mtd, part, new_part, num)) + return new_part; + else + return NULL; +} + +EXPORT_SYMBOL(pxa3xx_block_bad); +EXPORT_SYMBOL(pxa3xx_block_markbad); +EXPORT_SYMBOL(pxa3xx_scan_bbt); +EXPORT_SYMBOL(pxa3xx_update_bbt); diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 56ca62bb96bf5e..757b3a4e23507b 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -387,6 +387,10 @@ static int __init mtd_speedtest_init(void) speed = calc_speed(); printk(PRINT_PREF "eraseblock write speed is %ld KiB/s\n", speed); + err = erase_whole_device(); + if (err) + goto out; + /* Read all eraseblocks, 1 eraseblock at a time */ printk(PRINT_PREF "testing eraseblock read speed\n"); start_timing(); @@ -406,6 +410,9 @@ static int __init mtd_speedtest_init(void) if (err) goto out; + simple_srand(1); + set_random_data(iobuf, mtd->erasesize); + /* Write all eraseblocks, 1 page at a time */ printk(PRINT_PREF "testing page write speed\n"); start_timing(); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7b832c727f873b..43d73a1718c8a2 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -920,6 +920,12 @@ config SMC91X The module will be called smc91x. If you want to compile it as a module, say M here and read . +config PXA168_ETH + tristate "Marvell pxa168 ethernet support" + select MII + help + This driver supports the pxa168 ethernet + config NET_NETX tristate "NetX Ethernet support" select MII @@ -3191,6 +3197,23 @@ config PPPOL2TP and session setup). One such daemon is OpenL2TP (http://openl2tp.sourceforge.net/). +config PPPOLAC + tristate "PPP on L2TP Access Concentrator" + depends on PPP && INET + help + L2TP (RFC 2661) is a tunneling protocol widely used in virtual private + networks. This driver handles L2TP data packets between a UDP socket + and a PPP channel, but only permits one session per socket. Thus it is + fairly simple and suited for clients. + +config PPPOPNS + tristate "PPP on PPTP Network Server" + depends on PPP && INET + help + PPTP (RFC 2637) is a tunneling protocol widely used in virtual private + networks. This driver handles PPTP data packets between a RAW socket + and a PPP channel. It is fairly simple and easy to use. + config SLIP tristate "SLIP (serial line) support" ---help--- diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 12b280afdd5186..49b70112033497 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -162,6 +162,8 @@ obj-$(CONFIG_PPP_BSDCOMP) += bsd_comp.o obj-$(CONFIG_PPP_MPPE) += ppp_mppe.o obj-$(CONFIG_PPPOE) += pppox.o pppoe.o obj-$(CONFIG_PPPOL2TP) += pppox.o pppol2tp.o +obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o +obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o obj-$(CONFIG_SLIP) += slip.o obj-$(CONFIG_SLHC) += slhc.o @@ -242,6 +244,7 @@ obj-$(CONFIG_S2IO) += s2io.o obj-$(CONFIG_VXGE) += vxge/ obj-$(CONFIG_MYRI10GE) += myri10ge/ obj-$(CONFIG_SMC91X) += smc91x.o +obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o obj-$(CONFIG_SMC911X) += smc911x.o obj-$(CONFIG_SMSC911X) += smsc911x.o obj-$(CONFIG_BFIN_MAC) += bfin_mac.o diff --git a/drivers/net/pppolac.c b/drivers/net/pppolac.c new file mode 100644 index 00000000000000..af3202a920a005 --- /dev/null +++ b/drivers/net/pppolac.c @@ -0,0 +1,380 @@ +/* drivers/net/pppolac.c + * + * Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661) + * + * Copyright (C) 2009 Google, Inc. + * + * 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. + */ + +/* This driver handles L2TP data packets between a UDP socket and a PPP channel. + * To keep things simple, only one session per socket is permitted. Packets are + * sent via the socket, so it must keep connected to the same address. One must + * not set sequencing in ICCN but let LNS controll it. Currently this driver + * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define L2TP_CONTROL_BIT 0x80 +#define L2TP_LENGTH_BIT 0x40 +#define L2TP_SEQUENCE_BIT 0x08 +#define L2TP_OFFSET_BIT 0x02 +#define L2TP_VERSION 0x02 +#define L2TP_VERSION_MASK 0x0F + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +union unaligned { + __u32 u32; +} __attribute__((packed)); + +static inline union unaligned *unaligned(void *ptr) +{ + return (union unaligned *)ptr; +} + +static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *)sk_udp->sk_user_data; + struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac; + __u8 bits; + __u8 *ptr; + + /* Drop the packet if it is too short. */ + if (skb->len < sizeof(struct udphdr) + 6) + goto drop; + + /* Put it back if it is a control packet. */ + if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT) + return opt->backlog_rcv(sk_udp, skb); + + /* Skip UDP header. */ + skb_pull(skb, sizeof(struct udphdr)); + + /* Check the version. */ + if ((skb->data[1] & L2TP_VERSION_MASK) != L2TP_VERSION) + goto drop; + bits = skb->data[0]; + ptr = &skb->data[2]; + + /* Check the length if it is present. */ + if (bits & L2TP_LENGTH_BIT) { + if ((ptr[0] << 8 | ptr[1]) != skb->len) + goto drop; + ptr += 2; + } + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) + + (bits & L2TP_LENGTH_BIT ? 2 : 0) + + (bits & L2TP_OFFSET_BIT ? 2 : 0))) + goto drop; + + /* Skip the offset padding if it is present. */ + if (bits & L2TP_OFFSET_BIT && + !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1])) + goto drop; + + /* Check the tunnel and the session. */ + if (unaligned(ptr)->u32 != opt->local) + goto drop; + + /* Check the sequence if it is present. According to RFC 2661 section + * 5.4, the only thing to do is to update opt->sequencing. */ + opt->sequencing = bits & L2TP_SEQUENCE_BIT; + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Finally, deliver the packet to PPP channel. */ + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + return NET_RX_SUCCESS; +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb) +{ + sock_hold(sk_udp); + sk_receive_skb(sk_udp, skb, 0); + return 0; +} + +static struct sk_buff_head delivery_queue; + +static void pppolac_xmit_core(struct work_struct *delivery_work) +{ + mm_segment_t old_fs = get_fs(); + struct sk_buff *skb; + + set_fs(KERNEL_DS); + while ((skb = skb_dequeue(&delivery_queue))) { + struct sock *sk_udp = skb->sk; + struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; + struct msghdr msg = { + .msg_iov = (struct iovec *)&iov, + .msg_iovlen = 1, + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len); + kfree_skb(skb); + } + set_fs(old_fs); +} + +static DECLARE_WORK(delivery_work, pppolac_xmit_core); + +static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_udp = (struct sock *)chan->private; + struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + + /* Install L2TP header. */ + if (opt->sequencing) { + skb_push(skb, 10); + skb->data[0] = L2TP_SEQUENCE_BIT; + skb->data[6] = opt->sequence >> 8; + skb->data[7] = opt->sequence; + skb->data[8] = 0; + skb->data[9] = 0; + opt->sequence++; + } else { + skb_push(skb, 6); + skb->data[0] = 0; + } + skb->data[1] = L2TP_VERSION; + unaligned(&skb->data[2])->u32 = opt->remote; + + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_udp); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppolac_channel_ops = { + .start_xmit = pppolac_xmit, +}; + +static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppolac *addr = (struct sockaddr_pppolac *)useraddr; + struct socket *sock_udp = NULL; + struct sock *sk_udp; + int error; + + if (addrlen != sizeof(struct sockaddr_pppolac) || + !addr->local.tunnel || !addr->local.session || + !addr->remote.tunnel || !addr->remote.session) { + return -EINVAL; + } + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_udp = sockfd_lookup(addr->udp_socket, &error); + if (!sock_udp) + goto out; + sk_udp = sock_udp->sk; + lock_sock(sk_udp); + + /* Remove this check when IPv6 supports UDP encapsulation. */ + error = -EAFNOSUPPORT; + if (sk_udp->sk_family != AF_INET) + goto out; + error = -EPROTONOSUPPORT; + if (sk_udp->sk_protocol != IPPROTO_UDP) + goto out; + error = -EDESTADDRREQ; + if (sk_udp->sk_state != TCP_ESTABLISHED) + goto out; + error = -EBUSY; + if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data) + goto out; + if (!sk_udp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_udp); + error = -ENODEV; + if (!dst) + goto out; + sk_udp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } + + po->chan.hdrlen = 12; + po->chan.private = sk_udp; + po->chan.ops = &pppolac_channel_ops; + po->chan.mtu = PPP_MTU - 80; + po->proto.lac.local = unaligned(&addr->local)->u32; + po->proto.lac.remote = unaligned(&addr->remote)->u32; + po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP; + udp_sk(sk_udp)->encap_rcv = pppolac_recv; + sk_udp->sk_backlog_rcv = pppolac_recv_core; + sk_udp->sk_user_data = sk; +out: + if (sock_udp) { + release_sock(sk_udp); + if (error) + sockfd_put(sock_udp); + } + release_sock(sk); + return error; +} + +static int pppolac_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_udp); + pppox_unbind_sock(sk); + udp_sk(sk_udp)->encap_type = 0; + udp_sk(sk_udp)->encap_rcv = NULL; + sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv; + sk_udp->sk_user_data = NULL; + release_sock(sk_udp); + sockfd_put(sk_udp->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppolac_proto = { + .name = "PPPOLAC", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppolac_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppolac_release, + .bind = sock_no_bind, + .connect = pppolac_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppolac_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppolac_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppolac_proto_ops; + sk->sk_protocol = PX_PROTO_OLAC; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppolac_pppox_proto = { + .create = pppolac_create, + .owner = THIS_MODULE, +}; + +static int __init pppolac_init(void) +{ + int error; + + error = proto_register(&pppolac_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto); + if (error) + proto_unregister(&pppolac_proto); + else + skb_queue_head_init(&delivery_queue); + return error; +} + +static void __exit pppolac_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OLAC); + proto_unregister(&pppolac_proto); +} + +module_init(pppolac_init); +module_exit(pppolac_exit); + +MODULE_DESCRIPTION("PPP on L2TP Access Concentrator (PPPoLAC)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/pppopns.c b/drivers/net/pppopns.c new file mode 100644 index 00000000000000..298097127c90fe --- /dev/null +++ b/drivers/net/pppopns.c @@ -0,0 +1,357 @@ +/* drivers/net/pppopns.c + * + * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) + * + * Copyright (C) 2009 Google, Inc. + * + * 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. + */ + +/* This driver handles PPTP data packets between a RAW socket and a PPP channel. + * The socket is created in the kernel space and connected to the same address + * of the control socket. To keep things simple, packets are always sent with + * sequence but without acknowledgement. This driver should work on both IPv4 + * and IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRE_HEADER_SIZE 8 + +#define PPTP_GRE_BITS htons(0x2001) +#define PPTP_GRE_BITS_MASK htons(0xEF7F) +#define PPTP_GRE_SEQ_BIT htons(0x1000) +#define PPTP_GRE_ACK_BIT htons(0x0080) +#define PPTP_GRE_TYPE htons(0x880B) + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +struct header { + __u16 bits; + __u16 type; + __u16 length; + __u16 call; + __u32 sequence; +} __attribute__((packed)); + +static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) +{ + struct sock *sk = (struct sock *)sk_raw->sk_user_data; + struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; + struct header *hdr; + + /* Skip transport header */ + skb_pull(skb, skb_transport_header(skb) - skb->data); + + /* Drop the packet if it is too short. */ + if (skb->len < GRE_HEADER_SIZE) + goto drop; + + /* Check the header. */ + hdr = (struct header *)skb->data; + if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || + (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) + goto drop; + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, GRE_HEADER_SIZE + + (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) + + (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0))) + goto drop; + + /* Check the length. */ + if (skb->len != ntohs(hdr->length)) + goto drop; + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Finally, deliver the packet to PPP channel. */ + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + return NET_RX_SUCCESS; +drop: + kfree_skb(skb); + return NET_RX_DROP; +} + +static void pppopns_recv(struct sock *sk_raw, int length) +{ + struct sk_buff *skb; + while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { + sock_hold(sk_raw); + sk_receive_skb(sk_raw, skb, 0); + } +} + +static struct sk_buff_head delivery_queue; + +static void pppopns_xmit_core(struct work_struct *delivery_work) +{ + mm_segment_t old_fs = get_fs(); + struct sk_buff *skb; + + set_fs(KERNEL_DS); + while ((skb = skb_dequeue(&delivery_queue))) { + struct sock *sk_raw = skb->sk; + struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len}; + struct msghdr msg = { + .msg_iov = (struct iovec *)&iov, + .msg_iovlen = 1, + .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT, + }; + sk_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); + kfree_skb(skb); + } + set_fs(old_fs); +} + +static DECLARE_WORK(delivery_work, pppopns_xmit_core); + +static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_raw = (struct sock *)chan->private; + struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; + struct header *hdr; + __u16 length; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + length = skb->len; + + /* Install PPTP GRE header. */ + hdr = (struct header *)skb_push(skb, 12); + hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT; + hdr->type = PPTP_GRE_TYPE; + hdr->length = htons(length); + hdr->call = opt->remote; + hdr->sequence = htonl(opt->sequence); + opt->sequence++; + + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_raw); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppopns_channel_ops = { + .start_xmit = pppopns_xmit, +}; + +static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr; + struct sockaddr_storage ss; + struct socket *sock_tcp = NULL; + struct socket *sock_raw = NULL; + struct sock *sk_tcp; + struct sock *sk_raw; + int error; + + if (addrlen != sizeof(struct sockaddr_pppopns)) + return -EINVAL; + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_tcp = sockfd_lookup(addr->tcp_socket, &error); + if (!sock_tcp) + goto out; + sk_tcp = sock_tcp->sk; + error = -EPROTONOSUPPORT; + if (sk_tcp->sk_protocol != IPPROTO_TCP) + goto out; + addrlen = sizeof(struct sockaddr_storage); + error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); + if (error) + goto out; + if (!sk_tcp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_tcp); + error = -ENODEV; + if (!dst) + goto out; + sk_tcp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } + + error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); + if (error) + goto out; + sk_raw = sock_raw->sk; + sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if; + error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); + if (error) + goto out; + + po->chan.hdrlen = 14; + po->chan.private = sk_raw; + po->chan.ops = &pppopns_channel_ops; + po->chan.mtu = PPP_MTU - 80; + po->proto.pns.local = addr->local; + po->proto.pns.remote = addr->remote; + po->proto.pns.data_ready = sk_raw->sk_data_ready; + po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + lock_sock(sk_raw); + sk_raw->sk_data_ready = pppopns_recv; + sk_raw->sk_backlog_rcv = pppopns_recv_core; + sk_raw->sk_user_data = sk; + release_sock(sk_raw); +out: + if (sock_tcp) + sockfd_put(sock_tcp); + if (error && sock_raw) + sock_release(sock_raw); + release_sock(sk); + return error; +} + +static int pppopns_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_raw); + pppox_unbind_sock(sk); + sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; + sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; + sk_raw->sk_user_data = NULL; + release_sock(sk_raw); + sock_release(sk_raw->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppopns_proto = { + .name = "PPPOPNS", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppopns_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppopns_release, + .bind = sock_no_bind, + .connect = pppopns_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppopns_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppopns_proto_ops; + sk->sk_protocol = PX_PROTO_OPNS; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppopns_pppox_proto = { + .create = pppopns_create, + .owner = THIS_MODULE, +}; + +static int __init pppopns_init(void) +{ + int error; + + error = proto_register(&pppopns_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); + if (error) + proto_unregister(&pppopns_proto); + else + skb_queue_head_init(&delivery_queue); + return error; +} + +static void __exit pppopns_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OPNS); + proto_unregister(&pppopns_proto); +} + +module_init(pppopns_init); +module_exit(pppopns_exit); + +MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/pxa168_eth.c b/drivers/net/pxa168_eth.c new file mode 100644 index 00000000000000..b063ebcc1428db --- /dev/null +++ b/drivers/net/pxa168_eth.c @@ -0,0 +1,2186 @@ +/* + * driver/net/pxa168_eth.c + * + * Base on driver/net/mv643xx_eth.c + * + * Copyright (C) 2009 Marvell International Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#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 + +#define NAPI_PMR + +#if defined(MFU_TIMER) && defined(NAPI_PMR) +#error only MFU_TIMER or NAPI_PMR can be defined +#endif + +#define DRIVER_NAME "pxa168-mfu" +#define DRIVER_VERSION "0.1" + +/* + * Registers + */ + +#define PHY_ADDRESS 0x0000 +#define SMI 0x0010 +#define PORT_CONFIG 0x0400 +#define PORT_CONFIG_EXT 0x0408 +#define PORT_COMMAND 0x0410 +#define PORT_STATUS 0x0418 +#define HTPR 0x0428 +#define SDMA_CONFIG 0x0440 +#define SDMA_CMD 0x0448 +#define INT_CAUSE 0x0450 +#define INT_W_CLEAR 0x0454 +#define INT_MASK 0x0458 +#define ETH_F_RX_DESC_0 0x0480 +#define ETH_C_RX_DESC_0 0x04A0 +#define ETH_C_TX_DESC_1 0x04E4 + +/* smi register */ +#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */ +#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */ +#define SMI_OP_W (0<<26) /* Write operation */ +#define SMI_OP_R (1<<26) /* Read operation */ + +#define PHY_WAIT_ITERATIONS 1000 /* 1000 iterations * 10uS = 10mS max */ +#define PHY_WAIT_MICRO_SECONDS 10 + +/* RX & TX descriptor command */ +#define BUF_OWNED_BY_DMA (1<<31) + +/* RX descriptor status */ +#define RX_EN_INT (1<<23) +#define RX_FIRST_DESC (1<<17) +#define RX_LAST_DESC (1<<16) +#define RX_ERROR (1<<15) + +/* TX descriptor command */ +#define TX_EN_INT (1<<23) +#define TX_GEN_CRC (1<<22) +#define TX_ZERO_PADDING (1<<18) +#define TX_FIRST_DESC (1<<17) +#define TX_LAST_DESC (1<<16) +#define TX_ERROR (1<<15) + + +/* SDMA_CMD */ +#define SDMA_CMD_AT (1<<31) +#define SDMA_CMD_TXDL (1<<24) +#define SDMA_CMD_TXDH (1<<23) +#define SDMA_CMD_AR (1<<15) +#define SDMA_CMD_ERD (1<<7) + + +/* Bit definitions of the Port Config Reg */ +#define PCR_HS (1<<12) +#define PCR_EN (1<<7) +#define PCR_PM (1<<0) + +/* Bit definitions of the Port Config Extend Reg */ +#define PCXR_2BSM (1<<28) +#define PCXR_DSCP_EN (1<<21) +#define PCXR_MFL_1518 (0<<14) +#define PCXR_MFL_1536 (1<<14) +#define PCXR_MFL_2048 (2<<14) +#define PCXR_MFL_64K (3<<14) +#define PCXR_FLP (1<<11) +#define PCXR_PRIO_TX_OFF 3 +#define PCXR_TX_HIGH_PRI (7<base + offset); +} + +static inline void wrl(struct pxa168_private *mp, int offset, u32 data) +{ + writel(data, mp->base + offset); +} + +static void abortDMA(struct pxa168_private *mp) +{ + int delay; + int maxRetries = 40; + + do { + wrl(mp, SDMA_CMD, SDMA_CMD_AR | SDMA_CMD_AT); + udelay(100); + + delay = 10; + while ((rdl(mp, SDMA_CMD) & (SDMA_CMD_AR | SDMA_CMD_AT)) + && delay-- > 0) { + udelay(10); + } + } while (maxRetries-- > 0 && delay <= 0); + + if (maxRetries <= 0) + printk(KERN_ERR "%s : DMA Stuck\n", __func__); +} + + +static int ethernet_phy_get(struct pxa168_private *mp) +{ + unsigned int reg_data; + + /* only support 3 ports */ + BUG_ON(mp->port_num > 2); + + reg_data = rdl(mp, PHY_ADDRESS); + + return (reg_data >> (5 * mp->port_num)) & 0x1f; +} + + +static int eth_port_read_smi_reg(struct pxa168_private *mp, + unsigned int phy_reg, unsigned int *value) +{ + int phy_addr = ethernet_phy_get(mp); + unsigned int val; + int i = 0; + + /* wait for the SMI register to become available */ + for (i = 0; (val = rdl(mp, SMI)) & SMI_BUSY; i++) { + + if (i == PHY_WAIT_ITERATIONS) { + printk(KERN_ERR "pxa168 PHY timeout, port %d, val=0x%x\n", + mp->port_num, val); + goto out; + } + udelay(PHY_WAIT_MICRO_SECONDS); + } + + wrl(mp, SMI, (phy_addr << 16) | (phy_reg << 21) | SMI_OP_R); + + /* now wait for the data to be valid */ + for (i = 0; !((val = rdl(mp, SMI)) & SMI_R_VALID); i++) { + if (i == PHY_WAIT_ITERATIONS) { + printk(KERN_ERR "pxa168 PHY RD timeout, port %d, val=0x%x\n", + mp->port_num, val); + goto out; + } + udelay(PHY_WAIT_MICRO_SECONDS); + } + *value = val & 0xffff; + + return 0; +out: + return -1; +} + + +static void eth_port_write_smi_reg(struct pxa168_private *mp, + unsigned int phy_reg, unsigned int value) +{ + int phy_addr; + int i; + + phy_addr = ethernet_phy_get(mp); + + /* the SMI register is a shared resource */ + + /* wait for the SMI register to become available */ + for (i = 0; rdl(mp, SMI) & SMI_BUSY; i++) { + if (i == PHY_WAIT_ITERATIONS) { + printk(KERN_ERR "pxa168 PHY busy timeout, port %d\n", + mp->port_num); + goto out; + } + udelay(PHY_WAIT_MICRO_SECONDS); + } + + wrl(mp, SMI, (phy_addr << 16) | (phy_reg << 21) | + SMI_OP_W | (value & 0xffff)); +out: + return; +} + +static void ethernet_phy_set(struct pxa168_private *mp) +{ + u32 reg_data; + u16 phy_addr = mp->phy_addr; + int addr_shift = 5 * mp->port_num; + + /* only support 3 ports */ + BUG_ON(mp->port_num > 2); + + reg_data = rdl(mp, PHY_ADDRESS); + reg_data &= ~(0x1f << addr_shift); + reg_data |= (phy_addr & 0x1f) << addr_shift; + wrl(mp, PHY_ADDRESS, reg_data); +} + +static void ethernet_phy_reset(struct pxa168_private *mp) +{ + unsigned int phy_reg_data; + + /* Reset the PHY */ + eth_port_read_smi_reg(mp, 0, &phy_reg_data); + phy_reg_data |= 0x8000; /* Set bit 15 to reset the PHY */ + eth_port_write_smi_reg(mp, 0, phy_reg_data); + + /* wait for PHY to come out of reset */ + do { + udelay(1); + eth_port_read_smi_reg(mp, 0, &phy_reg_data); + } while (phy_reg_data & 0x8000); +} + +static int ethernet_phy_detect(struct pxa168_private *mp) +{ + unsigned int val, tmp, mii_status; + int addr; + + for (addr = 0; addr < 32; addr++) { + + mp->mii.phy_id = ethernet_phy_get(mp); + + if (eth_port_read_smi_reg(mp, MII_BMSR, &mii_status) != 0) { + /* try next phy */ + mp->phy_addr = (mp->phy_addr + 1) % 32; + ethernet_phy_set(mp); + continue; + } + + /* invalid MII status. More validation required here... */ + if (mii_status == 0 || mii_status == 0xffff) { + /* try next phy */ + mp->phy_addr = (mp->phy_addr + 1) % 32; + ethernet_phy_set(mp); + continue; + } + + if (eth_port_read_smi_reg(mp, MII_PHYSID1, &tmp) != 0) { + /* try next phy */ + mp->phy_addr = (mp->phy_addr + 1) % 32; + ethernet_phy_set(mp); + continue; + } + + val = tmp << 16; + if (eth_port_read_smi_reg(mp, MII_PHYSID2, &tmp) != 0) { + /* try next phy */ + mp->phy_addr = (mp->phy_addr + 1) % 32; + ethernet_phy_set(mp); + continue; + } + + val |= tmp; + + if ((val & 0xfffffff0) != 0) { + /* printk(KERN_INFO "PHY found at addr %x\n", + mp->phy_addr); */ + return 0; + } + + mp->phy_addr = (mp->phy_addr + 1) % 32; + ethernet_phy_set(mp); + } + + return -1; +} + +static void rxq_refill(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + struct sk_buff *skb; + volatile struct rx_desc *p_used_rx_desc; + int used_rx_desc; /* Where to return Rx resource */ + unsigned long flags; + + while (mp->rx_desc_count < mp->rx_ring_size) { + + skb = dev_alloc_skb(MAX_PKT_SIZE + ETH_HW_IP_ALIGN); + if (!skb) + break; + + mp->rx_desc_count++; + + spin_lock_irqsave(&mp->lock, flags); + + /* Get 'used' Rx descriptor */ + used_rx_desc = mp->rx_used_desc_q; + p_used_rx_desc = &mp->p_rx_desc_area[used_rx_desc]; + + p_used_rx_desc->buf_ptr = dma_map_single(NULL, + skb->data, + MAX_PKT_SIZE + ETH_HW_IP_ALIGN, + DMA_FROM_DEVICE); + + p_used_rx_desc->buf_size = MAX_PKT_SIZE + ETH_HW_IP_ALIGN; + mp->rx_skb[used_rx_desc] = skb; + + /* Return the descriptor to DMA ownership */ + wmb(); + p_used_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT; + wmb(); + + /* Move the used descriptor pointer to the next descriptor */ + mp->rx_used_desc_q = (used_rx_desc + 1) % mp->rx_ring_size; + + /* Any Rx return cancels the Rx resource error status */ + mp->rx_resource_err = 0; + + spin_unlock_irqrestore(&mp->lock, flags); + + skb_reserve(skb, ETH_HW_IP_ALIGN); + } + + /* + * If RX ring is empty of SKB, set a timer to try allocating + * again at a later time. + */ + if (mp->rx_desc_count == 0) { + printk(KERN_INFO "%s: Rx ring is empty\n", dev->name); + mp->timeout.expires = jiffies + (HZ / 10); /* 100 mSec */ + add_timer(&mp->timeout); + } +} + +/* + * rxq_refill_timer_wrapper + * + * Timer routine to wake up RX queue filling task. This function is + * used only in case the RX queue is empty, and all alloc_skb has + * failed (due to out of memory event). + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static inline void rxq_refill_timer_wrapper(unsigned long data) +{ + rxq_refill((struct net_device *)data); +} +static inline u32 nibble_swapping_32_bit(u32 x) +{ + return (((x) & 0xf0f0f0f0) >> 4) | (((x) & 0x0f0f0f0f) << 4); +} + +static inline u32 nibble_swapping_16_bit(u32 x) +{ + return (((x) & 0x0000f0f0) >> 4) | (((x) & 0x00000f0f) << 4); +} + +static inline u32 flip_4_bits(u32 x) +{ + return (((x) & 0x01) << 3) | (((x) & 0x002) << 1) + | (((x) & 0x04) >> 1) | (((x) & 0x008) >> 3); +} + +/* + * ---------------------------------------------------------------------------- + * This function will calculate the hash function of the address. + * depends on the hash mode and hash size. + * Inputs + * macH - the 2 most significant bytes of the MAC address. + * macL - the 4 least significant bytes of the MAC address. + * Outputs + * return the calculated entry. + */ +static u32 hash_function(u32 macH, u32 macL) +{ + u32 hashResult; + u32 addrH; + u32 addrL; + u32 addr0; + u32 addr1; + u32 addr2; + u32 addr3; + u32 addrHSwapped; + u32 addrLSwapped; + + addrH = nibble_swapping_16_bit(macH); + addrL = nibble_swapping_32_bit(macL); + + addrHSwapped = flip_4_bits(addrH & 0xf) + + ((flip_4_bits((addrH >> 4) & 0xf)) << 4) + + ((flip_4_bits((addrH >> 8) & 0xf)) << 8) + + ((flip_4_bits((addrH >> 12) & 0xf)) << 12); + + addrLSwapped = flip_4_bits(addrL & 0xf) + + ((flip_4_bits((addrL >> 4) & 0xf)) << 4) + + ((flip_4_bits((addrL >> 8) & 0xf)) << 8) + + ((flip_4_bits((addrL >> 12) & 0xf)) << 12) + + ((flip_4_bits((addrL >> 16) & 0xf)) << 16) + + ((flip_4_bits((addrL >> 20) & 0xf)) << 20) + + ((flip_4_bits((addrL >> 24) & 0xf)) << 24) + + ((flip_4_bits((addrL >> 28) & 0xf)) << 28); + + addrH = addrHSwapped; + addrL = addrLSwapped; + + addr0 = (addrL >> 2) & 0x03f; + addr1 = (addrL & 0x003) | ((addrL >> 8) & 0x7f) << 2; + addr2 = (addrL >> 15) & 0x1ff; + addr3 = ((addrL >> 24) & 0x0ff) | ((addrH & 1) << 8); + + hashResult = (addr0 << 9) | (addr1 ^ addr2 ^ addr3); + hashResult = hashResult & 0x07ff; + return hashResult; +} + +/* + * ---------------------------------------------------------------------------- + * This function will add an entry to the address table. + * depends on the hash mode and hash size that was initialized. + * Inputs + * mp - ETHERNET . + * macH - the 2 most significant bytes of the MAC address. + * macL - the 4 least significant bytes of the MAC address. + * skip - if 1, skip this address. + * rd - the RD field in the address table. + * Outputs + * address table entry is added. + * 0 if success. + * -ENOSPC if table full + */ +static int add_del_hash_entry(struct pxa168_private *mp, u32 macH, + u32 macL, u32 rd, + u32 skip, int del) +{ + addr_table_entry_t *entry, *start; + u32 newHi; + u32 newLo; + u32 i; + u8 *last; + + newLo = (((macH >> 4) & 0xf) << 15) + | (((macH >> 0) & 0xf) << 11) + | (((macH >> 12) & 0xf) << 7) + | (((macH >> 8) & 0xf) << 3) + | (((macL >> 20) & 0x1) << 31) + | (((macL >> 16) & 0xf) << 27) + | (((macL >> 28) & 0xf) << 23) + | (((macL >> 24) & 0xf) << 19) + | (skip << hteSkip) | (rd << hteRDBit) + | hteValid; + + newHi = (((macL >> 4) & 0xf) << 15) + | (((macL >> 0) & 0xf) << 11) + | (((macL >> 12) & 0xf) << 7) + | (((macL >> 8) & 0xf) << 3) + | (((macL >> 21) & 0x7) << 0); + + /* + * Pick the appropriate table, start scanning for free/reusable + * entries at the index obtained by hashing the specified MAC address + */ + start = (addr_table_entry_t *)(mp->htpr); + entry = start + hash_function(macH, macL); + for (i = 0; i < HOP_NUMBER; i++) { + if (!(entry->lo & hteValid)) { + break; + } else { + /* if same address put in same position */ + if (((entry->lo & 0xfffffff8) == (newLo & 0xfffffff8)) + && (entry->hi == newHi)) { + break; + } + } + if (entry == start + 0x7ff) + entry = start; + else + entry++; + } + + if (((entry->lo & 0xfffffff8) != (newLo & 0xfffffff8)) && + (entry->hi != newHi) && del) + return 0; + + if (i == HOP_NUMBER) { + if (!del) { + printk(KERN_INFO "%s: table section is full\n", + __FILE__); + return -ENOSPC; + } else { + return 0; + } + } + + /* + * Update the selected entry + */ + if (del) { + entry->hi = 0; + entry->lo = 0; + } else { + entry->hi = newHi; + entry->lo = newLo; + } + + last = (u8 *) entry; + last = last + sizeof(*entry); + + return 0; +} + +/* + * ---------------------------------------------------------------------------- + * Create an addressTable entry from MAC address info + * found in the specifed net_device struct + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + */ +static void update_hash_table_mac_address(struct pxa168_private *mp, + u8 *oaddr, u8 *addr) +{ + u32 macH; + u32 macL; + + /* Delete old entry */ + if (oaddr) { + macH = (oaddr[0] << 8) | oaddr[1]; + macL = (oaddr[2] << 24) | (oaddr[3] << 16) | + (oaddr[4] << 8) | oaddr[5]; + add_del_hash_entry(mp, macH, macL, 1, 0, HASH_DELETE); + } + + /* Add new entry */ + macH = (addr[0] << 8) | addr[1]; + macL = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5]; + add_del_hash_entry(mp, macH, macL, 1, 0, HASH_ADD); +} + +/* Address Table functions */ +static int init_hashtable(struct pxa168_private *mp) +{ + u8 *addr; + dma_addr_t reg_dma; + + if (mp->htpr == NULL) { + mp->htpr = dma_alloc_coherent(NULL, + HASH_ADDR_TABLE_SIZE + 7, + &mp->htpr_dma, + GFP_KERNEL); + if (mp->htpr == NULL) + return -ENOMEM; + } + + /* align to 8 byte boundary */ + addr = (u8 *)(((u32)mp->htpr+7) & ~0x7); + reg_dma = (dma_addr_t)(((u32)mp->htpr_dma+7) & ~0x7); + + memset(addr, 0, HASH_ADDR_TABLE_SIZE); + + wrl(mp, HTPR, reg_dma); + return 0; +} + +static void pxa168_eth_set_rx_mode(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + struct dev_mc_list *mclist; + int i; + u32 val; + + val = rdl(mp, PORT_CONFIG); + if (dev->flags & IFF_PROMISC) + val |= PCR_PM; + else + val &= ~PCR_PM; + wrl(mp, PORT_CONFIG, val); + + mclist = dev->mc_list; + for (i = 0; i < dev->mc_count; i++) { + update_hash_table_mac_address(mp, NULL, mclist->dmi_addr); + mclist = mclist->next; + } +} + +/* + * pxa168_eth_set_mac_address + * + * Change the interface's mac address. + * No special hardware thing should be done because interface is always + * put in promiscuous mode. + * + * Input : pointer to ethernet interface network device structure and + * a pointer to the designated entry to be added to the cache. + * Output : zero upon success, negative upon failure + */ +static int pxa168_eth_set_mac_address(struct net_device *dev, void *addr) +{ + struct pxa168_private *mp = netdev_priv(dev); + int i; + unsigned char oldMac[6]; + + memcpy(oldMac, dev->dev_addr, 6); + for (i = 0; i < 6; i++) + /* +2 is for the offset of the HW addr type */ + dev->dev_addr[i] = ((unsigned char *)addr)[i + 2]; + + update_hash_table_mac_address(mp, oldMac, dev->dev_addr); + + return 0; +} + +#ifdef ETH_DUMP_REGS +static void eth_dump_regs(struct pxa168_private *mp) +{ + unsigned int i = 0; + + printk(KERN_INFO "offset: 0x%x, value: 0x%x\n", + PHY_ADDRESS, rdl(mp, PHY_ADDRESS)); + printk(KERN_INFO "offset: 0x%x, value: 0x%x\n", + SMI, rdl(mp, SMI)); + + for (i = 0x400; i <= 0x4e4; i += 4) + printk(KERN_INFO "offset: 0x%x, value: 0x%x\n", + i, rdl(mp, i)); +} +#endif + +static void eth_port_start(struct net_device *dev) +{ + unsigned int val = 0; + struct pxa168_private *mp = netdev_priv(dev); + int tx_curr_desc, rx_curr_desc; + + /* Assignment of Tx CTRP of given queue */ + tx_curr_desc = mp->tx_curr_desc_q; + wrl(mp, ETH_C_TX_DESC_1, + (u32)((struct tx_desc *)mp->tx_desc_dma + tx_curr_desc)); + + /* Assignment of Rx CRDP of given queue */ + rx_curr_desc = mp->rx_curr_desc_q; + wrl(mp, ETH_C_RX_DESC_0, + (u32)((struct rx_desc *)mp->rx_desc_dma + rx_curr_desc)); + + wrl(mp, ETH_F_RX_DESC_0, + (u32)((struct rx_desc *)mp->rx_desc_dma + rx_curr_desc)); + + /* Clear all interrupts */ + wrl(mp, INT_CAUSE, 0); + +#ifdef MFU_TIMER + /* no ints if polling */ + wrl(mp, INT_MASK, 0); +#else + /* Enable all interrupts for receive, transmit and error. */ + wrl(mp, INT_MASK, ALL_INTS); +#endif + + val = rdl(mp, PORT_CONFIG); + val |= PCR_EN; + wrl(mp, PORT_CONFIG, val); + + /* Start RX DMA engine */ + val = rdl(mp, SDMA_CMD); + val |= SDMA_CMD_ERD; + wrl(mp, SDMA_CMD, val); + +#ifdef ETH_DUMP_REGS + eth_dump_regs(mp); +#endif +} + +static void eth_port_reset(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + unsigned int val = 0; + + /* Stop RX DMA */ + val = rdl(mp, SDMA_CMD); + val &= ~SDMA_CMD_ERD; /* abort dma command */ + + /* Abort any transmit and receive operations and put DMA + * in idle state. + */ + + abortDMA(mp); + + /* Stop all interrupts for receive, transmit and error. */ + wrl(mp, INT_MASK, 0); + + /* Clear all interrupts */ + wrl(mp, INT_CAUSE, 0); + + /* Disable port */ + val = rdl(mp, PORT_CONFIG); + val &= ~PCR_EN ; + wrl(mp, PORT_CONFIG, val); + +} + + +/** + * txq_reclaim - Free the tx desc data for completed descriptors + * + * If force is non-zero, frees uncompleted descriptors as well + */ +int txq_reclaim(struct net_device *dev, int force) +{ + struct pxa168_private *mp = netdev_priv(dev); + volatile struct tx_desc *desc; + u32 cmd_sts; + struct sk_buff *skb; + unsigned long flags; + int tx_index; + dma_addr_t addr; + int count; + int released = 0; + + while (mp->tx_desc_count > 0) { + spin_lock_irqsave(&mp->lock, flags); + + /* tx_desc_count might have changed before acquiring the lock */ + if (mp->tx_desc_count <= 0) { + spin_unlock_irqrestore(&mp->lock, flags); + return released; + } + + tx_index = mp->tx_used_desc_q; + desc = &mp->p_tx_desc_area[tx_index]; + cmd_sts = desc->cmd_sts; + + if (!force && (cmd_sts & BUF_OWNED_BY_DMA)) { + spin_unlock_irqrestore(&mp->lock, flags); + if (released > 0) + return released; + else + return -1; + } + + mp->tx_used_desc_q = (tx_index + 1) % mp->tx_ring_size; + mp->tx_desc_count--; + + addr = desc->buf_ptr; + count = desc->byte_cnt; + skb = mp->tx_skb[tx_index]; + if (skb) + mp->tx_skb[tx_index] = NULL; + + if (cmd_sts & TX_ERROR) { + printk(KERN_ERR "%s: Error in TX\n", dev->name); + dev->stats.tx_errors++; + } + + spin_unlock_irqrestore(&mp->lock, flags); + + dma_unmap_single(NULL, addr, count, DMA_TO_DEVICE); + + if (skb) { + dev_kfree_skb_irq(skb); + pr_debug("dev_kfree tx_descs\n"); + } + + released = 1; + } + + return released; +} + +static void pxa168_eth_tx_timeout(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + + printk(KERN_INFO "%s: TX timeout desc_count %d\n", + dev->name, mp->tx_desc_count); + + /* Do the reset outside of interrupt context */ + schedule_work(&mp->tx_timeout_task); +} + +static void pxa168_eth_tx_timeout_task(struct work_struct *work) +{ + struct pxa168_private *mp = + container_of(work, + struct pxa168_private, tx_timeout_task); + struct net_device *dev = mp->dev; + + pxa168_eth_stop(dev); + pxa168_eth_open(dev); +} + +static int rxq_process(struct net_device *dev, int budget) +{ + struct pxa168_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned int received_packets = 0; + struct sk_buff *skb; + + while (budget-- > 0) { + + int rx_next_curr_desc, rx_curr_desc, rx_used_desc; + volatile struct rx_desc *rx_desc; + unsigned int cmd_sts; + unsigned long flags; + + /* Do not process Rx ring in case of Rx ring resource error */ + if (mp->rx_resource_err) + break; + + spin_lock_irqsave(&mp->lock, flags); + + /* Get the Rx Desc ring 'curr and 'used' indexes */ + rx_curr_desc = mp->rx_curr_desc_q; + rx_used_desc = mp->rx_used_desc_q; + + rx_desc = &mp->p_rx_desc_area[rx_curr_desc]; + + cmd_sts = rx_desc->cmd_sts; + rmb(); + + /* Nothing to receive... */ + + if (cmd_sts & (BUF_OWNED_BY_DMA)) { + spin_unlock_irqrestore(&mp->lock, flags); + break; + } + + skb = mp->rx_skb[rx_curr_desc]; + mp->rx_skb[rx_curr_desc] = NULL; + + /* Update current index in data structure */ + rx_next_curr_desc = (rx_curr_desc + 1) % mp->rx_ring_size; + mp->rx_curr_desc_q = rx_next_curr_desc; + + /* Rx descriptors exhausted. */ + /* Set the Rx ring resource error flag */ + if (rx_next_curr_desc == rx_used_desc) + mp->rx_resource_err = 1; + + mp->rx_desc_count--; + spin_unlock_irqrestore(&mp->lock, flags); + dma_unmap_single(NULL, rx_desc->buf_ptr, + MAX_PKT_SIZE + ETH_HW_IP_ALIGN, DMA_FROM_DEVICE); + received_packets++; + + /* + * Update statistics. + * Note byte count includes 4 byte CRC count + */ + stats->rx_packets++; + stats->rx_bytes += rx_desc->byte_cnt; + /* + * In case received a packet without first / last bits on OR + * the error summary bit is on, the packets needs to be dropeed. + */ + if (((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != + (RX_FIRST_DESC | RX_LAST_DESC)) + || (cmd_sts & RX_ERROR)) { + + stats->rx_dropped++; + if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != + (RX_FIRST_DESC | RX_LAST_DESC)) { + if (net_ratelimit()) + printk(KERN_ERR + "%s: Rx pkt on multiple desc\n", + dev->name); + } + if (cmd_sts & RX_ERROR) + stats->rx_errors++; + + dev_kfree_skb_irq(skb); + + } else { + /* + * The -4 is for the CRC in the trailer of the + * received packet + */ + + skb_put(skb, rx_desc->byte_cnt - 4); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + } + dev->last_rx = jiffies; + } + + rxq_refill(dev); /* Fill RX ring with skb's */ + + return received_packets; +} + +#ifndef MFU_TIMER +static irqreturn_t pxa168_eth_int_handler(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct pxa168_private *mp = netdev_priv(dev); + u32 icr; + + /* Read interrupt cause registers */ + icr = rdl(mp, INT_CAUSE); + if (0x00 == icr) + return IRQ_NONE; + + /* Clear new interrupts */ + wrl(mp, INT_CAUSE, icr ^ 0xffffffff); + + /* PHY status changed */ + if (icr & ICR_MII_CH) { + int curr_desc_no; + struct tx_desc *desc; + + /* last tx desc we set */ + curr_desc_no = (mp->tx_curr_desc_q + mp->tx_ring_size - 1) + % mp->tx_ring_size; + /* only tx interrupt if we are about to stop the queue */ + /* set tx interrupt enabled */ + desc = &mp->p_tx_desc_area[curr_desc_no]; + /* + printk("curr_desc_no = %d, desc->cmd_sts = %08X\n", + curr_desc_no, desc->cmd_sts); + */ + desc = &mp->p_tx_desc_area[mp->tx_used_desc_q]; + /* + printk(KERN_INFO "tx_used_desc_q = %d," + "cmd_sts = %08X\n", + mp->tx_used_desc_q, desc->cmd_sts); + */ + + if (!mii_link_ok(&mp->mii) && netif_carrier_ok(dev)) { + netif_stop_queue(dev); + netif_carrier_off(dev); + } else if (mii_link_ok(&mp->mii) && !netif_carrier_ok(dev)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } + mii_check_link(&mp->mii); + } + +#ifdef NAPI_PMR + if (icr & (ICR_RXBUF | ICR_RXERR | ICR_TXBUF_H | + ICR_TXBUF_L | ICR_TX_UDR)) { + if (napi_schedule_prep(&mp->napi)) { + /* Disable interrupts */ + wrl(mp, INT_MASK, 0); + __napi_schedule(&mp->napi); + } + } +#else + if (icr & (ICR_RXBUF | ICR_RXERR)) { + rxq_process(dev, mp->rx_ring_size); + + + if (icr & (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR)) { + if (txq_reclaim(dev, 0) + && mp->tx_ring_size - mp->tx_desc_count >= TX_DESC_COUNT_LOW) + netif_wake_queue(dev); + } +#endif + + return IRQ_HANDLED; +} +#endif + +static int setPortConfigExt(struct pxa168_private *mp, int mtu) +{ + int mtuSize; + + /* printk("%s: mtu = %d\n", __func__, mtu); */ + /* 64 should work but does not -- dhcp packets NEVER get transmitted. */ + if ((mtu > MAX_PKT_SIZE) || (mtu < 64)) + return -EINVAL; + + /* add source/dest mac addr (12) + pid (2) + crc (4) */ + mtu += ETH_EXTRA_HEADER; + if (mtu <= 1518) + mtuSize = PCXR_MFL_1518; + else if (mtu <= 1536) + mtuSize = PCXR_MFL_1536; + else if (mtu <= 2048) + mtuSize = PCXR_MFL_2048; + else + mtuSize = PCXR_MFL_64K; + + /* Extended Port Configuration */ + wrl(mp, PORT_CONFIG_EXT, + PCXR_2BSM | /* Two byte suffix aligns IP hdr */ + PCXR_DSCP_EN | /* Enable DSCP in IP */ + mtuSize | + PCXR_FLP | /* do not force link pass */ + PCXR_TX_HIGH_PRI); /* Transmit - high priority queue */ + + /* subtract source/dest mac addr (12) + pid (2) + crc (4) */ + mtu -= ETH_EXTRA_HEADER; + (mp->dev)->mtu = mtu; + /* printk("%s: mtu = %d, mtuSize = %x\n", __func__, mtu, mtuSize); */ + return 0; +} + +static void pxa168_init_hw(struct pxa168_private *mp) +{ + if (mp->pd != NULL) { + mp->pd->init(); + ethernet_phy_set(mp); + } + + /* Disable interrupts */ + wrl(mp, INT_MASK, 0); + wrl(mp, INT_CAUSE, 0); + + /* Write to ICR to clear interrupts. */ + wrl(mp, INT_W_CLEAR, 0); + + /* Abort any transmit and receive operations and put DMA + * in idle state. + */ + + abortDMA(mp); + /* Initialize address hash table */ + init_hashtable(mp); + + /* SDMA configuration */ + wrl(mp, SDMA_CONFIG, + SDCR_BSZ8 | /* Burst size = 32 bytes */ + SDCR_RIFB | /* Rx interrupt on frame */ + SDCR_BLMT | /* Little endian transmit */ + SDCR_BLMR | /* Little endian receive */ + SDCR_RC_MAX_RETRANS); /* Max retransmit count */ + + /* Port Configuration */ + wrl(mp, PORT_CONFIG, PCR_HS); /* Hash size is 1/2kb */ + + setPortConfigExt(mp, (mp->dev)->mtu); +} + + +/* rx/tx queue initialisation ***********************************************/ +static int rxq_init(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + volatile struct rx_desc *p_rx_desc; + int size = 0, i = 0; + int rx_desc_num = mp->rx_ring_size; + + /* Allocate RX and TX skb rings */ + mp->rx_skb = kmalloc(sizeof(*mp->rx_skb) * mp->rx_ring_size, + GFP_KERNEL); + if (!mp->rx_skb) { + printk(KERN_ERR "%s: Cannot alloc RX skb ring\n", dev->name); + return -ENOMEM; + } + + /* Allocate RX ring */ + mp->rx_desc_count = 0; + size = mp->rx_ring_size * sizeof(struct rx_desc); + mp->rx_desc_area_size = size; + + mp->p_rx_desc_area = dma_alloc_coherent(NULL, size, + &mp->rx_desc_dma, + GFP_KERNEL); + if (!mp->p_rx_desc_area) { + printk(KERN_ERR "%s: Cannot alloc RX ring (size %d bytes)\n", + dev->name, size); + goto out; + } + memset((void *)mp->p_rx_desc_area, 0, size); + + /* initialize the next_desc_ptr links in the Rx descriptors ring */ + p_rx_desc = (struct rx_desc *)mp->p_rx_desc_area; + for (i = 0; i < rx_desc_num; i++) { + p_rx_desc[i].next_desc_ptr = mp->rx_desc_dma + + ((i + 1) % rx_desc_num) * sizeof(struct rx_desc); + } + + /* Save Rx desc pointer to driver struct. */ + mp->rx_curr_desc_q = 0; + mp->rx_used_desc_q = 0; + + mp->rx_desc_area_size = rx_desc_num * sizeof(struct rx_desc); + + return 0; + +out: + kfree(mp->rx_skb); + return -ENOMEM; +} + +static void rxq_deinit(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + int curr; + + /* Free preallocated skb's on RX rings */ + for (curr = 0; mp->rx_desc_count && curr < mp->rx_ring_size; curr++) { + if (mp->rx_skb[curr]) { + dev_kfree_skb(mp->rx_skb[curr]); + mp->rx_desc_count--; + } + } + + if (mp->rx_desc_count) + printk(KERN_ERR + "Error in freeing Rx Ring. %d skb's still\n", + mp->rx_desc_count); + + /* Free RX ring */ + if (mp->p_rx_desc_area) + dma_free_coherent(NULL, mp->rx_desc_area_size, + mp->p_rx_desc_area, mp->rx_desc_dma); + + kfree(mp->rx_skb); + +} + +static int txq_init(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + struct tx_desc *p_tx_desc; + int size = 0, i = 0; + int tx_desc_num = mp->tx_ring_size; + + mp->tx_skb = kmalloc(sizeof(*mp->tx_skb) * mp->tx_ring_size, + GFP_KERNEL); + if (!mp->tx_skb) { + printk(KERN_ERR "%s: Cannot alloc TX skb ring\n", dev->name); + return -ENOMEM; + } + + /* Allocate TX ring */ + mp->tx_desc_count = 0; + size = mp->tx_ring_size * sizeof(struct tx_desc); + mp->tx_desc_area_size = size; + + mp->p_tx_desc_area = dma_alloc_coherent(NULL, size, + &mp->tx_desc_dma, + GFP_KERNEL); + if (!mp->p_tx_desc_area) { + printk(KERN_ERR "%s: Cannot allocate Tx Ring (size %d bytes)\n", + dev->name, size); + goto out; + } + /* check 16-byte alignment */ + BUG_ON((u32) mp->p_tx_desc_area & 0xf); + memset((void *)mp->p_tx_desc_area, 0, mp->tx_desc_area_size); + + /* Initialize the next_desc_ptr links in the Tx descriptors ring */ + p_tx_desc = (struct tx_desc *)mp->p_tx_desc_area; + for (i = 0; i < tx_desc_num; i++) { + p_tx_desc[i].next_desc_ptr = mp->tx_desc_dma + + ((i + 1) % tx_desc_num) * sizeof(struct tx_desc); + } + + mp->tx_curr_desc_q = 0; + mp->tx_used_desc_q = 0; + + mp->tx_desc_area_size = tx_desc_num * sizeof(struct tx_desc); + + return 0; + +out: + kfree(mp->tx_skb); + return -ENOMEM; + +} + +static void txq_deinit(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + + /* Free outstanding skb's on TX ring */ + txq_reclaim(dev, 1); + + BUG_ON(mp->tx_used_desc_q != mp->tx_curr_desc_q); + + /* Free TX ring */ + if (mp->p_tx_desc_area) + dma_free_coherent(NULL, mp->tx_desc_area_size, + mp->p_tx_desc_area, mp->tx_desc_dma); + + kfree(mp->tx_skb); +} + +static int pxa168_eth_open(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + int err; + +#ifndef MFU_TIMER + err = request_irq(dev->irq, pxa168_eth_int_handler, + IRQF_DISABLED | IRQF_SAMPLE_RANDOM, dev->name, dev); + if (err) { + printk(KERN_ERR "Can not assign IRQ number to PXA168_eth\n"); + return -EAGAIN; + } +#endif + + pxa168_init_hw(mp); + pxa168_eth_set_rx_mode(dev); + mp->rx_resource_err = 0; + + err = ethernet_phy_setup(dev); + if (err) + return -EAGAIN; + + memset(&mp->timeout, 0, sizeof(struct timer_list)); + mp->timeout.function = rxq_refill_timer_wrapper; + mp->timeout.data = (unsigned long)dev; + + err = rxq_init(dev); + if (err != 0) + goto out_free_irq; + + err = txq_init(dev); + if (err != 0) + goto out_free_rx_skb; + + mp->rx_used_desc_q = 0; + mp->rx_curr_desc_q = 0; + rxq_refill(dev); /* Fill RX ring with skb's */ + mp->rx_used_desc_q = 0; + mp->rx_curr_desc_q = 0; + eth_port_start(dev); + + +#ifdef NAPI_PMR + napi_enable(&mp->napi); +#endif + +#ifndef MFU_TIMER + if (mii_link_ok(&mp->mii)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } else { + netif_carrier_off(dev); + netif_stop_queue(dev); + } + mii_check_link(&mp->mii); +#else + netif_carrier_on(dev); + netif_wake_queue(dev); +#endif + + +#ifdef MFU_TIMER + mp->mfu_timer->expires = jiffies + (HZ / 10); /* 100 mSec */ + add_timer(mp->mfu_timer); +#endif + + return 0; + +out_free_rx_skb: + rxq_deinit(dev); +out_free_irq: + free_irq(dev->irq, dev); + + return err; +} + +static int pxa168_eth_stop(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + eth_port_reset(dev); + +#ifdef NAPI_PMR + napi_disable(&mp->napi); +#endif + netif_stop_queue(dev); + + del_timer_sync(&mp->timeout); + + /* Disable interrupts */ + wrl(mp, INT_MASK, 0); + wrl(mp, INT_CAUSE, 0); + + /* Write to ICR to clear interrupts. */ + wrl(mp, INT_W_CLEAR, 0); + +#ifndef MFU_TIMER + free_irq(dev->irq, dev); +#endif + +#ifdef MFU_TIMER + del_timer_sync(mp->mfu_timer); +#endif + netif_carrier_off(dev); + + rxq_deinit(dev); + txq_deinit(dev); + + return 0; +} + +/* + * Changes MTU (maximum transfer unit) of the gigabit ethenret port + * + * Input : pointer to ethernet interface network device structure + * new mtu size + * Output : 0 upon success, -EINVAL upon failure + */ +static int pxa168_eth_change_mtu(struct net_device *dev, int mtu) +{ + struct pxa168_private *mp = netdev_priv(dev); + int retval; + + retval = setPortConfigExt(mp, mtu); + if (retval != 0) + return retval; + + /* + * Stop then re-open the interface. This will allocate RX skb's with + * the new MTU. + * There is a possible danger that the open will not successed, due + * to memory is full, which might fail the open function. + */ + if (netif_running(dev)) { + pxa168_eth_stop(dev); + if (pxa168_eth_open(dev)) + printk(KERN_ERR + "%s: Fatal error on opening device\n", + dev->name); + } + + return 0; +} + + +/** + * eth_alloc_tx_desc_index - return the index of the next available tx desc + */ +static int eth_alloc_tx_desc_index(struct pxa168_private *mp) +{ + int tx_desc_curr; + + BUG_ON(mp->tx_desc_count >= mp->tx_ring_size); + + tx_desc_curr = mp->tx_curr_desc_q; + mp->tx_curr_desc_q = (tx_desc_curr + 1) % mp->tx_ring_size; + + pr_debug("mp->tx_curr_desc_q=%d\n", mp->tx_curr_desc_q); + pr_debug("mp->tx_used_desc_q=%d\n", mp->tx_used_desc_q); + + if (mp->tx_curr_desc_q == mp->tx_used_desc_q) + pr_debug("mp->tx_curr_desc_q=%d\n", mp->tx_curr_desc_q); + + BUG_ON(mp->tx_curr_desc_q == mp->tx_used_desc_q); + + return tx_desc_curr; +} + + +/** + * eth_tx_submit_descs_for_skb - submit data from an skb to the tx hw + * + * Ensure the data for an skb to be transmitted is mapped properly, + * then fill in descriptors in the tx hw queue and start the hardware. + */ +static void eth_tx_submit_descs_for_skb(struct pxa168_private *mp, + struct sk_buff *skb) +{ + int tx_index; + struct tx_desc *desc; + int length; + + tx_index = eth_alloc_tx_desc_index(mp); + desc = &mp->p_tx_desc_area[tx_index]; + + length = skb->len; + mp->tx_skb[tx_index] = skb; + + desc->byte_cnt = length; + desc->buf_ptr = dma_map_single(NULL, skb->data, length, DMA_TO_DEVICE); + + /* ensure all other descriptors are written before first cmd_sts */ + wmb(); + desc->cmd_sts = BUF_OWNED_BY_DMA | TX_GEN_CRC | TX_FIRST_DESC | + TX_ZERO_PADDING | TX_LAST_DESC; + wmb(); + + /* only use high priority */ + wrl(mp, SDMA_CMD, SDMA_CMD_TXDL | SDMA_CMD_TXDH | SDMA_CMD_ERD); + + mp->tx_desc_count++; +} + +#ifdef NAPI_PMR +static int pxa168_rx_poll(struct napi_struct *napi, int budget) +{ + struct pxa168_private *mp = + container_of(napi, struct pxa168_private, napi); + struct net_device *dev = mp->dev; + int rxPacketsProcessed; + + txq_reclaim(dev, 0); + if (netif_queue_stopped(dev) + && mp->tx_ring_size - mp->tx_desc_count >= TX_DESC_COUNT_LOW) + netif_wake_queue(dev); + + rxPacketsProcessed = rxq_process(dev, budget); + if (mp->tx_ring_size - mp->tx_desc_count >= TX_DESC_COUNT_LOW) + netif_wake_queue(dev); + + if (rxPacketsProcessed <= budget) { + /* Enable interrupts */ + wrl(mp, INT_MASK, ALL_INTS); + + /* + * do NOT change this order + * if a packet arrived and interrupts were enabled after + * netif_rx_complete we might miss this packet + * if no other packet arrived + * this is why the extra call to rxq_process. + */ + rxq_process(dev, budget); + napi_complete(napi); + return 0; + } else { + return 1; + } +} + +#endif + +/** + * pxa168_eth_start_xmit - queue an skb to the hardware for transmission + * + */ +static int pxa168_eth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + struct net_device_stats *stats = &dev->stats; + unsigned long flags; +#ifdef RESTART_DMA_WORKAROUND + int w_cnt; + int timedout; + u32 mdelaycnt; +#else + struct tx_desc *desc; +#endif + + BUG_ON(netif_queue_stopped(dev)); + BUG_ON(skb == NULL); + +#ifndef RESTART_DMA_WORKAROUND + /* not used if RESTART_DMA_WORKAROUND SINCE TX QUEUE */ + /* NEVER HAS MORE THEN ONE XFER */ + txq_reclaim(dev, 0); + if (mp->tx_ring_size - mp->tx_desc_count < MAX_DESCS_PER_HIGH) { + int curr_desc_no; + + /* only tx interrupt if we are about to stop the queue */ + /* set tx interrupt enabled */ + spin_lock_irqsave(&mp->lock, flags); + curr_desc_no = (mp->tx_curr_desc_q + mp->tx_ring_size - 1) + % mp->tx_ring_size; + desc = &mp->p_tx_desc_area[curr_desc_no]; + desc->cmd_sts |= TX_EN_INT; + wmb(); + spin_unlock_irqrestore(&mp->lock, flags); + + /* if buf NOT owned by dma -- do not stop the queue */ + /* we will not see the interrupt */ + if (desc->cmd_sts & BUF_OWNED_BY_DMA) + netif_stop_queue(dev); + + return NETDEV_TX_BUSY; + } +#else + if (mp->tx_ring_size - mp->tx_desc_count < MAX_DESCS_PER_HIGH) { + netif_stop_queue(dev); + return NETDEV_TX_BUSY; + } +#endif + + if (skb_shinfo(skb)->nr_frags && __skb_linearize(skb)) { + spin_lock_irqsave(&mp->lock, flags); + stats->tx_dropped++; + spin_unlock_irqrestore(&mp->lock, flags); + printk(KERN_DEBUG "%s: failed to linearize tiny " + "unaligned fragment\n", dev->name); + return NETDEV_TX_OK; + } + + spin_lock_irqsave(&mp->lock, flags); + + eth_tx_submit_descs_for_skb(mp, skb); + stats->tx_bytes += skb->len; + stats->tx_packets++; + dev->trans_start = jiffies; + +#ifndef RESTART_DMA_WORKAROUND + if (mp->tx_ring_size - mp->tx_desc_count < MAX_DESCS_PER_HIGH) { + int curr_desc_no; + struct tx_desc *desc; + + curr_desc_no = (mp->tx_curr_desc_q + mp->tx_ring_size - 1) + % mp->tx_ring_size; + /* only tx interrupt if we are about to stop the queue */ + /* set tx interrupt enabled */ + desc = &mp->p_tx_desc_area[curr_desc_no]; + desc->cmd_sts |= TX_EN_INT; + wmb(); + + /* if buf NOT owned by dma do not stop the queue */ + /* we will not see the interrupt */ + if (desc->cmd_sts & BUF_OWNED_BY_DMA) + netif_stop_queue(dev); + } +#endif + spin_unlock_irqrestore(&mp->lock, flags); + +#ifdef RESTART_DMA_WORKAROUND + w_cnt = 0; + mdelaycnt = dev->trans_start; + /* just reclaim */ + timedout = 0; + while (txq_reclaim(dev, 0) < 0) { + udelay(100); + w_cnt++; + if (w_cnt > 15 && mp->tx_desc_count > 0) { + /* the DMA did not actually start, so hit it again */ + wrl(mp, SDMA_CMD, + SDMA_CMD_TXDL | SDMA_CMD_TXDH | SDMA_CMD_ERD); + } + + if (time_after(jiffies, mdelaycnt + msecs_to_jiffies(1))) { + /* let other components of linux run */ + mdelay(1); + mdelaycnt = jiffies; + } + + if (time_after(jiffies, + dev->trans_start + msecs_to_jiffies(1000))) { + timedout = 1; + break; + } + } + + if (timedout) { + printk(KERN_ERR "%s : Timed Out trans_start = %lu, jiffies = %lu\n", + __func__, dev->trans_start, jiffies); + abortDMA(mp); + txq_reclaim(dev, 1); + abortDMA(mp); + } +#endif + + if (mp->tx_ring_size - mp->tx_desc_count >= TX_DESC_COUNT_LOW) + netif_wake_queue(dev); + + return NETDEV_TX_OK; /* success */ +} + +static void pxa168_init_ethtool_cmd(struct net_device *dev, int phy_address, + int speed, int duplex, + struct ethtool_cmd *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + cmd->port = PORT_MII; + cmd->transceiver = XCVR_INTERNAL; + cmd->phy_address = phy_address; + + if (speed == 0) { + cmd->autoneg = AUTONEG_ENABLE; + /* mii lib checks, but doesn't use speed on AUTONEG_ENABLE */ + cmd->speed = SPEED_10; + cmd->advertising = ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full; + } else { + cmd->autoneg = AUTONEG_DISABLE; + cmd->speed = speed; + cmd->duplex = duplex; + } +} + +/* + * Wrappers for MII support library. + */ +static int pxa168_mdio_read(struct net_device *dev, + int phy_id, int location) +{ + int val; + struct pxa168_private *mp = netdev_priv(dev); + + eth_port_read_smi_reg(mp, location, &val); + return val; +} + +static void pxa168_mdio_write(struct net_device *dev, int phy_id, + int location, int val) +{ + struct pxa168_private *mp = netdev_priv(dev); + + eth_port_write_smi_reg(mp, location, val); +} + + +static int pxa168_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, + int cmd) +{ + struct pxa168_private *mp = netdev_priv(dev); + + return generic_mii_ioctl(&mp->mii, if_mii(ifr), cmd, NULL); +} + +static int pxa168_set_settings(struct net_device *dev, + struct ethtool_cmd *cmd) +{ + struct pxa168_private *mp = netdev_priv(dev); + int err; + + spin_lock_irq(&mp->lock); + err = mii_ethtool_sset(&mp->mii, cmd); + spin_unlock_irq(&mp->lock); + + return err; +} + +static int ethernet_phy_setup(struct net_device *dev) +{ + int err; + int speed = 0; + int duplex = DUPLEX_FULL; + struct pxa168_private *mp = netdev_priv(dev); + struct ethtool_cmd cmd; + + err = ethernet_phy_detect(mp); + if (err) { + printk(KERN_INFO "pxa168 ethernet port %d: " + "No PHY detected at addr %d\n", + mp->port_num, ethernet_phy_get(mp)); + return err; + } + + /* if no mac address assigned -- generate one */ + if (memcmp (dev->dev_addr, pxa168_mac_str, sizeof(pxa168_mac_str)) == 0) { + memcpy(dev->dev_addr, MarvellOUI, sizeof(MarvellOUI)); + dev->dev_addr[0] |= (1<<6); /* set locally admin bit */ + get_random_bytes(&dev->dev_addr[3], 3); + } + + ethernet_phy_reset(mp); + pxa168_init_ethtool_cmd(dev, mp->mii.phy_id, speed, duplex, &cmd); + pxa168_set_settings(dev, &cmd); + update_hash_table_mac_address(mp, dev->dev_addr, dev->dev_addr); + return 0; +} + +#ifdef MFU_TIMER +static void pxa168_eth_timer_handler(unsigned long d) +{ + struct net_device *dev = (struct net_device *)d; + struct pxa168_private *mp = netdev_priv(dev); + int ret; + + ret = rxq_process(dev, mp->rx_ring_size); + if (ret) + mp->mfu_timer->expires = jiffies+msecs_to_jiffies(1); + else + mp->mfu_timer->expires = jiffies+msecs_to_jiffies(2); + + add_timer(mp->mfu_timer); +} +#endif + + +/************* End ethtool support *************************/ + + +static int pxa168_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct pxa168_private *mp = netdev_priv(dev); + return mii_ethtool_gset(&mp->mii, cmd); +} + +static void pxa168_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRIVER_NAME); + strcpy(info->version, DRIVER_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, "N/A"); +} + +static u32 pxa168_get_link(struct net_device *dev) +{ + struct pxa168_private *mp = netdev_priv(dev); + return mii_link_ok(&mp->mii); +} + + +static int pxa168_phys_id(struct net_device *dev, u32 data) +{ + struct pxa168_private *mp = netdev_priv(dev); + mp->phy_addr = data % 32; + ethernet_phy_set(mp); + return 0; +} + +static const struct net_device_ops pxa168_netdev_ops = { + .ndo_open = pxa168_eth_open, + .ndo_stop = pxa168_eth_stop, + .ndo_start_xmit = pxa168_eth_start_xmit, + .ndo_set_mac_address = pxa168_eth_set_mac_address, + .ndo_set_multicast_list = pxa168_eth_set_rx_mode, + .ndo_change_mtu = pxa168_eth_change_mtu, + .ndo_do_ioctl = pxa168_eth_do_ioctl, + .ndo_tx_timeout = pxa168_eth_tx_timeout, +}; + +static const struct ethtool_ops pxa168_ethtool_ops = { + .get_settings = pxa168_get_settings, + .get_drvinfo = pxa168_get_drvinfo, + .get_link = pxa168_get_link, + .phys_id = pxa168_phys_id, +}; + + +static int pxa168_eth_probe(struct platform_device *pdev) +{ + struct pxa168_eth_platform_data *pd; + struct pxa168_private *mp; + struct net_device *dev = NULL; + struct resource *res; + struct clk *clk; + int err; + int duplex = DUPLEX_FULL; + int speed = 0; /* default to auto-negotiation */ + + printk(KERN_NOTICE "PXA168 10/100 Ethernet Driver\n"); + + /* enable MFU clock */ + clk = clk_get(&pdev->dev, "MFUCLK"); + if (IS_ERR(clk)) { + printk(KERN_ERR "fast Ethernet failed to get camera clock\n"); + return -1; + } + clk_enable(clk); + + dev = alloc_etherdev(sizeof(struct pxa168_private)); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + + mp = netdev_priv(dev); + mp->dev = dev; + mp->clk = clk; + + /*register*/ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) + return -ENODEV; + + mp->base = ioremap(res->start, res->end - res->start + 1); + if (mp->base == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + BUG_ON(!res); + dev->irq = res->start; + + /* No need to Tx Timeout */ + + dev->watchdog_timeo = 2 * HZ; + dev->base_addr = 0; + SET_ETHTOOL_OPS(dev, &pxa168_ethtool_ops); + dev->netdev_ops = &pxa168_netdev_ops; + + /* Configure the timeout task */ + INIT_WORK(&mp->tx_timeout_task, pxa168_eth_tx_timeout_task); + + spin_lock_init(&mp->lock); + + mp->rx_ring_size = NUM_RX_DESCS; + mp->tx_ring_size = NUM_TX_DESCS; + mp->port_num = 0; + memcpy (dev->dev_addr, pxa168_mac_str, sizeof(pxa168_mac_str)); + + /* start at 31 since it allows systems with phy's at */ + /* addr 31 and 0 to be found quickly. */ + /* Marvell boards have phys at addr 31 or 0 */ + mp->phy_addr = 31; + pd = pdev->dev.platform_data; + mp->pd = pd; + if (pd != NULL) { + + pd->init(); + mp->port_num = pd->port_number; + + /* use platform data has mac_addr if existed */ + if (is_valid_ether_addr(pd->mac_addr)) + memcpy(dev->dev_addr, pd->mac_addr, 6); + + if (pd->force_phy_addr) + mp->phy_addr = pd->phy_addr & 0x1f; /* 0 - 31 legal */ + + if (pd->rx_queue_size) + mp->rx_ring_size = pd->rx_queue_size; + + if (pd->tx_queue_size) + mp->tx_ring_size = pd->tx_queue_size; + + duplex = pd->duplex; + speed = pd->speed; + } + +#ifdef NAPI_PMR + netif_napi_add(dev, &mp->napi, pxa168_rx_poll, mp->rx_ring_size); +#endif + + /* Hook up MII support for ethtool */ + mp->mii.dev = dev; + mp->mii.mdio_read = pxa168_mdio_read; + mp->mii.mdio_write = pxa168_mdio_write; + mp->mii.phy_id = ethernet_phy_get(mp); + mp->mii.phy_id_mask = 0x1f; /* phy_id is 5 bits */ + mp->mii.reg_num_mask = 0x1f; /* 31 reg */ + + pxa168_init_hw(mp); + + SET_NETDEV_DEV(dev, &pdev->dev); + err = register_netdev(dev); + if (err) + goto out; + +#ifdef MFU_TIMER + mp->mfu_timer = + (struct timer_list *) + kmalloc(sizeof(struct timer_list), GFP_KERNEL); + init_timer(mp->mfu_timer); + mp->mfu_timer->function = pxa168_eth_timer_handler; + mp->mfu_timer->data = (unsigned long) dev; +#endif + + return 0; + +out: + /* disable MFU clock */ + if (mp->clk) { + clk_disable(mp->clk); + clk_put(mp->clk); + mp->clk = NULL; + } + + if (mp->base) { + iounmap(mp->base); + mp->base = NULL; + } + + if (dev) + free_netdev(dev); + return err; +} + +static int pxa168_eth_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct pxa168_private *mp = netdev_priv(dev); + +#ifdef MFU_TIMER + kfree(mp->mfu_timer); +#endif + + if (mp->htpr) { + dma_free_coherent(NULL, HASH_ADDR_TABLE_SIZE + 7, + mp->htpr, mp->htpr_dma); + mp->htpr = NULL; + } + + /* disable MFU clock */ + if (mp->clk) { + clk_disable(mp->clk); + clk_put(mp->clk); + mp->clk = NULL; + } + + iounmap(mp->base); + mp->base = NULL; + + unregister_netdev(dev); + flush_scheduled_work(); + + free_netdev(dev); + platform_set_drvdata(pdev, NULL); + + + return 0; +} + +static void pxa168_eth_shutdown(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + eth_port_reset(dev); +} + +#ifdef CONFIG_PM +static int pxa168_eth_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct pxa168_private *mp = netdev_priv(dev); + int err; + int duplex = DUPLEX_FULL; + int speed = 0; /* default to auto-negotiation */ + struct ethtool_cmd cmd; + + pxa168_init_hw(mp); + update_hash_table_mac_address(mp, NULL, dev->dev_addr); + + err = ethernet_phy_detect(mp); + if (err) { + printk(KERN_INFO "pxa168 ethernet port %d: " + "No PHY detected at addr %d\n", + mp->port_num, ethernet_phy_get(mp)); + goto out; + } + + ethernet_phy_reset(mp); + pxa168_init_ethtool_cmd(dev, mp->mii.phy_id, speed, duplex, &cmd); + pxa168_set_settings(dev, &cmd); + + pxa168_eth_set_rx_mode(dev); + eth_port_start(dev); + + if (netif_running(dev)) + netif_device_attach(dev); + +#ifndef MFU_TIMER + if (mii_link_ok(&mp->mii)) { + netif_carrier_on(dev); + netif_wake_queue(dev); + } else { + netif_carrier_off(dev); + netif_stop_queue(dev); + } +#else + netif_carrier_on(dev); + netif_wake_queue(dev); +#endif + + return 0; +out: + return -1; +} + +static int pxa168_eth_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + if (dev) { + if (netif_running(dev)) + netif_device_detach(dev); + } + return 0; +} + +#else +#define pxa168_eth_resume NULL +#define pxa168_eth_suspend NULL +#endif + + +static struct platform_driver pxa168_eth_driver = { + .probe = pxa168_eth_probe, + .remove = pxa168_eth_remove, + .shutdown = pxa168_eth_shutdown, + .resume = pxa168_eth_resume, + .suspend = pxa168_eth_suspend, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init pxa168_init_module(void) +{ + return platform_driver_register(&pxa168_eth_driver); +} + +static void __exit pxa168_cleanup_module(void) +{ + platform_driver_unregister(&pxa168_eth_driver); +} + +module_init(pxa168_init_module); +module_exit(pxa168_cleanup_module); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ethernet driver for Marvell PXA168"); +MODULE_ALIAS("platform:pxa168_eth"); + + diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 088c797eb73b8a..db88e1b4291c67 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -51,6 +51,23 @@ #endif #include "sky2.h" +#if defined(CONFIG_MACH_ASPENITE) + +extern void _pxa168_memcpy_toio(volatile void __iomem *to, const void *from, + size_t count); +extern void _pxa168_memcpy_fromio(void *to, const volatile void __iomem *from, + size_t count); +extern void _pxa168_memset_io(volatile void __iomem *dst, int c, size_t count); + +#undef memset_io +#undef memcpy_fromio +#undef memcpy_toio + + +#define memset_io(c,v,l) _pxa168_memset_io(__mem_pci(c),(v),(l)) +#define memcpy_fromio(a,c,l) _pxa168_memcpy_fromio((a),__mem_pci(c),(l)) +#define memcpy_toio(c,a,l) _pxa168_memcpy_toio(__mem_pci(c),(a),(l)) +#endif #define DRV_NAME "sky2" #define DRV_VERSION "1.27" @@ -155,6 +172,25 @@ static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 }; static void sky2_set_multicast(struct net_device *dev); + +static void *sky2_map_regs_base(struct pci_dev *pdev) +{ +#if defined(CONFIG_MACH_ASPENITE) + /* We shouldn't map an address that only the controller understands */ + return (void *) pci_resource_start(pdev, 0); +#else + return ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); +#endif +} + +static void sky2_unmap_regs_base(void *addr) +{ + /* No need to unmap is Aspenite */ +#if !defined(CONFIG_MACH_ASPENITE) + iounmap(addr); +#endif +} + /* Access to PHY via serial interconnect */ static int gm_phy_write(struct sky2_hw *hw, unsigned port, u16 reg, u16 val) { @@ -2882,7 +2918,27 @@ static int sky2_poll(struct napi_struct *napi, int work_limit) static irqreturn_t sky2_intr(int irq, void *dev_id) { struct sky2_hw *hw = dev_id; - u32 status; + u32 status, stat; + + /* TODO: Hack alert!! Fix it! + * Clears the interrupts on the PCIe controller + * Should this be a call back into + * arch/arm/mach-mmp/pxa168_pcie.c? + */ +#if defined(CONFIG_MACH_ASPENITE) + stat = __raw_readl(0xfee01820); + + /* Clear interrupts only TLP de-assert message */ + /* TODO: Optimize it */ + if (stat & 0x2) + __raw_writel((stat & 0x3), 0xfee01820); + if (stat & 0x8) + __raw_writel((stat & 0xC), 0xfee01820); + if (stat & 0x20) + __raw_writel((stat & 0x30), 0xfee01820); + if (stat & 0x80) + __raw_writel((stat & 0xC0), 0xfee01820); +#endif /* Reading this mask interrupts as side effect */ status = sky2_read32(hw, B0_Y2_SP_ISRC2); @@ -3595,6 +3651,7 @@ static int sky2_set_mac_address(struct net_device *dev, void *p) return -EADDRNOTAVAIL; memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + memcpy_toio(hw->regs + B2_MAC_1 + port * 8, dev->dev_addr, ETH_ALEN); memcpy_toio(hw->regs + B2_MAC_2 + port * 8, @@ -4675,9 +4732,12 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } hw->pdev = pdev; +<<<<<<< HEAD:drivers/net/sky2.c + hw->regs = sky2_map_regs_base(pdev); +======= sprintf(hw->irq_name, DRV_NAME "@pci:%s", pci_name(pdev)); +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/net/sky2.c - hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); if (!hw->regs) { dev_err(&pdev->dev, "cannot map device registers\n"); goto err_out_free_hw; @@ -4768,7 +4828,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, sky2_write8(hw, B0_CTST, CS_RST_SET); pci_free_consistent(pdev, STATUS_LE_BYTES, hw->st_le, hw->st_dma); err_out_iounmap: - iounmap(hw->regs); + sky2_unmap_regs_base(hw->regs); err_out_free_hw: kfree(hw); err_out_free_regions: @@ -4811,7 +4871,7 @@ static void __devexit sky2_remove(struct pci_dev *pdev) for (i = hw->ports-1; i >= 0; --i) free_netdev(hw->dev[i]); - iounmap(hw->regs); + sky2_unmap_regs_base(hw->regs); kfree(hw); pci_set_drvdata(pdev, NULL); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index a5e182dd981980..54334c210579cf 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -4,8 +4,41 @@ #ifndef _SKY2_H #define _SKY2_H +#if defined(CONFIG_MACH_ASPENITE) +extern u8 pxa168_pcie_read8(u32 addr); +extern u16 pxa168_pcie_read16(u32 addr); +extern u32 pxa168_pcie_read32(u32 addr); +extern void pxa168_pcie_write8(u8 val, u32 addr); +extern void pxa168_pcie_write16(u16 val, u32 addr); +extern void pxa168_pcie_write32(u32 val, u32 addr); + +#undef readb +#undef readw +#undef readl + +#define __pxa168_mem_pci(a) ((u32)a) + +#define readb(c) ({ __u8 __v = pxa168_pcie_read8(__pxa168_mem_pci(c)); __v; }) +#define readw(c) ({ __u16 __v = le16_to_cpu((__force __le16) \ + pxa168_pcie_read16(__pxa168_mem_pci(c))); __v; }) +#define readl(c) ({ __u32 __v = le32_to_cpu((__force __le32) \ + pxa168_pcie_read32(__pxa168_mem_pci(c))); __v; }) + +#undef writeb +#undef writew +#undef writel + +#define writeb(v,c) pxa168_pcie_write8(v,__pxa168_mem_pci(c)) +#define writew(v,c) pxa168_pcie_write16((__force __u16) \ + cpu_to_le16(v),__pxa168_mem_pci(c)) +#define writel(v,c) pxa168_pcie_write32((__force __u32) \ + cpu_to_le32(v),__pxa168_mem_pci(c)) +#endif + + #define ETH_JUMBO_MTU 9000 /* Maximum MTU supported */ + /* PCI config registers */ enum { PCI_DEV_REG1 = 0x40, diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 588943660755fa..21060a8c2baff1 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -31,6 +31,195 @@ config PCMCIA_RAYCS To compile this driver as a module, choose M here: the module will be called ray_cs. If unsure, say N. +config IPW2100 + tristate "Intel PRO/Wireless 2100 Network Connection" + depends on PCI && WLAN_80211 + select WIRELESS_EXT + select FW_LOADER + select IEEE80211 + ---help--- + A driver for the Intel PRO/Wireless 2100 Network + Connection 802.11b wireless network adapter. + + See for information on + the capabilities currently enabled in this driver and for tips + for debugging issues and problems. + + In order to use this driver, you will need a firmware image for it. + You can obtain the firmware from + . Once you have the firmware image, you + will need to place it in /lib/firmware. + + You will also very likely need the Wireless Tools in order to + configure your card: + + . + + It is recommended that you compile this driver as a module (M) + rather than built-in (Y). This driver requires firmware at device + initialization time, and when built-in this typically happens + before the filesystem is accessible (hence firmware will be + unavailable and initialization will fail). If you do choose to build + this driver into your kernel image, you can avoid this problem by + including the firmware and a firmware loader in an initramfs. + +config IPW2100_MONITOR + bool "Enable promiscuous mode" + depends on IPW2100 + ---help--- + Enables promiscuous/monitor mode support for the ipw2100 driver. + With this feature compiled into the driver, you can switch to + promiscuous mode via the Wireless Tool's Monitor mode. While in this + mode, no packets can be sent. + +config IPW2100_DEBUG + bool "Enable full debugging output in IPW2100 module." + depends on IPW2100 + ---help--- + This option will enable debug tracing output for the IPW2100. + + This will result in the kernel module being ~60k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/bus/pci/drivers/ipw2100/debug_level + + This entry will only exist if this option is enabled. + + If you are not trying to debug or develop the IPW2100 driver, you + most likely want to say N here. + +config IPW2200 + tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" + depends on PCI && WLAN_80211 + select WIRELESS_EXT + select FW_LOADER + select IEEE80211 + ---help--- + A driver for the Intel PRO/Wireless 2200BG and 2915ABG Network + Connection adapters. + + See for + information on the capabilities currently enabled in this + driver and for tips for debugging issues and problems. + + In order to use this driver, you will need a firmware image for it. + You can obtain the firmware from + . See the above referenced README.ipw2200 + for information on where to install the firmware images. + + You will also very likely need the Wireless Tools in order to + configure your card: + + . + + It is recommended that you compile this driver as a module (M) + rather than built-in (Y). This driver requires firmware at device + initialization time, and when built-in this typically happens + before the filesystem is accessible (hence firmware will be + unavailable and initialization will fail). If you do choose to build + this driver into your kernel image, you can avoid this problem by + including the firmware and a firmware loader in an initramfs. + +config IPW2200_MONITOR + bool "Enable promiscuous mode" + depends on IPW2200 + ---help--- + Enables promiscuous/monitor mode support for the ipw2200 driver. + With this feature compiled into the driver, you can switch to + promiscuous mode via the Wireless Tool's Monitor mode. While in this + mode, no packets can be sent. + +config IPW2200_RADIOTAP + bool "Enable radiotap format 802.11 raw packet support" + depends on IPW2200_MONITOR + +config IPW2200_PROMISCUOUS + bool "Enable creation of a RF radiotap promiscuous interface" + depends on IPW2200_MONITOR + select IPW2200_RADIOTAP + ---help--- + Enables the creation of a second interface prefixed 'rtap'. + This second interface will provide every received in radiotap + format. + + This is useful for performing wireless network analysis while + maintaining an active association. + + Example usage: + + % modprobe ipw2200 rtap_iface=1 + % ifconfig rtap0 up + % tethereal -i rtap0 + + If you do not specify 'rtap_iface=1' as a module parameter then + the rtap interface will not be created and you will need to turn + it on via sysfs: + + % echo 1 > /sys/bus/pci/drivers/ipw2200/*/rtap_iface + +config IPW2200_QOS + bool "Enable QoS support" + depends on IPW2200 && EXPERIMENTAL + +config IPW2200_DEBUG + bool "Enable full debugging output in IPW2200 module." + depends on IPW2200 + ---help--- + This option will enable low level debug tracing output for IPW2200. + + Note, normal debug code is already compiled in. This low level + debug option enables debug on hot paths (e.g Tx, Rx, ISR) and + will result in the kernel module being ~70 larger. Most users + will typically not need this high verbosity debug information. + + If you are not sure, say N here. + +config WLAN_8688_SDIO + tristate "Marvell WLAN sd8688 support" + select WIRELESS_EXT + select WEXT_SPY + select WEXT_PRIV + select FW_LOADER + select MMC + ---help--- + Say Y if you have sd8688 hardware. + This option does not require driver just platform configuration. + +config LIBERTAS + tristate "Marvell 8xxx Libertas WLAN driver support" + depends on WLAN_80211 + select WIRELESS_EXT + select LIB80211 + select FW_LOADER + ---help--- + A library for Marvell Libertas 8xxx devices. + +config LIBERTAS_USB + tristate "Marvell Libertas 8388 USB 802.11b/g cards" + depends on LIBERTAS && USB + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_CS + tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards" + depends on LIBERTAS && PCMCIA + select FW_LOADER + ---help--- + A driver for Marvell Libertas 8385 CompactFlash devices. + +config LIBERTAS_SDIO + tristate "Marvell Libertas 8385 and 8686 SDIO 802.11b/g cards" + depends on LIBERTAS && MMC + ---help--- + A driver for Marvell Libertas 8385 and 8686 SDIO devices. + +config LIBERTAS_DEBUG + bool "Enable full debugging output in the Libertas module." + depends on LIBERTAS + ---help--- + Debugging support. + config LIBERTAS_THINFIRM tristate "Marvell 8xxx Libertas WLAN driver support with thin firmware" depends on MAC80211 diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index d189e4743e6997..500f083512bfa6 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -316,6 +316,22 @@ config ELECTRA_CF Say Y here to support the CompactFlash controller on the PA Semi Electra eval board. +config PXA168_CF + tristate "PXA168 On-Chip CompactFlash Controller" + depends on PCMCIA && CPU_PXA168 + select PXA168_CF_USE_GPIO_CARDDETECT + help + Say Y here to support the CompactFlash controller on PXA168 platforms. + Or choose M to compile the driver as a module named "pxa168_cf". + +config PXA168_CF_USE_GPIO_CARDDETECT + tristate "Use Card Detect MFP's as GPIO for card detection" + depends on PXA168_CF + default y + help + Say Y here to use CompactFlash Card Detect MFP's as a GPIO to detect + card insertion/removal + config PCCARD_NONSTATIC tristate diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 381b031d9d7539..7fa2859bc8b06a 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -35,6 +35,10 @@ obj-$(CONFIG_OMAP_CF) += omap_cf.o obj-$(CONFIG_BFIN_CFPCMCIA) += bfin_cf_pcmcia.o obj-$(CONFIG_AT91_CF) += at91_cf.o obj-$(CONFIG_ELECTRA_CF) += electra_cf.o +obj-$(CONFIG_PXA168_CF) += pxa168_cf.o + +sa11xx_core-y += soc_common.o sa11xx_base.o +pxa2xx_core-y += soc_common.o pxa2xx_base.o obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o au1x00_ss-y += au1000_generic.o diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 854959cada3ae3..4ae17386b1ccbd 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -54,6 +54,11 @@ static const u_int exponent[] = { /* Upper limit on reasonable # of tuples */ #define MAX_TUPLES 200 +#ifdef CONFIG_PXA168_CF +extern void pxa168_cf_mem_writeb(u8 attr_mem_transfer, u8 val, u32 addr); +extern u8 pxa168_cf_mem_readb(u8 attr_mem_transfer, u32 addr); +#endif /* CONFIG_PXA168_CF */ + /* 16-bit CIS? */ static int cis_width; module_param(cis_width, int, 0444); @@ -89,6 +94,7 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s, pccard_mem_map *mem = &s->cis_mem; int ret; +#ifndef CONFIG_PXA168_CF if (!(s->features & SS_CAP_STATIC_MAP) && (mem->res == NULL)) { mem->res = pcmcia_find_mem_region(0, s->map_size, s->map_size, 0, s); @@ -118,7 +124,12 @@ static void __iomem *set_cis_map(struct pcmcia_socket *s, iounmap(s->cis_virt); s->cis_virt = ioremap(mem->static_start, s->map_size); } - +#else + if (!s->cis_virt) { + ret = s->ops->set_mem_map(s, mem); + s->cis_virt = (void __iomem *)mem->static_start; + } +#endif return s->cis_virt; } @@ -150,20 +161,43 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); +#ifndef CONFIG_PXA168_CF if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); mutex_unlock(&s->ops_mutex); return -1; } +#else + sys = 0; /* pxa168 CF Controller doesnot support + memory-mapping Common/Attrib Memory*/ +#endif +#ifndef CONFIG_PXA168_CF writeb(flags, sys+CISREG_ICTRL0); writeb(addr & 0xff, sys+CISREG_IADDR0); writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); +#else + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)flags, (u32)(sys+CISREG_ICTRL0)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), (u8)(addr & 0xff), + (u32)(sys+CISREG_IADDR0)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)((addr>>8) & 0xff), (u32)(sys+CISREG_IADDR1)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)((addr>>16) & 0xff), (u32)(sys+CISREG_IADDR2)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)((addr>>24) & 0xff), (u32)(sys+CISREG_IADDR3)); +#endif for ( ; len > 0; len--, buf++) +#ifndef CONFIG_PXA168_CF *buf = readb(sys+CISREG_IDATA0); +#else + *buf = pxa168_cf_mem_readb((flags & MAP_ATTRIB), + ((u32)(sys+CISREG_IDATA0))); +#endif } else { u_int inc = 1, card_offset, flags; @@ -181,18 +215,27 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, card_offset = addr & ~(s->map_size-1); while (len) { sys = set_cis_map(s, card_offset, flags); +#ifndef CONFIG_PXA168_CF if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); memset(ptr, 0xff, len); mutex_unlock(&s->ops_mutex); return -1; } +#else + sys = 0; +#endif end = sys + s->map_size; sys = sys + (addr & (s->map_size-1)); for ( ; len > 0; len--, buf++, sys += inc) { if (sys == end) break; - *buf = readb(sys); +#ifndef CONFIG_PXA168_CF + *buf = readb(sys); +#else + *buf = pxa168_cf_mem_readb((flags & MAP_ATTRIB), + ((u32) sys)); +#endif } card_offset += s->map_size; addr = 0; @@ -232,19 +275,41 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0)); +#ifndef CONFIG_PXA168_CF if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); mutex_unlock(&s->ops_mutex); return; /* FIXME: Error */ } +#else + sys = 0; +#endif +#ifndef CONFIG_PXA168_CF writeb(flags, sys+CISREG_ICTRL0); writeb(addr & 0xff, sys+CISREG_IADDR0); writeb((addr>>8) & 0xff, sys+CISREG_IADDR1); writeb((addr>>16) & 0xff, sys+CISREG_IADDR2); writeb((addr>>24) & 0xff, sys+CISREG_IADDR3); +#else + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), (u8)flags, + (u32)(sys+CISREG_ICTRL0)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), (u8)(addr & 0xff), + (u32)(sys+CISREG_IADDR0)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)((addr>>8) & 0xff), (u32)(sys+CISREG_IADDR1)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)((addr>>16) & 0xff), (u32)(sys+CISREG_IADDR2)); + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)((addr>>24) & 0xff), (u32)(sys+CISREG_IADDR3)); +#endif for ( ; len > 0; len--, buf++) +#ifndef CONFIG_PXA168_CF writeb(*buf, sys+CISREG_IDATA0); +#else + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), (u8)(*buf), + (u32)(sys+CISREG_IDATA0)); +#endif } else { u_int inc = 1, card_offset, flags; @@ -258,18 +323,27 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, card_offset = addr & ~(s->map_size-1); while (len) { sys = set_cis_map(s, card_offset, flags); +#ifndef CONFIG_PXA168_CF if (!sys) { dev_dbg(&s->dev, "could not map memory\n"); mutex_unlock(&s->ops_mutex); return; /* FIXME: error */ } +#else + sys = 0; +#endif end = sys + s->map_size; sys = sys + (addr & (s->map_size-1)); for ( ; len > 0; len--, buf++, sys += inc) { if (sys == end) break; +#ifndef CONFIG_PXA168_CF writeb(*buf, sys); +#else + pxa168_cf_mem_writeb((flags & MAP_ATTRIB), + (u8)(*buf), (u32)sys); +#endif } card_offset += s->map_size; addr = 0; diff --git a/drivers/pcmcia/pxa168_cf.c b/drivers/pcmcia/pxa168_cf.c new file mode 100644 index 00000000000000..16be686fdbcf0c --- /dev/null +++ b/drivers/pcmcia/pxa168_cf.c @@ -0,0 +1,1079 @@ +/* + * pxa168_cf.c -- Aspen CompactFlash Host controller driver + * Copyright (c) 2008 Marvell International Ltd. (kvedere@marvell.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include "pxa168_cf.h" + +static const char driver_name[] = "pxa168-cf"; +struct device *dev; + +#define BUFFER_AVAIL_TIMEOUT (2*HZ/10) +#define TRANSFER_DONE_TIMEOUT (2*HZ/10) +#define CARD_READY_TIMEOUT (2*HZ/10) + +#define GPIO_CF_nCD1 32 +#define GPIO_CF_nCD2 33 + +struct pxa168_cf_socket { + struct pcmcia_socket socket; + struct resource *res; + struct clk *clk; + unsigned present:1; + unsigned active:1; + struct platform_device *pdev; + unsigned long phys_baseaddr; + void __iomem *base; + u_int irq; +}; + +static void __iomem *cf_base; +static struct completion transfer_done, buffer_avail, card_ready; +volatile int transf_done, buf_avail, card_rdy; + +#define SZ_2K (2 * SZ_1K) + +static inline int is_card_ready(void __iomem *cf_base); + +static int pxa168_cf_present(struct pxa168_cf_socket *cf) +{ + int ret; +#ifndef CONFIG_PXA168_CF_USE_GPIO_CARDDETECT + u32 cfi_reg; + cfi_reg = readl(cf->base + CFI_SR_OFFSET); + dev_dbg(dev,"Card Present = 0x%x 0x%x\n",cfi_reg, + ~((cfi_reg & CFI_SR_CARD_DETECT_1) >> 2)); + return !(cfi_reg & CFI_SR_CARD_DETECT_1); +#else + if (gpio_request(GPIO_CF_nCD1, "CF_nCD1")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", GPIO_CF_nCD1); + return -EIO; + } + if (gpio_request(GPIO_CF_nCD2, "CF_nCD2")) { + printk(KERN_ERR "Request GPIO failed," + "gpio: %d \n", GPIO_CF_nCD2); + return -EIO; + } + if (gpio_get_value(GPIO_CF_nCD1) && gpio_get_value(GPIO_CF_nCD2)) + ret = 0; + else + ret = 1; + gpio_free(GPIO_CF_nCD1); + gpio_free(GPIO_CF_nCD2); + return ret; +#endif +} + +#if 0 /* unused */ +static int pxa168_cf_ready(struct pxa168_cf_socket *cf) +{ + u32 cfi_reg; + cfi_reg = readl(cf->base + CFI_SR_OFFSET); + return ((cfi_reg & CFI_SR_CARD_READY) >> 5); +} +#endif + +/* Card Ready Interrupt doesnot occur for some + * reason. (May be routine called with interrupts + * disabled). Using polling mode to getaround this + * This routine will be called only once after card is + * inserted. So this shouldn't be bad. + */ + +static int pxa168_cf_reset(struct pxa168_cf_socket *cf) +{ + u32 opmode, ret; + + opmode = readl(cf->base + CF_OPMODE_OFFSET); + opmode = opmode | CF_OPMODE_CARD_RESET; + writel(opmode, cf->base + CF_OPMODE_OFFSET); + udelay(5); + opmode = opmode & ~(CF_OPMODE_CARD_RESET); + writel(opmode, cf->base + CF_OPMODE_OFFSET); + ret = is_card_ready(cf->base); + if(!ret) { + printk(KERN_ERR "%s: Card Ready timeout\n",__func__); + return 0; + } + return 1; +} + +/* When the process is in SOFT_IRQ Context interrupts seem + * to be disabled in some cases. So we use polling to monitor + * if each of the events have occured + */ +static inline int is_buffer_avail(void __iomem *cf_base) +{ + int ret=1, count=10000; + while(!(readl(cf_base + CF_IRQ_OFFSET) & CF_IRQ_BUFF_AVAL_IRQ) && count--); + if(!count) + ret = 0; + writel(CF_IRQ_BUFF_AVAL_IRQ, cf_base + CF_IRQ_OFFSET); + return ret; +} + +static inline int is_transfer_done(void __iomem *cf_base) +{ + int ret=1, count = 10000; + while(!(readl(cf_base + CF_IRQ_OFFSET) & CF_IRQ_TRANS_DONE_IRQ) && count--); + if(!count) + ret = 0; + writel(CF_IRQ_TRANS_DONE_IRQ, cf_base + CF_IRQ_OFFSET); + return ret; +} + +static inline int is_card_ready(void __iomem *cf_base) +{ + int ret=1, count = 10000; + while(!(readl(cf_base + CF_IRQ_OFFSET) & CF_IRQ_MEMO_MODE_READY) && count--); + if(!count) + ret = 0; + writel(CF_IRQ_MEMO_MODE_READY, cf_base + CF_IRQ_OFFSET); + return ret; +} + +/**************** pccard_operations.init ***********************************/ + +static int pxa168_cf_ss_init(struct pcmcia_socket *s) +{ + return 0; +} + +static int pxa168_cf_get_status(struct pcmcia_socket *sock, u_int *sp) +{ + struct pxa168_cf_socket *cf; + cf = container_of(sock, struct pxa168_cf_socket, socket); + + if (!sp) + return -EINVAL; + if (cf->present) { + *sp = SS_DETECT | SS_3VCARD | SS_POWERON; + + if (cf->active) + *sp |= SS_READY; + } else + *sp = 0; + return 0; +} + +static int pxa168_cf_set_socket(struct pcmcia_socket *sock, + struct socket_state_t *s) +{ + struct pxa168_cf_socket *cf; + + cf = container_of(sock, struct pxa168_cf_socket, socket); + dev_dbg(dev,"%s: Vcc= %d\n",driver_name, s->Vcc); + + switch (s->Vcc) { + case 0: + case 33: + break; + default: + return -EINVAL; + } + + if (s->flags & SS_RESET) + { + cf->active = pxa168_cf_reset(cf); + /* If Card Reset fails, issue warning + * and set cf->active to 1 to proceed + * further with card initialization. + */ + printk("%s:CF Card Reset %s\n",__func__, + cf->active?"Successfull":"Failed"); + cf->active = 1; + } + + dev_dbg(dev,"%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", + driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); + + return 0; +} + +#ifdef CONFIG_PM +static int pxa168_cf_ss_suspend(struct pcmcia_socket *s) +{ + dev_dbg(dev,"%s: %s\n", driver_name, __FUNCTION__); + return pxa168_cf_set_socket(s, &dead_socket); +} +#else +#define pxa168_cf_ss_suspend NULL +#endif + +/* PXA168 Platform doesnot have an IO-Map for the CF IO + * Memory Space. We use the CF Host Controller to read/write + * to the IO Memory Space + */ +static int pxa168_cf_set_io_map(struct pcmcia_socket *s, + struct pccard_io_map *io) +{ + struct pxa168_cf_socket *cf; + + cf = container_of(s, struct pxa168_cf_socket, socket); + io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ); + io->start = cf->socket.io_offset; + io->stop = io->start + SZ_2K - CF_IO_BASE_OFFSET - 1; + return 0; +} + +/* PXA168 Platform doesnot support Memory-Mapping the CF Common/ + * Attrib. Memory Space. We use the CF Host Controller to read/write + * to the these Memory Spaces + */ +static int pxa168_cf_set_mem_map(struct pcmcia_socket *s, + struct pccard_mem_map *map) +{ + struct pxa168_cf_socket *cf; + + cf = container_of(s, struct pxa168_cf_socket, socket); + map->static_start = (unsigned long) cf->base; + return 0; +} + +static struct pccard_operations pxa168_cf_ops = { + .init = pxa168_cf_ss_init, + .suspend = pxa168_cf_ss_suspend, + .get_status = pxa168_cf_get_status, + .set_socket = pxa168_cf_set_socket, + .set_io_map = pxa168_cf_set_io_map, + .set_mem_map = pxa168_cf_set_mem_map, +}; + +/*---- PXA168 CF Memory Read/Write Routines -----*/ + +void pxa168_cf_mem_writeb(u8 attr_mem_transfer, u8 val, u32 addr) +{ + u32 tmp, trans_ctrl, intr_en, ret, retries=5; + + attr_mem_transfer = attr_mem_transfer?1:0; + if (!in_softirq()) + INIT_COMPLETION(transfer_done); + else { + dev_dbg(dev,"%s: In SoftIRQ Context\n",__func__); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en & ~(CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ); + } + /* Program Transfer Address Register with Common/Attrib. Mem Addr. */ +retry_again: + writel((addr & 0x7FF), cf_base + CF_TADDR_OFFSET); + trans_ctrl = ((CF_TCNTR_MEM_TRANS_START) | + (CF_TCNTR_TRANS_DIR_WRITE) | + (CF_TCNTR_COMMON_ATTR_MEM &(attr_mem_transfer<<26)) | + (CF_TCNTR_DISABLE_ADDR_INC) | + (CF_TCNTR_COUNT_MASK & 0x1)); + writel(trans_ctrl, cf_base + CF_TCNTR_OFFSET); + /* Write value to Host Controller WriteFIFO */ + writel((u32) val, cf_base + CF_WDPR_OFFSET); + dev_dbg(dev,"%s: Before Transfer: intr_en = 0x%x addr = 0x%x \ + trans_ctrl_reg=0x%x val=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base+CF_TCNTR_OFFSET),val); + if (in_softirq()) { + ret = is_transfer_done(cf_base); + } else { + ret = wait_for_completion_timeout(&transfer_done, + TRANSFER_DONE_TIMEOUT); + } + if(!ret) { + if(retries--) { + writel(trans_ctrl & ~(CF_TCNTR_MEM_TRANS_START), + cf_base + CF_TCNTR_OFFSET); + goto retry_again; + } + printk(KERN_WARNING "%s: Transfer Done timeout\n",__func__); + if (!in_softirq()) + complete(&transfer_done); + } + /* Disable Transfer Start Bit Explicitly */ + tmp = readl(cf_base + CF_TCNTR_OFFSET); + writel(tmp & ~(CF_TCNTR_MEM_TRANS_START), cf_base + CF_TCNTR_OFFSET); + if (in_softirq()) { + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + } + dev_dbg(dev,"%s: After Transfer: intr_en = 0x%x addr = 0x%x \ + trans_ctrl_reg=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base + CF_TCNTR_OFFSET)); +} + +u8 pxa168_cf_mem_readb(u8 attr_mem_transfer, u32 addr) +{ + u32 val, tmp, trans_ctrl, intr_en, ret, retries=5; + + attr_mem_transfer = attr_mem_transfer?1:0; + if (!in_softirq()) { + INIT_COMPLETION(transfer_done); + INIT_COMPLETION(buffer_avail); + } else { + dev_dbg(dev,"%s: In SoftIRQ Context\n",__func__); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en & ~(CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ); + } +retry_again: + writel((addr & 0x7FF), cf_base + CF_TADDR_OFFSET); + /* Program Transfer Address Register with Common/Attrib. Mem Addr. */ + trans_ctrl = ((CF_TCNTR_MEM_TRANS_START) | + (CF_TCNTR_COMMON_ATTR_MEM &(attr_mem_transfer<<26)) | + (CF_TCNTR_DISABLE_ADDR_INC) | + (CF_TCNTR_COUNT_MASK & 0x1)); + writel(trans_ctrl, cf_base + CF_TCNTR_OFFSET); + dev_dbg(dev,"%s: Before Transfer: intr_en = 0x%x addr = 0x%x \ + trans_ctrl_reg=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), + addr, readl(cf_base+CF_TCNTR_OFFSET)); + if (in_softirq()) { + ret = is_buffer_avail(cf_base); + } else { + ret = wait_for_completion_timeout(&buffer_avail, + BUFFER_AVAIL_TIMEOUT); + } + if(!ret) { + if(retries--) { + val = readl(cf_base + CF_RDPR_OFFSET); + writel(trans_ctrl & ~(CF_TCNTR_MEM_TRANS_START), + cf_base + CF_TCNTR_OFFSET); + goto retry_again; + } + printk(KERN_WARNING "%s: Buffer Available timeout\n", + __func__); + if (!in_softirq()) + complete(&buffer_avail); + val = 0x0; + } else { + /* Read value from Host Controller ReadFIFO */ + val = readl(cf_base + CF_RDPR_OFFSET); + } + if (in_softirq()) { + ret = is_transfer_done(cf_base); + } else { + ret = wait_for_completion_timeout(&transfer_done, TRANSFER_DONE_TIMEOUT); + } + if(!ret) { + dev_dbg(dev,"%s: Transfer Done timeout\n",__func__); + if (!in_softirq()) + complete(&transfer_done); + } + tmp = readl(cf_base + CF_TCNTR_OFFSET); + writel(tmp & ~(CF_TCNTR_MEM_TRANS_START), cf_base + CF_TCNTR_OFFSET); + dev_dbg(dev,"%s: After Transfer: intr_en = 0x%x addr = 0x%x\ + trans_ctrl_reg=0x%x val=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base + CF_TCNTR_OFFSET),val); + if (in_softirq()) { + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + } + return (u8) val & 0xFF; +} + +void pxa168_cf_mem_writew(u8 attr_mem_transfer, u16 val, u32 addr) +{ + u32 tmp, trans_ctrl, intr_en, ret, retries=5; + + attr_mem_transfer = attr_mem_transfer?1:0; + if (!in_softirq()) + INIT_COMPLETION(transfer_done); + else { + dev_dbg(dev,"%s: In SoftIRQ Context\n",__func__); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en & ~(CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ); + } +retry_again: + /* Program Transfer Address Register with Common/Attrib. Mem Addr. */ + writel((addr & 0x7FF), cf_base + CF_TADDR_OFFSET); + trans_ctrl = ((CF_TCNTR_MEM_TRANS_START) | + (CF_TCNTR_TRANS_DIR_WRITE) | + (CF_TCNTR_COMMON_ATTR_MEM &(attr_mem_transfer<<26)) | + (CF_TCNTR_TRANS_SIZE_WORD) | + (CF_TCNTR_DISABLE_ADDR_INC)| + (CF_TCNTR_COUNT_MASK & 0x2)); + writel(trans_ctrl, cf_base + CF_TCNTR_OFFSET); + /* Write value to Host Controller WriteFIFO */ + writel((u32) val, cf_base + CF_WDPR_OFFSET); + dev_dbg(dev,"%s: Before Transfer: intr_en = 0x%x addr = 0x%x\ + trans_ctrl_reg=0x%x val=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base+CF_TCNTR_OFFSET),val); + if (in_softirq()) { + ret = is_transfer_done(cf_base); + } else { + ret = wait_for_completion_timeout(&transfer_done, + TRANSFER_DONE_TIMEOUT); + } + if(!ret) { + if(retries--) { + writel(trans_ctrl & ~(CF_TCNTR_MEM_TRANS_START), + cf_base + CF_TCNTR_OFFSET); + goto retry_again; + } + printk(KERN_WARNING "%s: Transfer Done timeout\n",__func__); + if (!in_softirq()) + complete(&transfer_done); + } + /* Disable Transfer Start Bit */ + tmp = readl(cf_base + CF_TCNTR_OFFSET); + writel(tmp & ~(CF_TCNTR_MEM_TRANS_START), cf_base + CF_TCNTR_OFFSET); + dev_dbg(dev,"%s: After Transfer: intr_en = 0x%x addr = 0x%x\ + trans_ctrl_reg=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base+CF_TCNTR_OFFSET)); + if (in_softirq()) { + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + } +} + +u32 pxa168_cf_mem_readw(u8 attr_mem_transfer, u32 addr) +{ + u32 val, tmp, trans_ctrl, intr_en, ret, retries=5; + + attr_mem_transfer = attr_mem_transfer?1:0; + if (!in_softirq()) { + INIT_COMPLETION(transfer_done); + INIT_COMPLETION(buffer_avail); + } else { + dev_dbg(dev,"%s: In SoftIRQ Context\n",__func__); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en & ~(CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ); + } +retry_again: + writel((addr & 0x7FF), cf_base + CF_TADDR_OFFSET); + /* Program Transfer Address Register with Common/Attrib. Mem Addr. */ + trans_ctrl = ((CF_TCNTR_MEM_TRANS_START) | + (CF_TCNTR_COMMON_ATTR_MEM &(attr_mem_transfer<<26)) | + (CF_TCNTR_TRANS_SIZE_WORD) | + (CF_TCNTR_DISABLE_ADDR_INC) | + (CF_TCNTR_COUNT_MASK & 0x2)); + writel(trans_ctrl, cf_base + CF_TCNTR_OFFSET); + dev_dbg(dev,"%s: Before Transfer: intr_en = 0x%x addr = 0x%x\ + trans_ctrl_reg=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base+CF_TCNTR_OFFSET)); + if (in_softirq()) { + ret = is_buffer_avail(cf_base); + } else { + ret = wait_for_completion_timeout(&buffer_avail, + BUFFER_AVAIL_TIMEOUT); + } + if(!ret) { + if(retries--) { + val = readl(cf_base + CF_RDPR_OFFSET); + writel(trans_ctrl & ~(CF_TCNTR_MEM_TRANS_START), + cf_base + CF_TCNTR_OFFSET); + goto retry_again; + } + printk(KERN_WARNING "%s: Buffer Available timeout\n", + __func__); + if (!in_softirq()) + complete(&buffer_avail); + val = 0x0; + } else { + /* Read value from Host Controller ReadFIFO */ + val = readl(cf_base + CF_RDPR_OFFSET); + } + if (in_softirq()) { + ret = is_transfer_done(cf_base); + } else { + ret = wait_for_completion_timeout(&transfer_done, + TRANSFER_DONE_TIMEOUT); + } + if(!ret) { + dev_dbg(dev,"%s: Transfer Done timeout\n",__func__); + if (!in_softirq()) + complete(&transfer_done); + } + /* Disable Transfer Start Bit */ + tmp = readl(cf_base + CF_TCNTR_OFFSET); + writel(tmp & ~(CF_TCNTR_MEM_TRANS_START), cf_base + CF_TCNTR_OFFSET); + dev_dbg(dev,"%s: After Transfer: intr_en = 0x%x addr = 0x%x \ + trans_ctrl_reg=0x%x val=0x%x\n",__func__, + readl(cf_base + CF_INTEN_OFFSET), addr, + readl(cf_base+CF_TCNTR_OFFSET),val); + if (in_softirq()) { + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + } + return val & 0xFFFF; +} + +void pxa168_cf_mem_write(u8 attr_mem_transfer, u8* buf, u32 addr, u32 words) +{ + u32 tmp, trans_ctrl, val, intr_en, ret; + u32 buf_len = words*2; /* Length of buffer in bytes */ + u32 len, trans_len; + u32 ext_wp_offset; + + if(words<1) { + printk(KERN_WARNING "%s: Invalid word count\n",__func__); + return; + } + dev_dbg(dev,"%s: addr=0x%x words=0x%x\n",__func__, addr, words); + attr_mem_transfer = attr_mem_transfer?1:0; + if (in_softirq()) { + dev_dbg(dev,"%s: In SoftIRQ Context\n",__func__); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en & ~(CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ); + } + writel((addr & 0x7FF), cf_base + CF_TADDR_OFFSET); + while(buf_len) + { + if (!in_softirq()) + INIT_COMPLETION(transfer_done); + len = (buf_len>=0x20000)?0x1FFFF:buf_len; + trans_ctrl = ((CF_TCNTR_MEM_TRANS_START) | + (CF_TCNTR_TRANS_DIR_WRITE) | + (CF_TCNTR_COMMON_ATTR_MEM &(attr_mem_transfer<<26)) | + (CF_TCNTR_TRANS_SIZE_WORD) | + (CF_TCNTR_DISABLE_ADDR_INC)| + (CF_TCNTR_COUNT_MASK & len)); + writel(trans_ctrl, cf_base + CF_TCNTR_OFFSET); + buf_len = buf_len - len; + dev_dbg(dev,"%s: len=%d trans_ctrl=0x%x",__func__,len,trans_ctrl); + do { + if (!in_softirq()) + INIT_COMPLETION(buffer_avail); + trans_len = (len>512)?512:len; + ext_wp_offset=CF_EXT_WDPR_OFFSET; + len = len - trans_len; + while(trans_len) { + if(trans_len==2) { + val = ((buf[1]<<8)|(buf[0])); + buf+= 2; + trans_len = 0; + } else { + val = ((buf[3]<<24)|(buf[2]<<16)| + (buf[1]<<8)|(buf[0])); + buf+= 4; + trans_len = trans_len - 4; + } + writel((u32) val, cf_base + ext_wp_offset); + ext_wp_offset+=4; + } + if (!len) + break; + if (in_softirq()) { + ret = is_buffer_avail(cf_base); + } else { + ret = wait_for_completion_timeout(&buffer_avail,BUFFER_AVAIL_TIMEOUT); + } + if(!ret) { + printk(KERN_WARNING "%s: Buffer Available timeout\n", + __func__); + if (!in_softirq()) { + dev_dbg(dev,"Process Context\n"); + complete(&buffer_avail); + } + } + } while (len); + if (in_softirq()) { + ret = is_transfer_done(cf_base); + } else { + ret = wait_for_completion_timeout(&transfer_done, + TRANSFER_DONE_TIMEOUT); + } + if(!ret) { + if(buf_len) + printk(KERN_WARNING "%s: Transfer Done timeout\n",__func__); + if (!in_softirq()) { + dev_dbg(dev,"Process Context\n"); + complete(&transfer_done); + } + } + } + tmp = readl(cf_base + CF_TCNTR_OFFSET); + /* Disable Transfer Start Bit explicitly */ + writel(tmp & ~(CF_TCNTR_MEM_TRANS_START), cf_base + CF_TCNTR_OFFSET); + if (in_softirq()) { + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + } +} + +void pxa168_cf_mem_read(u8 attr_mem_transfer, u8* buf, u32 addr, u32 words) +{ + u32 tmp, val, trans_ctrl, intr_en, ret; + u32 buf_len = words*2; /* Length of buffer in bytes */ + u32 len, trans_len; + u32 ext_rp_offset; + + if(words<1) { + printk(KERN_WARNING "%s: Invalid word count\n",__func__); + return; + } + dev_dbg(dev,"%s: addr=0x%x words=0x%x\n",__func__, addr, words); + attr_mem_transfer = attr_mem_transfer?1:0; + if (in_softirq()) { + dev_dbg(dev,"%s: In SoftIRQ Context\n",__func__); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en & ~(CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ); + } + writel((addr & 0x7FF), cf_base + CF_TADDR_OFFSET); + if (!in_softirq()) + INIT_COMPLETION(buffer_avail); + while(buf_len) + { + if (!in_softirq()) + INIT_COMPLETION(transfer_done); + len = (buf_len>=0x20000)?0x1FFFF:buf_len; + trans_ctrl = ((CF_TCNTR_MEM_TRANS_START) | + (CF_TCNTR_COMMON_ATTR_MEM &(attr_mem_transfer<<26)) | + (CF_TCNTR_TRANS_SIZE_WORD) | + (CF_TCNTR_DISABLE_ADDR_INC)| + (CF_TCNTR_COUNT_MASK & len)); + writel(trans_ctrl, cf_base + CF_TCNTR_OFFSET); + buf_len = buf_len - len; + dev_dbg(dev,"%s: len=%d trans_ctrl=0x%x\n",__func__, + len,trans_ctrl); + do { + if (in_softirq()) { + ret = is_buffer_avail(cf_base); + } else { + ret = wait_for_completion_timeout(&buffer_avail,BUFFER_AVAIL_TIMEOUT); + } + if(!ret) { + printk(KERN_WARNING "%s: Buffer Available timeout\n", + __func__); + if (!in_softirq()) { + dev_dbg(dev,"Process Context\n"); + complete(&buffer_avail); + } + } + if (!in_softirq()) + INIT_COMPLETION(buffer_avail); + trans_len = (len>512)?512:len; + ext_rp_offset=CF_EXT_RDPR_OFFSET; + len = len - trans_len; + while(trans_len) { + val = readl(cf_base + ext_rp_offset); + if(trans_len==2) { + buf[0] = val & 0xFF; + buf[1] = (val>>8) & 0xFF; + buf+= 2; + trans_len = 0; + } else { + buf[0] = val & 0xFF; + buf[1] = (val>>8) & 0xFF; + buf[2] = (val>>16) & 0xFF; + buf[3] = (val>>24) & 0xFF; + buf+= 4; + trans_len = trans_len - 4; + } + ext_rp_offset+=4; + } + dev_dbg(dev,"%s: bytes_left(len)=%d\n",__func__,len); + } while (len); + if (in_softirq()) { + ret = is_transfer_done(cf_base); + } else { + ret = wait_for_completion_timeout(&transfer_done, + TRANSFER_DONE_TIMEOUT); + } + if(!ret) { + if(buf_len) + printk(KERN_WARNING "%s: Transfer Done timeout\n",__func__); + if (!in_softirq()) { + dev_dbg(dev,"Process Context\n"); + complete(&transfer_done); + } + } + dev_dbg(dev,"%s: bytes_left(buf_len)=%d\n",__func__,buf_len); + } + tmp = readl(cf_base + CF_TCNTR_OFFSET); + /* Disable Transfer Start Bit explicitly */ + writel(tmp & ~(CF_TCNTR_MEM_TRANS_START), cf_base + CF_TCNTR_OFFSET); + if (in_softirq()) { + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + } +} + +void pxa168_cf_enable_trueide_mode(void) +{ + u32 opmode, intr_en; + opmode = readl(cf_base + CF_OPMODE_OFFSET); + opmode = opmode | (CF_OPMODE_CARD_MODE_MASK && CARD_TRUE_IDE); + writel(opmode, cf_base + CF_OPMODE_OFFSET); + udelay(100); + intr_en = readl(cf_base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_TRUEIDE_MODE_IREQ; + writel(intr_en, cf_base + CF_INTEN_OFFSET); + udelay(100); +} + +static irqreturn_t pxa168_cf_wakeup_irq(int irq, void *_cf) +{ + return IRQ_HANDLED; +} + +static irqreturn_t pxa168_cf_irq(int irq, void *_cf) +{ +#ifndef CONFIG_PXA168_CF_USE_GPIO_CARDDETECT + int present; +#endif + unsigned int irq_reg, intr_en; + struct pxa168_cf_socket *cf = _cf; + + writel(0x40, APMU_WAKE_CLR); /* Clear CF Wakeup */ + irq_reg = readl(cf->base + CF_IRQ_OFFSET); + intr_en = readl(cf->base + CF_INTEN_OFFSET); + +#ifndef CONFIG_PXA168_CF_USE_GPIO_CARDDETECT + if ((irq_reg & CF_IRQ_CARD_DETECT) && + (intr_en & CF_INTEN_CARD_DETECT)) { + dev_dbg(dev,"Card Detect Interrupt occured\n"); + + /* Need to Clear Card Detect Interrupt Enable bit + * to clear Card Detect interrupt in IRQ Register + */ + intr_en = intr_en & ~CF_INTEN_CARD_DETECT; + writel(intr_en, cf->base + CF_INTEN_OFFSET); + /* Clear Card Detect IRQ */ + writel(CF_IRQ_CARD_DETECT, cf->base + CF_IRQ_OFFSET); + irq_reg = readl(cf->base + CF_IRQ_OFFSET); + /* Re-enable Card Detect IRQ */ + intr_en = readl(cf->base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_CARD_DETECT; + writel(intr_en, cf->base + CF_INTEN_OFFSET); + /* Kick off pccardd operations */ + present = pxa168_cf_present(cf); + if (present != cf->present) { + cf->present = present; + dev_dbg(dev,"%s: card %s\n", driver_name, + present ? "inserted" : "removed"); + pcmcia_parse_events(&cf->socket, SS_DETECT); + } + } +#endif + if ((irq_reg & CF_IRQ_MEMO_MODE_READY) && + (intr_en & CF_INTEN_MEM_MODE_READY)) { + dev_dbg(dev,"Memory Mode Ready Interrupt occured\n"); + writel(CF_IRQ_MEMO_MODE_READY, cf->base + CF_IRQ_OFFSET); + irq_reg = readl(cf->base + CF_IRQ_OFFSET); + intr_en = intr_en & ~CF_INTEN_MEM_MODE_READY; + writel(intr_en, cf->base + CF_INTEN_OFFSET); + complete(&card_ready); + } + if ((irq_reg & CF_IRQ_BUFF_AVAL_IRQ) && + (intr_en & CF_INTEN_BUFF_AVAL_IRQ)) { + dev_dbg(dev,"Buffer Available Interrupt occured\n"); + writel(CF_IRQ_BUFF_AVAL_IRQ, cf->base+CF_IRQ_OFFSET); + irq_reg = readl(cf->base + CF_IRQ_OFFSET); + complete(&buffer_avail); + } + if ((irq_reg & CF_IRQ_TRANS_DONE_IRQ) && + (intr_en & CF_INTEN_TRANS_DONE_IRQ)) { + dev_dbg(dev,"Transfer Done Interrupt occured\n"); + writel(CF_IRQ_TRANS_DONE_IRQ, cf->base+CF_IRQ_OFFSET); + irq_reg = readl(cf->base + CF_IRQ_OFFSET); + complete(&transfer_done); + } + if ((irq_reg & CF_IRQ_PIO_TRANS_ERR_IRQ) && + (intr_en & CF_INTEN_PIO_TRANS_ERR_IRQ)) { + printk(KERN_ERR "PIO Transfer Error!\n"); + writel(CF_IRQ_PIO_TRANS_ERR_IRQ, cf->base+CF_IRQ_OFFSET); + irq_reg = readl(cf->base + CF_IRQ_OFFSET); + } + return IRQ_HANDLED; +} + +#ifdef CONFIG_PXA168_CF_USE_GPIO_CARDDETECT +/* CF Host Controller Card Detect Interrupt doesnot occur. + * As a temporary workaround we are configuring the CF + * Card Detect Pin as GPIO and using it to trigger interrupt + * during a Falling/Rising Edge + */ +static irqreturn_t pxa168_cf_carddetect_irq(int irq, void *_cf) +{ + struct pxa168_cf_socket *cf = _cf; + int present; + /* Kick off pccardd operations */ + present = pxa168_cf_present(cf); + if (present != cf->present) { + cf->present = present; + printk("%s: CF card %s\n", driver_name, + present ? "inserted" : "removed"); + pcmcia_parse_events(&cf->socket, SS_DETECT); + } + return IRQ_HANDLED; +} +#endif + +static void pxa168_cfhost_init(struct pxa168_cf_socket *cf) +{ + int opmode; + u32 intr_en, clk_cfg_reg; + /* Clear IRQ/Inten/Opmode Registers*/ + writel(0x1FFF, cf->base + CF_IRQ_OFFSET); + writel(0x0, cf->base + CF_INTEN_OFFSET); + writel(0x0, cf->base + CF_OPMODE_OFFSET); + udelay(10); + + /* Enable the CFHost Controller*/ + opmode = readl(cf->base + CF_OPMODE_OFFSET); + opmode = opmode | CF_OPMODE_CFHOST_ENABLE; + writel(opmode, cf->base + CF_OPMODE_OFFSET); + + /* Program Clock Configuration Register */ + clk_cfg_reg = readl(cf->base + CFI_CLOCK_CONFIG_OFFSET); + clk_cfg_reg = (CFI_CLOCK_CONFIG_MASK & CLOCK_CONFIG_100M) | + (CFI_CLOCK_RATIO_MASK & (CLOCK_RATIO_1 << 4)); + writel(clk_cfg_reg, cf->base + CFI_CLOCK_CONFIG_OFFSET); + + /* Set Interrupt Enable Register*/ + intr_en = readl(cf->base + CF_INTEN_OFFSET); + intr_en = intr_en | CF_INTEN_CARD_DETECT | + CF_INTEN_MEM_MODE_READY | + CF_INTEN_TRANS_DONE_IRQ | + CF_INTEN_PIO_TRANS_ERR_IRQ | + CF_INTEN_BUFF_AVAL_IRQ; + writel(intr_en, cf->base + CF_INTEN_OFFSET); +} + +static int __init pxa168_cf_probe(struct platform_device *pdev) +{ + struct pxa168_cf_socket *cf; + int irq, cd_irq; + int status; + struct resource *res; + struct clk *clk = NULL; + + dev = &pdev->dev; + res = platform_get_resource(pdev,IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + cd_irq = platform_get_irq(pdev, 1); + dev_dbg(dev,"%s: res->start: 0x%8x irq:%d",__func__,res->start,irq); + if (!res || irq < 0 || cd_irq < 0) + { + dev_err(&pdev->dev, "Unable to get resource/irq\n"); + return -ENXIO; + } + cf = kzalloc(sizeof *cf, GFP_KERNEL); + if (!cf) + { + dev_err(&pdev->dev, "Unable to allocate CF Memory\n"); + return -ENOMEM; + } + cf->pdev = pdev; + cf->res = res; + cf->phys_baseaddr = res->start; + platform_set_drvdata(pdev, cf); + + res = request_mem_region(res->start, SZ_2K, driver_name); + if (!res) { + dev_err(&pdev->dev, "Unable to request CF Memory\n"); + status = -EBUSY; + goto fail0; + } + cf->base = ioremap(res->start,SZ_2K); + printk("cf->base:0x%8x\n",(unsigned int) cf->base); + if (!cf->base) { + status = -ENOMEM; + goto fail1; + } + cf_base = cf->base; + + /* Enable CKEN_CF */ + clk = clk_get(&pdev->dev, "CFCLK"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get cf clock\n"); + return PTR_ERR(clk); + } + clk_enable(clk); + cf->clk = clk; + + status = request_irq(irq, pxa168_cf_irq, IRQF_SHARED | IRQF_DISABLED, + driver_name, cf); + if (status < 0) + { + dev_err(&pdev->dev, "Unable to request CF irq\n"); + goto fail2; + } + status = request_irq(IRQ_PXA168_CF_WAKEUP, pxa168_cf_wakeup_irq, + IRQF_SHARED, driver_name, cf); + + if (status < 0) + { + dev_err(&pdev->dev, "Unable to request CF Wakeup irq\n"); + goto fail2; + } + + status = request_irq(cd_irq, pxa168_cf_carddetect_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, driver_name, cf); + + if (status < 0) + { + dev_err(&pdev->dev, "Unable to request CF CardDetect GPIO\ + irq\n"); + goto fail2; + } + + init_completion(&transfer_done); + init_completion(&buffer_avail); + init_completion(&card_ready); + + transf_done = buf_avail = card_rdy = 1; + + cf->irq = irq; + /* FIXME - Revisit Socket Initialization */ + cf->socket.pci_irq = irq; + /* pcmcia layer only remaps "real" memory not iospace */ + cf->socket.io_offset = (u32) cf->base + CF_IO_BASE_OFFSET; + if (!cf->socket.io_offset) { + status = -ENXIO; + goto fail3; + } + cf->socket.owner = THIS_MODULE; + cf->socket.dev.parent = &pdev->dev; + cf->socket.ops = &pxa168_cf_ops; + cf->socket.resource_ops = &pccard_static_ops; + cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP + | SS_CAP_MEM_ALIGN; + cf->socket.map_size = SZ_1K; + cf->socket.io[0].res = res; + + dev_dbg(dev,"%s:Registering PCMCIA Socket\n",__func__); + status = pcmcia_register_socket(&cf->socket); + if (status < 0) + { + dev_err(&pdev->dev, "Unable to Register Socket\n"); + goto fail4; + } + cf->active = 0; + cf->present = 0; + + pxa168_cfhost_init(cf); + + /* Kick off pccardd operations if card detected during boot */ + cf->present = pxa168_cf_present(cf); + printk("%s:%sCF card detected\n", driver_name, + cf->present ? "" : "No "); + if (cf->present) + pcmcia_parse_events(&cf->socket, SS_DETECT); + + return 0; + +fail4: + if (cf->socket.io_offset) + iounmap((void __iomem *) cf->socket.io_offset); +fail3: + free_irq(irq, cf); +fail2: + clk_disable(cf->clk); + if (cf->base) + iounmap(cf->base); +fail1: + release_resource(cf->res); +fail0: + kfree(cf); + return status; +} + +static int __exit pxa168_cf_remove(struct platform_device *pdev) +{ + struct pxa168_cf_socket *cf = platform_get_drvdata(pdev); + + cf->active = 0; + pcmcia_unregister_socket(&cf->socket); + iounmap(cf->base); + iounmap((void __iomem *) cf->socket.io_offset); + clk_disable(cf->clk); + release_resource(cf->res); + free_irq(cf->irq, cf); + kfree(cf); + return 0; +} + +#ifdef CONFIG_PM +static int pxa168_cf_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return pcmcia_socket_dev_suspend(&pdev->dev, mesg); +} + +static int pxa168_cf_resume(struct platform_device *pdev) +{ + struct pxa168_cf_socket *cf; + cf = platform_get_drvdata(pdev); + clk_enable(cf->clk); + pxa168_cfhost_init(cf); + init_completion(&transfer_done); + init_completion(&buffer_avail); + init_completion(&card_ready); + transf_done = buf_avail = card_rdy = 1; + return pcmcia_socket_dev_resume(&pdev->dev); +} +#else +#define pxa168_cf_suspend NULL +#define pxa168_cf_resume NULL +#endif + + +static struct platform_driver pxa168_cf_driver = { + .driver = { + .name = (char *) driver_name, + }, + .remove = __exit_p(pxa168_cf_remove), + .suspend = pxa168_cf_suspend, + .resume = pxa168_cf_resume, +}; + +static int __init pxa168_cf_init(void) +{ + return platform_driver_probe(&pxa168_cf_driver, pxa168_cf_probe); +} + +static void __exit pxa168_cf_exit(void) +{ + platform_driver_unregister(&pxa168_cf_driver); +} + +module_init(pxa168_cf_init); +module_exit(pxa168_cf_exit); + +MODULE_DESCRIPTION("Aspen CF Interface Driver"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL_GPL(pxa168_cf_mem_readb); +EXPORT_SYMBOL_GPL(pxa168_cf_mem_writeb); +EXPORT_SYMBOL_GPL(pxa168_cf_mem_readw); +EXPORT_SYMBOL_GPL(pxa168_cf_mem_writew); +EXPORT_SYMBOL_GPL(pxa168_cf_mem_read); +EXPORT_SYMBOL_GPL(pxa168_cf_mem_write); diff --git a/drivers/pcmcia/pxa168_cf.h b/drivers/pcmcia/pxa168_cf.h new file mode 100644 index 00000000000000..e36ea28674d41b --- /dev/null +++ b/drivers/pcmcia/pxa168_cf.h @@ -0,0 +1,227 @@ +/* + * linux/drivers/pcmcia/aspen_cf.h + * + * Author: Alex Kaluzhny + * Created: May 1, 2008 + * Copyright (C) 2006 Marvell International Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __LINUX_ASPEN_CF_H +#define __LINUX_ASPEN_CF_H + +#include + +/* CF Registers */ +#define CF_BASE_ADDR 0xD4285000 + +#define CF_MEM_BASE_OFFSET 0x800 +#define CF_ATTR_BASE_OFFSET 0xC00 +#define CF_IO_BASE_OFFSET 0x30 + +/* CFI Status Register, all bits read only (RO) */ +#define CFI_SR_OFFSET (0x0000) +#define CFI_SR_STATUS_CHANGE (0x1<<0) +#define CFI_SR_BINARY_AUDIO_OUT (0x1<<1) +#define CFI_SR_CARD_DETECT_1 (0x1<<2) +#define CFI_SR_CARD_DETECT_2 (0x1<<3) +#define CFI_SR_INPUT_ACK (0x1<<4) +#define CFI_SR_CARD_READY (0x1<<5) +#define CFI_SR_BUS_CYCLE_WAIT (0x1<<6) +#define CFI_SR_16BIT_IO_PORT (0x1<<7) + +/* CF IRQ Register */ +#define CF_IRQ_OFFSET (0x0004) +#define CF_IRQ_CARD_DETECT (0x1<<0) +#define CF_IRQ_STATUS_CHANGE (0x1<<1) +#define CF_IRQ_MEMO_MODE_READY (0x1<<2) +#define CF_IRQ_IO_MODE_IREQ (0x1<<3) +#define CF_IRQ_TRUEIDE_MODE_IREQ (0x1<<8) +#define CF_IRQ_PIO_TRANS_ERR_IRQ (0x1<<9) +#define CF_IRQ_BUFF_AVAL_IRQ (0x1<<10) +#define CF_IRQ_TRANS_DONE_IRQ (0x1<<11) +#define CF_IRQ_WAKEUP (0x1<<12) + +/* CF Interrupt Enable Register */ +#define CF_INTEN_OFFSET (0x0008) +#define CF_INTEN_CARD_DETECT (0x1<<0) +#define CF_INTEN_STATUS_CHANGE (0x1<<1) +#define CF_INTEN_MEM_MODE_READY (0x1<<2) +#define CF_INTEN_IO_MODE_IREQ (0x1<<3) +#define CF_INTEN_TRUEIDE_MODE_IREQ (0x1<<8) +#define CF_INTEN_PIO_TRANS_ERR_IRQ (0x1<<9) +#define CF_INTEN_BUFF_AVAL_IRQ (0x1<<10) +#define CF_INTEN_TRANS_DONE_IRQ (0x1<<11) +#define CF_INTEN_WAKEUP_EN (0x1<<12) + +/* CF Operation Mode Register */ +#define CF_OPMODE_OFFSET (0x000C) +#define CF_OPMODE_CARD_MODE_MASK (0x3<<0) +#define CF_OPMODE_CARD_TYPE (0x1<<2) +#define CF_OPMODE_CARD_RESET (0x1<<3) +#define CF_OPMODE_CFHOST_ENABLE (0x1<<4) +#define CF_OPMODE_TRISTATE (0x1<<5) +#define CF_OPMODE_ULTADMA_ENABLE (0x1<<8) +#define CF_OPMODE_TRUEIDE_MW_DMA_ENABLE (0x1<<9) +#define CF_OPMODE_SLAVE_DMA_ENABLE (0x1<<10) +#define CF_OPMODE_DRQ_BLOCK_SIZE (0x3<<11) +#define CF_OPMODE_AHB_STRICT_BURST (0x1<<13) +#define CF_OPMODE_SLAVE_DMA_BURST_LENGTH (0xFF<<16) + +/* CF Interface Clock Configuration Register */ +#define CFI_CLOCK_CONFIG_OFFSET (0x0010) +#define CFI_CLOCK_CONFIG_MASK (0xF) +#define CFI_CLOCK_RATIO_MASK (0x7<<4) + +/* CF Timing Mode Configuration Register */ +#define CF_TMCFG_OFFSET (0x0014) +#define CF_TMCFG_MEM_TRANS_MASK (0x3<<0) +#define CF_TMCFG_IO_TRANS_MASK (0x3<<2) +#define CF_TMCFG_TRUEIDE_PIO_TRANS_MASK (0x7<<4) +#define CF_TMCFG_TRUEIDE_MW_DMA_TRANS_MASK (0x7<<7) +#define CF_TMCFG_ULTRA_DMA_TRANS_MASK (0x7<<10) + +/* CF Transfer Address Register */ +#define CF_TADDR_OFFSET (0x0018) +#define CF_TADDR_TRANS_ADDR_MASK (0x7FF) + +/* CF Transfer Control Register */ +#define CF_TCNTR_OFFSET (0x001C) +#define CF_TCNTR_COUNT_MASK (0x3FFFF) +#define CF_TCNTR_DISABLE_ADDR_INC (0x1<<24) +#define CF_TCNTR_TRANS_SIZE_WORD (0x1<<25) +#define CF_TCNTR_COMMON_ATTR_MEM (0x1<<26) +#define CF_TCNTR_MEM_IO_TRANS (0x1<<27) +#define CF_TCNTR_DMA_TRANS_MODE (0x1<<28) +#define CF_TCNTR_SLAVE_DMA_TRANS (0x1<<29) +#define CF_TCNTR_TRANS_DIR_WRITE (0x1<<30) +#define CF_TCNTR_MEM_TRANS_START (0x1<<31) + +/* CF Write Data Port Register */ +#define CF_WDPR_OFFSET (0x0024) + +/* CF Read Data Port Register */ +#define CF_RDPR_OFFSET (0x0028) + +/* CF Write Data Port Register */ +#define CF_EXT_WDPR_OFFSET (0x0200) + +/* CF Read Data Port Register */ +#define CF_EXT_RDPR_OFFSET (0x0400) + +/* CF ATA Data Port Register */ +#define CF_ATADPR_OFFSET (0x0030) +#define CF_ATADPR_MASK (0xFFFF) + +/* CF ATA Error/Features Register */ +#define CF_ATAERR_OFFSET (0x0034) +#define CF_ATAERR_MASK (0xFF) + +/* CF ATA Sector Count Register */ +#define CF_ATASCOUNTR_OFFSET (0x0038) +#define CF_ATASCOUNTR_MASK (0xFF) + +/* CF ATA Sector Number Register */ +#define CF_ATASNR_OFFSET (0x003C) +#define CF_ATASNR_MASK (0xFF) + +/* CF ATA Cylinder Low Register */ +#define CF_ATACLR_OFFSET (0x0040) +#define CF_ATACLR_MASK (0xFF) + +/* CF ATA Cylinder High Register */ +#define CF_ATACHR_OFFSET (0x0044) +#define CF_ATACHR_MASK (0xFF) + +/* CF ATA Select Card/Head Register */ +#define CF_ATASCARDR_OFFSET (0x0048) +#define CF_ATASCARDR_MASK (0xFF) + +/* CF ATA Status/Command Register */ +#define CF_ATASTAT_OFFSET (0x004C) +#define CF_ATASTAT_MASK (0xFF) + +/* CF ATA Alt Status/Device Control Register */ +#define CF_ATAALTSTAT_OFFSET (0x0050) +#define CF_ATAALTSTAT_MASK (0xFF) + + +enum cf_op_card_mode { + PC_CARD_MEM = 0, + PC_CARD_IO = 0x1, + CARD_TRUE_IDE = 0x2, +}; + +enum cf_op_card_type { + COMPACT_FLASH = 0, + CF_PLUS = 0x1, +}; + +enum cf_op_drq_block_size { + DRQ_BLOCK_SIZE_512 = 0, + DRQ_BLOCK_SIZE_1024 = 0x1, + DRQ_BLOCK_SIZE_2048 = 0x2, + DRQ_BLOCK_SIZE_4096 = 0x3, +}; + +enum cf_clock_config { + CLOCK_CONFIG_100M = 0, + CLOCK_CONFIG_75M = 0x1, + CLOCK_CONFIG_66M = 0x2, + CLOCK_CONFIG_50M = 0x3, + CLOCK_CONFIG_40M = 0x4, + CLOCK_CONFIG_33M = 0x5, + CLOCK_CONFIG_25M = 0x6, +}; + +enum cf_clock_ratio { + CLOCK_SAME_SOURCE = 0, + CLOCK_RATIO_1 = 0x1, + CLOCK_RATIO_2 = 0x2, + CLOCK_RATIO_3 = 0x3, + CLOCK_RATIO_4 = 0x4, + CLOCK_RATIO_5 = 0x5, + CLOCK_RATIO_6 = 0x6, + CLOCK_RATIO_7 = 0x7, +}; + +enum cf_timing_mode_mem_trans { + MEM_TRANS_250NS = 0, + MEM_TRANS_120NS = 0x1, + MEM_TRANS_100NS = 0x2, + MEM_TRANS_80NS = 0x3, +}; + +enum cf_timing_mode_io_trans { + IO_TRANS_250NS = 0, + IO_TRANS_120NS = 0x1, + IO_TRANS_100NS = 0x2, + IO_TRANS_80NS = 0x3, +}; + +enum cf_timing_true_ide_mode { + TRUE_IDE_MODE_0 = 0, + TRUE_IDE_MODE_1 = 0x1, + TRUE_IDE_MODE_2 = 0x2, + TRUE_IDE_MODE_3 = 0x3, + TRUE_IDE_MODE_4 = 0x4, + TRUE_IDE_MODE_5 = 0x5, + TRUE_IDE_MODE_6 = 0x6, +}; + + + +#endif /* __LINUX_ASPEN_CF_H */ diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index df4532e91b1a0d..aa34e817359f6b 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -226,6 +226,19 @@ static const char *skt_names[] = { "PCMCIA socket 0", "PCMCIA socket 1", }; +<<<<<<< HEAD:drivers/pcmcia/pxa2xx_base.c + +#define SKT_DEV_INFO_SIZE(n) \ + (sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket)) + +int __pxa2xx_drv_pcmcia_probe(struct device *dev) +{ + int i, ret; + struct pcmcia_low_level *ops; + struct skt_dev_info *sinfo; + struct soc_pcmcia_socket *skt; +======= +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/pcmcia/pxa2xx_base.c #define SKT_DEV_INFO_SIZE(n) \ (sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket)) @@ -256,8 +269,45 @@ int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt) } EXPORT_SYMBOL(pxa2xx_drv_pcmcia_add_one); +<<<<<<< HEAD:drivers/pcmcia/pxa2xx_base.c + sinfo = kzalloc(SKT_DEV_INFO_SIZE(ops->nr), GFP_KERNEL); + if (!sinfo) + return -ENOMEM; + + sinfo->nskt = ops->nr; + + /* Initialize processor specific parameters */ + for (i = 0; i < ops->nr; i++) { + skt = &sinfo->skt[i]; + + skt->nr = i; + skt->irq = NO_IRQ; + + skt->res_skt.start = _PCMCIA(skt->nr); + skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1; + skt->res_skt.name = skt_names[skt->nr]; + skt->res_skt.flags = IORESOURCE_MEM; + + skt->res_io.start = _PCMCIAIO(skt->nr); + skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1; + skt->res_io.name = "io"; + skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + + skt->res_mem.start = _PCMCIAMem(skt->nr); + skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1; + skt->res_mem.name = "memory"; + skt->res_mem.flags = IORESOURCE_MEM; + + skt->res_attr.start = _PCMCIAAttr(skt->nr); + skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1; + skt->res_attr.name = "attribute"; + skt->res_attr.flags = IORESOURCE_MEM; + } + +======= void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops) { +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/pcmcia/pxa2xx_base.c /* Provide our PXA2xx specific timing routines. */ ops->set_timing = pxa2xx_pcmcia_set_timing; #ifdef CONFIG_CPU_FREQ @@ -266,12 +316,16 @@ void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops) } EXPORT_SYMBOL(pxa2xx_drv_pcmcia_ops); +<<<<<<< HEAD:drivers/pcmcia/pxa2xx_base.c + ret = soc_common_drv_pcmcia_probe(dev, ops, sinfo); +======= static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev) { int i, ret = 0; struct pcmcia_low_level *ops; struct skt_dev_info *sinfo; struct soc_pcmcia_socket *skt; +>>>>>>> e40152ee1e1c7a63f4777791863215e3faa37a86:drivers/pcmcia/pxa2xx_base.c ops = (struct pcmcia_low_level *)dev->dev.platform_data; if (!ops) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index faaa9b4d0d0711..1ba2fe9107c995 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -64,6 +64,12 @@ config BATTERY_DS2760 help Say Y here to enable support for batteries with ds2760 chip. +config BATTERY_SANREMO + tristate "SANREMO battery driver (Used with sanremo)" + depends on SANREMO + help + Say Y here to enable support for batteries with sanremo chip + config BATTERY_DS2782 tristate "DS2782 standalone gas-gauge" depends on I2C @@ -131,4 +137,20 @@ config CHARGER_PCF50633 help Say Y to include support for NXP PCF50633 Main Battery Charger. +config POWER_AVENGERS_LITE + tristate "avengers-lite power driver " + help + Say Y here to enable support for power supply. + +config BATTERY_ASPENITE + tristate "ASPENITE battery driver" + help + Say Y to enable support for virtual battery on ASPENITE platform. + +config MCU_PM + bool "Avengers-lite Power Management Chips" + depends on I2C && I2C_PXA + help + if you say yes you get support for the power management MCU chip + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index a2ba7c85c97a55..61b66d46451cc8 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -30,5 +30,10 @@ obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o +obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o +obj-$(CONFIG_BATTERY_SANREMO) += sanremo_battery.o +obj-$(CONFIG_POWER_AVENGERS_LITE) += avengers_lite_power.o +obj-$(CONFIG_BATTERY_ASPENITE) += aspenite_battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o +obj-$(CONFIG_MCU_PM) += power_mcu.o diff --git a/drivers/power/aspenite_battery.c b/drivers/power/aspenite_battery.c new file mode 100644 index 00000000000000..b4edadee95d0be --- /dev/null +++ b/drivers/power/aspenite_battery.c @@ -0,0 +1,162 @@ +/* + * PXA168 Power supply driver + * + * Android requires the platform to report battery status to the + * com.android.server.BatteryService through linux power_supply framework + * + * Copyright (C) 2009 Marvell International Ltd. + * All rights reserved. + * Author: Mark Brown + * Based on goldfish_battery.c driver by Mike Lockwood + * + * + * 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 +#include + +#define TRUE 1 + +#define FAKE_CAPACITY 100 + +struct pxa168_battery_data { + struct power_supply battery; + struct power_supply ac; +} pxa168_battery_data; + +static int pxa168_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = TRUE; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int pxa168_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + + /* XXX Report fake information temporarily */ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = TRUE; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = FAKE_CAPACITY; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property pxa168_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property pxa168_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static int pxa168_battery_probe(struct platform_device *pdev) +{ + int ret = -1; + + pxa168_battery_data.battery.properties = pxa168_battery_props; + pxa168_battery_data.battery.num_properties = ARRAY_SIZE(pxa168_battery_props); + pxa168_battery_data.battery.get_property = pxa168_battery_get_property; + pxa168_battery_data.battery.name = "battery"; + pxa168_battery_data.battery.type = POWER_SUPPLY_TYPE_BATTERY; + + pxa168_battery_data.ac.properties = pxa168_ac_props; + pxa168_battery_data.ac.num_properties = ARRAY_SIZE(pxa168_ac_props); + pxa168_battery_data.ac.get_property = pxa168_ac_get_property; + pxa168_battery_data.ac.name = "ac"; + pxa168_battery_data.ac.type = POWER_SUPPLY_TYPE_MAINS; + + ret = power_supply_register(&pdev->dev, &pxa168_battery_data.ac); + if (ret) + goto err_ac_failed; + + ret = power_supply_register(&pdev->dev, &pxa168_battery_data.battery); + if (ret) + goto err_battery_failed; + + return 0; + +err_battery_failed: + power_supply_unregister(&pxa168_battery_data.ac); +err_ac_failed: + return ret; +} + +static int pxa168_battery_remove(struct platform_device *pdev) +{ + power_supply_unregister(&pxa168_battery_data.battery); + power_supply_unregister(&pxa168_battery_data.ac); + return 0; +} + +static struct platform_driver pxa168_battery_device = { + .probe = pxa168_battery_probe, + .remove = pxa168_battery_remove, + .driver = { + .name = "aspenite-battery"} +}; + +static int __init pxa168_battery_init(void) +{ + return platform_driver_register(&pxa168_battery_device); +} + +static void __exit pxa168_battery_exit(void) +{ + platform_driver_unregister(&pxa168_battery_device); +} + +module_init(pxa168_battery_init); +module_exit(pxa168_battery_exit); + +MODULE_AUTHOR("Mark F. Brown "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Battery driver for the PXA168"); diff --git a/drivers/power/avengers_lite_power.c b/drivers/power/avengers_lite_power.c new file mode 100644 index 00000000000000..f1d00645cb139a --- /dev/null +++ b/drivers/power/avengers_lite_power.c @@ -0,0 +1,506 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static unsigned int cache_time = 1000; +module_param(cache_time, uint, 0644); + + +MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); + + + +static int avengers_lite_init_status(struct avengers_lite_device_info *di) +{ + di->ec_status = POWER_SUPPLY_STATUS_UNKNOWN; + di->STATUS = EC_NO_CONNECTION ; + di->VERSION = -1; + di->RARC = 0; + di->BATT_FAULT = 1; + di->AC_IN = 0; + di->BATT_PROTECT = -1; + di->CURRENT = -1; + di->VOLT1 = 0; + di->VOLT2 = 0; + di->TEMP = 0; + + return 0; + +} + + + +static int avengers_lite_read_status(struct avengers_lite_device_info *di) +{ + int ret; + unsigned char temp_byte; + short temp_word; + + if (di->update_time && time_before(jiffies, di->update_time + msecs_to_jiffies(cache_time))) + return 0; + + di->update_time = jiffies; + + ret = 0; + + if (power_mcu_read(MCU_VERSION , &di->VERSION) == -EFAULT) + di->STATUS = EC_NO_CONNECTION; + else + di->STATUS = 0; + + if (di->STATUS != EC_NO_CONNECTION) { + + /* power_mcu_read( MCU_VERSION , &di->VERSION ); */ + /* printk("********************************%d\n",di->VERSION); */ + + power_mcu_read(MCU_DS_RARC, &temp_byte); + if ((temp_byte < di->RARC) || (temp_byte > di->RARC)) { + ret = 1; + di->RARC = temp_byte; + } + + /* power_mcu_read( MCU_DS_RSRC , &di->RSRC ); */ + /* power_mcu_read( MCU_DS_AS , &di->AS ); */ + /* power_mcu_read( MCU_DS_SFR , &di->SFR ); */ + /* power_mcu_read( MCU_DS_CONTROL , &di->CONTROL ); */ + /* power_mcu_read( MCU_DS_AB , &di->AB ); */ + /* power_mcu_read( MCU_DS_VCHG , &di->VCHG ); */ + /* power_mcu_read( MCU_DS_IMIN , &di->IMIN ); */ + /* power_mcu_read( MCU_DS_VAE , &di->VAE ); */ + /* power_mcu_read( MCU_DS_IAE , &di->IAE ); */ + /* power_mcu_read( MCU_DS_RSNSP , &di->RSNSP ); */ + + power_mcu_read(MCU_BATT_ABSENT, &temp_byte); + if (temp_byte != di->BATT_ABSENT) { + printk(KERN_ALERT "battery present event change" + "from %d to %d\n", di->BATT_ABSENT, temp_byte); + ret = 1; + di->BATT_ABSENT = temp_byte; + } + + power_mcu_read(MCU_AC_IN, &temp_byte); + if (temp_byte != di->AC_IN) { + ret = 1; + di->AC_IN = temp_byte; + } + + power_mcu_read(MCU_BATT_PROTECT, &temp_byte); + if (temp_byte != di->BATT_PROTECT) { + ret = 1; + di->BATT_PROTECT = temp_byte; + } + /* power_mcu_read( MCU_BATT_CYCLE , &di->BATT_CYCLE ); */ + /* power_mcu_read( MCU_DS_RAAC , &di->RAAC ); */ + /* power_mcu_read( MCU_DS_RSAC , &di->RSAC ); */ + /* power_mcu_read( MCU_DS_IAVG , &di->IAVG ); */ + power_mcu_read(MCU_DS_VOLT1 , &temp_word); + temp_word = temp_word >> 5; + if ((temp_word > di->VOLT1 + 1) || (temp_word < di->VOLT1 - 1)) { + ret = 1; + di->VOLT1 = temp_word; + } + + power_mcu_read(MCU_DS_CURRENT , &temp_word); + + if (((temp_word < 0) && (di->CURRENT > 0)) || ((temp_word > 0) && (di->CURRENT < 0))) { + ret = 1; + di->CURRENT = temp_word; + } + + if ((temp_word > di->CURRENT + 5) || (temp_word < di->CURRENT - 5)) { + ret = 1; + di->CURRENT = temp_word; + } + + /* power_mcu_read( MCU_DS_ACR , &di->ACR); */ + /* power_mcu_read( MCU_DS_ACRL , &di->ACRL): */ + /* power_mcu_read( MCU_DS_FULL , &di->FULL); */ + /* power_mcu_read( MCU_DS_AE , &di->AE); */ + /* power_mcu_read( MCU_DS_SE , &di->SE); */ + /* power_mcu_read( MCU_DS_AC , &di->AC); */ + power_mcu_read( MCU_DS_TEMP , &temp_word); + temp_word = temp_word >> 5; + if ((temp_word > di->TEMP + 5) || (temp_word < di->TEMP - 5)) { + ret = 1; + di->TEMP = temp_word; + } + + /* power_mcu_read( MCU_DS_FULL40 , &di->FULL40 ); */ + power_mcu_read( MCU_DS_VOLT2 , &temp_word); + temp_word = temp_word >> 5; + if ((temp_word > di->VOLT2 + 1) || (temp_word < di->VOLT2 - 1)) { + ret = 1; + di->VOLT2 = temp_word; + } + + /* + power_mcu_read(MCU_BATT_FULL, &di->BATT_FULL); + + power_mcu_write_word(MCU_RTC_LLSB, 0xff); + power_mcu_write_word(MCU_RTC_LMSB, 0x44); + power_mcu_write_word(MCU_RTC_HLSB, 0x11); + power_mcu_write_word(MCU_RTC_HMSB, 0x0); + //power_mcu_write_byte(MCU_RTC_STOP, 0); + + power_mcu_read(MCU_RTC_LLSB, &di->RTC_LLSB); + power_mcu_read(MCU_RTC_LMSB, &di->RTC_LMSB); + power_mcu_read(MCU_RTC_HLSB, &di->RTC_HLSB); + power_mcu_read(MCU_RTC_HMSB, &di->RTC_HMSB); + printk("llsb0x%x,lmsb0x%x,hlsb0x%x,hmsb0x%x\n", di->RTC_LLSB, di->RTC_LMSB, di->RTC_HLSB, di->RTC_HMSB); + */ + + } + + return ret; +} + + +static void avengers_lite_update_status(struct avengers_lite_device_info *di) +{ + int old_charge_status = di->ec_status; + short current_temp; + + if (avengers_lite_read_status(di) != 0) + kobject_uevent(&di->bat.dev->kobj , KOBJ_CHANGE); + + if (di->ec_status == POWER_SUPPLY_STATUS_UNKNOWN) + di->full_counter = 0; + + current_temp = di->CURRENT ; + + if (power_supply_am_i_supplied(&di->bat)) { + if (current_temp > 0) { + di->ec_status = POWER_SUPPLY_STATUS_CHARGING; + di->full_counter = 0; + } else if (current_temp < 0) { + if (di->ec_status != POWER_SUPPLY_STATUS_NOT_CHARGING) + dev_notice(di->dev, "not enough power to charge\n"); + di->ec_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + di->full_counter = 0; + } else if (current_temp < 10000 && di->ec_status != POWER_SUPPLY_STATUS_FULL) { + /* Don't consider the battery to be full unless + * we've seen the current < 10 mA at least two + * consecutive times. */ + + di->full_counter++; + + if (di->full_counter < 2) + di->ec_status = POWER_SUPPLY_STATUS_CHARGING; + else + di->ec_status = POWER_SUPPLY_STATUS_FULL; + } + } else { + di->ec_status = POWER_SUPPLY_STATUS_DISCHARGING; + di->full_counter = 0; + } + + if (di->ec_status != old_charge_status) + power_supply_changed(&di->bat); + + +} + + +static void avengers_lite_work(struct work_struct *work) +{ + struct avengers_lite_device_info *di = container_of(work, + struct avengers_lite_device_info, monitor_work.work); + const int interval = 1; + + dev_dbg(di->dev, "%s\n", __func__); + + avengers_lite_update_status(di); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); +} + +#define to_avengers_lite_device_info(x) container_of((x), struct avengers_lite_device_info, bat); + +static void avengers_lite_external_power_changed(struct power_supply *psy) +{ + struct avengers_lite_device_info *di = to_avengers_lite_device_info(psy); + + dev_dbg(di->dev, "%s\n", __func__); + + cancel_delayed_work(&di->monitor_work); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, 1); +} + +static int avengers_lite_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct avengers_lite_device_info *di = to_avengers_lite_device_info(psy); + + + avengers_lite_read_status(di); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = di->VOLT1 + di->VOLT2 ; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = di->CURRENT ; + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL: + if ((di->CURRENT < 50) && (di->CURRENT > 0)) + val->intval = 1; + else + val->intval = 0; + break; + + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + if (di->RARC < 5) + val->intval = 1; + else + val->intval = 0; + break; + + case POWER_SUPPLY_PROP_CHARGE_NOW: + if (di->CURRENT > 50) + val->intval = 1; + else + val->intval = 0; + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = di->TEMP; + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = di->AC_IN ; /* 1---ac online 0---ac offline */ + break; + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = !di->BATT_ABSENT ; /* battery is online/offline */ + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = di->RARC; + if (di->BATT_ABSENT) + val->intval = -1; + break; + + case POWER_SUPPLY_PROP_STATUS: /*1--charging 2--discharging 4--Full 0--Unkhown 3--Not charging*/ + if (di->STATUS == EC_NO_CONNECTION) + val->intval = 3; + else { + if (di->CURRENT < 0) + val->intval = 2; + else + val->intval = 1; + if (di->RARC > 95) + val->intval = 4; + /* report unknown status if battery not present */ + if (di->BATT_ABSENT) + val->intval = 0; + } + + break; + + case POWER_SUPPLY_PROP_HEALTH:/* 3 */ + /*3--Dead 1--Good 2--Overheat 5--Unspeified failure 0--Unknown 4--Over voltage*/ + if (di->BATT_PROTECT & MASK_BATT_OV) + val->intval = 4 ; + else + val->intval = 1; + + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = 2; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property avengers_lite_battery_props[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + +}; + +static enum power_supply_property avengers_lite_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static int avengers_lite_ac_read_status(struct avengers_lite_device_info *di) +{ + int ret; + unsigned char temp_byte; + + power_mcu_read(MCU_AC_IN, &temp_byte); + if (temp_byte != di->AC_IN) { + ret = 1; + di->AC_IN = temp_byte; + } + + return ret; +} + +static int avengers_lite_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct avengers_lite_device_info *di = to_avengers_lite_device_info(psy); + + avengers_lite_ac_read_status(di); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = di->AC_IN; + break; + default: + return -EINVAL; + } + return 0; +} + + +static int avengers_lite_power_probe(struct platform_device *pdev) +{ + int retval = 0; + struct avengers_lite_device_info *di; + + di = kzalloc(sizeof(*di), GFP_KERNEL); + if (!di) { + retval = -ENOMEM; + goto di_alloc_failed; + } + + platform_set_drvdata(pdev, di); + + di->dev = &pdev->dev; + di->bat.name = pdev->name; + di->bat.type = POWER_SUPPLY_TYPE_BATTERY; + di->bat.properties = avengers_lite_battery_props; + di->bat.num_properties = ARRAY_SIZE(avengers_lite_battery_props); + di->bat.get_property = avengers_lite_get_property; + di->bat.external_power_changed = avengers_lite_external_power_changed; + + di->ac.properties = avengers_lite_ac_props; + di->ac.num_properties = ARRAY_SIZE(avengers_lite_ac_props); + di->ac.get_property = avengers_lite_ac_get_property; + di->ac.name = "ac"; + di->ac.type = POWER_SUPPLY_TYPE_MAINS; + + di->ec_status = POWER_SUPPLY_STATUS_UNKNOWN; + + avengers_lite_init_status(di); + + retval = power_supply_register(&pdev->dev, &di->ac); + if (retval){ + dev_err(di->dev, "failed to register ac\n"); + goto ac_failed; + } + retval = power_supply_register(&pdev->dev, &di->bat); + if (retval) { + dev_err(di->dev, "failed to register battery\n"); + goto batt_failed; + } + + INIT_DELAYED_WORK(&di->monitor_work, avengers_lite_work); + di->monitor_wqueue = create_singlethread_workqueue("avlite power"); + if (!di->monitor_wqueue) { + retval = -ESRCH; + goto workqueue_failed; + } + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, 1); + + goto success; + +workqueue_failed: + power_supply_unregister(&di->bat); +batt_failed: + power_supply_unregister(&di->ac); +ac_failed: + kfree(di); +di_alloc_failed: +success: + return retval; +} + +static int avengers_lite_power_remove(struct platform_device *pdev) +{ + struct avengers_lite_device_info *di = platform_get_drvdata(pdev); + + cancel_rearming_delayed_workqueue(di->monitor_wqueue , &di->monitor_work); + destroy_workqueue(di->monitor_wqueue); + power_supply_unregister(&di->bat); + power_supply_unregister(&di->ac); + + return 0; +} + +static int avengers_lite_power_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct avengers_lite_device_info *di = platform_get_drvdata(pdev); + + di->ec_status = POWER_SUPPLY_STATUS_UNKNOWN; + + return 0; +} + +static int avengers_lite_power_resume(struct platform_device *pdev) +{ + struct avengers_lite_device_info *di = platform_get_drvdata(pdev); + + di->ec_status = POWER_SUPPLY_STATUS_UNKNOWN; + power_supply_changed(&di->bat); + + cancel_delayed_work(&di->monitor_work); + queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); + + return 0; +} + +static struct platform_driver avengers_lite_power_driver = { + .driver = { + .name = "battery", + }, + .probe = avengers_lite_power_probe, + .remove = avengers_lite_power_remove, + .suspend = avengers_lite_power_suspend, + .resume = avengers_lite_power_resume, +}; + +static int __init avengers_lite_power_init(void) +{ + return platform_driver_register(&avengers_lite_power_driver); +} + +static void __exit avengers_lite_power_exit(void) +{ + platform_driver_unregister(&avengers_lite_power_driver); +} + +module_init(avengers_lite_power_init); +module_exit(avengers_lite_power_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Danny +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct i2c_client *g_client; +static struct class *power_mcu_class; + +static ssize_t led_blink_show_status(struct device *dev, + struct device_attribute *attr, char *buf) { + return sprintf(buf, "%d\n", 0); +} + +static ssize_t reset_show_status(struct device *dev, + struct device_attribute *attr, char *buf) { + return sprintf(buf, "%d\n", 0); +} + +static ssize_t vcore_show_status(struct device *dev, + struct device_attribute *attr, char *buf) { + unsigned short vcore, adj; + unsigned char flag; + power_mcu_read(MCU_VCORE_VOLT, &vcore); + power_mcu_read(MCU_VCORE_ADJ, &adj); + power_mcu_read(MCU_VCORE_FLAG, &flag); + return sprintf(buf, "level %d vcore 0x%x flag 0x%x\n", adj, vcore, flag); +} + +static ssize_t ecversion_show_status(struct device *dev, + struct device_attribute *attr, char *buf) { + unsigned char version; + power_mcu_read(MCU_VERSION, &version); + return sprintf(buf, "%d\n", version); +} + +static ssize_t led_blink_store_status(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + power_mcu_write_byte(MCU_LED_BLINK, 1); + return 0; +} + +static ssize_t reset_store_status(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + return 0; +} + +static ssize_t vcore_store_status(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + unsigned char flag = 0; + unsigned long vcore; + int ret; + + strict_strtoul(buf, 0, &vcore); + if (vcore > 3) { + printk(KERN_WARNING "vcore is invalid %d\n", vcore); + return 0; + } + + /* vcore: + * 0 ---- 1.0373V + * 1 ---- 1.1049V + * 2 ---- 1.1788V + * 3 ---- 1.2457V + */ + power_mcu_write_byte(MCU_VCORE_ADJ, vcore); + + msleep(100); + power_mcu_read(MCU_VCORE_FLAG, &flag); + if (flag == 0) + printk(KERN_WARNING "Vcore adjust failed\n"); + return count; +} + +static struct device_attribute power_mcu_attributes[] = { + __ATTR(led_blink, 0644, led_blink_show_status, led_blink_store_status), + __ATTR(reset, 0644, reset_show_status, reset_store_status), + __ATTR(vcore, 0644, vcore_show_status, vcore_store_status), + __ATTR(ecversion, 0644, ecversion_show_status, NULL), + __ATTR_NULL, +}; + +int power_mcu_read(unsigned char reg, void *pval) +{ + int ret, status = -EIO; + + if (g_client == NULL) + return -EFAULT; + + if ((reg < MCU_OFFSET_WORD) && (reg >= MCU_OFFSET_BYTE)) { + /* + if (set_bus_busy() == -1) + return -EINVAL; + */ + ret = i2c_smbus_read_byte_data(g_client, reg); + /* set_bus_free(); */ + if (ret >= 0) { + *((unsigned char *)pval) = (unsigned char)ret; + status = 0; + } + } else if ((reg >= MCU_OFFSET_WORD) && (reg < MCU_END)) { + /* + if (set_bus_busy() == -1) + return -EINVAL; + */ + ret = i2c_smbus_read_word_data(g_client, reg); + /* set_bus_free(); */ + + if (ret >= 0) { + *((unsigned short *)pval) = (unsigned short)ret; + status = 0; + } + } else + return -EINVAL; + + return status; +} + + +EXPORT_SYMBOL(power_mcu_read); + +int power_mcu_write_word(unsigned char reg, unsigned short val) +{ + unsigned char data; + int ret; + + if (g_client == NULL) + return -EFAULT; + if (reg >= MCU_END) + return -EINVAL; + + /* + if (set_bus_busy() == -1) + return -EINVAL; + */ + ret = i2c_smbus_write_word_data(g_client, reg, val); + /* set_bus_free(); */ + + return ret; +} + +int power_mcu_write_byte(unsigned char reg, unsigned char val) +{ + unsigned char data; + int ret; + + if (g_client == NULL) + return -EFAULT; + + if (reg >= MCU_END) + return -EINVAL; + + /* + if (set_bus_busy() == -1) + return -EINVAL; + */ + ret = i2c_smbus_write_byte_data(g_client, reg, val); + /* set_bus_free(); */ + + return ret; +} + +EXPORT_SYMBOL(power_mcu_write_byte); +EXPORT_SYMBOL(power_mcu_write_word); + +void power_mcu_write_rtc(u32 rtc) +{ + power_mcu_write_word(MCU_RTC_LSB, rtc); + power_mcu_write_word(MCU_RTC_MSB , rtc >> 16); +} + +void power_mcu_read_rtc(u32 *rtc) +{ + power_mcu_read(MCU_RTC_LSB, rtc); + power_mcu_read(MCU_RTC_MSB, ((u8 *)rtc + 2)); +} + +EXPORT_SYMBOL(power_mcu_write_rtc); +EXPORT_SYMBOL(power_mcu_read_rtc); + +void power_mcu_write_alarm(u32 rtc) +{ + power_mcu_write_word(MCU_ALARM_LSB , rtc); + power_mcu_write_word(MCU_ALARM_MSB , rtc >> 16); +} + +void power_mcu_read_alarm(u32 *rtc) +{ + power_mcu_read(MCU_ALARM_LSB, rtc); + power_mcu_read(MCU_ALARM_MSB, ((u8 *)rtc + 2)); +} + + +EXPORT_SYMBOL(power_mcu_write_alarm); +EXPORT_SYMBOL(power_mcu_read_alarm); + +static int __devinit power_i2c_probe(struct i2c_client *client) +{ + int i; + int nretry = 25; + unsigned char version; + g_client = client; + + for (i = 0; i < nretry; i++) { + if (power_mcu_read(MCU_VERSION, &version) == 0) + return 0; + } + printk("power_mcu: probe failed\n"); + g_client = NULL; + return -1; +} + +static int __devexit power_i2c_remove(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_device_id power_mcu_idtable[] = +{ + {"power_mcu", 0}, + {}, +}; + +static struct i2c_driver power_mcu_driver = { + .driver = { + .name = "power_mcu", + }, + .id_table = power_mcu_idtable, + .probe = &power_i2c_probe, + .remove = &power_i2c_remove, +}; + +static struct device *dev; + +static int __init power_mcu_init(void) +{ + int ret; + + printk("power_mcu_init\n"); + if ((ret = i2c_add_driver(&power_mcu_driver))) { + printk(KERN_ERR "power mcu Driver registration failed\n"); + return ret; + } + + power_mcu_class = class_create(THIS_MODULE, "power_mcu"); + if (IS_ERR(power_mcu_class)) { + printk(KERN_WARNING "Unable to create \ + power_mcu class;errno=%ld\n", PTR_ERR(power_mcu_class)); + return PTR_ERR(power_mcu_class); + } + + power_mcu_class->dev_attrs = power_mcu_attributes; + dev = kzalloc(sizeof(struct device), GFP_KERNEL); + dev->class = power_mcu_class; + dev->release = NULL; + ret = device_register(dev); + if (ret) + printk("power_mcu error:%d\n", -EEXIST); + return 0; +} + +static void __exit power_mcu_exit(void) +{ + i2c_del_driver(&power_mcu_driver); + class_destroy(power_mcu_class); +} + +module_init(power_mcu_init); +module_exit(power_mcu_exit); + + diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index cce75b40b4351b..381460a1ca9c2b 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -38,23 +38,40 @@ static int __power_supply_changed_work(struct device *dev, void *data) static void power_supply_changed_work(struct work_struct *work) { + unsigned long flags; struct power_supply *psy = container_of(work, struct power_supply, changed_work); dev_dbg(psy->dev, "%s\n", __func__); - class_for_each_device(power_supply_class, NULL, psy, - __power_supply_changed_work); + spin_lock_irqsave(&psy->changed_lock, flags); + if (psy->changed) { + psy->changed = false; + spin_unlock_irqrestore(&psy->changed_lock, flags); - power_supply_update_leds(psy); + class_for_each_device(power_supply_class, NULL, psy, + __power_supply_changed_work); - kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + power_supply_update_leds(psy); + + kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); + spin_lock_irqsave(&psy->changed_lock, flags); + } + if (!psy->changed) + wake_unlock(&psy->work_wake_lock); + spin_unlock_irqrestore(&psy->changed_lock, flags); } void power_supply_changed(struct power_supply *psy) { + unsigned long flags; + dev_dbg(psy->dev, "%s\n", __func__); + spin_lock_irqsave(&psy->changed_lock, flags); + psy->changed = true; + wake_lock(&psy->work_wake_lock); + spin_unlock_irqrestore(&psy->changed_lock, flags); schedule_work(&psy->changed_work); } EXPORT_SYMBOL_GPL(power_supply_changed); @@ -156,6 +173,8 @@ int power_supply_register(struct device *parent, struct power_supply *psy) } INIT_WORK(&psy->changed_work, power_supply_changed_work); + spin_lock_init(&psy->changed_lock); + wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply"); rc = power_supply_create_attrs(psy); if (rc) @@ -172,6 +191,7 @@ int power_supply_register(struct device *parent, struct power_supply *psy) create_triggers_failed: power_supply_remove_attrs(psy); create_attrs_failed: + wake_lock_destroy(&psy->work_wake_lock); device_unregister(psy->dev); dev_create_failed: success: @@ -184,6 +204,7 @@ void power_supply_unregister(struct power_supply *psy) flush_scheduled_work(); power_supply_remove_triggers(psy); power_supply_remove_attrs(psy); + wake_lock_destroy(&psy->work_wake_lock); device_unregister(psy->dev); } EXPORT_SYMBOL_GPL(power_supply_unregister); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 5b6e352ac7c1e9..995c7b8b7fa531 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -106,6 +106,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(voltage_max_design), POWER_SUPPLY_ATTR(voltage_min_design), POWER_SUPPLY_ATTR(voltage_now), +// POWER_SUPPLY_ATTR(batt_vol), POWER_SUPPLY_ATTR(voltage_avg), POWER_SUPPLY_ATTR(current_now), POWER_SUPPLY_ATTR(current_avg), diff --git a/drivers/power/sanremo_battery.c b/drivers/power/sanremo_battery.c new file mode 100644 index 00000000000000..6851ba8c1360d6 --- /dev/null +++ b/drivers/power/sanremo_battery.c @@ -0,0 +1,196 @@ +/* + * Driver for batteries with sanremo chips inside. + * + * Copyright © 2009 Bin Yang + * 2007 Anton Vorontsov + * 2004-2007 Matt Reimer + * 2004 Szabolcs Gyurko + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * Author: + * Bin Yang + * June 2009 + * + * Anton Vorontsov + * February 2007 + * + * Matt Reimer + * April 2004, 2005, 2007 + * + * Szabolcs Gyurko + * September 2004 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct power_supply bat; + +static int sanremo_battery_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + u8 tmp; + int value; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + sanremo_read(SANREMO_CHG_CTRL1, &tmp); + if(tmp & 0x3) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CAPACITY: + value = sanremo_get_vbat(); + if(value <= 3500) + val->intval = 1; + else if(value > 4000) + val->intval = 100; + else + val->intval = 100 - (4000-value)/5; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + sanremo_read(SANREMO_VBAT_MEAS2, &tmp); + value = tmp&0xf; + sanremo_read(SANREMO_VBAT_MEAS1, &tmp); + value += tmp<<4; + val->intval = (value * 1000); /*uV*/ + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + sanremo_read(SANREMO_IBAT_MEAS2, &tmp); + value = tmp&0x3f; + sanremo_read(SANREMO_IBAT_MEAS1, &tmp); + value += tmp<<6; + val->intval = value;/*uA*/ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = 1000000; /*uA*/ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = 1000000; /*uAh*/ + break; + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + val->intval = 900000; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = 500000; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = 250; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + default: + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property sanremo_battery_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_TEMP, +}; + +static int sanremo_battery_battery_probe(struct platform_device *pdev) +{ + int retval = 0; + + bat.name = "battery"; + bat.type = POWER_SUPPLY_TYPE_BATTERY; + bat.properties = sanremo_battery_battery_props; + bat.num_properties = ARRAY_SIZE(sanremo_battery_battery_props); + bat.get_property = sanremo_battery_battery_get_property; + + retval = power_supply_register(&pdev->dev, &bat); + if (retval) + printk(KERN_ERR "failed to register battery\n"); + + return retval; +} + +static int sanremo_battery_battery_remove(struct platform_device *pdev) +{ + power_supply_unregister(&bat); + return 0; +} + +#ifdef CONFIG_PM + +static int sanremo_battery_battery_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int sanremo_battery_battery_resume(struct platform_device *pdev) +{ + return 0; +} + +#else + +#define sanremo_battery_battery_suspend NULL +#define sanremo_battery_battery_resume NULL + +#endif /* CONFIG_PM */ + +MODULE_ALIAS("platform:sanremo_battery-battery"); + +static struct platform_driver sanremo_battery_battery_driver = { + .driver = { + .name = "sanremo_battery", + }, + .probe = sanremo_battery_battery_probe, + .remove = sanremo_battery_battery_remove, + .suspend = sanremo_battery_battery_suspend, + .resume = sanremo_battery_battery_resume, +}; + +static int __init sanremo_battery_battery_init(void) +{ + return platform_driver_register(&sanremo_battery_battery_driver); +} + +static void __exit sanremo_battery_battery_exit(void) +{ + platform_driver_unregister(&sanremo_battery_battery_driver); +} + +module_init(sanremo_battery_battery_init); +module_exit(sanremo_battery_battery_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bin Yang +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ANDROID_ALARM_PRINT_INFO (1U << 0) +#define ANDROID_ALARM_PRINT_IO (1U << 1) +#define ANDROID_ALARM_PRINT_INT (1U << 2) + +static int debug_mask = ANDROID_ALARM_PRINT_INFO; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define pr_alarm(debug_level_mask, args...) \ + do { \ + if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ + pr_info(args); \ + } \ + } while (0) + +#define ANDROID_ALARM_WAKEUP_MASK ( \ + ANDROID_ALARM_RTC_WAKEUP_MASK | \ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + +/* support old usespace code */ +#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ +#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) + +static int alarm_opened; +static DEFINE_SPINLOCK(alarm_slock); +static struct wake_lock alarm_wake_lock; +static DECLARE_WAIT_QUEUE_HEAD(alarm_wait_queue); +static uint32_t alarm_pending; +static uint32_t alarm_enabled; +static uint32_t wait_pending; + +static struct alarm alarms[ANDROID_ALARM_TYPE_COUNT]; + +static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rv = 0; + unsigned long flags; + struct timespec new_alarm_time; + struct timespec new_rtc_time; + struct timespec tmp_time; + enum android_alarm_type alarm_type = ANDROID_ALARM_IOCTL_TO_TYPE(cmd); + uint32_t alarm_type_mask = 1U << alarm_type; + + if (alarm_type >= ANDROID_ALARM_TYPE_COUNT) + return -EINVAL; + + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_GET_TIME(0)) { + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return -EPERM; + if (file->private_data == NULL && + cmd != ANDROID_ALARM_SET_RTC) { + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_opened) { + spin_unlock_irqrestore(&alarm_slock, flags); + return -EBUSY; + } + alarm_opened = 1; + file->private_data = (void *)1; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + + switch (ANDROID_ALARM_BASE_CMD(cmd)) { + case ANDROID_ALARM_CLEAR(0): + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d clear\n", alarm_type); + alarm_try_to_cancel(&alarms[alarm_type]); + if (alarm_pending) { + alarm_pending &= ~alarm_type_mask; + if (!alarm_pending && !wait_pending) + wake_unlock(&alarm_wake_lock); + } + alarm_enabled &= ~alarm_type_mask; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + + case ANDROID_ALARM_SET_OLD: + case ANDROID_ALARM_SET_AND_WAIT_OLD: + if (get_user(new_alarm_time.tv_sec, (int __user *)arg)) { + rv = -EFAULT; + goto err1; + } + new_alarm_time.tv_nsec = 0; + goto from_old_alarm_set; + + case ANDROID_ALARM_SET_AND_WAIT(0): + case ANDROID_ALARM_SET(0): + if (copy_from_user(&new_alarm_time, (void __user *)arg, + sizeof(new_alarm_time))) { + rv = -EFAULT; + goto err1; + } +from_old_alarm_set: + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm %d set %ld.%09ld\n", alarm_type, + new_alarm_time.tv_sec, new_alarm_time.tv_nsec); + alarm_enabled |= alarm_type_mask; + alarm_start_range(&alarms[alarm_type], + timespec_to_ktime(new_alarm_time), + timespec_to_ktime(new_alarm_time)); + spin_unlock_irqrestore(&alarm_slock, flags); + if (ANDROID_ALARM_BASE_CMD(cmd) != ANDROID_ALARM_SET_AND_WAIT(0) + && cmd != ANDROID_ALARM_SET_AND_WAIT_OLD) + break; + /* fall though */ + case ANDROID_ALARM_WAIT: + spin_lock_irqsave(&alarm_slock, flags); + pr_alarm(IO, "alarm wait\n"); + if (!alarm_pending && wait_pending) { + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + rv = wait_event_interruptible(alarm_wait_queue, alarm_pending); + if (rv) + goto err1; + spin_lock_irqsave(&alarm_slock, flags); + rv = alarm_pending; + wait_pending = 1; + alarm_pending = 0; + spin_unlock_irqrestore(&alarm_slock, flags); + break; + case ANDROID_ALARM_SET_RTC: + if (copy_from_user(&new_rtc_time, (void __user *)arg, + sizeof(new_rtc_time))) { + rv = -EFAULT; + goto err1; + } + rv = alarm_set_rtc(new_rtc_time); + spin_lock_irqsave(&alarm_slock, flags); + alarm_pending |= ANDROID_ALARM_TIME_CHANGE_MASK; + wake_up(&alarm_wait_queue); + spin_unlock_irqrestore(&alarm_slock, flags); + if (rv < 0) + goto err1; + break; + case ANDROID_ALARM_GET_TIME(0): + switch (alarm_type) { + case ANDROID_ALARM_RTC_WAKEUP: + case ANDROID_ALARM_RTC: + getnstimeofday(&tmp_time); + break; + case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP: + case ANDROID_ALARM_ELAPSED_REALTIME: + tmp_time = + ktime_to_timespec(alarm_get_elapsed_realtime()); + break; + case ANDROID_ALARM_TYPE_COUNT: + case ANDROID_ALARM_SYSTEMTIME: + ktime_get_ts(&tmp_time); + break; + } + if (copy_to_user((void __user *)arg, &tmp_time, + sizeof(tmp_time))) { + rv = -EFAULT; + goto err1; + } + break; + + default: + rv = -EINVAL; + goto err1; + } +err1: + return rv; +} + +static int alarm_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int alarm_release(struct inode *inode, struct file *file) +{ + int i; + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + if (file->private_data != 0) { + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) { + uint32_t alarm_type_mask = 1U << i; + if (alarm_enabled & alarm_type_mask) { + pr_alarm(INFO, "alarm_release: clear alarm, " + "pending %d\n", + !!(alarm_pending & alarm_type_mask)); + alarm_enabled &= ~alarm_type_mask; + } + spin_unlock_irqrestore(&alarm_slock, flags); + alarm_cancel(&alarms[i]); + spin_lock_irqsave(&alarm_slock, flags); + } + if (alarm_pending | wait_pending) { + if (alarm_pending) + pr_alarm(INFO, "alarm_release: clear " + "pending alarms %x\n", alarm_pending); + wake_unlock(&alarm_wake_lock); + wait_pending = 0; + alarm_pending = 0; + } + alarm_opened = 0; + } + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static void alarm_triggered(struct alarm *alarm) +{ + unsigned long flags; + uint32_t alarm_type_mask = 1U << alarm->type; + + pr_alarm(INT, "alarm_triggered type %d\n", alarm->type); + spin_lock_irqsave(&alarm_slock, flags); + if (alarm_enabled & alarm_type_mask) { + wake_lock_timeout(&alarm_wake_lock, 5 * HZ); + alarm_enabled &= ~alarm_type_mask; + alarm_pending |= alarm_type_mask; + wake_up(&alarm_wait_queue); + } + spin_unlock_irqrestore(&alarm_slock, flags); +} + +static const struct file_operations alarm_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = alarm_ioctl, + .open = alarm_open, + .release = alarm_release, +}; + +static struct miscdevice alarm_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "alarm", + .fops = &alarm_fops, +}; + +static int __init alarm_dev_init(void) +{ + int err; + int i; + + err = misc_register(&alarm_device); + if (err) + return err; + + for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) + alarm_init(&alarms[i], i, alarm_triggered); + wake_lock_init(&alarm_wake_lock, WAKE_LOCK_SUSPEND, "alarm"); + + return 0; +} + +static void __exit alarm_dev_exit(void) +{ + misc_deregister(&alarm_device); + wake_lock_destroy(&alarm_wake_lock); +} + +module_init(alarm_dev_init); +module_exit(alarm_dev_exit); + diff --git a/drivers/rtc/alarm.c b/drivers/rtc/alarm.c new file mode 100644 index 00000000000000..d80e33b5e8006c --- /dev/null +++ b/drivers/rtc/alarm.c @@ -0,0 +1,601 @@ +/* drivers/rtc/alarm.c + * + * Copyright (C) 2007-2009 Google, Inc. + * + * 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 +#include +#include +#include +#include + +#define ANDROID_ALARM_PRINT_ERROR (1U << 0) +#define ANDROID_ALARM_PRINT_INIT_STATUS (1U << 1) +#define ANDROID_ALARM_PRINT_TSET (1U << 2) +#define ANDROID_ALARM_PRINT_CALL (1U << 3) +#define ANDROID_ALARM_PRINT_SUSPEND (1U << 4) +#define ANDROID_ALARM_PRINT_INT (1U << 5) +#define ANDROID_ALARM_PRINT_FLOW (1U << 6) + +static int debug_mask = ANDROID_ALARM_PRINT_ERROR | \ + ANDROID_ALARM_PRINT_INIT_STATUS; +module_param_named(debug_mask, debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +/** + * save_time_delta - Save the offset between system time and RTC time + * @delta: pointer to timespec to store delta + * @rtc: pointer to timespec for current RTC time + * + * Return a delta between the system time and the RTC time, such + * that system time can be restored later with restore_time_delta() + */ +static void save_time_delta(struct timespec *delta, struct timespec *rtc) +{ + set_normalized_timespec(delta, + xtime.tv_sec - rtc->tv_sec, + xtime.tv_nsec - rtc->tv_nsec); +} + +#define pr_alarm(debug_level_mask, args...) \ + do { \ + if (debug_mask & ANDROID_ALARM_PRINT_##debug_level_mask) { \ + pr_info(args); \ + } \ + } while (0) + +#define ANDROID_ALARM_WAKEUP_MASK ( \ + ANDROID_ALARM_RTC_WAKEUP_MASK | \ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK) + +/* support old usespace code */ +#define ANDROID_ALARM_SET_OLD _IOW('a', 2, time_t) /* set alarm */ +#define ANDROID_ALARM_SET_AND_WAIT_OLD _IOW('a', 3, time_t) + +struct alarm_queue { + struct rb_root alarms; + struct rb_node *first; + struct hrtimer timer; + ktime_t delta; + bool stopped; + ktime_t stopped_time; +}; + +static struct rtc_device *alarm_rtc_dev; +static DEFINE_SPINLOCK(alarm_slock); +static DEFINE_MUTEX(alarm_setrtc_mutex); +static struct wake_lock alarm_rtc_wake_lock; +static struct platform_device *alarm_platform_dev; +struct alarm_queue alarms[ANDROID_ALARM_TYPE_COUNT]; +static bool suspended; + +static void update_timer_locked(struct alarm_queue *base, bool head_removed) +{ + struct alarm *alarm; + bool is_wakeup = base == &alarms[ANDROID_ALARM_RTC_WAKEUP] || + base == &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; + + if (base->stopped) { + pr_alarm(FLOW, "changed alarm while setting the wall time\n"); + return; + } + + if (is_wakeup && !suspended && head_removed) + wake_unlock(&alarm_rtc_wake_lock); + + if (!base->first) + return; + + alarm = container_of(base->first, struct alarm, node); + + pr_alarm(FLOW, "selected alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, ktime_to_ns(alarm->expires)); + + if (is_wakeup && suspended) { + pr_alarm(FLOW, "changed alarm while suspened\n"); + wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); + return; + } + + hrtimer_try_to_cancel(&base->timer); + base->timer._expires = ktime_add(base->delta, alarm->expires); + base->timer._softexpires = ktime_add(base->delta, alarm->softexpires); + hrtimer_start_expires(&base->timer, HRTIMER_MODE_ABS); +} + +static void alarm_enqueue_locked(struct alarm *alarm) +{ + struct alarm_queue *base = &alarms[alarm->type]; + struct rb_node **link = &base->alarms.rb_node; + struct rb_node *parent = NULL; + struct alarm *entry; + int leftmost = 1; + + pr_alarm(FLOW, "added alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, ktime_to_ns(alarm->expires)); + + if (base->first == &alarm->node) + base->first = rb_next(&alarm->node); + if (!RB_EMPTY_NODE(&alarm->node)) { + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + } + + while (*link) { + parent = *link; + entry = rb_entry(parent, struct alarm, node); + /* + * We dont care about collisions. Nodes with + * the same expiry time stay together. + */ + if (alarm->expires.tv64 < entry->expires.tv64) { + link = &(*link)->rb_left; + } else { + link = &(*link)->rb_right; + leftmost = 0; + } + } + if (leftmost) { + base->first = &alarm->node; + update_timer_locked(base, false); + } + + rb_link_node(&alarm->node, parent, link); + rb_insert_color(&alarm->node, &base->alarms); +} + +/** + * alarm_init - initialize an alarm + * @alarm: the alarm to be initialized + * @type: the alarm type to be used + * @function: alarm callback function + */ +void alarm_init(struct alarm *alarm, + enum android_alarm_type type, void (*function)(struct alarm *)) +{ + RB_CLEAR_NODE(&alarm->node); + alarm->type = type; + alarm->function = function; + + pr_alarm(FLOW, "created alarm, type %d, func %pF\n", type, function); +} + + +/** + * alarm_start_range - (re)start an alarm + * @alarm: the alarm to be added + * @start: earliest expiry time + * @end: expiry time + */ +void alarm_start_range(struct alarm *alarm, ktime_t start, ktime_t end) +{ + unsigned long flags; + + spin_lock_irqsave(&alarm_slock, flags); + alarm->softexpires = start; + alarm->expires = end; + alarm_enqueue_locked(alarm); + spin_unlock_irqrestore(&alarm_slock, flags); +} + +/** + * alarm_try_to_cancel - try to deactivate an alarm + * @alarm: alarm to stop + * + * Returns: + * 0 when the alarm was not active + * 1 when the alarm was active + * -1 when the alarm may currently be excuting the callback function and + * cannot be stopped (it may also be inactive) + */ +int alarm_try_to_cancel(struct alarm *alarm) +{ + struct alarm_queue *base = &alarms[alarm->type]; + unsigned long flags; + bool first = false; + int ret = 0; + + spin_lock_irqsave(&alarm_slock, flags); + if (!RB_EMPTY_NODE(&alarm->node)) { + pr_alarm(FLOW, "canceled alarm, type %d, func %pF at %lld\n", + alarm->type, alarm->function, + ktime_to_ns(alarm->expires)); + ret = 1; + if (base->first == &alarm->node) { + base->first = rb_next(&alarm->node); + first = true; + } + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + if (first) + update_timer_locked(base, true); + } else + pr_alarm(FLOW, "tried to cancel alarm, type %d, func %pF\n", + alarm->type, alarm->function); + spin_unlock_irqrestore(&alarm_slock, flags); + if (!ret && hrtimer_callback_running(&base->timer)) + ret = -1; + return ret; +} + +/** + * alarm_cancel - cancel an alarm and wait for the handler to finish. + * @alarm: the alarm to be cancelled + * + * Returns: + * 0 when the alarm was not active + * 1 when the alarm was active + */ +int alarm_cancel(struct alarm *alarm) +{ + for (;;) { + int ret = alarm_try_to_cancel(alarm); + if (ret >= 0) + return ret; + cpu_relax(); + } +} + +/** + * alarm_set_rtc - set the kernel and rtc walltime + * @new_time: timespec value containing the new time + */ +int alarm_set_rtc(struct timespec new_time) +{ + int i; + int ret; + unsigned long flags; + struct rtc_time rtc_new_rtc_time; + struct timespec tmp_time; + + rtc_time_to_tm(new_time.tv_sec, &rtc_new_rtc_time); + + pr_alarm(TSET, "set rtc %ld %ld - rtc %02d:%02d:%02d %02d/%02d/%04d\n", + new_time.tv_sec, new_time.tv_nsec, + rtc_new_rtc_time.tm_hour, rtc_new_rtc_time.tm_min, + rtc_new_rtc_time.tm_sec, rtc_new_rtc_time.tm_mon + 1, + rtc_new_rtc_time.tm_mday, + rtc_new_rtc_time.tm_year + 1900); + + mutex_lock(&alarm_setrtc_mutex); + spin_lock_irqsave(&alarm_slock, flags); + wake_lock(&alarm_rtc_wake_lock); + getnstimeofday(&tmp_time); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_try_to_cancel(&alarms[i].timer); + alarms[i].stopped = true; + alarms[i].stopped_time = timespec_to_ktime(tmp_time); + } + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + ktime_sub(alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta, + timespec_to_ktime(timespec_sub(tmp_time, new_time))); + spin_unlock_irqrestore(&alarm_slock, flags); + ret = do_settimeofday(&new_time); + spin_lock_irqsave(&alarm_slock, flags); + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + alarms[i].stopped = false; + update_timer_locked(&alarms[i], false); + } + spin_unlock_irqrestore(&alarm_slock, flags); + if (ret < 0) { + pr_alarm(ERROR, "alarm_set_rtc: Failed to set time\n"); + goto err; + } + if (!alarm_rtc_dev) { + pr_alarm(ERROR, + "alarm_set_rtc: no RTC, time will be lost on reboot\n"); + goto err; + } + ret = rtc_set_time(alarm_rtc_dev, &rtc_new_rtc_time); + if (ret < 0) + pr_alarm(ERROR, "alarm_set_rtc: " + "Failed to set RTC, time will be lost on reboot\n"); +err: + wake_unlock(&alarm_rtc_wake_lock); + mutex_unlock(&alarm_setrtc_mutex); + return ret; +} + +/** + * alarm_get_elapsed_realtime - get the elapsed real time in ktime_t format + * + * returns the time in ktime_t format + */ +ktime_t alarm_get_elapsed_realtime(void) +{ + ktime_t now; + unsigned long flags; + struct alarm_queue *base = &alarms[ANDROID_ALARM_ELAPSED_REALTIME]; + + spin_lock_irqsave(&alarm_slock, flags); + now = base->stopped ? base->stopped_time : ktime_get_real(); + now = ktime_sub(now, base->delta); + spin_unlock_irqrestore(&alarm_slock, flags); + return now; +} + +static enum hrtimer_restart alarm_timer_triggered(struct hrtimer *timer) +{ + struct alarm_queue *base; + struct alarm *alarm; + unsigned long flags; + ktime_t now; + + spin_lock_irqsave(&alarm_slock, flags); + + base = container_of(timer, struct alarm_queue, timer); + now = base->stopped ? base->stopped_time : hrtimer_cb_get_time(timer); + now = ktime_sub(now, base->delta); + + pr_alarm(INT, "alarm_timer_triggered type %d at %lld\n", + base - alarms, ktime_to_ns(now)); + + while (base->first) { + alarm = container_of(base->first, struct alarm, node); + if (alarm->softexpires.tv64 > now.tv64) { + pr_alarm(FLOW, "don't call alarm, %pF, %lld (s %lld)\n", + alarm->function, ktime_to_ns(alarm->expires), + ktime_to_ns(alarm->softexpires)); + break; + } + base->first = rb_next(&alarm->node); + rb_erase(&alarm->node, &base->alarms); + RB_CLEAR_NODE(&alarm->node); + pr_alarm(CALL, "call alarm, type %d, func %pF, %lld (s %lld)\n", + alarm->type, alarm->function, + ktime_to_ns(alarm->expires), + ktime_to_ns(alarm->softexpires)); + spin_unlock_irqrestore(&alarm_slock, flags); + alarm->function(alarm); + spin_lock_irqsave(&alarm_slock, flags); + } + if (!base->first) + pr_alarm(FLOW, "no more alarms of type %d\n", base - alarms); + update_timer_locked(base, true); + spin_unlock_irqrestore(&alarm_slock, flags); + return HRTIMER_NORESTART; +} + +static void alarm_triggered_func(void *p) +{ + struct rtc_device *rtc = alarm_rtc_dev; + if (!(rtc->irq_data & RTC_AF)) + return; + pr_alarm(INT, "rtc alarm triggered\n"); + wake_lock_timeout(&alarm_rtc_wake_lock, 1 * HZ); +} + +static int alarm_suspend(struct platform_device *pdev, pm_message_t state) +{ + int err = 0; + unsigned long flags; + struct rtc_wkalrm rtc_alarm; + struct rtc_time rtc_current_rtc_time; + unsigned long rtc_current_time; + unsigned long rtc_alarm_time; + struct timespec rtc_current_timespec; + struct timespec rtc_delta; + struct alarm_queue *wakeup_queue = NULL; + struct alarm_queue *tmp_queue = NULL; + + pr_alarm(SUSPEND, "alarm_suspend(%p, %d)\n", pdev, state.event); + + spin_lock_irqsave(&alarm_slock, flags); + suspended = true; + spin_unlock_irqrestore(&alarm_slock, flags); + + hrtimer_cancel(&alarms[ANDROID_ALARM_RTC_WAKEUP].timer); + hrtimer_cancel(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK].timer); + + tmp_queue = &alarms[ANDROID_ALARM_RTC_WAKEUP]; + if (tmp_queue->first) + wakeup_queue = tmp_queue; + tmp_queue = &alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP]; + if (tmp_queue->first && (!wakeup_queue || + hrtimer_get_expires(&tmp_queue->timer).tv64 < + hrtimer_get_expires(&wakeup_queue->timer).tv64)) + wakeup_queue = tmp_queue; + if (wakeup_queue) { + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + rtc_current_timespec.tv_nsec = 0; + rtc_tm_to_time(&rtc_current_rtc_time, + &rtc_current_timespec.tv_sec); + save_time_delta(&rtc_delta, &rtc_current_timespec); + + rtc_alarm_time = timespec_sub(ktime_to_timespec( + hrtimer_get_expires(&wakeup_queue->timer)), + rtc_delta).tv_sec; + + rtc_time_to_tm(rtc_alarm_time, &rtc_alarm.time); + rtc_alarm.enabled = 1; + rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); + rtc_read_time(alarm_rtc_dev, &rtc_current_rtc_time); + rtc_tm_to_time(&rtc_current_rtc_time, &rtc_current_time); + pr_alarm(SUSPEND, + "rtc alarm set at %ld, now %ld, rtc delta %ld.%09ld\n", + rtc_alarm_time, rtc_current_time, + rtc_delta.tv_sec, rtc_delta.tv_nsec); + if (rtc_current_time + 1 >= rtc_alarm_time) { + pr_alarm(SUSPEND, "alarm about to go off\n"); + memset(&rtc_alarm, 0, sizeof(rtc_alarm)); + rtc_alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &rtc_alarm); + + spin_lock_irqsave(&alarm_slock, flags); + suspended = false; + wake_lock_timeout(&alarm_rtc_wake_lock, 2 * HZ); + update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], + false); + update_timer_locked(&alarms[ + ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], false); + err = -EBUSY; + spin_unlock_irqrestore(&alarm_slock, flags); + } + } + return err; +} + +static int alarm_resume(struct platform_device *pdev) +{ + struct rtc_wkalrm alarm; + unsigned long flags; + + pr_alarm(SUSPEND, "alarm_resume(%p)\n", pdev); + + memset(&alarm, 0, sizeof(alarm)); + alarm.enabled = 0; + rtc_set_alarm(alarm_rtc_dev, &alarm); + + spin_lock_irqsave(&alarm_slock, flags); + suspended = false; + update_timer_locked(&alarms[ANDROID_ALARM_RTC_WAKEUP], false); + update_timer_locked(&alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP], + false); + spin_unlock_irqrestore(&alarm_slock, flags); + + return 0; +} + +static struct rtc_task alarm_rtc_task = { + .func = alarm_triggered_func +}; + +static int rtc_alarm_add_device(struct device *dev, + struct class_interface *class_intf) +{ + int err; + struct rtc_device *rtc = to_rtc_device(dev); + + mutex_lock(&alarm_setrtc_mutex); + + if (alarm_rtc_dev) { + err = -EBUSY; + goto err1; + } + + alarm_platform_dev = + platform_device_register_simple("alarm", -1, NULL, 0); + if (IS_ERR(alarm_platform_dev)) { + err = PTR_ERR(alarm_platform_dev); + goto err2; + } + err = rtc_irq_register(rtc, &alarm_rtc_task); + if (err) + goto err3; + alarm_rtc_dev = rtc; + pr_alarm(INIT_STATUS, "using rtc device, %s, for alarms", rtc->name); + mutex_unlock(&alarm_setrtc_mutex); + + return 0; + +err3: + platform_device_unregister(alarm_platform_dev); +err2: +err1: + mutex_unlock(&alarm_setrtc_mutex); + return err; +} + +static void rtc_alarm_remove_device(struct device *dev, + struct class_interface *class_intf) +{ + if (dev == &alarm_rtc_dev->dev) { + pr_alarm(INIT_STATUS, "lost rtc device for alarms"); + rtc_irq_unregister(alarm_rtc_dev, &alarm_rtc_task); + platform_device_unregister(alarm_platform_dev); + alarm_rtc_dev = NULL; + } +} + +static struct class_interface rtc_alarm_interface = { + .add_dev = &rtc_alarm_add_device, + .remove_dev = &rtc_alarm_remove_device, +}; + +static struct platform_driver alarm_driver = { + .suspend = alarm_suspend, + .resume = alarm_resume, + .driver = { + .name = "alarm" + } +}; + +static int __init alarm_late_init(void) +{ + unsigned long flags; + struct timespec tmp_time, system_time; + + /* this needs to run after the rtc is read at boot */ + spin_lock_irqsave(&alarm_slock, flags); + /* We read the current rtc and system time so we can later calulate + * elasped realtime to be (boot_systemtime + rtc - boot_rtc) == + * (rtc - (boot_rtc - boot_systemtime)) + */ + getnstimeofday(&tmp_time); + ktime_get_ts(&system_time); + alarms[ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP].delta = + alarms[ANDROID_ALARM_ELAPSED_REALTIME].delta = + timespec_to_ktime(timespec_sub(tmp_time, system_time)); + + spin_unlock_irqrestore(&alarm_slock, flags); + return 0; +} + +static int __init alarm_driver_init(void) +{ + int err; + int i; + + for (i = 0; i < ANDROID_ALARM_SYSTEMTIME; i++) { + hrtimer_init(&alarms[i].timer, + CLOCK_REALTIME, HRTIMER_MODE_ABS); + alarms[i].timer.function = alarm_timer_triggered; + } + hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].timer, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + alarms[ANDROID_ALARM_SYSTEMTIME].timer.function = alarm_timer_triggered; + err = platform_driver_register(&alarm_driver); + if (err < 0) + goto err1; + wake_lock_init(&alarm_rtc_wake_lock, WAKE_LOCK_SUSPEND, "alarm_rtc"); + rtc_alarm_interface.class = rtc_class; + err = class_interface_register(&rtc_alarm_interface); + if (err < 0) + goto err2; + + return 0; + +err2: + wake_lock_destroy(&alarm_rtc_wake_lock); + platform_driver_unregister(&alarm_driver); +err1: + return err; +} + +static void __exit alarm_exit(void) +{ + class_interface_unregister(&rtc_alarm_interface); + wake_lock_destroy(&alarm_rtc_wake_lock); + platform_driver_unregister(&alarm_driver); +} + +late_initcall(alarm_late_init); +module_init(alarm_driver_init); +module_exit(alarm_exit); + diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 565562ba6ac9da..11d7ab90a67cc1 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -41,25 +41,32 @@ static void rtc_device_release(struct device *dev) */ static struct timespec delta; +static struct timespec delta_delta; static time_t oldtime; static int rtc_suspend(struct device *dev, pm_message_t mesg) { struct rtc_device *rtc = to_rtc_device(dev); struct rtc_time tm; - struct timespec ts = current_kernel_time(); + struct timespec ts; + struct timespec new_delta; if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0) return 0; + getnstimeofday(&ts); rtc_read_time(rtc, &tm); rtc_tm_to_time(&tm, &oldtime); /* RTC precision is 1 second; adjust delta for avg 1/2 sec err */ - set_normalized_timespec(&delta, + set_normalized_timespec(&new_delta, ts.tv_sec - oldtime, ts.tv_nsec - (NSEC_PER_SEC >> 1)); + /* prevent 1/2 sec errors from accumulating */ + delta_delta = timespec_sub(new_delta, delta); + if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) + delta = new_delta; return 0; } @@ -79,6 +86,8 @@ static int rtc_resume(struct device *dev) return 0; } rtc_tm_to_time(&tm, &newtime); + if (delta_delta.tv_sec < -1) + newtime++; if (newtime <= oldtime) { if (newtime < oldtime) pr_debug("%s: time travel!\n", dev_name(&rtc->dev)); diff --git a/drivers/rtc/rtc-ec.c b/drivers/rtc/rtc-ec.c new file mode 100644 index 00000000000000..f4ba2941c27c61 --- /dev/null +++ b/drivers/rtc/rtc-ec.c @@ -0,0 +1,270 @@ +/* + * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * + * Original Driver by Nils Faerber + * + * Modifications from: + * CIH + * Nicolas Pitre + * Andrew Christian + * + * Converted to the RTC subsystem and Driver Model + * by Richard Purdie + * + * Support RTC based on EC PIC16F883 + * by Danny Song + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include +#include +#include + +#define TIMER_FREQ CLOCK_TICK_RATE + + + +static void __iomem *rtc_base; +static int irq_1hz = -1, irq_alrm = -1; +static unsigned long rtc_freq = 1024; +static struct rtc_time rtc_alarm; +static DEFINE_SPINLOCK(ec_rtc_lock); + + +static int ec_rtc_open(struct device *dev) +{ + return 0; +} + +static void ec_rtc_release(struct device *dev) +{ + +} + + +static int ec_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + int ret; + unsigned long rtc_value; + unsigned long *ptr_temp; + void *pvalue; + struct rtc_time *tm; + + switch (cmd) { + case RTC_AIE_OFF: + return 0; + case RTC_AIE_ON: + return 0; + case RTC_UIE_OFF: + return -EINVAL; + case RTC_UIE_ON: + return -EINVAL; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + pvalue = (void *)arg; + if (*(unsigned long *)pvalue < 1 || + *(unsigned long *)pvalue > TIMER_FREQ) + return -EINVAL; + rtc_freq = arg; + return 0; + case RTC_PIE_ON: /*not support*/ + return -EINVAL; + case RTC_PIE_OFF: /*not support*/ + return -EINVAL; + case RTC_ALM_READ: + tm = (struct rtc_time *) arg; + power_mcu_read_alarm((u32 *)&rtc_value); + rtc_time_to_tm(rtc_value, tm); + return 0; + break; + case RTC_ALM_SET: + spin_lock_irq(&ec_rtc_lock); + tm = (struct rtc_time *) arg; + ret = rtc_tm_to_time(tm, &rtc_value); + if (ret == 0) + power_mcu_write_alarm((u32)rtc_value); + + spin_unlock_irq(&ec_rtc_lock); + return ret; + break; + case RTC_RD_TIME: + tm = (struct rtc_time *) arg; + power_mcu_read_rtc((u32 *)&rtc_value); + rtc_time_to_tm(rtc_value, tm); + return 0; + break; + case RTC_SET_TIME: + spin_lock_irq(&ec_rtc_lock); + tm = (struct rtc_time *) arg; + ret = rtc_tm_to_time(tm, &rtc_value); + if (ret == 0) + power_mcu_write_rtc((u32)rtc_value); + spin_unlock_irq(&ec_rtc_lock); + return ret; + break; + case RTC_EPOCH_READ: /*not support*/ + ptr_temp = (unsigned long *) arg; + *ptr_temp = 0; + return 0; + break; + case RTC_EPOCH_SET: /*not support*/ + return -EINVAL; + break; + } + return -ENOIOCTLCMD; +} + +static int ec_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long rtc_value; + + power_mcu_read_rtc((u32 *)&rtc_value); + rtc_time_to_tm(rtc_value, tm); + return 0; +} + +static int ec_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time; + int ret; + + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) + power_mcu_write_rtc(time); + + return ret; +} + +static int ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + return 0; +} + +static int ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + unsigned long time; + struct pxa168_rtc_platform_data *rtcops; + + spin_lock_irq(&ec_rtc_lock); + + if (alrm->enabled) { + ret = rtc_tm_to_time(&alrm->time, &time); + if (ret == 0) + power_mcu_write_alarm(time); + } else + power_mcu_write_alarm(0xFFFFFFFF); + + spin_unlock_irq(&ec_rtc_lock); + + return ret; +} + +static int ec_rtc_proc(struct device *dev, struct seq_file *seq) +{ + return 0; +} + +static const struct rtc_class_ops ec_rtc_ops = { + .open = ec_rtc_open, + .release = ec_rtc_release, + .ioctl = ec_rtc_ioctl, + .read_time = ec_rtc_read_time, + .set_time = ec_rtc_set_time, + .read_alarm = ec_rtc_read_alarm, + .set_alarm = ec_rtc_set_alarm, + .proc = ec_rtc_proc, +}; + +static int ec_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + rtc = rtc_device_register(pdev->name, &pdev->dev, &ec_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(pdev, rtc); + + return 0; +} + +static int ec_rtc_remove(struct platform_device *pdev) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + + if (rtc) + rtc_device_unregister(rtc); + return 0; +} + +#ifdef CONFIG_PM +static int ec_rtc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int ec_rtc_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define ec_rtc_suspend NULL +#define ec_rtc_resume NULL +#endif + +static struct platform_driver ec_rtc_driver = { + .probe = ec_rtc_probe, + .remove = ec_rtc_remove, + .suspend = ec_rtc_suspend, + .resume = ec_rtc_resume, + .driver = { + .name = "ec-rtc", + }, +}; + +static int __init ec_rtc_init(void) +{ + return platform_driver_register(&ec_rtc_driver); +} + +static void __exit ec_rtc_exit(void) +{ + platform_driver_unregister(&ec_rtc_driver); +} + +module_init(ec_rtc_init); +module_exit(ec_rtc_exit); + +MODULE_DESCRIPTION("EC Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ec-rtc"); diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 054e05294af856..51ec7d9cf92f8f 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -137,6 +137,18 @@ isl1208_i2c_get_sr(struct i2c_client *client) return sr; } +static int +isl1208_i2c_get_intr(struct i2c_client *client) +{ + return i2c_smbus_read_byte_data(client, ISL1208_REG_INT); +} + +static int +isl1208_i2c_set_intr(struct i2c_client *client, u8 intr) +{ + return i2c_smbus_write_byte_data(client, ISL1208_REG_INT, intr); +} + static int isl1208_i2c_get_atr(struct i2c_client *client) { @@ -309,9 +321,9 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) tm->tm_min = bcd2bin(regs[ISL1208_REG_MNA - ISL1208_REG_SCA] & 0x7f); tm->tm_hour = bcd2bin(regs[ISL1208_REG_HRA - ISL1208_REG_SCA] & 0x3f); tm->tm_mday = bcd2bin(regs[ISL1208_REG_DTA - ISL1208_REG_SCA] & 0x3f); - tm->tm_mon = - bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1; + tm->tm_mon = bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1; tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03); + tm->tm_year = 2009; /* any value > 200 to print **** in year */ return 0; } @@ -378,6 +390,47 @@ isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm) return 0; } +static int +isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm * alarm) +{ + struct rtc_time * tm = &alarm->time; + int sr; + u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, }; + + regs[ISL1208_REG_SCA - ISL1208_REG_SCA] = bin2bcd(tm->tm_sec) | 0x80; + regs[ISL1208_REG_MNA - ISL1208_REG_SCA] = bin2bcd(tm->tm_min) | 0x80; + regs[ISL1208_REG_HRA - ISL1208_REG_SCA] = bin2bcd(tm->tm_hour) | 0x80; + + regs[ISL1208_REG_DTA - ISL1208_REG_SCA] = bin2bcd(tm->tm_mday) | 0x80; + regs[ISL1208_REG_MOA - ISL1208_REG_SCA] = bin2bcd(tm->tm_mon + 1) | 0x80; + + regs[ISL1208_REG_DWA - ISL1208_REG_SCA] = bin2bcd(tm->tm_wday & 7); + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + + /* write ALARM registers */ + sr = isl1208_i2c_set_regs(client, ISL1208_REG_SCA, regs, ISL1208_ALARM_SECTION_LEN); + if (sr < 0) { + dev_err(&client->dev, "%s: writing ALARM section failed\n", + __func__); + return sr; + } + + /* write INT registers */ + sr = isl1208_i2c_set_intr(client, 0xc0); + if (sr < 0) { + dev_err(&client->dev, "%s: writing INT failed\n", + __func__); + return sr; + } + + return 0; +} + static int isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm) @@ -391,12 +444,18 @@ isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); } +static int +isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); +} + static const struct rtc_class_ops isl1208_rtc_ops = { .proc = isl1208_rtc_proc, .read_time = isl1208_rtc_read_time, .set_time = isl1208_rtc_set_time, .read_alarm = isl1208_rtc_read_alarm, - /*.set_alarm = isl1208_rtc_set_alarm, */ + .set_alarm = isl1208_rtc_set_alarm, }; /* sysfs interface */ @@ -462,6 +521,135 @@ isl1208_sysfs_store_usr(struct device *dev, static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr, isl1208_sysfs_store_usr); +static ssize_t +isl1208_sysfs_show_time(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rtc_time tm; + int ret; + + ret = isl1208_i2c_read_time(to_i2c_client(dev), &tm); + if (ret < 0) + return ret; + + tm.tm_year += 1900; + tm.tm_mon += 1; + return sprintf(buf, "%d %d %d %d %d %d\n", tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); +} + +static ssize_t +isl1208_sysfs_store_time(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rtc_time tm; + struct timespec tv; + + if (sscanf(buf, "%d %d %d %d %d %d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) + goto err; + + if (tm.tm_year < 2000 || tm.tm_year > 2099) + goto err; + + tm.tm_year -= 1900; + tm.tm_mon -= 1; + + printk(buf, "%d %d %d %d %d %d\n", tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + tv.tv_nsec = NSEC_PER_SEC >> 1; + rtc_tm_to_time(&tm, &tv.tv_sec); + do_settimeofday(&tv); + dev_info(dev, + "setting system clock to " + "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, + (unsigned int) tv.tv_sec); + + return isl1208_i2c_set_time(to_i2c_client(dev), &tm) ? -EIO : count; +err: + printk("wrong format, echo \"year mon day hour min sec\" > time\n"); + return -EINVAL; +} + +static DEVICE_ATTR(time, S_IRUGO | S_IWUSR, isl1208_sysfs_show_time, + isl1208_sysfs_store_time); + +static ssize_t +isl1208_sysfs_show_alarm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rtc_wkalrm alarm; + struct rtc_time * tm = &alarm.time; + int ret; + + ret = isl1208_i2c_read_alarm(to_i2c_client(dev), &alarm); + if (ret < 0) + return ret; + + tm->tm_mon += 1; + return sprintf(buf, "%d %d %d %d %d %d\n", tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); +} + +static ssize_t +isl1208_sysfs_store_alarm(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rtc_wkalrm alarm; + struct rtc_time * tm = &alarm.time; + + if (sscanf(buf, "%d %d %d %d %d %d", &tm->tm_year, &tm->tm_mon, &tm->tm_mday, + &tm->tm_hour, &tm->tm_min, &tm->tm_sec) != 6) + goto err; + + tm->tm_mon -= 1; + + printk(buf, "%d %d %d %d %d %d\n", tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return isl1208_i2c_set_alarm(to_i2c_client(dev), &alarm) ? -EIO : count; +err: + printk("wrong format, echo \"year mon day hour min sec\" > alarm\n"); + return -EINVAL; +} + +static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, isl1208_sysfs_show_alarm, + isl1208_sysfs_store_alarm); + +static ssize_t +isl1208_sysfs_show_intr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + + ret = isl1208_i2c_get_intr(to_i2c_client(dev)); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t +isl1208_sysfs_store_intr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + + if (sscanf(buf, "%d", &ret) != 1) + return -EIO; + + return isl1208_i2c_set_intr(to_i2c_client(dev), ret) ? -EIO: count; +} + +static DEVICE_ATTR(intr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_intr, + isl1208_sysfs_store_intr); + static int isl1208_sysfs_register(struct device *dev) { @@ -469,21 +657,42 @@ isl1208_sysfs_register(struct device *dev) err = device_create_file(dev, &dev_attr_atrim); if (err) - return err; + goto err1; err = device_create_file(dev, &dev_attr_dtrim); - if (err) { - device_remove_file(dev, &dev_attr_atrim); - return err; - } + if (err) + goto err2; err = device_create_file(dev, &dev_attr_usr); - if (err) { - device_remove_file(dev, &dev_attr_atrim); - device_remove_file(dev, &dev_attr_dtrim); - } + if (err) + goto err3; + + err = device_create_file(dev, &dev_attr_time); + if (err) + goto err4; + + err = device_create_file(dev, &dev_attr_alarm); + if (err) + goto err5; + + err = device_create_file(dev, &dev_attr_intr); + if (err) + goto err6; return 0; + +err6: + device_remove_file(dev, &dev_attr_alarm); +err5: + device_remove_file(dev, &dev_attr_time); +err4: + device_remove_file(dev, &dev_attr_usr); +err3: + device_remove_file(dev, &dev_attr_dtrim); +err2: + device_remove_file(dev, &dev_attr_atrim); +err1: + return err; } static int @@ -558,12 +767,48 @@ static const struct i2c_device_id isl1208_id[] = { }; MODULE_DEVICE_TABLE(i2c, isl1208_id); +extern unsigned int pm_sleeptime; /* In seconds. */ + +#ifdef CONFIG_PM +static int isl1208_suspend(struct i2c_client *client, pm_message_t state) +{ + struct rtc_wkalrm alarm; + struct rtc_time * tm = &alarm.time; + int ret = 0; + unsigned long time; + + if (pm_sleeptime) { + ret = isl1208_i2c_get_sr(client); + if ((ret < 0) || (ret & ISL1208_REG_SR_RTCF)) + return ret; + ret = isl1208_i2c_read_time(client, tm); + if (ret < 0) + return ret; + rtc_tm_to_time(tm, &time); + time += pm_sleeptime; + rtc_time_to_tm(time, tm); + ret = isl1208_i2c_set_alarm(client, &alarm); + } + return ret; +} + +static int isl1208_resume(struct i2c_client *client) +{ + return 0; +} +#else +#define isl1208_suspend NULL +#define isl1208_resume NULL +#endif + static struct i2c_driver isl1208_driver = { .driver = { .name = "rtc-isl1208", }, .probe = isl1208_probe, .remove = isl1208_remove, + .suspend = isl1208_suspend, + .resume = isl1208_resume, .id_table = isl1208_id, }; diff --git a/drivers/rtc/rtc-mmp.c b/drivers/rtc/rtc-mmp.c new file mode 100644 index 00000000000000..8f4b0915606bc7 --- /dev/null +++ b/drivers/rtc/rtc-mmp.c @@ -0,0 +1,435 @@ +/* + * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * + * Original Driver by Nils Faerber + * + * Modifications from: + * CIH + * Nicolas Pitre + * Andrew Christian + * + * Converted to the RTC subsystem and Driver Model + * by Richard Purdie + * + * Support RTC on Marvell MMP + * by Bin Yang + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include +#include + +#define TIMER_FREQ CLOCK_TICK_RATE +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ +static void __iomem *rtc_base; +static int irq_1hz = -1, irq_alrm = -1; +static unsigned long rtc_freq = 1024; +static struct rtc_time rtc_alarm; +static DEFINE_SPINLOCK(mmp_rtc_lock); + +static inline int rtc_periodic_alarm(struct rtc_time *tm) +{ + return (tm->tm_year == -1) || + ((unsigned)tm->tm_mon >= 12) || + ((unsigned)(tm->tm_mday - 1) >= 31) || + ((unsigned)tm->tm_hour > 23) || + ((unsigned)tm->tm_min > 59) || + ((unsigned)tm->tm_sec > 59); +} + +/* + * Calculate the next alarm time given the requested alarm time mask + * and the current time. + */ +static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm) +{ + unsigned long next_time; + unsigned long now_time; + + next->tm_year = now->tm_year; + next->tm_mon = now->tm_mon; + next->tm_mday = now->tm_mday; + next->tm_hour = alrm->tm_hour; + next->tm_min = alrm->tm_min; + next->tm_sec = alrm->tm_sec; + + rtc_tm_to_time(now, &now_time); + rtc_tm_to_time(next, &next_time); + + if (next_time < now_time) { + /* Advance one day */ + next_time += 60 * 60 * 24; + rtc_time_to_tm(next_time, next); + } +} + +static int rtc_update_alarm(struct rtc_time *alrm) +{ + struct rtc_time alarm_tm, now_tm; + unsigned long now, time; + int ret; + + do { + now = RCNR; + rtc_time_to_tm(now, &now_tm); + rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); + ret = rtc_tm_to_time(&alarm_tm, &time); + if (ret != 0) + break; + + RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); + RTAR = time; + } while (now != RCNR); + + return ret; +} + +static irqreturn_t mmp_rtc_interrupt(int irq, void *dev_id) +{ + struct platform_device *pdev = to_platform_device(dev_id); + struct rtc_device *rtc = platform_get_drvdata(pdev); + unsigned int rtsr; + unsigned long events = 0; + + spin_lock(&mmp_rtc_lock); + + rtsr = RTSR; + /* clear interrupt sources */ + RTSR = 0; + RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + RTSR = rtsr & (RTSR_ALE | RTSR_HZE); + + /* update irq data & counter */ + if (rtsr & RTSR_AL) + events |= RTC_AF | RTC_IRQF; + if (rtsr & RTSR_HZ) + events |= RTC_UF | RTC_IRQF; + + rtc_update_irq(rtc, 1, events); + + if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) + rtc_update_alarm(&rtc_alarm); + + spin_unlock(&mmp_rtc_lock); + + return IRQ_HANDLED; +} + +static int mmp_rtc_open(struct device *dev) +{ + spin_lock_irq(&mmp_rtc_lock); + enable_irq(irq_1hz); + enable_irq(irq_alrm); + spin_unlock_irq(&mmp_rtc_lock); + return 0; +} + +static void mmp_rtc_release(struct device *dev) +{ + spin_lock_irq(&mmp_rtc_lock); + disable_irq(irq_1hz); + disable_irq(irq_alrm); + spin_unlock_irq(&mmp_rtc_lock); +} + + +static int mmp_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + switch(cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&mmp_rtc_lock); + RTSR &= ~RTSR_ALE; + spin_unlock_irq(&mmp_rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&mmp_rtc_lock); + RTSR |= RTSR_ALE; + spin_unlock_irq(&mmp_rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&mmp_rtc_lock); + RTSR &= ~RTSR_HZE; + spin_unlock_irq(&mmp_rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&mmp_rtc_lock); + RTSR |= RTSR_HZE; + spin_unlock_irq(&mmp_rtc_lock); + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + if (arg < 1 || arg > TIMER_FREQ) + return -EINVAL; + rtc_freq = arg; + return 0; + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user(epoch, (unsigned long __user *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + return -ENOIOCTLCMD; + } + } + return -ENOIOCTLCMD; +} + +static int mmp_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + rtc_time_to_tm(RCNR, tm); + return 0; +} + +static int mmp_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long time; + int ret; + + if(tm->tm_year>138) + return EINVAL; + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) { + RCNR = time; + } + return ret; +} + +static int mmp_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u32 rtsr; + + rtc_time_to_tm(RTAR, &rtc_alarm); + memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); + rtsr = RTSR; + alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; + alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; + return 0; +} + +static int mmp_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + int ret; + + spin_lock_irq(&mmp_rtc_lock); + + memcpy(&rtc_alarm,&alrm->time,sizeof(struct rtc_time)); + ret = rtc_update_alarm(&rtc_alarm); + if (ret == 0) { + if (alrm->enabled) + RTSR |= RTSR_ALE; + else + RTSR &= ~RTSR_ALE; + } + spin_unlock_irq(&mmp_rtc_lock); + + return ret; +} + +static int mmp_rtc_proc(struct device *dev, struct seq_file *seq) +{ + seq_printf(seq, "trim/divider\t: 0x%08x\n", (u32) RTTR); + seq_printf(seq, "update_IRQ\t: %s\n", + (RTSR & RTSR_HZE) ? "yes" : "no"); + + return 0; +} + +static const struct rtc_class_ops mmp_rtc_ops = { + .open = mmp_rtc_open, + .release = mmp_rtc_release, + .ioctl = mmp_rtc_ioctl, + .read_time = mmp_rtc_read_time, + .set_time = mmp_rtc_set_time, + .read_alarm = mmp_rtc_read_alarm, + .set_alarm = mmp_rtc_set_alarm, + .proc = mmp_rtc_proc, +}; + +#define res_size(res) ((res)->end - (res)->start + 1) +static int mmp_rtc_probe(struct platform_device *pdev) +{ + int ret; + struct rtc_device *rtc; + struct device *dev = &pdev->dev; + struct resource *res; + + irq_1hz = platform_get_irq(pdev, 0); + irq_alrm = platform_get_irq(pdev, 1); + if (irq_1hz < 0 || irq_alrm < 0) { + dev_err(&pdev->dev, "failed to get rtc irq\n"); + irq_1hz = irq_alrm = -1; + ret = -ENXIO; + goto err; + } + + ret = request_irq(irq_1hz, mmp_rtc_interrupt, IRQF_DISABLED, + "rtc 1Hz", dev); + if (ret) { + dev_err(dev, "IRQ %d already in use.\n", irq_1hz); + irq_1hz = irq_alrm = -1; + ret = -ENXIO; + goto err; + } + disable_irq(irq_1hz); + + ret = request_irq(irq_alrm, mmp_rtc_interrupt, IRQF_DISABLED, + "rtc Alrm", dev); + if (ret) { + dev_err(dev, "IRQ %d already in use.\n", irq_alrm); + irq_alrm = -1; + ret = -ENXIO; + goto err; + } + //disable_irq(irq_alrm); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get I/O memory\n"); + ret = -ENXIO; + goto err; + } + + res = request_mem_region(res->start, res_size(res), pdev->name); + if (res == NULL) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto err; + } + + rtc_base = ioremap(res->start, res_size(res)); + if (rtc_base == NULL) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto err; + } + + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + dev_warn(&pdev->dev, "warning: initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + RCNR = 0; + } + + device_init_wakeup(&pdev->dev, 1); + + rtc = rtc_device_register(pdev->name, &pdev->dev, &mmp_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + platform_set_drvdata(pdev, rtc); + + return 0; + +err: + if(irq_1hz >= 0) + free_irq(irq_1hz, dev); + if(irq_alrm >= 0) + free_irq(irq_alrm, dev); + return ret; +} + +static int mmp_rtc_remove(struct platform_device *pdev) +{ + struct rtc_device *rtc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct resource *res; + + RTSR = 0; + free_irq(irq_1hz, dev); + free_irq(irq_alrm, dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res_size(res)); + + if (rtc) + rtc_device_unregister(rtc); + + return 0; +} + +#ifdef CONFIG_PM +static int mmp_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (device_may_wakeup(&pdev->dev)) + enable_irq_wake(irq_alrm); + return 0; +} + +static int mmp_rtc_resume(struct platform_device *pdev) +{ + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(irq_alrm); + return 0; +} +#else +#define mmp_rtc_suspend NULL +#define mmp_rtc_resume NULL +#endif + +static struct platform_driver mmp_rtc_driver = { + .probe = mmp_rtc_probe, + .remove = mmp_rtc_remove, + .suspend = mmp_rtc_suspend, + .resume = mmp_rtc_resume, + .driver = { + .name = "mmp-rtc", + }, +}; + +static int __init mmp_rtc_init(void) +{ + return platform_driver_register(&mmp_rtc_driver); +} + +static void __exit mmp_rtc_exit(void) +{ + platform_driver_unregister(&mmp_rtc_driver); +} + +module_init(mmp_rtc_init); +module_exit(mmp_rtc_exit); + +MODULE_AUTHOR("Bin Yang #include #include +#include +#include +#include +#include + +#ifdef CONFIG_DVFM +#include +#endif + +#define UART_IER_DMA (1 << 7) +#define UART_LSR_FIFOE (1 << 7) +#define UART_FCR_PXA_BUS32 (1 << 5) +#define DMA_BLOCK UART_XMIT_SIZE struct uart_pxa_port { struct uart_port port; - unsigned char ier; + unsigned int ier; unsigned char lcr; - unsigned char mcr; + unsigned int mcr; unsigned int lsr_break_flag; struct clk *clk; char *name; + struct timer_list pxa_timer; +#ifdef CONFIG_DVFM + int dvfm_dev_idx; + struct dvfm_lock dvfm_lock; +#endif + int txdma; + int rxdma; + void *txdma_addr; + void *rxdma_addr; + dma_addr_t txdma_addr_phys; + dma_addr_t rxdma_addr_phys; + int tx_stop; + int rx_stop; + int data_len; + volatile long *txdrcmr; + volatile long *rxdrcmr; + struct tasklet_struct tklet; + void *buf_save; }; +static int uart_dma = 0; +static int __init uart_dma_setup(char *__unused) +{ + uart_dma = 1; + return 1; +} +__setup("uart_dma", uart_dma_setup); + +static void pxa_uart_transmit_dma(int channel, void *data); +static void pxa_uart_receive_dma(int channel, void *data); +static void pxa_uart_receive_dma_err(struct uart_pxa_port *up,int *status); +static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count); +static void pxa_uart_receive_dma_start(struct uart_pxa_port *up); +static inline void wait_for_xmitr(struct uart_pxa_port *up); +static inline void serial_out(struct uart_pxa_port *up, int offset, int value); + +#ifdef CONFIG_DVFM +#define PXA_TIMER_TIMEOUT 15*HZ +static void set_dvfm_constraint(struct uart_pxa_port *sport) +{ + spin_lock_irqsave(&sport->dvfm_lock.lock, sport->dvfm_lock.flags); + dvfm_disable_op_name("apps_idle", sport->dvfm_dev_idx); + dvfm_disable_op_name("apps_sleep", sport->dvfm_dev_idx); + dvfm_disable_op_name("sys_sleep", sport->dvfm_dev_idx); + spin_unlock_irqrestore(&sport->dvfm_lock.lock, sport->dvfm_lock.flags); +} + +static void unset_dvfm_constraint(struct uart_pxa_port *sport) +{ + spin_lock_irqsave(&sport->dvfm_lock.lock, sport->dvfm_lock.flags); + dvfm_enable_op_name("apps_idle", sport->dvfm_dev_idx); + dvfm_enable_op_name("apps_sleep", sport->dvfm_dev_idx); + dvfm_enable_op_name("sys_sleep", sport->dvfm_dev_idx); + spin_unlock_irqrestore(&sport->dvfm_lock.lock, sport->dvfm_lock.flags); +} +#else +static void set_dvfm_constraint(struct uart_pxa_port *sport) {} +static void unset_dvfm_constraint(struct uart_pxa_port *sport) {} +#endif + static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) { offset <<= 2; @@ -72,6 +143,8 @@ static void serial_pxa_enable_ms(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; + if (uart_dma) + return; up->ier |= UART_IER_MSI; serial_out(up, UART_IER, up->ier); } @@ -80,9 +153,17 @@ static void serial_pxa_stop_tx(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; - if (up->ier & UART_IER_THRI) { - up->ier &= ~UART_IER_THRI; - serial_out(up, UART_IER, up->ier); + if (uart_dma) { + up->tx_stop = 1; + if (up->ier & UART_IER_DMA) { + while (!(DCSR(up->txdma) & DCSR_STOPSTATE)) + rmb(); + } + } else { + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } } } @@ -90,9 +171,18 @@ static void serial_pxa_stop_rx(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; - up->ier &= ~UART_IER_RLSI; - up->port.read_status_mask &= ~UART_LSR_DR; - serial_out(up, UART_IER, up->ier); + if (uart_dma) { + if (up->ier & UART_IER_DMA) { + DCSR(up->rxdma) &= ~DCSR_RUN; + while (!(DCSR(up->rxdma) & DCSR_STOPSTATE)) + rmb(); + } + up->rx_stop = 1; + } else { + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + } } static inline void receive_chars(struct uart_pxa_port *up, int *status) @@ -193,13 +283,111 @@ static void transmit_chars(struct uart_pxa_port *up) serial_pxa_stop_tx(&up->port); } +static inline void +dma_receive_chars(struct uart_pxa_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned char ch; + int max_count = 256; + int count = 0; + unsigned char *tmp; + unsigned int flag = TTY_NORMAL; + + DCSR(up->rxdma) &= ~DCSR_RUN; + count = DTADR(up->rxdma) - up->rxdma_addr_phys; + tmp = up->rxdma_addr; + + while (count > 0) { + if (!uart_handle_sysrq_char(&up->port, *tmp)) + uart_insert_char(&up->port, *status, 0, *tmp, flag); + tmp++; + count--; + } + + *status = serial_in(up, UART_LSR); + if (!(*status & UART_LSR_DR)) { + ch = serial_in(up, UART_RX); + goto done; + } + + do { + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char2; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_PXA_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + if (!uart_handle_sysrq_char(&up->port, ch)) + uart_insert_char(&up->port, *status, UART_LSR_OE, + ch, flag); + ignore_char2: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + +done: + tty_schedule_flip(tty); + if (up->rx_stop) + return; + pxa_uart_receive_dma_start(up); +} + static void serial_pxa_start_tx(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; - if (!(up->ier & UART_IER_THRI)) { - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, up->ier); +#ifdef CONFIG_DVFM + if (!mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) + set_dvfm_constraint(up); +#endif + + if (uart_dma) { + up->tx_stop = 0; + tasklet_schedule(&up->tklet); + } else { + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } } } @@ -235,12 +423,24 @@ static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id) iir = serial_in(up, UART_IIR); if (iir & UART_IIR_NO_INT) return IRQ_NONE; +#if defined(CONFIG_DVFM) + if (!mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) + set_dvfm_constraint(up); +#endif lsr = serial_in(up, UART_LSR); - if (lsr & UART_LSR_DR) - receive_chars(up, &lsr); - check_modem_status(up); - if (lsr & UART_LSR_THRE) - transmit_chars(up); + if (uart_dma) { + if (UART_LSR_FIFOE & lsr) + pxa_uart_receive_dma_err(up, &lsr); + if (iir & UART_IIR_TOD) { + dma_receive_chars(up, &lsr); + } + } else { + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + check_modem_status(up); + if (lsr & UART_LSR_THRE) + transmit_chars(up); + } return IRQ_HANDLED; } @@ -251,6 +451,14 @@ static unsigned int serial_pxa_tx_empty(struct uart_port *port) unsigned int ret; spin_lock_irqsave(&up->port.lock, flags); + if (uart_dma) { + if (up->ier & UART_IER_DMA) { + if (DCSR(up->txdma) & DCSR_RUN) { + spin_unlock_irqrestore(&up->port.lock, flags); + return 0; + } + } + } ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; spin_unlock_irqrestore(&up->port.lock, flags); @@ -280,7 +488,7 @@ static unsigned int serial_pxa_get_mctrl(struct uart_port *port) static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; - unsigned char mcr = 0; + unsigned int mcr = 0; if (mctrl & TIOCM_RTS) mcr |= UART_MCR_RTS; @@ -337,6 +545,304 @@ static void serial_pxa_dma_init(struct pxa_uart *up) } #endif +static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count) +{ + if (!(DCSR(up->txdma) & DCSR_STOPSTATE)) + return; + DCSR(up->txdma) = DCSR_NODESC; + DSADR(up->txdma) = up->txdma_addr_phys; + DTADR(up->txdma) = up->port.mapbase; + DCMD(up->txdma) = DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST16 | count; + DCSR(up->txdma) |= DCSR_RUN; +} + +static void pxa_uart_receive_dma_start(struct uart_pxa_port *up) +{ + DCSR(up->rxdma) = DCSR_NODESC; + DSADR(up->rxdma) = up->port.mapbase; + DTADR(up->rxdma) = up->rxdma_addr_phys; + DCMD(up->rxdma) = DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_ENDIRQEN | DCMD_WIDTH1 | DCMD_BURST16 | DMA_BLOCK; + DCSR(up->rxdma) |= DCSR_RUN; +} + +static void pxa_uart_receive_dma_err(struct uart_pxa_port *up,int *status) +{ + unsigned char ch; + struct tty_struct *tty = up->port.state->port.tty; + unsigned char *tmp; + int count; + unsigned int flag = 0; + + DCSR(up->rxdma) &= ~DCSR_RUN; + + /* if have DMA reqeust, wait. */ + while (!(DCSR(up->rxdma) & DCSR_STOPSTATE)) + rmb(); + + count = DTADR(up->rxdma) - up->rxdma_addr_phys; + tmp = up->rxdma_addr; + + tty_insert_flip_string(tty, tmp, count); + up->port.icount.rx += count; + + do { + ch = serial_in(up, UART_RX); + up->port.icount.rx++; + + /* + * For statistics only + */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) { + up->port.icount.parity++; + flag = TTY_PARITY; + } else if (*status & UART_LSR_FE) { + up->port.icount.frame++; + flag = TTY_FRAME; + } + + if (*status & UART_LSR_OE){ + up->port.icount.overrun++; + } + + /* + * Mask off conditions which should be ignored. + */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_PXA_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); + + ignore_char: + *status = serial_in(up, UART_LSR); + } while (*status & UART_LSR_DR); + + tty_flip_buffer_push(tty); + if (up->rx_stop) + return; + pxa_uart_receive_dma_start(up); +} + +static void pxa_uart_receive_dma(int channel, void *data) +{ + volatile unsigned long dcsr; + struct uart_pxa_port *up = (struct uart_pxa_port *)data; + struct tty_struct *tty = up->port.state->port.tty; + unsigned int count; + unsigned char *tmp = up->rxdma_addr; + + DCSR(channel) &= ~DCSR_RUN; + dcsr = DCSR(channel); + + if ((dcsr & DCSR_ENDINTR) || (dcsr & DCSR_STOPSTATE)) { + if (dcsr & DCSR_ENDINTR) + DCSR(channel) |= DCSR_ENDINTR; + if (dcsr & DCSR_STOPSTATE) + DCSR(channel) &= ~DCSR_STOPSTATE; + + count = DTADR(channel) - up->rxdma_addr_phys; + tty_insert_flip_string(tty, tmp, count); + up->port.icount.rx += count; + tty_flip_buffer_push(tty); + if (up->rx_stop) + return; + pxa_uart_receive_dma_start(up); + } + return; +} + +static void pxa_uart_transmit_dma(int channel, void *data) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)data; + struct circ_buf *xmit = &up->port.state->xmit; + volatile unsigned long dcsr; + + DCSR(channel) &= ~DCSR_RUN; + dcsr = DCSR(channel); + + if (dcsr & DCSR_BUSERR) { + DCSR(channel) |= DCSR_BUSERR; + printk(KERN_ALERT "%s(): DMA channel bus error\n", __func__); + } + + if ((dcsr & DCSR_ENDINTR) || (dcsr & DCSR_STOPSTATE)) { + if (dcsr & DCSR_STOPSTATE) { + DCSR(channel) &= ~DCSR_STOPSTATE; + } + + if (dcsr & DCSR_ENDINTR) { + DCSR(channel) |= DCSR_ENDINTR; + } + + /* if tx stop, stop transmit DMA and return */ + if (up->tx_stop) { + return; + } + + if (up->port.x_char) { + serial_out(up, UART_TX,up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + } + + if(uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (!uart_circ_empty(xmit)) { + tasklet_schedule(&up->tklet); + } + } + return; +} + +extern void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *handle, gfp_t gfp); +extern void dma_free_coherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t handle); +static void uart_pxa_dma_init(struct uart_pxa_port *up) +{ + if (0 == up->rxdma) { + up->rxdma = + pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_uart_receive_dma, up); + if (up->rxdma < 0) + goto out; + } + + if (0 == up->txdma) { + up->txdma = + pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_uart_transmit_dma, up); + if (up->txdma < 0) + goto err_txdma; + } + + if (NULL == up->txdma_addr) { + up->txdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK, &up->txdma_addr_phys, GFP_KERNEL); + if (!up->txdma_addr) + goto txdma_err_alloc; + } + + if (NULL == up->rxdma_addr) { + up->rxdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK, &up->rxdma_addr_phys, GFP_KERNEL); + if (!up->rxdma_addr) + goto rxdma_err_alloc; + } + + up->buf_save = kmalloc(DMA_BLOCK, GFP_KERNEL); + if (!up->buf_save) { + goto buf_err_alloc; + } + + writel(up->rxdma | DRCMR_MAPVLD, up->rxdrcmr); + writel(up->txdma | DRCMR_MAPVLD, up->txdrcmr); + return; + +buf_err_alloc: + dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr, up->rxdma_addr_phys); + up->rxdma_addr = NULL; +rxdma_err_alloc: + dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr, up->txdma_addr_phys); + up->txdma_addr = NULL; +txdma_err_alloc: + pxa_free_dma(up->txdma); + up->txdma = 0; +err_txdma: + pxa_free_dma(up->rxdma); + up->rxdma = 0; +out: + return; +} + +static void uart_pxa_dma_uninit(struct uart_pxa_port *up) +{ + if ( DCSR(up->rxdma) & DCSR_RUN) + DCSR(up->rxdma) &= ~DCSR_RUN; + + if ( DCSR(up->txdma) & DCSR_RUN) + DCSR(up->txdma) &= ~DCSR_RUN; + + if (up->txdma_addr != NULL) { + dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr, up->txdma_addr_phys); + up->txdma_addr = NULL; + } + if (up->txdma != 0) { + pxa_free_dma(up->txdma); + writel(0, up->txdrcmr); + up->txdma = 0; + } + + if (up->rxdma_addr != NULL) { + dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr, up->rxdma_addr_phys); + up->rxdma_addr = NULL; + } + + if (up->rxdma != 0) { + pxa_free_dma(up->rxdma); + writel(0, up->rxdrcmr); + up->rxdma = 0; + } + + return; +} + +static void uart_task_action(unsigned long data) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)data; + struct circ_buf *xmit = &up->port.state->xmit; + unsigned char *tmp = up->txdma_addr; + unsigned long flags; + int count = 0,c; + + /* if the tx is stop, just return.*/ + if (up->tx_stop) + return; + + if ((DCSR(up->txdma) & DCSR_RUN)) + return; + + spin_lock_irqsave(&up->port.lock, flags); + while (1) { + c = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (c <= 0) + break; + + memcpy(tmp, xmit->buf + xmit->tail, c); + xmit->tail = (xmit->tail + c) & (UART_XMIT_SIZE -1); + tmp += c; + count += c; + up->port.icount.tx += c; + } + spin_unlock_irqrestore(&up->port.lock, flags); + + tmp = up->txdma_addr; + up->tx_stop = 0; + + pr_debug("count =%d", count); + pxa_uart_transmit_dma_start(up,count); +} + + static int serial_pxa_startup(struct uart_port *port) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; @@ -389,7 +895,15 @@ static int serial_pxa_startup(struct uart_port *port) * are set via set_termios(), which will be occurring imminently * anyway, so we don't enable them here. */ - up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; + if (uart_dma) { + uart_pxa_dma_init(up); + up->rx_stop = 0; + pxa_uart_receive_dma_start(up); + up->ier = UART_IER_DMA | UART_IER_UUE | UART_IER_RTOIE; + tasklet_init(&up->tklet, uart_task_action, (unsigned long)up); + } else { + up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE; + } serial_out(up, UART_IER, up->ier); /* @@ -409,6 +923,10 @@ static void serial_pxa_shutdown(struct uart_port *port) unsigned long flags; free_irq(up->port.irq, up); + if (uart_dma) { + tasklet_kill(&up->tklet); + uart_pxa_dma_uninit(up); + } /* * Disable interrupts from this port @@ -438,8 +956,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, struct uart_pxa_port *up = (struct uart_pxa_port *)port; unsigned char cval, fcr = 0; unsigned long flags; - unsigned int baud, quot; - unsigned int dll; + unsigned int baud, quot, baud_max; switch (termios->c_cflag & CSIZE) { case CS5: @@ -467,15 +984,34 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); +#define IER_HSE (1 << 8) /* High Speed UART Enable in IER */ + baud_max = port->uartclk / 16; + baud = uart_get_baud_rate(port, termios, old, 0, + baud_max * APBC_UART_HS_MULTI); + if (baud > baud_max) { + clk_set_rate(up->clk, port->uartclk * APBC_UART_HS_MULTI); + up->ier |= IER_HSE; + if (B1500000 == (termios->c_cflag & B1500000)) + quot = 2; + if (B3500000 == (termios->c_cflag & B3500000)) + quot = 1; + } else { + clk_set_rate(up->clk, port->uartclk); + up->ier &= ~IER_HSE; + quot = uart_get_divisor(port, baud); + } - if ((up->port.uartclk / quot) < (2400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1; - else if ((up->port.uartclk / quot) < (230400 * 16)) - fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8; - else + if (uart_dma) { fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32; + fcr &= ~UART_FCR_PXA_BUS32; + } else { + if (baud < 400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1; + else if (baud < 230400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32; + } /* * Ok, we're now changing the port state. Do it with @@ -525,9 +1061,16 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, /* * CTS flow control flag and modem status interrupts */ - up->ier &= ~UART_IER_MSI; - if (UART_ENABLE_MS(&up->port, termios->c_cflag)) - up->ier |= UART_IER_MSI; + if (uart_dma) { + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + else + up->mcr &= UART_MCR_AFE; + } else { + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + } serial_out(up, UART_IER, up->ier); @@ -538,14 +1081,6 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ - - /* - * work around Errata #75 according to Intel(R) PXA27x Processor Family - * Specification Update (Nov 2005) - */ - dll = serial_in(up, UART_DLL); - WARN_ON(dll != (quot & 0xff)); - serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ serial_out(up, UART_LCR, cval); /* reset DLAB */ up->lcr = cval; /* Save LCR */ @@ -650,6 +1185,11 @@ serial_pxa_console_write(struct console *co, const char *s, unsigned int count) struct uart_pxa_port *up = serial_pxa_ports[co->index]; unsigned int ier; +#ifdef CONFIG_DVFM + if (!mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) + set_dvfm_constraint(up); +#endif + clk_enable(up->clk); /* @@ -741,6 +1281,32 @@ static int serial_pxa_suspend(struct device *dev) { struct uart_pxa_port *sport = dev_get_drvdata(dev); + if (sport && (sport->ier & UART_IER_DMA)) { + int length = 0,sent = 0; + unsigned long flags; + + local_irq_save(flags); + sport->tx_stop = 1; + sport->rx_stop = 1; + sport->data_len = 0; + if (DCSR(sport->txdma) & DCSR_RUN) { + DCSR(sport->txdma) &= ~DCSR_RUN; + length = DCMD(sport->txdma) & 0x1FFF; + sent = DSADR(sport->txdma) - + sport->txdma_addr_phys; + memcpy(sport->buf_save, sport->txdma_addr + + sent, length); + sport->data_len = length; + + } + + if (DCSR(sport->rxdma) & DCSR_RUN) + DCSR(sport->rxdma) &= ~DCSR_RUN; + pxa_uart_receive_dma(sport->rxdma, sport); + + local_irq_restore(flags); + } + if (sport) uart_suspend_port(&serial_pxa_reg, &sport->port); @@ -751,9 +1317,25 @@ static int serial_pxa_resume(struct device *dev) { struct uart_pxa_port *sport = dev_get_drvdata(dev); +#if defined(CONFIG_DVFM) + if (!mod_timer(&sport->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) + set_dvfm_constraint(sport); +#endif if (sport) uart_resume_port(&serial_pxa_reg, &sport->port); + if (sport->ier & UART_IER_DMA) { + if (sport->data_len > 0) { + memcpy( sport->txdma_addr, sport->buf_save, + sport->data_len); + pxa_uart_transmit_dma_start(sport, + sport->data_len); + } else + tasklet_schedule(&sport->tklet); + + pxa_uart_receive_dma_start(sport); + } + return 0; } @@ -763,14 +1345,24 @@ static const struct dev_pm_ops serial_pxa_pm_ops = { }; #endif +#if defined(CONFIG_DVFM) +static void pxa_timer_handler(unsigned long data) +{ + struct uart_pxa_port *up = (struct uart_pxa_port *)data; + unset_dvfm_constraint(up); +} +#endif + static int serial_pxa_probe(struct platform_device *dev) { struct uart_pxa_port *sport; - struct resource *mmres, *irqres; + struct resource *mmres, *irqres,*dma0, *dma1; int ret; mmres = platform_get_resource(dev, IORESOURCE_MEM, 0); irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0); + dma0=platform_get_resource(dev,IORESOURCE_DMA,0); + dma1=platform_get_resource(dev,IORESOURCE_DMA,1); if (!mmres || !irqres) return -ENODEV; @@ -795,6 +1387,13 @@ static int serial_pxa_probe(struct platform_device *dev) sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; sport->port.uartclk = clk_get_rate(sport->clk); + sport->txdma = 0; + sport->rxdma = 0; + sport->txdma_addr = NULL; + sport->rxdma_addr = NULL; + sport->rxdrcmr = (long *)&DRCMR(dma0->start); + sport->txdrcmr = (long *)&DRCMR(dma1->start); + switch (dev->id) { case 0: sport->name = "FFUART"; break; case 1: sport->name = "BTUART"; break; @@ -805,6 +1404,15 @@ static int serial_pxa_probe(struct platform_device *dev) break; } +#if defined(CONFIG_DVFM) + sport->dvfm_lock.lock = SPIN_LOCK_UNLOCKED; + sport->dvfm_dev_idx = -1; + dvfm_register(sport->name, &(sport->dvfm_dev_idx)); + init_timer(&sport->pxa_timer); + sport->pxa_timer.function = pxa_timer_handler; + sport->pxa_timer.data = (long)sport; +#endif + sport->port.membase = ioremap(mmres->start, mmres->end - mmres->start + 1); if (!sport->port.membase) { ret = -ENOMEM; @@ -819,6 +1427,9 @@ static int serial_pxa_probe(struct platform_device *dev) return 0; err_clk: +#if defined(CONFIG_DVFM) + dvfm_unregister(sport->name, &(sport->dvfm_dev_idx)); +#endif clk_put(sport->clk); err_free: kfree(sport); @@ -829,11 +1440,16 @@ static int serial_pxa_remove(struct platform_device *dev) { struct uart_pxa_port *sport = platform_get_drvdata(dev); +#if defined(CONFIG_DVFM) + dvfm_unregister(sport->name, &(sport->dvfm_dev_idx)); +#endif + platform_set_drvdata(dev, NULL); uart_remove_one_port(&serial_pxa_reg, &sport->port); clk_put(sport->clk); kfree(sport); + serial_pxa_ports[dev->id] = NULL; return 0; } diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 7f283070951217..435bf3b213d065 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -94,6 +94,9 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; + if (port->ops->wake_peer) + port->ops->wake_peer(port); + if (!uart_circ_empty(&state->xmit) && state->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a191fa2be7c5e4..0d34186c66038d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -219,7 +219,7 @@ config SPI_PPC4xx config SPI_PXA2XX tristate "PXA2xx SSP SPI master" - depends on ARCH_PXA && EXPERIMENTAL + depends on (ARCH_PXA || ARCH_MMP) && EXPERIMENTAL select PXA_SSP help This enables using a PXA2xx SSP port as a SPI master controller. diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 36828358a4d860..8aeafb94e924ff 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -34,11 +34,12 @@ #include #include #include +#include #include #include #include -#include +#include MODULE_AUTHOR("Stephen Street"); MODULE_DESCRIPTION("PXA2xx SSP SPI Controller"); @@ -350,6 +351,7 @@ static int map_dma_buffers(struct driver_data *drv_data) { struct spi_message *msg = drv_data->cur_msg; struct device *dev = &msg->spi->dev; + struct spi_transfer *transfer = drv_data->cur_transfer; if (!drv_data->cur_chip->enable_dma) return 0; @@ -364,6 +366,7 @@ static int map_dma_buffers(struct driver_data *drv_data) if (drv_data->rx == NULL) { *drv_data->null_dma_buf = 0; drv_data->rx = drv_data->null_dma_buf; + drv_data->rx_end = drv_data->rx + transfer->len; drv_data->rx_map_len = 4; } else drv_data->rx_map_len = drv_data->len; @@ -373,6 +376,7 @@ static int map_dma_buffers(struct driver_data *drv_data) if (drv_data->tx == NULL) { *drv_data->null_dma_buf = 0; drv_data->tx = drv_data->null_dma_buf; + drv_data->tx_end = drv_data->tx + transfer->len; drv_data->tx_map_len = 4; } else drv_data->tx_map_len = drv_data->len; @@ -948,6 +952,12 @@ static void pump_transfers(unsigned long data) giveback(drv_data); return; } + + if (chip->enable_dma) + drv_data->len = transfer->len & DCMD_LENGTH; + else + drv_data->len = transfer->len; + drv_data->n_bytes = chip->n_bytes; drv_data->dma_width = chip->dma_width; drv_data->tx = (void *)transfer->tx_buf; @@ -956,7 +966,6 @@ static void pump_transfers(unsigned long data) drv_data->rx_end = drv_data->rx + transfer->len; drv_data->rx_dma = transfer->rx_dma; drv_data->tx_dma = transfer->tx_dma; - drv_data->len = transfer->len & DCMD_LENGTH; drv_data->write = drv_data->tx ? chip->write : null_writer; drv_data->read = drv_data->rx ? chip->read : null_reader; diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 7696a664f8a554..3ed19c93222d3f 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -41,6 +41,8 @@ config STAGING_EXCLUDE_BUILD if !STAGING_EXCLUDE_BUILD +source "drivers/staging/android/Kconfig" + source "drivers/staging/et131x/Kconfig" source "drivers/staging/slicoss/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ea2e70e2fed466..ffce24a85502f2 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -51,3 +51,4 @@ obj-$(CONFIG_PCMCIA_NETWAVE) += netwave/ obj-$(CONFIG_FB_SM7XX) += sm7xx/ obj-$(CONFIG_DT3155) += dt3155/ obj-$(CONFIG_CRYSTALHD) += crystalhd/ +obj-$(CONFIG_ANDROID) += android/ diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig new file mode 100644 index 00000000000000..247194992374b2 --- /dev/null +++ b/drivers/staging/android/Kconfig @@ -0,0 +1,95 @@ +menu "Android" + +config ANDROID + bool "Android Drivers" + default N + ---help--- + Enable support for various drivers needed on the Android platform + +if ANDROID + +config ANDROID_BINDER_IPC + bool "Android Binder IPC Driver" + default n + +config ANDROID_LOGGER + tristate "Android log driver" + default n + +config ANDROID_RAM_CONSOLE + bool "Android RAM buffer console" + default n + +config ANDROID_RAM_CONSOLE_ENABLE_VERBOSE + bool "Enable verbose console messages on Android RAM console" + default y + depends on ANDROID_RAM_CONSOLE + +menuconfig ANDROID_RAM_CONSOLE_ERROR_CORRECTION + bool "Android RAM Console Enable error correction" + default n + depends on ANDROID_RAM_CONSOLE + depends on !ANDROID_RAM_CONSOLE_EARLY_INIT + select REED_SOLOMON + select REED_SOLOMON_ENC8 + select REED_SOLOMON_DEC8 + +if ANDROID_RAM_CONSOLE_ERROR_CORRECTION + +config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE + int "Android RAM Console Data data size" + default 128 + help + Must be a power of 2. + +config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE + int "Android RAM Console ECC size" + default 16 + +config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE + int "Android RAM Console Symbol size" + default 8 + +config ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL + hex "Android RAM Console Polynomial" + default 0x19 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 4) + default 0x29 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 5) + default 0x61 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 6) + default 0x89 if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 7) + default 0x11d if (ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE = 8) + +endif # ANDROID_RAM_CONSOLE_ERROR_CORRECTION + +config ANDROID_RAM_CONSOLE_EARLY_INIT + bool "Start Android RAM console early" + default n + depends on ANDROID_RAM_CONSOLE + +config ANDROID_RAM_CONSOLE_EARLY_ADDR + hex "Android RAM console virtual address" + default 0 + depends on ANDROID_RAM_CONSOLE_EARLY_INIT + +config ANDROID_RAM_CONSOLE_EARLY_SIZE + hex "Android RAM console buffer size" + default 0 + depends on ANDROID_RAM_CONSOLE_EARLY_INIT + +config ANDROID_TIMED_OUTPUT + bool "Timed output class driver" + default y + +config ANDROID_TIMED_GPIO + tristate "Android timed gpio driver" + depends on GENERIC_GPIO && ANDROID_TIMED_OUTPUT + default n + +config ANDROID_LOW_MEMORY_KILLER + bool "Android Low Memory Killer" + default N + ---help--- + Register processes to be killed when memory is low + +endif # if ANDROID + +endmenu diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile new file mode 100644 index 00000000000000..8e057e626d11c8 --- /dev/null +++ b/drivers/staging/android/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o +obj-$(CONFIG_ANDROID_LOGGER) += logger.o +obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o +obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o +obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o +obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o diff --git a/drivers/staging/android/TODO b/drivers/staging/android/TODO new file mode 100644 index 00000000000000..e59c5be4be2bfa --- /dev/null +++ b/drivers/staging/android/TODO @@ -0,0 +1,10 @@ +TODO: + - checkpatch.pl cleanups + - sparse fixes + - rename files to be not so "generic" + - make sure things build as modules properly + - add proper arch dependancies as needed + - audit userspace interfaces to make sure they are sane + +Please send patches to Greg Kroah-Hartman and Cc: +Brian Swetland diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c new file mode 100644 index 00000000000000..17e5c24eae162a --- /dev/null +++ b/drivers/staging/android/binder.c @@ -0,0 +1,3775 @@ +/* binder.c + * + * Android IPC Subsystem + * + * Copyright (C) 2007-2008 Google, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "binder.h" + +static DEFINE_MUTEX(binder_lock); +static DEFINE_MUTEX(binder_deferred_lock); + +static HLIST_HEAD(binder_procs); +static HLIST_HEAD(binder_deferred_list); +static HLIST_HEAD(binder_dead_nodes); + +static struct proc_dir_entry *binder_proc_dir_entry_root; +static struct proc_dir_entry *binder_proc_dir_entry_proc; +static struct binder_node *binder_context_mgr_node; +static uid_t binder_context_mgr_uid = -1; +static int binder_last_id; +static struct workqueue_struct *binder_deferred_workqueue; + +static int binder_read_proc_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* This is only defined in include/asm-arm/sizes.h */ +#ifndef SZ_1K +#define SZ_1K 0x400 +#endif + +#ifndef SZ_4M +#define SZ_4M 0x400000 +#endif + +#define FORBIDDEN_MMAP_FLAGS (VM_WRITE) + +#define BINDER_SMALL_BUF_SIZE (PAGE_SIZE * 64) + +enum { + BINDER_DEBUG_USER_ERROR = 1U << 0, + BINDER_DEBUG_FAILED_TRANSACTION = 1U << 1, + BINDER_DEBUG_DEAD_TRANSACTION = 1U << 2, + BINDER_DEBUG_OPEN_CLOSE = 1U << 3, + BINDER_DEBUG_DEAD_BINDER = 1U << 4, + BINDER_DEBUG_DEATH_NOTIFICATION = 1U << 5, + BINDER_DEBUG_READ_WRITE = 1U << 6, + BINDER_DEBUG_USER_REFS = 1U << 7, + BINDER_DEBUG_THREADS = 1U << 8, + BINDER_DEBUG_TRANSACTION = 1U << 9, + BINDER_DEBUG_TRANSACTION_COMPLETE = 1U << 10, + BINDER_DEBUG_FREE_BUFFER = 1U << 11, + BINDER_DEBUG_INTERNAL_REFS = 1U << 12, + BINDER_DEBUG_BUFFER_ALLOC = 1U << 13, + BINDER_DEBUG_PRIORITY_CAP = 1U << 14, + BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15, +}; +static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR | + BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION; +module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO); + +static int binder_debug_no_lock; +module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO); + +static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait); +static int binder_stop_on_user_error; + +static int binder_set_stop_on_user_error(const char *val, + struct kernel_param *kp) +{ + int ret; + ret = param_set_int(val, kp); + if (binder_stop_on_user_error < 2) + wake_up(&binder_user_error_wait); + return ret; +} +module_param_call(stop_on_user_error, binder_set_stop_on_user_error, + param_get_int, &binder_stop_on_user_error, S_IWUSR | S_IRUGO); + +#define binder_debug(mask, x...) \ + do { \ + if (binder_debug_mask & mask) \ + printk(KERN_INFO x); \ + } while (0) + +#define binder_user_error(x...) \ + do { \ + if (binder_debug_mask & BINDER_DEBUG_USER_ERROR) \ + printk(KERN_INFO x); \ + if (binder_stop_on_user_error) \ + binder_stop_on_user_error = 2; \ + } while (0) + +enum binder_stat_types { + BINDER_STAT_PROC, + BINDER_STAT_THREAD, + BINDER_STAT_NODE, + BINDER_STAT_REF, + BINDER_STAT_DEATH, + BINDER_STAT_TRANSACTION, + BINDER_STAT_TRANSACTION_COMPLETE, + BINDER_STAT_COUNT +}; + +struct binder_stats { + int br[_IOC_NR(BR_FAILED_REPLY) + 1]; + int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1]; + int obj_created[BINDER_STAT_COUNT]; + int obj_deleted[BINDER_STAT_COUNT]; +}; + +static struct binder_stats binder_stats; + +static inline void binder_stats_deleted(enum binder_stat_types type) +{ + binder_stats.obj_deleted[type]++; +} + +static inline void binder_stats_created(enum binder_stat_types type) +{ + binder_stats.obj_created[type]++; +} + +struct binder_transaction_log_entry { + int debug_id; + int call_type; + int from_proc; + int from_thread; + int target_handle; + int to_proc; + int to_thread; + int to_node; + int data_size; + int offsets_size; +}; +struct binder_transaction_log { + int next; + int full; + struct binder_transaction_log_entry entry[32]; +}; +static struct binder_transaction_log binder_transaction_log; +static struct binder_transaction_log binder_transaction_log_failed; + +static struct binder_transaction_log_entry *binder_transaction_log_add( + struct binder_transaction_log *log) +{ + struct binder_transaction_log_entry *e; + e = &log->entry[log->next]; + memset(e, 0, sizeof(*e)); + log->next++; + if (log->next == ARRAY_SIZE(log->entry)) { + log->next = 0; + log->full = 1; + } + return e; +} + +struct binder_work { + struct list_head entry; + enum { + BINDER_WORK_TRANSACTION = 1, + BINDER_WORK_TRANSACTION_COMPLETE, + BINDER_WORK_NODE, + BINDER_WORK_DEAD_BINDER, + BINDER_WORK_DEAD_BINDER_AND_CLEAR, + BINDER_WORK_CLEAR_DEATH_NOTIFICATION, + } type; +}; + +struct binder_node { + int debug_id; + struct binder_work work; + union { + struct rb_node rb_node; + struct hlist_node dead_node; + }; + struct binder_proc *proc; + struct hlist_head refs; + int internal_strong_refs; + int local_weak_refs; + int local_strong_refs; + void __user *ptr; + void __user *cookie; + unsigned has_strong_ref:1; + unsigned pending_strong_ref:1; + unsigned has_weak_ref:1; + unsigned pending_weak_ref:1; + unsigned has_async_transaction:1; + unsigned accept_fds:1; + unsigned min_priority:8; + struct list_head async_todo; +}; + +struct binder_ref_death { + struct binder_work work; + void __user *cookie; +}; + +struct binder_ref { + /* Lookups needed: */ + /* node + proc => ref (transaction) */ + /* desc + proc => ref (transaction, inc/dec ref) */ + /* node => refs + procs (proc exit) */ + int debug_id; + struct rb_node rb_node_desc; + struct rb_node rb_node_node; + struct hlist_node node_entry; + struct binder_proc *proc; + struct binder_node *node; + uint32_t desc; + int strong; + int weak; + struct binder_ref_death *death; +}; + +struct binder_buffer { + struct list_head entry; /* free and allocated entries by addesss */ + struct rb_node rb_node; /* free entry by size or allocated entry */ + /* by address */ + unsigned free:1; + unsigned allow_user_free:1; + unsigned async_transaction:1; + unsigned debug_id:29; + + struct binder_transaction *transaction; + + struct binder_node *target_node; + size_t data_size; + size_t offsets_size; + uint8_t data[0]; +}; + +enum binder_deferred_state { + BINDER_DEFERRED_PUT_FILES = 0x01, + BINDER_DEFERRED_FLUSH = 0x02, + BINDER_DEFERRED_RELEASE = 0x04, +}; + +struct binder_proc { + struct hlist_node proc_node; + struct rb_root threads; + struct rb_root nodes; + struct rb_root refs_by_desc; + struct rb_root refs_by_node; + int pid; + struct vm_area_struct *vma; + struct task_struct *tsk; + struct files_struct *files; + struct hlist_node deferred_work_node; + int deferred_work; + void *buffer; + ptrdiff_t user_buffer_offset; + + struct list_head buffers; + struct rb_root free_buffers; + struct rb_root allocated_buffers; + size_t free_async_space; + + struct page **pages; + size_t buffer_size; + uint32_t buffer_free; + struct list_head todo; + wait_queue_head_t wait; + struct binder_stats stats; + struct list_head delivered_death; + int max_threads; + int requested_threads; + int requested_threads_started; + int ready_threads; + long default_priority; +}; + +enum { + BINDER_LOOPER_STATE_REGISTERED = 0x01, + BINDER_LOOPER_STATE_ENTERED = 0x02, + BINDER_LOOPER_STATE_EXITED = 0x04, + BINDER_LOOPER_STATE_INVALID = 0x08, + BINDER_LOOPER_STATE_WAITING = 0x10, + BINDER_LOOPER_STATE_NEED_RETURN = 0x20 +}; + +struct binder_thread { + struct binder_proc *proc; + struct rb_node rb_node; + int pid; + int looper; + struct binder_transaction *transaction_stack; + struct list_head todo; + uint32_t return_error; /* Write failed, return error code in read buf */ + uint32_t return_error2; /* Write failed, return error code in read */ + /* buffer. Used when sending a reply to a dead process that */ + /* we are also waiting on */ + wait_queue_head_t wait; + struct binder_stats stats; +}; + +struct binder_transaction { + int debug_id; + struct binder_work work; + struct binder_thread *from; + struct binder_transaction *from_parent; + struct binder_proc *to_proc; + struct binder_thread *to_thread; + struct binder_transaction *to_parent; + unsigned need_reply:1; + /* unsigned is_dead:1; */ /* not used at the moment */ + + struct binder_buffer *buffer; + unsigned int code; + unsigned int flags; + long priority; + long saved_priority; + uid_t sender_euid; +}; + +static void +binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer); + +/* + * copied from get_unused_fd_flags + */ +int task_get_unused_fd_flags(struct binder_proc *proc, int flags) +{ + struct files_struct *files = proc->files; + int fd, error; + struct fdtable *fdt; + unsigned long rlim_cur; + unsigned long irqs; + + if (files == NULL) + return -ESRCH; + + error = -EMFILE; + spin_lock(&files->file_lock); + +repeat: + fdt = files_fdtable(files); + fd = find_next_zero_bit(fdt->open_fds->fds_bits, fdt->max_fds, + files->next_fd); + + /* + * N.B. For clone tasks sharing a files structure, this test + * will limit the total number of files that can be opened. + */ + rlim_cur = 0; + if (lock_task_sighand(proc->tsk, &irqs)) { + rlim_cur = proc->tsk->signal->rlim[RLIMIT_NOFILE].rlim_cur; + unlock_task_sighand(proc->tsk, &irqs); + } + if (fd >= rlim_cur) + goto out; + + /* Do we need to expand the fd array or fd set? */ + error = expand_files(files, fd); + if (error < 0) + goto out; + + if (error) { + /* + * If we needed to expand the fs array we + * might have blocked - try again. + */ + error = -EMFILE; + goto repeat; + } + + FD_SET(fd, fdt->open_fds); + if (flags & O_CLOEXEC) + FD_SET(fd, fdt->close_on_exec); + else + FD_CLR(fd, fdt->close_on_exec); + files->next_fd = fd + 1; +#if 1 + /* Sanity check */ + if (fdt->fd[fd] != NULL) { + printk(KERN_WARNING "get_unused_fd: slot %d not NULL!\n", fd); + fdt->fd[fd] = NULL; + } +#endif + error = fd; + +out: + spin_unlock(&files->file_lock); + return error; +} + +/* + * copied from fd_install + */ +static void task_fd_install( + struct binder_proc *proc, unsigned int fd, struct file *file) +{ + struct files_struct *files = proc->files; + struct fdtable *fdt; + + if (files == NULL) + return; + + spin_lock(&files->file_lock); + fdt = files_fdtable(files); + BUG_ON(fdt->fd[fd] != NULL); + rcu_assign_pointer(fdt->fd[fd], file); + spin_unlock(&files->file_lock); +} + +/* + * copied from __put_unused_fd in open.c + */ +static void __put_unused_fd(struct files_struct *files, unsigned int fd) +{ + struct fdtable *fdt = files_fdtable(files); + __FD_CLR(fd, fdt->open_fds); + if (fd < files->next_fd) + files->next_fd = fd; +} + +/* + * copied from sys_close + */ +static long task_close_fd(struct binder_proc *proc, unsigned int fd) +{ + struct file *filp; + struct files_struct *files = proc->files; + struct fdtable *fdt; + int retval; + + if (files == NULL) + return -ESRCH; + + spin_lock(&files->file_lock); + fdt = files_fdtable(files); + if (fd >= fdt->max_fds) + goto out_unlock; + filp = fdt->fd[fd]; + if (!filp) + goto out_unlock; + rcu_assign_pointer(fdt->fd[fd], NULL); + FD_CLR(fd, fdt->close_on_exec); + __put_unused_fd(files, fd); + spin_unlock(&files->file_lock); + retval = filp_close(filp, files); + + /* can't restart close syscall because file table entry was cleared */ + if (unlikely(retval == -ERESTARTSYS || + retval == -ERESTARTNOINTR || + retval == -ERESTARTNOHAND || + retval == -ERESTART_RESTARTBLOCK)) + retval = -EINTR; + + return retval; + +out_unlock: + spin_unlock(&files->file_lock); + return -EBADF; +} + +static void binder_set_nice(long nice) +{ + long min_nice; + if (can_nice(current, nice)) { + set_user_nice(current, nice); + return; + } + min_nice = 20 - current->signal->rlim[RLIMIT_NICE].rlim_cur; + binder_debug(BINDER_DEBUG_PRIORITY_CAP, + "binder: %d: nice value %ld not allowed use " + "%ld instead\n", current->pid, nice, min_nice); + set_user_nice(current, min_nice); + if (min_nice < 20) + return; + binder_user_error("binder: %d RLIMIT_NICE not set\n", current->pid); +} + +static size_t binder_buffer_size(struct binder_proc *proc, + struct binder_buffer *buffer) +{ + if (list_is_last(&buffer->entry, &proc->buffers)) + return proc->buffer + proc->buffer_size - (void *)buffer->data; + else + return (size_t)list_entry(buffer->entry.next, + struct binder_buffer, entry) - (size_t)buffer->data; +} + +static void binder_insert_free_buffer(struct binder_proc *proc, + struct binder_buffer *new_buffer) +{ + struct rb_node **p = &proc->free_buffers.rb_node; + struct rb_node *parent = NULL; + struct binder_buffer *buffer; + size_t buffer_size; + size_t new_buffer_size; + + BUG_ON(!new_buffer->free); + + new_buffer_size = binder_buffer_size(proc, new_buffer); + + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: add free buffer, size %zd, " + "at %p\n", proc->pid, new_buffer_size, new_buffer); + + while (*p) { + parent = *p; + buffer = rb_entry(parent, struct binder_buffer, rb_node); + BUG_ON(!buffer->free); + + buffer_size = binder_buffer_size(proc, buffer); + + if (new_buffer_size < buffer_size) + p = &parent->rb_left; + else + p = &parent->rb_right; + } + rb_link_node(&new_buffer->rb_node, parent, p); + rb_insert_color(&new_buffer->rb_node, &proc->free_buffers); +} + +static void binder_insert_allocated_buffer(struct binder_proc *proc, + struct binder_buffer *new_buffer) +{ + struct rb_node **p = &proc->allocated_buffers.rb_node; + struct rb_node *parent = NULL; + struct binder_buffer *buffer; + + BUG_ON(new_buffer->free); + + while (*p) { + parent = *p; + buffer = rb_entry(parent, struct binder_buffer, rb_node); + BUG_ON(buffer->free); + + if (new_buffer < buffer) + p = &parent->rb_left; + else if (new_buffer > buffer) + p = &parent->rb_right; + else + BUG(); + } + rb_link_node(&new_buffer->rb_node, parent, p); + rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers); +} + +static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc, + void __user *user_ptr) +{ + struct rb_node *n = proc->allocated_buffers.rb_node; + struct binder_buffer *buffer; + struct binder_buffer *kern_ptr; + + kern_ptr = user_ptr - proc->user_buffer_offset + - offsetof(struct binder_buffer, data); + + while (n) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + BUG_ON(buffer->free); + + if (kern_ptr < buffer) + n = n->rb_left; + else if (kern_ptr > buffer) + n = n->rb_right; + else + return buffer; + } + return NULL; +} + +static int binder_update_page_range(struct binder_proc *proc, int allocate, + void *start, void *end, + struct vm_area_struct *vma) +{ + void *page_addr; + unsigned long user_page_addr; + struct vm_struct tmp_area; + struct page **page; + struct mm_struct *mm; + + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: %s pages %p-%p\n", proc->pid, + allocate ? "allocate" : "free", start, end); + + if (end <= start) + return 0; + + if (vma) + mm = NULL; + else + mm = get_task_mm(proc->tsk); + + if (mm) { + down_write(&mm->mmap_sem); + vma = proc->vma; + } + + if (allocate == 0) + goto free_range; + + if (vma == NULL) { + printk(KERN_ERR "binder: %d: binder_alloc_buf failed to " + "map pages in userspace, no vma\n", proc->pid); + goto err_no_vma; + } + + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + int ret; + struct page **page_array_ptr; + page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; + + BUG_ON(*page); + *page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (*page == NULL) { + printk(KERN_ERR "binder: %d: binder_alloc_buf failed " + "for page at %p\n", proc->pid, page_addr); + goto err_alloc_page_failed; + } + tmp_area.addr = page_addr; + tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */; + page_array_ptr = page; + ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr); + if (ret) { + printk(KERN_ERR "binder: %d: binder_alloc_buf failed " + "to map page at %p in kernel\n", + proc->pid, page_addr); + goto err_map_kernel_failed; + } + user_page_addr = + (uintptr_t)page_addr + proc->user_buffer_offset; + ret = vm_insert_page(vma, user_page_addr, page[0]); + if (ret) { + printk(KERN_ERR "binder: %d: binder_alloc_buf failed " + "to map page at %lx in userspace\n", + proc->pid, user_page_addr); + goto err_vm_insert_page_failed; + } + /* vm_insert_page does not seem to increment the refcount */ + } + if (mm) { + up_write(&mm->mmap_sem); + mmput(mm); + } + return 0; + +free_range: + for (page_addr = end - PAGE_SIZE; page_addr >= start; + page_addr -= PAGE_SIZE) { + page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; + if (vma) + zap_page_range(vma, (uintptr_t)page_addr + + proc->user_buffer_offset, PAGE_SIZE, NULL); +err_vm_insert_page_failed: + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); +err_map_kernel_failed: + __free_page(*page); + *page = NULL; +err_alloc_page_failed: + ; + } +err_no_vma: + if (mm) { + up_write(&mm->mmap_sem); + mmput(mm); + } + return -ENOMEM; +} + +static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, + size_t data_size, + size_t offsets_size, int is_async) +{ + struct rb_node *n = proc->free_buffers.rb_node; + struct binder_buffer *buffer; + size_t buffer_size; + struct rb_node *best_fit = NULL; + void *has_page_addr; + void *end_page_addr; + size_t size; + + if (proc->vma == NULL) { + printk(KERN_ERR "binder: %d: binder_alloc_buf, no vma\n", + proc->pid); + return NULL; + } + + size = ALIGN(data_size, sizeof(void *)) + + ALIGN(offsets_size, sizeof(void *)); + + if (size < data_size || size < offsets_size) { + binder_user_error("binder: %d: got transaction with invalid " + "size %zd-%zd\n", proc->pid, data_size, offsets_size); + return NULL; + } + + if (is_async && + proc->free_async_space < size + sizeof(struct binder_buffer)) { + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: binder_alloc_buf size %zd" + "failed, no async space left\n", proc->pid, size); + return NULL; + } + + while (n) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + BUG_ON(!buffer->free); + buffer_size = binder_buffer_size(proc, buffer); + + if (size < buffer_size) { + best_fit = n; + n = n->rb_left; + } else if (size > buffer_size) + n = n->rb_right; + else { + best_fit = n; + break; + } + } + if (best_fit == NULL) { + printk(KERN_ERR "binder: %d: binder_alloc_buf size %zd failed, " + "no address space\n", proc->pid, size); + return NULL; + } + if (n == NULL) { + buffer = rb_entry(best_fit, struct binder_buffer, rb_node); + buffer_size = binder_buffer_size(proc, buffer); + } + + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: binder_alloc_buf size %zd got buff" + "er %p size %zd\n", proc->pid, size, buffer, buffer_size); + + has_page_addr = + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); + if (n == NULL) { + if (size + sizeof(struct binder_buffer) + 4 >= buffer_size) + buffer_size = size; /* no room for other buffers */ + else + buffer_size = size + sizeof(struct binder_buffer); + } + end_page_addr = + (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size); + if (end_page_addr > has_page_addr) + end_page_addr = has_page_addr; + if (binder_update_page_range(proc, 1, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL)) + return NULL; + + rb_erase(best_fit, &proc->free_buffers); + buffer->free = 0; + binder_insert_allocated_buffer(proc, buffer); + if (buffer_size != size) { + struct binder_buffer *new_buffer = (void *)buffer->data + size; + list_add(&new_buffer->entry, &buffer->entry); + new_buffer->free = 1; + binder_insert_free_buffer(proc, new_buffer); + } + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: binder_alloc_buf size %zd got " + "%p\n", proc->pid, size, buffer); + buffer->data_size = data_size; + buffer->offsets_size = offsets_size; + buffer->async_transaction = is_async; + if (is_async) { + proc->free_async_space -= size + sizeof(struct binder_buffer); + binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, + "binder: %d: binder_alloc_buf size %zd " + "async free %zd\n", proc->pid, size, + proc->free_async_space); + } + + return buffer; +} + +static void *buffer_start_page(struct binder_buffer *buffer) +{ + return (void *)((uintptr_t)buffer & PAGE_MASK); +} + +static void *buffer_end_page(struct binder_buffer *buffer) +{ + return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK); +} + +static void binder_delete_free_buffer(struct binder_proc *proc, + struct binder_buffer *buffer) +{ + struct binder_buffer *prev, *next = NULL; + int free_page_end = 1; + int free_page_start = 1; + + BUG_ON(proc->buffers.next == &buffer->entry); + prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); + BUG_ON(!prev->free); + if (buffer_end_page(prev) == buffer_start_page(buffer)) { + free_page_start = 0; + if (buffer_end_page(prev) == buffer_end_page(buffer)) + free_page_end = 0; + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: merge free, buffer %p " + "share page with %p\n", proc->pid, buffer, prev); + } + + if (!list_is_last(&buffer->entry, &proc->buffers)) { + next = list_entry(buffer->entry.next, + struct binder_buffer, entry); + if (buffer_start_page(next) == buffer_end_page(buffer)) { + free_page_end = 0; + if (buffer_start_page(next) == + buffer_start_page(buffer)) + free_page_start = 0; + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: merge free, buffer" + " %p share page with %p\n", proc->pid, + buffer, prev); + } + } + list_del(&buffer->entry); + if (free_page_start || free_page_end) { + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: merge free, buffer %p do " + "not share page%s%s with with %p or %p\n", + proc->pid, buffer, free_page_start ? "" : " end", + free_page_end ? "" : " start", prev, next); + binder_update_page_range(proc, 0, free_page_start ? + buffer_start_page(buffer) : buffer_end_page(buffer), + (free_page_end ? buffer_end_page(buffer) : + buffer_start_page(buffer)) + PAGE_SIZE, NULL); + } +} + +static void binder_free_buf(struct binder_proc *proc, + struct binder_buffer *buffer) +{ + size_t size, buffer_size; + + buffer_size = binder_buffer_size(proc, buffer); + + size = ALIGN(buffer->data_size, sizeof(void *)) + + ALIGN(buffer->offsets_size, sizeof(void *)); + + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder: %d: binder_free_buf %p size %zd buffer" + "_size %zd\n", proc->pid, buffer, size, buffer_size); + + BUG_ON(buffer->free); + BUG_ON(size > buffer_size); + BUG_ON(buffer->transaction != NULL); + BUG_ON((void *)buffer < proc->buffer); + BUG_ON((void *)buffer > proc->buffer + proc->buffer_size); + + if (buffer->async_transaction) { + proc->free_async_space += size + sizeof(struct binder_buffer); + + binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, + "binder: %d: binder_free_buf size %zd " + "async free %zd\n", proc->pid, size, + proc->free_async_space); + } + + binder_update_page_range(proc, 0, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), + NULL); + rb_erase(&buffer->rb_node, &proc->allocated_buffers); + buffer->free = 1; + if (!list_is_last(&buffer->entry, &proc->buffers)) { + struct binder_buffer *next = list_entry(buffer->entry.next, + struct binder_buffer, entry); + if (next->free) { + rb_erase(&next->rb_node, &proc->free_buffers); + binder_delete_free_buffer(proc, next); + } + } + if (proc->buffers.next != &buffer->entry) { + struct binder_buffer *prev = list_entry(buffer->entry.prev, + struct binder_buffer, entry); + if (prev->free) { + binder_delete_free_buffer(proc, buffer); + rb_erase(&prev->rb_node, &proc->free_buffers); + buffer = prev; + } + } + binder_insert_free_buffer(proc, buffer); +} + +static struct binder_node *binder_get_node(struct binder_proc *proc, + void __user *ptr) +{ + struct rb_node *n = proc->nodes.rb_node; + struct binder_node *node; + + while (n) { + node = rb_entry(n, struct binder_node, rb_node); + + if (ptr < node->ptr) + n = n->rb_left; + else if (ptr > node->ptr) + n = n->rb_right; + else + return node; + } + return NULL; +} + +static struct binder_node *binder_new_node(struct binder_proc *proc, + void __user *ptr, + void __user *cookie) +{ + struct rb_node **p = &proc->nodes.rb_node; + struct rb_node *parent = NULL; + struct binder_node *node; + + while (*p) { + parent = *p; + node = rb_entry(parent, struct binder_node, rb_node); + + if (ptr < node->ptr) + p = &(*p)->rb_left; + else if (ptr > node->ptr) + p = &(*p)->rb_right; + else + return NULL; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (node == NULL) + return NULL; + binder_stats_created(BINDER_STAT_NODE); + rb_link_node(&node->rb_node, parent, p); + rb_insert_color(&node->rb_node, &proc->nodes); + node->debug_id = ++binder_last_id; + node->proc = proc; + node->ptr = ptr; + node->cookie = cookie; + node->work.type = BINDER_WORK_NODE; + INIT_LIST_HEAD(&node->work.entry); + INIT_LIST_HEAD(&node->async_todo); + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: %d:%d node %d u%p c%p created\n", + proc->pid, current->pid, node->debug_id, + node->ptr, node->cookie); + return node; +} + +static int binder_inc_node(struct binder_node *node, int strong, int internal, + struct list_head *target_list) +{ + if (strong) { + if (internal) { + if (target_list == NULL && + node->internal_strong_refs == 0 && + !(node == binder_context_mgr_node && + node->has_strong_ref)) { + printk(KERN_ERR "binder: invalid inc strong " + "node for %d\n", node->debug_id); + return -EINVAL; + } + node->internal_strong_refs++; + } else + node->local_strong_refs++; + if (!node->has_strong_ref && target_list) { + list_del_init(&node->work.entry); + list_add_tail(&node->work.entry, target_list); + } + } else { + if (!internal) + node->local_weak_refs++; + if (!node->has_weak_ref && list_empty(&node->work.entry)) { + if (target_list == NULL) { + printk(KERN_ERR "binder: invalid inc weak node " + "for %d\n", node->debug_id); + return -EINVAL; + } + list_add_tail(&node->work.entry, target_list); + } + } + return 0; +} + +static int binder_dec_node(struct binder_node *node, int strong, int internal) +{ + if (strong) { + if (internal) + node->internal_strong_refs--; + else + node->local_strong_refs--; + if (node->local_strong_refs || node->internal_strong_refs) + return 0; + } else { + if (!internal) + node->local_weak_refs--; + if (node->local_weak_refs || !hlist_empty(&node->refs)) + return 0; + } + if (node->proc && (node->has_strong_ref || node->has_weak_ref)) { + if (list_empty(&node->work.entry)) { + list_add_tail(&node->work.entry, &node->proc->todo); + wake_up_interruptible(&node->proc->wait); + } + } else { + if (hlist_empty(&node->refs) && !node->local_strong_refs && + !node->local_weak_refs) { + list_del_init(&node->work.entry); + if (node->proc) { + rb_erase(&node->rb_node, &node->proc->nodes); + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: refless node %d deleted\n", + node->debug_id); + } else { + hlist_del(&node->dead_node); + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: dead node %d deleted\n", + node->debug_id); + } + kfree(node); + binder_stats_deleted(BINDER_STAT_NODE); + } + } + + return 0; +} + + +static struct binder_ref *binder_get_ref(struct binder_proc *proc, + uint32_t desc) +{ + struct rb_node *n = proc->refs_by_desc.rb_node; + struct binder_ref *ref; + + while (n) { + ref = rb_entry(n, struct binder_ref, rb_node_desc); + + if (desc < ref->desc) + n = n->rb_left; + else if (desc > ref->desc) + n = n->rb_right; + else + return ref; + } + return NULL; +} + +static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, + struct binder_node *node) +{ + struct rb_node *n; + struct rb_node **p = &proc->refs_by_node.rb_node; + struct rb_node *parent = NULL; + struct binder_ref *ref, *new_ref; + + while (*p) { + parent = *p; + ref = rb_entry(parent, struct binder_ref, rb_node_node); + + if (node < ref->node) + p = &(*p)->rb_left; + else if (node > ref->node) + p = &(*p)->rb_right; + else + return ref; + } + new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (new_ref == NULL) + return NULL; + binder_stats_created(BINDER_STAT_REF); + new_ref->debug_id = ++binder_last_id; + new_ref->proc = proc; + new_ref->node = node; + rb_link_node(&new_ref->rb_node_node, parent, p); + rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); + + new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1; + for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { + ref = rb_entry(n, struct binder_ref, rb_node_desc); + if (ref->desc > new_ref->desc) + break; + new_ref->desc = ref->desc + 1; + } + + p = &proc->refs_by_desc.rb_node; + while (*p) { + parent = *p; + ref = rb_entry(parent, struct binder_ref, rb_node_desc); + + if (new_ref->desc < ref->desc) + p = &(*p)->rb_left; + else if (new_ref->desc > ref->desc) + p = &(*p)->rb_right; + else + BUG(); + } + rb_link_node(&new_ref->rb_node_desc, parent, p); + rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); + if (node) { + hlist_add_head(&new_ref->node_entry, &node->refs); + + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: %d new ref %d desc %d for " + "node %d\n", proc->pid, new_ref->debug_id, + new_ref->desc, node->debug_id); + } else { + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: %d new ref %d desc %d for " + "dead node\n", proc->pid, new_ref->debug_id, + new_ref->desc); + } + return new_ref; +} + +static void binder_delete_ref(struct binder_ref *ref) +{ + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: %d delete ref %d desc %d for " + "node %d\n", ref->proc->pid, ref->debug_id, + ref->desc, ref->node->debug_id); + + rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc); + rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node); + if (ref->strong) + binder_dec_node(ref->node, 1, 1); + hlist_del(&ref->node_entry); + binder_dec_node(ref->node, 0, 1); + if (ref->death) { + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "binder: %d delete ref %d desc %d " + "has death notification\n", ref->proc->pid, + ref->debug_id, ref->desc); + list_del(&ref->death->work.entry); + kfree(ref->death); + binder_stats_deleted(BINDER_STAT_DEATH); + } + kfree(ref); + binder_stats_deleted(BINDER_STAT_REF); +} + +static int binder_inc_ref(struct binder_ref *ref, int strong, + struct list_head *target_list) +{ + int ret; + if (strong) { + if (ref->strong == 0) { + ret = binder_inc_node(ref->node, 1, 1, target_list); + if (ret) + return ret; + } + ref->strong++; + } else { + if (ref->weak == 0) { + ret = binder_inc_node(ref->node, 0, 1, target_list); + if (ret) + return ret; + } + ref->weak++; + } + return 0; +} + + +static int binder_dec_ref(struct binder_ref *ref, int strong) +{ + if (strong) { + if (ref->strong == 0) { + binder_user_error("binder: %d invalid dec strong, " + "ref %d desc %d s %d w %d\n", + ref->proc->pid, ref->debug_id, + ref->desc, ref->strong, ref->weak); + return -EINVAL; + } + ref->strong--; + if (ref->strong == 0) { + int ret; + ret = binder_dec_node(ref->node, strong, 1); + if (ret) + return ret; + } + } else { + if (ref->weak == 0) { + binder_user_error("binder: %d invalid dec weak, " + "ref %d desc %d s %d w %d\n", + ref->proc->pid, ref->debug_id, + ref->desc, ref->strong, ref->weak); + return -EINVAL; + } + ref->weak--; + } + if (ref->strong == 0 && ref->weak == 0) + binder_delete_ref(ref); + return 0; +} + +static void binder_pop_transaction(struct binder_thread *target_thread, + struct binder_transaction *t) +{ + if (target_thread) { + BUG_ON(target_thread->transaction_stack != t); + BUG_ON(target_thread->transaction_stack->from != target_thread); + target_thread->transaction_stack = + target_thread->transaction_stack->from_parent; + t->from = NULL; + } + t->need_reply = 0; + if (t->buffer) + t->buffer->transaction = NULL; + kfree(t); + binder_stats_deleted(BINDER_STAT_TRANSACTION); +} + +static void binder_send_failed_reply(struct binder_transaction *t, + uint32_t error_code) +{ + struct binder_thread *target_thread; + BUG_ON(t->flags & TF_ONE_WAY); + while (1) { + target_thread = t->from; + if (target_thread) { + if (target_thread->return_error != BR_OK && + target_thread->return_error2 == BR_OK) { + target_thread->return_error2 = + target_thread->return_error; + target_thread->return_error = BR_OK; + } + if (target_thread->return_error == BR_OK) { + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "binder: send failed reply for " + "transaction %d to %d:%d\n", + t->debug_id, target_thread->proc->pid, + target_thread->pid); + + binder_pop_transaction(target_thread, t); + target_thread->return_error = error_code; + wake_up_interruptible(&target_thread->wait); + } else { + printk(KERN_ERR "binder: reply failed, target " + "thread, %d:%d, has error code %d " + "already\n", target_thread->proc->pid, + target_thread->pid, + target_thread->return_error); + } + return; + } else { + struct binder_transaction *next = t->from_parent; + + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "binder: send failed reply " + "for transaction %d, target dead\n", + t->debug_id); + + binder_pop_transaction(target_thread, t); + if (next == NULL) { + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "binder: reply failed," + " no target thread at root\n"); + return; + } + t = next; + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "binder: reply failed, no target " + "thread -- retry %d\n", t->debug_id); + } + } +} + +static void binder_transaction_buffer_release(struct binder_proc *proc, + struct binder_buffer *buffer, + size_t *failed_at) +{ + size_t *offp, *off_end; + int debug_id = buffer->debug_id; + + binder_debug(BINDER_DEBUG_TRANSACTION, + "binder: %d buffer release %d, size %zd-%zd, failed at %p\n", + proc->pid, buffer->debug_id, + buffer->data_size, buffer->offsets_size, failed_at); + + if (buffer->target_node) + binder_dec_node(buffer->target_node, 1, 0); + + offp = (size_t *)(buffer->data + ALIGN(buffer->data_size, sizeof(void *))); + if (failed_at) + off_end = failed_at; + else + off_end = (void *)offp + buffer->offsets_size; + for (; offp < off_end; offp++) { + struct flat_binder_object *fp; + if (*offp > buffer->data_size - sizeof(*fp) || + buffer->data_size < sizeof(*fp) || + !IS_ALIGNED(*offp, sizeof(void *))) { + printk(KERN_ERR "binder: transaction release %d bad" + "offset %zd, size %zd\n", debug_id, + *offp, buffer->data_size); + continue; + } + fp = (struct flat_binder_object *)(buffer->data + *offp); + switch (fp->type) { + case BINDER_TYPE_BINDER: + case BINDER_TYPE_WEAK_BINDER: { + struct binder_node *node = binder_get_node(proc, fp->binder); + if (node == NULL) { + printk(KERN_ERR "binder: transaction release %d" + " bad node %p\n", debug_id, fp->binder); + break; + } + binder_debug(BINDER_DEBUG_TRANSACTION, + " node %d u%p\n", + node->debug_id, node->ptr); + binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0); + } break; + case BINDER_TYPE_HANDLE: + case BINDER_TYPE_WEAK_HANDLE: { + struct binder_ref *ref = binder_get_ref(proc, fp->handle); + if (ref == NULL) { + printk(KERN_ERR "binder: transaction release %d" + " bad handle %ld\n", debug_id, + fp->handle); + break; + } + binder_debug(BINDER_DEBUG_TRANSACTION, + " ref %d desc %d (node %d)\n", + ref->debug_id, ref->desc, ref->node->debug_id); + binder_dec_ref(ref, fp->type == BINDER_TYPE_HANDLE); + } break; + + case BINDER_TYPE_FD: + binder_debug(BINDER_DEBUG_TRANSACTION, + " fd %ld\n", fp->handle); + if (failed_at) + task_close_fd(proc, fp->handle); + break; + + default: + printk(KERN_ERR "binder: transaction release %d bad " + "object type %lx\n", debug_id, fp->type); + break; + } + } +} + +static void binder_transaction(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_transaction_data *tr, int reply) +{ + struct binder_transaction *t; + struct binder_work *tcomplete; + size_t *offp, *off_end; + struct binder_proc *target_proc; + struct binder_thread *target_thread = NULL; + struct binder_node *target_node = NULL; + struct list_head *target_list; + wait_queue_head_t *target_wait; + struct binder_transaction *in_reply_to = NULL; + struct binder_transaction_log_entry *e; + uint32_t return_error; + + e = binder_transaction_log_add(&binder_transaction_log); + e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY); + e->from_proc = proc->pid; + e->from_thread = thread->pid; + e->target_handle = tr->target.handle; + e->data_size = tr->data_size; + e->offsets_size = tr->offsets_size; + + if (reply) { + in_reply_to = thread->transaction_stack; + if (in_reply_to == NULL) { + binder_user_error("binder: %d:%d got reply transaction " + "with no transaction stack\n", + proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + goto err_empty_call_stack; + } + binder_set_nice(in_reply_to->saved_priority); + if (in_reply_to->to_thread != thread) { + binder_user_error("binder: %d:%d got reply transaction " + "with bad transaction stack," + " transaction %d has target %d:%d\n", + proc->pid, thread->pid, in_reply_to->debug_id, + in_reply_to->to_proc ? + in_reply_to->to_proc->pid : 0, + in_reply_to->to_thread ? + in_reply_to->to_thread->pid : 0); + return_error = BR_FAILED_REPLY; + in_reply_to = NULL; + goto err_bad_call_stack; + } + thread->transaction_stack = in_reply_to->to_parent; + target_thread = in_reply_to->from; + if (target_thread == NULL) { + return_error = BR_DEAD_REPLY; + goto err_dead_binder; + } + if (target_thread->transaction_stack != in_reply_to) { + binder_user_error("binder: %d:%d got reply transaction " + "with bad target transaction stack %d, " + "expected %d\n", + proc->pid, thread->pid, + target_thread->transaction_stack ? + target_thread->transaction_stack->debug_id : 0, + in_reply_to->debug_id); + return_error = BR_FAILED_REPLY; + in_reply_to = NULL; + target_thread = NULL; + goto err_dead_binder; + } + target_proc = target_thread->proc; + } else { + if (tr->target.handle) { + struct binder_ref *ref; + ref = binder_get_ref(proc, tr->target.handle); + if (ref == NULL) { + binder_user_error("binder: %d:%d got " + "transaction to invalid handle\n", + proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + goto err_invalid_target_handle; + } + target_node = ref->node; + } else { + target_node = binder_context_mgr_node; + if (target_node == NULL) { + return_error = BR_DEAD_REPLY; + goto err_no_context_mgr_node; + } + } + e->to_node = target_node->debug_id; + target_proc = target_node->proc; + if (target_proc == NULL) { + return_error = BR_DEAD_REPLY; + goto err_dead_binder; + } + if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { + struct binder_transaction *tmp; + tmp = thread->transaction_stack; + if (tmp->to_thread != thread) { + binder_user_error("binder: %d:%d got new " + "transaction with bad transaction stack" + ", transaction %d has target %d:%d\n", + proc->pid, thread->pid, tmp->debug_id, + tmp->to_proc ? tmp->to_proc->pid : 0, + tmp->to_thread ? + tmp->to_thread->pid : 0); + return_error = BR_FAILED_REPLY; + goto err_bad_call_stack; + } + while (tmp) { + if (tmp->from && tmp->from->proc == target_proc) + target_thread = tmp->from; + tmp = tmp->from_parent; + } + } + } + if (target_thread) { + e->to_thread = target_thread->pid; + target_list = &target_thread->todo; + target_wait = &target_thread->wait; + } else { + target_list = &target_proc->todo; + target_wait = &target_proc->wait; + } + e->to_proc = target_proc->pid; + + /* TODO: reuse incoming transaction for reply */ + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (t == NULL) { + return_error = BR_FAILED_REPLY; + goto err_alloc_t_failed; + } + binder_stats_created(BINDER_STAT_TRANSACTION); + + tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); + if (tcomplete == NULL) { + return_error = BR_FAILED_REPLY; + goto err_alloc_tcomplete_failed; + } + binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); + + t->debug_id = ++binder_last_id; + e->debug_id = t->debug_id; + + if (reply) + binder_debug(BINDER_DEBUG_TRANSACTION, + "binder: %d:%d BC_REPLY %d -> %d:%d, " + "data %p-%p size %zd-%zd\n", + proc->pid, thread->pid, t->debug_id, + target_proc->pid, target_thread->pid, + tr->data.ptr.buffer, tr->data.ptr.offsets, + tr->data_size, tr->offsets_size); + else + binder_debug(BINDER_DEBUG_TRANSACTION, + "binder: %d:%d BC_TRANSACTION %d -> " + "%d - node %d, data %p-%p size %zd-%zd\n", + proc->pid, thread->pid, t->debug_id, + target_proc->pid, target_node->debug_id, + tr->data.ptr.buffer, tr->data.ptr.offsets, + tr->data_size, tr->offsets_size); + + if (!reply && !(tr->flags & TF_ONE_WAY)) + t->from = thread; + else + t->from = NULL; + t->sender_euid = proc->tsk->cred->euid; + t->to_proc = target_proc; + t->to_thread = target_thread; + t->code = tr->code; + t->flags = tr->flags; + t->priority = task_nice(current); + t->buffer = binder_alloc_buf(target_proc, tr->data_size, + tr->offsets_size, !reply && (t->flags & TF_ONE_WAY)); + if (t->buffer == NULL) { + return_error = BR_FAILED_REPLY; + goto err_binder_alloc_buf_failed; + } + t->buffer->allow_user_free = 0; + t->buffer->debug_id = t->debug_id; + t->buffer->transaction = t; + t->buffer->target_node = target_node; + if (target_node) + binder_inc_node(target_node, 1, 0, NULL); + + offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); + + if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) { + binder_user_error("binder: %d:%d got transaction with invalid " + "data ptr\n", proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + goto err_copy_data_failed; + } + if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) { + binder_user_error("binder: %d:%d got transaction with invalid " + "offsets ptr\n", proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + goto err_copy_data_failed; + } + if (!IS_ALIGNED(tr->offsets_size, sizeof(size_t))) { + binder_user_error("binder: %d:%d got transaction with " + "invalid offsets size, %zd\n", + proc->pid, thread->pid, tr->offsets_size); + return_error = BR_FAILED_REPLY; + goto err_bad_offset; + } + off_end = (void *)offp + tr->offsets_size; + for (; offp < off_end; offp++) { + struct flat_binder_object *fp; + if (*offp > t->buffer->data_size - sizeof(*fp) || + t->buffer->data_size < sizeof(*fp) || + !IS_ALIGNED(*offp, sizeof(void *))) { + binder_user_error("binder: %d:%d got transaction with " + "invalid offset, %zd\n", + proc->pid, thread->pid, *offp); + return_error = BR_FAILED_REPLY; + goto err_bad_offset; + } + fp = (struct flat_binder_object *)(t->buffer->data + *offp); + switch (fp->type) { + case BINDER_TYPE_BINDER: + case BINDER_TYPE_WEAK_BINDER: { + struct binder_ref *ref; + struct binder_node *node = binder_get_node(proc, fp->binder); + if (node == NULL) { + node = binder_new_node(proc, fp->binder, fp->cookie); + if (node == NULL) { + return_error = BR_FAILED_REPLY; + goto err_binder_new_node_failed; + } + node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); + } + if (fp->cookie != node->cookie) { + binder_user_error("binder: %d:%d sending u%p " + "node %d, cookie mismatch %p != %p\n", + proc->pid, thread->pid, + fp->binder, node->debug_id, + fp->cookie, node->cookie); + goto err_binder_get_ref_for_node_failed; + } + ref = binder_get_ref_for_node(target_proc, node); + if (ref == NULL) { + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_for_node_failed; + } + if (fp->type == BINDER_TYPE_BINDER) + fp->type = BINDER_TYPE_HANDLE; + else + fp->type = BINDER_TYPE_WEAK_HANDLE; + fp->handle = ref->desc; + binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, + &thread->todo); + + binder_debug(BINDER_DEBUG_TRANSACTION, + " node %d u%p -> ref %d desc %d\n", + node->debug_id, node->ptr, ref->debug_id, + ref->desc); + } break; + case BINDER_TYPE_HANDLE: + case BINDER_TYPE_WEAK_HANDLE: { + struct binder_ref *ref = binder_get_ref(proc, fp->handle); + if (ref == NULL) { + binder_user_error("binder: %d:%d got " + "transaction with invalid " + "handle, %ld\n", proc->pid, + thread->pid, fp->handle); + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_failed; + } + if (ref->node->proc == target_proc) { + if (fp->type == BINDER_TYPE_HANDLE) + fp->type = BINDER_TYPE_BINDER; + else + fp->type = BINDER_TYPE_WEAK_BINDER; + fp->binder = ref->node->ptr; + fp->cookie = ref->node->cookie; + binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL); + binder_debug(BINDER_DEBUG_TRANSACTION, + " ref %d desc %d -> node %d u%p\n", + ref->debug_id, ref->desc, ref->node->debug_id, + ref->node->ptr); + } else { + struct binder_ref *new_ref; + new_ref = binder_get_ref_for_node(target_proc, ref->node); + if (new_ref == NULL) { + return_error = BR_FAILED_REPLY; + goto err_binder_get_ref_for_node_failed; + } + fp->handle = new_ref->desc; + binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); + binder_debug(BINDER_DEBUG_TRANSACTION, + " ref %d desc %d -> ref %d desc %d (node %d)\n", + ref->debug_id, ref->desc, new_ref->debug_id, + new_ref->desc, ref->node->debug_id); + } + } break; + + case BINDER_TYPE_FD: { + int target_fd; + struct file *file; + + if (reply) { + if (!(in_reply_to->flags & TF_ACCEPT_FDS)) { + binder_user_error("binder: %d:%d got reply with fd, %ld, but target does not allow fds\n", + proc->pid, thread->pid, fp->handle); + return_error = BR_FAILED_REPLY; + goto err_fd_not_allowed; + } + } else if (!target_node->accept_fds) { + binder_user_error("binder: %d:%d got transaction with fd, %ld, but target does not allow fds\n", + proc->pid, thread->pid, fp->handle); + return_error = BR_FAILED_REPLY; + goto err_fd_not_allowed; + } + + file = fget(fp->handle); + if (file == NULL) { + binder_user_error("binder: %d:%d got transaction with invalid fd, %ld\n", + proc->pid, thread->pid, fp->handle); + return_error = BR_FAILED_REPLY; + goto err_fget_failed; + } + target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); + if (target_fd < 0) { + fput(file); + return_error = BR_FAILED_REPLY; + goto err_get_unused_fd_failed; + } + task_fd_install(target_proc, target_fd, file); + binder_debug(BINDER_DEBUG_TRANSACTION, + " fd %ld -> %d\n", fp->handle, target_fd); + /* TODO: fput? */ + fp->handle = target_fd; + } break; + + default: + binder_user_error("binder: %d:%d got transactio" + "n with invalid object type, %lx\n", + proc->pid, thread->pid, fp->type); + return_error = BR_FAILED_REPLY; + goto err_bad_object_type; + } + } + if (reply) { + BUG_ON(t->buffer->async_transaction != 0); + binder_pop_transaction(target_thread, in_reply_to); + } else if (!(t->flags & TF_ONE_WAY)) { + BUG_ON(t->buffer->async_transaction != 0); + t->need_reply = 1; + t->from_parent = thread->transaction_stack; + thread->transaction_stack = t; + } else { + BUG_ON(target_node == NULL); + BUG_ON(t->buffer->async_transaction != 1); + if (target_node->has_async_transaction) { + target_list = &target_node->async_todo; + target_wait = NULL; + } else + target_node->has_async_transaction = 1; + } + t->work.type = BINDER_WORK_TRANSACTION; + list_add_tail(&t->work.entry, target_list); + tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; + list_add_tail(&tcomplete->entry, &thread->todo); + if (target_wait) + wake_up_interruptible(target_wait); + return; + +err_get_unused_fd_failed: +err_fget_failed: +err_fd_not_allowed: +err_binder_get_ref_for_node_failed: +err_binder_get_ref_failed: +err_binder_new_node_failed: +err_bad_object_type: +err_bad_offset: +err_copy_data_failed: + binder_transaction_buffer_release(target_proc, t->buffer, offp); + t->buffer->transaction = NULL; + binder_free_buf(target_proc, t->buffer); +err_binder_alloc_buf_failed: + kfree(tcomplete); + binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); +err_alloc_tcomplete_failed: + kfree(t); + binder_stats_deleted(BINDER_STAT_TRANSACTION); +err_alloc_t_failed: +err_bad_call_stack: +err_empty_call_stack: +err_dead_binder: +err_invalid_target_handle: +err_no_context_mgr_node: + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "binder: %d:%d transaction failed %d, size %zd-%zd\n", + proc->pid, thread->pid, return_error, + tr->data_size, tr->offsets_size); + + { + struct binder_transaction_log_entry *fe; + fe = binder_transaction_log_add(&binder_transaction_log_failed); + *fe = *e; + } + + BUG_ON(thread->return_error != BR_OK); + if (in_reply_to) { + thread->return_error = BR_TRANSACTION_COMPLETE; + binder_send_failed_reply(in_reply_to, return_error); + } else + thread->return_error = return_error; +} + +int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, + void __user *buffer, int size, signed long *consumed) +{ + uint32_t cmd; + void __user *ptr = buffer + *consumed; + void __user *end = buffer + size; + + while (ptr < end && thread->return_error == BR_OK) { + if (get_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { + binder_stats.bc[_IOC_NR(cmd)]++; + proc->stats.bc[_IOC_NR(cmd)]++; + thread->stats.bc[_IOC_NR(cmd)]++; + } + switch (cmd) { + case BC_INCREFS: + case BC_ACQUIRE: + case BC_RELEASE: + case BC_DECREFS: { + uint32_t target; + struct binder_ref *ref; + const char *debug_string; + + if (get_user(target, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (target == 0 && binder_context_mgr_node && + (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { + ref = binder_get_ref_for_node(proc, + binder_context_mgr_node); + if (ref->desc != target) { + binder_user_error("binder: %d:" + "%d tried to acquire " + "reference to desc 0, " + "got %d instead\n", + proc->pid, thread->pid, + ref->desc); + } + } else + ref = binder_get_ref(proc, target); + if (ref == NULL) { + binder_user_error("binder: %d:%d refcou" + "nt change on invalid ref %d\n", + proc->pid, thread->pid, target); + break; + } + switch (cmd) { + case BC_INCREFS: + debug_string = "IncRefs"; + binder_inc_ref(ref, 0, NULL); + break; + case BC_ACQUIRE: + debug_string = "Acquire"; + binder_inc_ref(ref, 1, NULL); + break; + case BC_RELEASE: + debug_string = "Release"; + binder_dec_ref(ref, 1); + break; + case BC_DECREFS: + default: + debug_string = "DecRefs"; + binder_dec_ref(ref, 0); + break; + } + binder_debug(BINDER_DEBUG_USER_REFS, + "binder: %d:%d %s ref %d desc %d s %d w %d for node %d\n", + proc->pid, thread->pid, debug_string, ref->debug_id, + ref->desc, ref->strong, ref->weak, ref->node->debug_id); + break; + } + case BC_INCREFS_DONE: + case BC_ACQUIRE_DONE: { + void __user *node_ptr; + void *cookie; + struct binder_node *node; + + if (get_user(node_ptr, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + if (get_user(cookie, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + node = binder_get_node(proc, node_ptr); + if (node == NULL) { + binder_user_error("binder: %d:%d " + "%s u%p no match\n", + proc->pid, thread->pid, + cmd == BC_INCREFS_DONE ? + "BC_INCREFS_DONE" : + "BC_ACQUIRE_DONE", + node_ptr); + break; + } + if (cookie != node->cookie) { + binder_user_error("binder: %d:%d %s u%p node %d" + " cookie mismatch %p != %p\n", + proc->pid, thread->pid, + cmd == BC_INCREFS_DONE ? + "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", + node_ptr, node->debug_id, + cookie, node->cookie); + break; + } + if (cmd == BC_ACQUIRE_DONE) { + if (node->pending_strong_ref == 0) { + binder_user_error("binder: %d:%d " + "BC_ACQUIRE_DONE node %d has " + "no pending acquire request\n", + proc->pid, thread->pid, + node->debug_id); + break; + } + node->pending_strong_ref = 0; + } else { + if (node->pending_weak_ref == 0) { + binder_user_error("binder: %d:%d " + "BC_INCREFS_DONE node %d has " + "no pending increfs request\n", + proc->pid, thread->pid, + node->debug_id); + break; + } + node->pending_weak_ref = 0; + } + binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0); + binder_debug(BINDER_DEBUG_USER_REFS, + "binder: %d:%d %s node %d ls %d lw %d\n", + proc->pid, thread->pid, + cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", + node->debug_id, node->local_strong_refs, node->local_weak_refs); + break; + } + case BC_ATTEMPT_ACQUIRE: + printk(KERN_ERR "binder: BC_ATTEMPT_ACQUIRE not supported\n"); + return -EINVAL; + case BC_ACQUIRE_RESULT: + printk(KERN_ERR "binder: BC_ACQUIRE_RESULT not supported\n"); + return -EINVAL; + + case BC_FREE_BUFFER: { + void __user *data_ptr; + struct binder_buffer *buffer; + + if (get_user(data_ptr, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + + buffer = binder_buffer_lookup(proc, data_ptr); + if (buffer == NULL) { + binder_user_error("binder: %d:%d " + "BC_FREE_BUFFER u%p no match\n", + proc->pid, thread->pid, data_ptr); + break; + } + if (!buffer->allow_user_free) { + binder_user_error("binder: %d:%d " + "BC_FREE_BUFFER u%p matched " + "unreturned buffer\n", + proc->pid, thread->pid, data_ptr); + break; + } + binder_debug(BINDER_DEBUG_FREE_BUFFER, + "binder: %d:%d BC_FREE_BUFFER u%p found buffer %d for %s transaction\n", + proc->pid, thread->pid, data_ptr, buffer->debug_id, + buffer->transaction ? "active" : "finished"); + + if (buffer->transaction) { + buffer->transaction->buffer = NULL; + buffer->transaction = NULL; + } + if (buffer->async_transaction && buffer->target_node) { + BUG_ON(!buffer->target_node->has_async_transaction); + if (list_empty(&buffer->target_node->async_todo)) + buffer->target_node->has_async_transaction = 0; + else + list_move_tail(buffer->target_node->async_todo.next, &thread->todo); + } + binder_transaction_buffer_release(proc, buffer, NULL); + binder_free_buf(proc, buffer); + break; + } + + case BC_TRANSACTION: + case BC_REPLY: { + struct binder_transaction_data tr; + + if (copy_from_user(&tr, ptr, sizeof(tr))) + return -EFAULT; + ptr += sizeof(tr); + binder_transaction(proc, thread, &tr, cmd == BC_REPLY); + break; + } + + case BC_REGISTER_LOOPER: + binder_debug(BINDER_DEBUG_THREADS, + "binder: %d:%d BC_REGISTER_LOOPER\n", + proc->pid, thread->pid); + if (thread->looper & BINDER_LOOPER_STATE_ENTERED) { + thread->looper |= BINDER_LOOPER_STATE_INVALID; + binder_user_error("binder: %d:%d ERROR:" + " BC_REGISTER_LOOPER called " + "after BC_ENTER_LOOPER\n", + proc->pid, thread->pid); + } else if (proc->requested_threads == 0) { + thread->looper |= BINDER_LOOPER_STATE_INVALID; + binder_user_error("binder: %d:%d ERROR:" + " BC_REGISTER_LOOPER called " + "without request\n", + proc->pid, thread->pid); + } else { + proc->requested_threads--; + proc->requested_threads_started++; + } + thread->looper |= BINDER_LOOPER_STATE_REGISTERED; + break; + case BC_ENTER_LOOPER: + binder_debug(BINDER_DEBUG_THREADS, + "binder: %d:%d BC_ENTER_LOOPER\n", + proc->pid, thread->pid); + if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) { + thread->looper |= BINDER_LOOPER_STATE_INVALID; + binder_user_error("binder: %d:%d ERROR:" + " BC_ENTER_LOOPER called after " + "BC_REGISTER_LOOPER\n", + proc->pid, thread->pid); + } + thread->looper |= BINDER_LOOPER_STATE_ENTERED; + break; + case BC_EXIT_LOOPER: + binder_debug(BINDER_DEBUG_THREADS, + "binder: %d:%d BC_EXIT_LOOPER\n", + proc->pid, thread->pid); + thread->looper |= BINDER_LOOPER_STATE_EXITED; + break; + + case BC_REQUEST_DEATH_NOTIFICATION: + case BC_CLEAR_DEATH_NOTIFICATION: { + uint32_t target; + void __user *cookie; + struct binder_ref *ref; + struct binder_ref_death *death; + + if (get_user(target, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (get_user(cookie, (void __user * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + ref = binder_get_ref(proc, target); + if (ref == NULL) { + binder_user_error("binder: %d:%d %s " + "invalid ref %d\n", + proc->pid, thread->pid, + cmd == BC_REQUEST_DEATH_NOTIFICATION ? + "BC_REQUEST_DEATH_NOTIFICATION" : + "BC_CLEAR_DEATH_NOTIFICATION", + target); + break; + } + + binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, + "binder: %d:%d %s %p ref %d desc %d s %d w %d for node %d\n", + proc->pid, thread->pid, + cmd == BC_REQUEST_DEATH_NOTIFICATION ? + "BC_REQUEST_DEATH_NOTIFICATION" : + "BC_CLEAR_DEATH_NOTIFICATION", + cookie, ref->debug_id, ref->desc, + ref->strong, ref->weak, ref->node->debug_id); + + if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { + if (ref->death) { + binder_user_error("binder: %d:%" + "d BC_REQUEST_DEATH_NOTI" + "FICATION death notific" + "ation already set\n", + proc->pid, thread->pid); + break; + } + death = kzalloc(sizeof(*death), GFP_KERNEL); + if (death == NULL) { + thread->return_error = BR_ERROR; + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "binder: %d:%d " + "BC_REQUEST_DEATH_NOTIFICATION failed\n", + proc->pid, thread->pid); + break; + } + binder_stats_created(BINDER_STAT_DEATH); + INIT_LIST_HEAD(&death->work.entry); + death->cookie = cookie; + ref->death = death; + if (ref->node->proc == NULL) { + ref->death->work.type = BINDER_WORK_DEAD_BINDER; + if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { + list_add_tail(&ref->death->work.entry, &thread->todo); + } else { + list_add_tail(&ref->death->work.entry, &proc->todo); + wake_up_interruptible(&proc->wait); + } + } + } else { + if (ref->death == NULL) { + binder_user_error("binder: %d:%" + "d BC_CLEAR_DEATH_NOTIFI" + "CATION death notificat" + "ion not active\n", + proc->pid, thread->pid); + break; + } + death = ref->death; + if (death->cookie != cookie) { + binder_user_error("binder: %d:%" + "d BC_CLEAR_DEATH_NOTIFI" + "CATION death notificat" + "ion cookie mismatch " + "%p != %p\n", + proc->pid, thread->pid, + death->cookie, cookie); + break; + } + ref->death = NULL; + if (list_empty(&death->work.entry)) { + death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; + if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { + list_add_tail(&death->work.entry, &thread->todo); + } else { + list_add_tail(&death->work.entry, &proc->todo); + wake_up_interruptible(&proc->wait); + } + } else { + BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER); + death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR; + } + } + } break; + case BC_DEAD_BINDER_DONE: { + struct binder_work *w; + void __user *cookie; + struct binder_ref_death *death = NULL; + if (get_user(cookie, (void __user * __user *)ptr)) + return -EFAULT; + + ptr += sizeof(void *); + list_for_each_entry(w, &proc->delivered_death, entry) { + struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work); + if (tmp_death->cookie == cookie) { + death = tmp_death; + break; + } + } + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "binder: %d:%d BC_DEAD_BINDER_DONE %p found %p\n", + proc->pid, thread->pid, cookie, death); + if (death == NULL) { + binder_user_error("binder: %d:%d BC_DEAD" + "_BINDER_DONE %p not found\n", + proc->pid, thread->pid, cookie); + break; + } + + list_del_init(&death->work.entry); + if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) { + death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; + if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { + list_add_tail(&death->work.entry, &thread->todo); + } else { + list_add_tail(&death->work.entry, &proc->todo); + wake_up_interruptible(&proc->wait); + } + } + } break; + + default: + printk(KERN_ERR "binder: %d:%d unknown command %d\n", + proc->pid, thread->pid, cmd); + return -EINVAL; + } + *consumed = ptr - buffer; + } + return 0; +} + +void binder_stat_br(struct binder_proc *proc, struct binder_thread *thread, + uint32_t cmd) +{ + if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { + binder_stats.br[_IOC_NR(cmd)]++; + proc->stats.br[_IOC_NR(cmd)]++; + thread->stats.br[_IOC_NR(cmd)]++; + } +} + +static int binder_has_proc_work(struct binder_proc *proc, + struct binder_thread *thread) +{ + return !list_empty(&proc->todo) || + (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); +} + +static int binder_has_thread_work(struct binder_thread *thread) +{ + return !list_empty(&thread->todo) || thread->return_error != BR_OK || + (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); +} + +static int binder_thread_read(struct binder_proc *proc, + struct binder_thread *thread, + void __user *buffer, int size, + signed long *consumed, int non_block) +{ + void __user *ptr = buffer + *consumed; + void __user *end = buffer + size; + + int ret = 0; + int wait_for_proc_work; + + if (*consumed == 0) { + if (put_user(BR_NOOP, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + } + +retry: + wait_for_proc_work = thread->transaction_stack == NULL && + list_empty(&thread->todo); + + if (thread->return_error != BR_OK && ptr < end) { + if (thread->return_error2 != BR_OK) { + if (put_user(thread->return_error2, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (ptr == end) + goto done; + thread->return_error2 = BR_OK; + } + if (put_user(thread->return_error, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + thread->return_error = BR_OK; + goto done; + } + + + thread->looper |= BINDER_LOOPER_STATE_WAITING; + if (wait_for_proc_work) + proc->ready_threads++; + mutex_unlock(&binder_lock); + if (wait_for_proc_work) { + if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED))) { + binder_user_error("binder: %d:%d ERROR: Thread waiting " + "for process work before calling BC_REGISTER_" + "LOOPER or BC_ENTER_LOOPER (state %x)\n", + proc->pid, thread->pid, thread->looper); + wait_event_interruptible(binder_user_error_wait, + binder_stop_on_user_error < 2); + } + binder_set_nice(proc->default_priority); + if (non_block) { + if (!binder_has_proc_work(proc, thread)) + ret = -EAGAIN; + } else + ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread)); + } else { + if (non_block) { + if (!binder_has_thread_work(thread)) + ret = -EAGAIN; + } else + ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread)); + } + mutex_lock(&binder_lock); + if (wait_for_proc_work) + proc->ready_threads--; + thread->looper &= ~BINDER_LOOPER_STATE_WAITING; + + if (ret) + return ret; + + while (1) { + uint32_t cmd; + struct binder_transaction_data tr; + struct binder_work *w; + struct binder_transaction *t = NULL; + + if (!list_empty(&thread->todo)) + w = list_first_entry(&thread->todo, struct binder_work, entry); + else if (!list_empty(&proc->todo) && wait_for_proc_work) + w = list_first_entry(&proc->todo, struct binder_work, entry); + else { + if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */ + goto retry; + break; + } + + if (end - ptr < sizeof(tr) + 4) + break; + + switch (w->type) { + case BINDER_WORK_TRANSACTION: { + t = container_of(w, struct binder_transaction, work); + } break; + case BINDER_WORK_TRANSACTION_COMPLETE: { + cmd = BR_TRANSACTION_COMPLETE; + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + + binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE, + "binder: %d:%d BR_TRANSACTION_COMPLETE\n", + proc->pid, thread->pid); + + list_del(&w->entry); + kfree(w); + binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); + } break; + case BINDER_WORK_NODE: { + struct binder_node *node = container_of(w, struct binder_node, work); + uint32_t cmd = BR_NOOP; + const char *cmd_name; + int strong = node->internal_strong_refs || node->local_strong_refs; + int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong; + if (weak && !node->has_weak_ref) { + cmd = BR_INCREFS; + cmd_name = "BR_INCREFS"; + node->has_weak_ref = 1; + node->pending_weak_ref = 1; + node->local_weak_refs++; + } else if (strong && !node->has_strong_ref) { + cmd = BR_ACQUIRE; + cmd_name = "BR_ACQUIRE"; + node->has_strong_ref = 1; + node->pending_strong_ref = 1; + node->local_strong_refs++; + } else if (!strong && node->has_strong_ref) { + cmd = BR_RELEASE; + cmd_name = "BR_RELEASE"; + node->has_strong_ref = 0; + } else if (!weak && node->has_weak_ref) { + cmd = BR_DECREFS; + cmd_name = "BR_DECREFS"; + node->has_weak_ref = 0; + } + if (cmd != BR_NOOP) { + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (put_user(node->ptr, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + if (put_user(node->cookie, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + + binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_USER_REFS, + "binder: %d:%d %s %d u%p c%p\n", + proc->pid, thread->pid, cmd_name, node->debug_id, node->ptr, node->cookie); + } else { + list_del_init(&w->entry); + if (!weak && !strong) { + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: %d:%d node %d u%p c%p deleted\n", + proc->pid, thread->pid, node->debug_id, + node->ptr, node->cookie); + rb_erase(&node->rb_node, &proc->nodes); + kfree(node); + binder_stats_deleted(BINDER_STAT_NODE); + } else { + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "binder: %d:%d node %d u%p c%p state unchanged\n", + proc->pid, thread->pid, node->debug_id, node->ptr, + node->cookie); + } + } + } break; + case BINDER_WORK_DEAD_BINDER: + case BINDER_WORK_DEAD_BINDER_AND_CLEAR: + case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { + struct binder_ref_death *death; + uint32_t cmd; + + death = container_of(w, struct binder_ref_death, work); + if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) + cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; + else + cmd = BR_DEAD_BINDER; + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (put_user(death->cookie, (void * __user *)ptr)) + return -EFAULT; + ptr += sizeof(void *); + binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, + "binder: %d:%d %s %p\n", + proc->pid, thread->pid, + cmd == BR_DEAD_BINDER ? + "BR_DEAD_BINDER" : + "BR_CLEAR_DEATH_NOTIFICATION_DONE", + death->cookie); + + if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) { + list_del(&w->entry); + kfree(death); + binder_stats_deleted(BINDER_STAT_DEATH); + } else + list_move(&w->entry, &proc->delivered_death); + if (cmd == BR_DEAD_BINDER) + goto done; /* DEAD_BINDER notifications can cause transactions */ + } break; + } + + if (!t) + continue; + + BUG_ON(t->buffer == NULL); + if (t->buffer->target_node) { + struct binder_node *target_node = t->buffer->target_node; + tr.target.ptr = target_node->ptr; + tr.cookie = target_node->cookie; + t->saved_priority = task_nice(current); + if (t->priority < target_node->min_priority && + !(t->flags & TF_ONE_WAY)) + binder_set_nice(t->priority); + else if (!(t->flags & TF_ONE_WAY) || + t->saved_priority > target_node->min_priority) + binder_set_nice(target_node->min_priority); + cmd = BR_TRANSACTION; + } else { + tr.target.ptr = NULL; + tr.cookie = NULL; + cmd = BR_REPLY; + } + tr.code = t->code; + tr.flags = t->flags; + tr.sender_euid = t->sender_euid; + + if (t->from) { + struct task_struct *sender = t->from->proc->tsk; + tr.sender_pid = task_tgid_nr_ns(sender, + current->nsproxy->pid_ns); + } else { + tr.sender_pid = 0; + } + + tr.data_size = t->buffer->data_size; + tr.offsets_size = t->buffer->offsets_size; + tr.data.ptr.buffer = (void *)t->buffer->data + + proc->user_buffer_offset; + tr.data.ptr.offsets = tr.data.ptr.buffer + + ALIGN(t->buffer->data_size, + sizeof(void *)); + + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (copy_to_user(ptr, &tr, sizeof(tr))) + return -EFAULT; + ptr += sizeof(tr); + + binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_TRANSACTION, + "binder: %d:%d %s %d %d:%d, cmd %d" + "size %zd-%zd ptr %p-%p\n", + proc->pid, thread->pid, + (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" : + "BR_REPLY", + t->debug_id, t->from ? t->from->proc->pid : 0, + t->from ? t->from->pid : 0, cmd, + t->buffer->data_size, t->buffer->offsets_size, + tr.data.ptr.buffer, tr.data.ptr.offsets); + + list_del(&t->work.entry); + t->buffer->allow_user_free = 1; + if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { + t->to_parent = thread->transaction_stack; + t->to_thread = thread; + thread->transaction_stack = t; + } else { + t->buffer->transaction = NULL; + kfree(t); + binder_stats_deleted(BINDER_STAT_TRANSACTION); + } + break; + } + +done: + + *consumed = ptr - buffer; + if (proc->requested_threads + proc->ready_threads == 0 && + proc->requested_threads_started < proc->max_threads && + (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */ + /*spawn a new thread if we leave this out */) { + proc->requested_threads++; + binder_debug(BINDER_DEBUG_THREADS, + "binder: %d:%d BR_SPAWN_LOOPER\n", + proc->pid, thread->pid); + if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) + return -EFAULT; + } + return 0; +} + +static void binder_release_work(struct list_head *list) +{ + struct binder_work *w; + while (!list_empty(list)) { + w = list_first_entry(list, struct binder_work, entry); + list_del_init(&w->entry); + switch (w->type) { + case BINDER_WORK_TRANSACTION: { + struct binder_transaction *t; + + t = container_of(w, struct binder_transaction, work); + if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) + binder_send_failed_reply(t, BR_DEAD_REPLY); + } break; + case BINDER_WORK_TRANSACTION_COMPLETE: { + kfree(w); + binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); + } break; + default: + break; + } + } + +} + +static struct binder_thread *binder_get_thread(struct binder_proc *proc) +{ + struct binder_thread *thread = NULL; + struct rb_node *parent = NULL; + struct rb_node **p = &proc->threads.rb_node; + + while (*p) { + parent = *p; + thread = rb_entry(parent, struct binder_thread, rb_node); + + if (current->pid < thread->pid) + p = &(*p)->rb_left; + else if (current->pid > thread->pid) + p = &(*p)->rb_right; + else + break; + } + if (*p == NULL) { + thread = kzalloc(sizeof(*thread), GFP_KERNEL); + if (thread == NULL) + return NULL; + binder_stats_created(BINDER_STAT_THREAD); + thread->proc = proc; + thread->pid = current->pid; + init_waitqueue_head(&thread->wait); + INIT_LIST_HEAD(&thread->todo); + rb_link_node(&thread->rb_node, parent, p); + rb_insert_color(&thread->rb_node, &proc->threads); + thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; + thread->return_error = BR_OK; + thread->return_error2 = BR_OK; + } + return thread; +} + +static int binder_free_thread(struct binder_proc *proc, + struct binder_thread *thread) +{ + struct binder_transaction *t; + struct binder_transaction *send_reply = NULL; + int active_transactions = 0; + + rb_erase(&thread->rb_node, &proc->threads); + t = thread->transaction_stack; + if (t && t->to_thread == thread) + send_reply = t; + while (t) { + active_transactions++; + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "binder: release %d:%d transaction %d " + "%s, still active\n", proc->pid, thread->pid, + t->debug_id, + (t->to_thread == thread) ? "in" : "out"); + + if (t->to_thread == thread) { + t->to_proc = NULL; + t->to_thread = NULL; + if (t->buffer) { + t->buffer->transaction = NULL; + t->buffer = NULL; + } + t = t->to_parent; + } else if (t->from == thread) { + t->from = NULL; + t = t->from_parent; + } else + BUG(); + } + if (send_reply) + binder_send_failed_reply(send_reply, BR_DEAD_REPLY); + binder_release_work(&thread->todo); + kfree(thread); + binder_stats_deleted(BINDER_STAT_THREAD); + return active_transactions; +} + +static unsigned int binder_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct binder_proc *proc = filp->private_data; + struct binder_thread *thread = NULL; + int wait_for_proc_work; + + mutex_lock(&binder_lock); + thread = binder_get_thread(proc); + + wait_for_proc_work = thread->transaction_stack == NULL && + list_empty(&thread->todo) && thread->return_error == BR_OK; + mutex_unlock(&binder_lock); + + if (wait_for_proc_work) { + if (binder_has_proc_work(proc, thread)) + return POLLIN; + poll_wait(filp, &proc->wait, wait); + if (binder_has_proc_work(proc, thread)) + return POLLIN; + } else { + if (binder_has_thread_work(thread)) + return POLLIN; + poll_wait(filp, &thread->wait, wait); + if (binder_has_thread_work(thread)) + return POLLIN; + } + return 0; +} + +static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret; + struct binder_proc *proc = filp->private_data; + struct binder_thread *thread; + unsigned int size = _IOC_SIZE(cmd); + void __user *ubuf = (void __user *)arg; + + /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ + + ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); + if (ret) + return ret; + + mutex_lock(&binder_lock); + thread = binder_get_thread(proc); + if (thread == NULL) { + ret = -ENOMEM; + goto err; + } + + switch (cmd) { + case BINDER_WRITE_READ: { + struct binder_write_read bwr; + if (size != sizeof(struct binder_write_read)) { + ret = -EINVAL; + goto err; + } + if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { + ret = -EFAULT; + goto err; + } + binder_debug(BINDER_DEBUG_READ_WRITE, + "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n", + proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, + bwr.read_size, bwr.read_buffer); + + if (bwr.write_size > 0) { + ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed); + if (ret < 0) { + bwr.read_consumed = 0; + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + ret = -EFAULT; + goto err; + } + } + if (bwr.read_size > 0) { + ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); + if (!list_empty(&proc->todo)) + wake_up_interruptible(&proc->wait); + if (ret < 0) { + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) + ret = -EFAULT; + goto err; + } + } + binder_debug(BINDER_DEBUG_READ_WRITE, + "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n", + proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, + bwr.read_consumed, bwr.read_size); + if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { + ret = -EFAULT; + goto err; + } + break; + } + case BINDER_SET_MAX_THREADS: + if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { + ret = -EINVAL; + goto err; + } + break; + case BINDER_SET_CONTEXT_MGR: + if (binder_context_mgr_node != NULL) { + printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n"); + ret = -EBUSY; + goto err; + } + if (binder_context_mgr_uid != -1) { + if (binder_context_mgr_uid != current->cred->euid) { + printk(KERN_ERR "binder: BINDER_SET_" + "CONTEXT_MGR bad uid %d != %d\n", + current->cred->euid, + binder_context_mgr_uid); + ret = -EPERM; + goto err; + } + } else + binder_context_mgr_uid = current->cred->euid; + binder_context_mgr_node = binder_new_node(proc, NULL, NULL); + if (binder_context_mgr_node == NULL) { + ret = -ENOMEM; + goto err; + } + binder_context_mgr_node->local_weak_refs++; + binder_context_mgr_node->local_strong_refs++; + binder_context_mgr_node->has_strong_ref = 1; + binder_context_mgr_node->has_weak_ref = 1; + break; + case BINDER_THREAD_EXIT: + binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n", + proc->pid, thread->pid); + binder_free_thread(proc, thread); + thread = NULL; + break; + case BINDER_VERSION: + if (size != sizeof(struct binder_version)) { + ret = -EINVAL; + goto err; + } + if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) { + ret = -EINVAL; + goto err; + } + break; + default: + ret = -EINVAL; + goto err; + } + ret = 0; +err: + if (thread) + thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; + mutex_unlock(&binder_lock); + wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); + if (ret && ret != -ERESTARTSYS) + printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); + return ret; +} + +static void binder_vma_open(struct vm_area_struct *vma) +{ + struct binder_proc *proc = vma->vm_private_data; + binder_debug(BINDER_DEBUG_OPEN_CLOSE, + "binder: %d open vm area %lx-%lx (%ld K) vma %lx pagep %lx\n", + proc->pid, vma->vm_start, vma->vm_end, + (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, + (unsigned long)pgprot_val(vma->vm_page_prot)); + dump_stack(); +} + +static void binder_vma_close(struct vm_area_struct *vma) +{ + struct binder_proc *proc = vma->vm_private_data; + binder_debug(BINDER_DEBUG_OPEN_CLOSE, + "binder: %d close vm area %lx-%lx (%ld K) vma %lx pagep %lx\n", + proc->pid, vma->vm_start, vma->vm_end, + (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, + (unsigned long)pgprot_val(vma->vm_page_prot)); + proc->vma = NULL; + binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); +} + +static struct vm_operations_struct binder_vm_ops = { + .open = binder_vma_open, + .close = binder_vma_close, +}; + +static int binder_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + struct vm_struct *area; + struct binder_proc *proc = filp->private_data; + const char *failure_string; + struct binder_buffer *buffer; + + if ((vma->vm_end - vma->vm_start) > SZ_4M) + vma->vm_end = vma->vm_start + SZ_4M; + + binder_debug(BINDER_DEBUG_OPEN_CLOSE, + "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", + proc->pid, vma->vm_start, vma->vm_end, + (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, + (unsigned long)pgprot_val(vma->vm_page_prot)); + + if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) { + ret = -EPERM; + failure_string = "bad vm_flags"; + goto err_bad_arg; + } + vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; + + if (proc->buffer) { + ret = -EBUSY; + failure_string = "already mapped"; + goto err_already_mapped; + } + + area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); + if (area == NULL) { + ret = -ENOMEM; + failure_string = "get_vm_area"; + goto err_get_vm_area_failed; + } + proc->buffer = area->addr; + proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; + +#ifdef CONFIG_CPU_CACHE_VIPT + if (cache_is_vipt_aliasing()) { + while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) { + printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer); + vma->vm_start += PAGE_SIZE; + } + } +#endif + proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); + if (proc->pages == NULL) { + ret = -ENOMEM; + failure_string = "alloc page array"; + goto err_alloc_pages_failed; + } + proc->buffer_size = vma->vm_end - vma->vm_start; + + vma->vm_ops = &binder_vm_ops; + vma->vm_private_data = proc; + + if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) { + ret = -ENOMEM; + failure_string = "alloc small buf"; + goto err_alloc_small_buf_failed; + } + buffer = proc->buffer; + INIT_LIST_HEAD(&proc->buffers); + list_add(&buffer->entry, &proc->buffers); + buffer->free = 1; + binder_insert_free_buffer(proc, buffer); + proc->free_async_space = proc->buffer_size / 2; + barrier(); + proc->files = get_files_struct(current); + proc->vma = vma; + + /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", + proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/ + return 0; + +err_alloc_small_buf_failed: + kfree(proc->pages); + proc->pages = NULL; +err_alloc_pages_failed: + vfree(proc->buffer); + proc->buffer = NULL; +err_get_vm_area_failed: +err_already_mapped: +err_bad_arg: + printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", + proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); + return ret; +} + +static int binder_open(struct inode *nodp, struct file *filp) +{ + struct binder_proc *proc; + + binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n", + current->group_leader->pid, current->pid); + + proc = kzalloc(sizeof(*proc), GFP_KERNEL); + if (proc == NULL) + return -ENOMEM; + get_task_struct(current); + proc->tsk = current; + INIT_LIST_HEAD(&proc->todo); + init_waitqueue_head(&proc->wait); + proc->default_priority = task_nice(current); + mutex_lock(&binder_lock); + binder_stats_created(BINDER_STAT_PROC); + hlist_add_head(&proc->proc_node, &binder_procs); + proc->pid = current->group_leader->pid; + INIT_LIST_HEAD(&proc->delivered_death); + filp->private_data = proc; + mutex_unlock(&binder_lock); + + if (binder_proc_dir_entry_proc) { + char strbuf[11]; + snprintf(strbuf, sizeof(strbuf), "%u", proc->pid); + remove_proc_entry(strbuf, binder_proc_dir_entry_proc); + create_proc_read_entry(strbuf, S_IRUGO, + binder_proc_dir_entry_proc, + binder_read_proc_proc, proc); + } + + return 0; +} + +static int binder_flush(struct file *filp, fl_owner_t id) +{ + struct binder_proc *proc = filp->private_data; + + binder_defer_work(proc, BINDER_DEFERRED_FLUSH); + + return 0; +} + +static void binder_deferred_flush(struct binder_proc *proc) +{ + struct rb_node *n; + int wake_count = 0; + for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) { + struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node); + thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; + if (thread->looper & BINDER_LOOPER_STATE_WAITING) { + wake_up_interruptible(&thread->wait); + wake_count++; + } + } + wake_up_interruptible_all(&proc->wait); + + binder_debug(BINDER_DEBUG_OPEN_CLOSE, + "binder_flush: %d woke %d threads\n", proc->pid, + wake_count); +} + +static int binder_release(struct inode *nodp, struct file *filp) +{ + struct binder_proc *proc = filp->private_data; + if (binder_proc_dir_entry_proc) { + char strbuf[11]; + snprintf(strbuf, sizeof(strbuf), "%u", proc->pid); + remove_proc_entry(strbuf, binder_proc_dir_entry_proc); + } + + binder_defer_work(proc, BINDER_DEFERRED_RELEASE); + + return 0; +} + +static void binder_deferred_release(struct binder_proc *proc) +{ + struct hlist_node *pos; + struct binder_transaction *t; + struct rb_node *n; + int threads, nodes, incoming_refs, outgoing_refs, buffers, active_transactions, page_count; + + BUG_ON(proc->vma); + BUG_ON(proc->files); + + hlist_del(&proc->proc_node); + if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) { + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "binder_release: %d context_mgr_node gone\n", + proc->pid); + binder_context_mgr_node = NULL; + } + + threads = 0; + active_transactions = 0; + while ((n = rb_first(&proc->threads))) { + struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node); + threads++; + active_transactions += binder_free_thread(proc, thread); + } + nodes = 0; + incoming_refs = 0; + while ((n = rb_first(&proc->nodes))) { + struct binder_node *node = rb_entry(n, struct binder_node, rb_node); + + nodes++; + rb_erase(&node->rb_node, &proc->nodes); + list_del_init(&node->work.entry); + if (hlist_empty(&node->refs)) { + kfree(node); + binder_stats_deleted(BINDER_STAT_NODE); + } else { + struct binder_ref *ref; + int death = 0; + + node->proc = NULL; + node->local_strong_refs = 0; + node->local_weak_refs = 0; + hlist_add_head(&node->dead_node, &binder_dead_nodes); + + hlist_for_each_entry(ref, pos, &node->refs, node_entry) { + incoming_refs++; + if (ref->death) { + death++; + if (list_empty(&ref->death->work.entry)) { + ref->death->work.type = BINDER_WORK_DEAD_BINDER; + list_add_tail(&ref->death->work.entry, &ref->proc->todo); + wake_up_interruptible(&ref->proc->wait); + } else + BUG(); + } + } + binder_debug(BINDER_DEBUG_DEAD_BINDER, + "binder: node %d now dead, " + "refs %d, death %d\n", node->debug_id, + incoming_refs, death); + } + } + outgoing_refs = 0; + while ((n = rb_first(&proc->refs_by_desc))) { + struct binder_ref *ref = rb_entry(n, struct binder_ref, + rb_node_desc); + outgoing_refs++; + binder_delete_ref(ref); + } + binder_release_work(&proc->todo); + buffers = 0; + + while ((n = rb_first(&proc->allocated_buffers))) { + struct binder_buffer *buffer = rb_entry(n, struct binder_buffer, + rb_node); + t = buffer->transaction; + if (t) { + t->buffer = NULL; + buffer->transaction = NULL; + printk(KERN_ERR "binder: release proc %d, " + "transaction %d, not freed\n", + proc->pid, t->debug_id); + /*BUG();*/ + } + binder_free_buf(proc, buffer); + buffers++; + } + + binder_stats_deleted(BINDER_STAT_PROC); + + page_count = 0; + if (proc->pages) { + int i; + for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) { + if (proc->pages[i]) { + void *page_addr = proc->buffer + i * PAGE_SIZE; + binder_debug(BINDER_DEBUG_BUFFER_ALLOC, + "binder_release: %d: " + "page %d at %p not freed\n", + proc->pid, i, + page_addr); + unmap_kernel_range((unsigned long)page_addr, + PAGE_SIZE); + __free_page(proc->pages[i]); + page_count++; + } + } + kfree(proc->pages); + vfree(proc->buffer); + } + + put_task_struct(proc->tsk); + + binder_debug(BINDER_DEBUG_OPEN_CLOSE, + "binder_release: %d threads %d, nodes %d (ref %d), " + "refs %d, active transactions %d, buffers %d, " + "pages %d\n", + proc->pid, threads, nodes, incoming_refs, outgoing_refs, + active_transactions, buffers, page_count); + + kfree(proc); +} + +static void binder_deferred_func(struct work_struct *work) +{ + struct binder_proc *proc; + struct files_struct *files; + + int defer; + do { + mutex_lock(&binder_lock); + mutex_lock(&binder_deferred_lock); + if (!hlist_empty(&binder_deferred_list)) { + proc = hlist_entry(binder_deferred_list.first, + struct binder_proc, deferred_work_node); + hlist_del_init(&proc->deferred_work_node); + defer = proc->deferred_work; + proc->deferred_work = 0; + } else { + proc = NULL; + defer = 0; + } + mutex_unlock(&binder_deferred_lock); + + files = NULL; + if (defer & BINDER_DEFERRED_PUT_FILES) { + files = proc->files; + if (files) + proc->files = NULL; + } + + if (defer & BINDER_DEFERRED_FLUSH) + binder_deferred_flush(proc); + + if (defer & BINDER_DEFERRED_RELEASE) + binder_deferred_release(proc); /* frees proc */ + + mutex_unlock(&binder_lock); + if (files) + put_files_struct(files); + } while (proc); +} +static DECLARE_WORK(binder_deferred_work, binder_deferred_func); + +static void +binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) +{ + mutex_lock(&binder_deferred_lock); + proc->deferred_work |= defer; + if (hlist_unhashed(&proc->deferred_work_node)) { + hlist_add_head(&proc->deferred_work_node, + &binder_deferred_list); + queue_work(binder_deferred_workqueue, &binder_deferred_work); + } + mutex_unlock(&binder_deferred_lock); +} + +static char *print_binder_transaction(char *buf, char *end, const char *prefix, + struct binder_transaction *t) +{ + buf += snprintf(buf, end - buf, + "%s %d: %p from %d:%d to %d:%d code %x " + "flags %x pri %ld r%d", + prefix, t->debug_id, t, + t->from ? t->from->proc->pid : 0, + t->from ? t->from->pid : 0, + t->to_proc ? t->to_proc->pid : 0, + t->to_thread ? t->to_thread->pid : 0, + t->code, t->flags, t->priority, t->need_reply); + if (buf >= end) + return buf; + if (t->buffer == NULL) { + buf += snprintf(buf, end - buf, " buffer free\n"); + return buf; + } + if (t->buffer->target_node) { + buf += snprintf(buf, end - buf, " node %d", + t->buffer->target_node->debug_id); + if (buf >= end) + return buf; + } + buf += snprintf(buf, end - buf, " size %zd:%zd data %p\n", + t->buffer->data_size, t->buffer->offsets_size, + t->buffer->data); + return buf; +} + +static char *print_binder_buffer(char *buf, char *end, const char *prefix, + struct binder_buffer *buffer) +{ + buf += snprintf(buf, end - buf, "%s %d: %p size %zd:%zd %s\n", + prefix, buffer->debug_id, buffer->data, + buffer->data_size, buffer->offsets_size, + buffer->transaction ? "active" : "delivered"); + return buf; +} + +static char *print_binder_work(char *buf, char *end, const char *prefix, + const char *transaction_prefix, + struct binder_work *w) +{ + struct binder_node *node; + struct binder_transaction *t; + + switch (w->type) { + case BINDER_WORK_TRANSACTION: + t = container_of(w, struct binder_transaction, work); + buf = print_binder_transaction(buf, end, transaction_prefix, t); + break; + case BINDER_WORK_TRANSACTION_COMPLETE: + buf += snprintf(buf, end - buf, + "%stransaction complete\n", prefix); + break; + case BINDER_WORK_NODE: + node = container_of(w, struct binder_node, work); + buf += snprintf(buf, end - buf, "%snode work %d: u%p c%p\n", + prefix, node->debug_id, node->ptr, + node->cookie); + break; + case BINDER_WORK_DEAD_BINDER: + buf += snprintf(buf, end - buf, "%shas dead binder\n", prefix); + break; + case BINDER_WORK_DEAD_BINDER_AND_CLEAR: + buf += snprintf(buf, end - buf, + "%shas cleared dead binder\n", prefix); + break; + case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: + buf += snprintf(buf, end - buf, + "%shas cleared death notification\n", prefix); + break; + default: + buf += snprintf(buf, end - buf, "%sunknown work: type %d\n", + prefix, w->type); + break; + } + return buf; +} + +static char *print_binder_thread(char *buf, char *end, + struct binder_thread *thread, + int print_always) +{ + struct binder_transaction *t; + struct binder_work *w; + char *start_buf = buf; + char *header_buf; + + buf += snprintf(buf, end - buf, " thread %d: l %02x\n", + thread->pid, thread->looper); + header_buf = buf; + t = thread->transaction_stack; + while (t) { + if (buf >= end) + break; + if (t->from == thread) { + buf = print_binder_transaction(buf, end, + " outgoing transaction", t); + t = t->from_parent; + } else if (t->to_thread == thread) { + buf = print_binder_transaction(buf, end, + " incoming transaction", t); + t = t->to_parent; + } else { + buf = print_binder_transaction(buf, end, + " bad transaction", t); + t = NULL; + } + } + list_for_each_entry(w, &thread->todo, entry) { + if (buf >= end) + break; + buf = print_binder_work(buf, end, " ", + " pending transaction", w); + } + if (!print_always && buf == header_buf) + buf = start_buf; + return buf; +} + +static char *print_binder_node(char *buf, char *end, struct binder_node *node) +{ + struct binder_ref *ref; + struct hlist_node *pos; + struct binder_work *w; + int count; + + count = 0; + hlist_for_each_entry(ref, pos, &node->refs, node_entry) + count++; + + buf += snprintf(buf, end - buf, + " node %d: u%p c%p hs %d hw %d ls %d lw %d " + "is %d iw %d", + node->debug_id, node->ptr, node->cookie, + node->has_strong_ref, node->has_weak_ref, + node->local_strong_refs, node->local_weak_refs, + node->internal_strong_refs, count); + if (buf >= end) + return buf; + if (count) { + buf += snprintf(buf, end - buf, " proc"); + if (buf >= end) + return buf; + hlist_for_each_entry(ref, pos, &node->refs, node_entry) { + buf += snprintf(buf, end - buf, " %d", ref->proc->pid); + if (buf >= end) + return buf; + } + } + buf += snprintf(buf, end - buf, "\n"); + list_for_each_entry(w, &node->async_todo, entry) { + if (buf >= end) + break; + buf = print_binder_work(buf, end, " ", + " pending async transaction", w); + } + return buf; +} + +static char *print_binder_ref(char *buf, char *end, struct binder_ref *ref) +{ + buf += snprintf(buf, end - buf, + " ref %d: desc %d %snode %d s %d w %d d %p\n", + ref->debug_id, ref->desc, + ref->node->proc ? "" : "dead ", ref->node->debug_id, + ref->strong, ref->weak, ref->death); + return buf; +} + +static char *print_binder_proc(char *buf, char *end, + struct binder_proc *proc, int print_all) +{ + struct binder_work *w; + struct rb_node *n; + char *start_buf = buf; + char *header_buf; + + buf += snprintf(buf, end - buf, "proc %d\n", proc->pid); + header_buf = buf; + + for (n = rb_first(&proc->threads); + n != NULL && buf < end; + n = rb_next(n)) + buf = print_binder_thread(buf, end, + rb_entry(n, struct binder_thread, + rb_node), print_all); + for (n = rb_first(&proc->nodes); + n != NULL && buf < end; + n = rb_next(n)) { + struct binder_node *node = rb_entry(n, struct binder_node, + rb_node); + if (print_all || node->has_async_transaction) + buf = print_binder_node(buf, end, node); + } + if (print_all) { + for (n = rb_first(&proc->refs_by_desc); + n != NULL && buf < end; + n = rb_next(n)) + buf = print_binder_ref(buf, end, + rb_entry(n, struct binder_ref, + rb_node_desc)); + } + for (n = rb_first(&proc->allocated_buffers); + n != NULL && buf < end; + n = rb_next(n)) + buf = print_binder_buffer(buf, end, " buffer", + rb_entry(n, struct binder_buffer, + rb_node)); + list_for_each_entry(w, &proc->todo, entry) { + if (buf >= end) + break; + buf = print_binder_work(buf, end, " ", + " pending transaction", w); + } + list_for_each_entry(w, &proc->delivered_death, entry) { + if (buf >= end) + break; + buf += snprintf(buf, end - buf, + " has delivered dead binder\n"); + break; + } + if (!print_all && buf == header_buf) + buf = start_buf; + return buf; +} + +static const char *binder_return_strings[] = { + "BR_ERROR", + "BR_OK", + "BR_TRANSACTION", + "BR_REPLY", + "BR_ACQUIRE_RESULT", + "BR_DEAD_REPLY", + "BR_TRANSACTION_COMPLETE", + "BR_INCREFS", + "BR_ACQUIRE", + "BR_RELEASE", + "BR_DECREFS", + "BR_ATTEMPT_ACQUIRE", + "BR_NOOP", + "BR_SPAWN_LOOPER", + "BR_FINISHED", + "BR_DEAD_BINDER", + "BR_CLEAR_DEATH_NOTIFICATION_DONE", + "BR_FAILED_REPLY" +}; + +static const char *binder_command_strings[] = { + "BC_TRANSACTION", + "BC_REPLY", + "BC_ACQUIRE_RESULT", + "BC_FREE_BUFFER", + "BC_INCREFS", + "BC_ACQUIRE", + "BC_RELEASE", + "BC_DECREFS", + "BC_INCREFS_DONE", + "BC_ACQUIRE_DONE", + "BC_ATTEMPT_ACQUIRE", + "BC_REGISTER_LOOPER", + "BC_ENTER_LOOPER", + "BC_EXIT_LOOPER", + "BC_REQUEST_DEATH_NOTIFICATION", + "BC_CLEAR_DEATH_NOTIFICATION", + "BC_DEAD_BINDER_DONE" +}; + +static const char *binder_objstat_strings[] = { + "proc", + "thread", + "node", + "ref", + "death", + "transaction", + "transaction_complete" +}; + +static char *print_binder_stats(char *buf, char *end, const char *prefix, + struct binder_stats *stats) +{ + int i; + + BUILD_BUG_ON(ARRAY_SIZE(stats->bc) != + ARRAY_SIZE(binder_command_strings)); + for (i = 0; i < ARRAY_SIZE(stats->bc); i++) { + if (stats->bc[i]) + buf += snprintf(buf, end - buf, "%s%s: %d\n", prefix, + binder_command_strings[i], + stats->bc[i]); + if (buf >= end) + return buf; + } + + BUILD_BUG_ON(ARRAY_SIZE(stats->br) != + ARRAY_SIZE(binder_return_strings)); + for (i = 0; i < ARRAY_SIZE(stats->br); i++) { + if (stats->br[i]) + buf += snprintf(buf, end - buf, "%s%s: %d\n", prefix, + binder_return_strings[i], stats->br[i]); + if (buf >= end) + return buf; + } + + BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != + ARRAY_SIZE(binder_objstat_strings)); + BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != + ARRAY_SIZE(stats->obj_deleted)); + for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) { + if (stats->obj_created[i] || stats->obj_deleted[i]) + buf += snprintf(buf, end - buf, + "%s%s: active %d total %d\n", prefix, + binder_objstat_strings[i], + stats->obj_created[i] - + stats->obj_deleted[i], + stats->obj_created[i]); + if (buf >= end) + return buf; + } + return buf; +} + +static char *print_binder_proc_stats(char *buf, char *end, + struct binder_proc *proc) +{ + struct binder_work *w; + struct rb_node *n; + int count, strong, weak; + + buf += snprintf(buf, end - buf, "proc %d\n", proc->pid); + if (buf >= end) + return buf; + count = 0; + for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) + count++; + buf += snprintf(buf, end - buf, " threads: %d\n", count); + if (buf >= end) + return buf; + buf += snprintf(buf, end - buf, " requested threads: %d+%d/%d\n" + " ready threads %d\n" + " free async space %zd\n", proc->requested_threads, + proc->requested_threads_started, proc->max_threads, + proc->ready_threads, proc->free_async_space); + if (buf >= end) + return buf; + count = 0; + for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) + count++; + buf += snprintf(buf, end - buf, " nodes: %d\n", count); + if (buf >= end) + return buf; + count = 0; + strong = 0; + weak = 0; + for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { + struct binder_ref *ref = rb_entry(n, struct binder_ref, + rb_node_desc); + count++; + strong += ref->strong; + weak += ref->weak; + } + buf += snprintf(buf, end - buf, " refs: %d s %d w %d\n", + count, strong, weak); + if (buf >= end) + return buf; + + count = 0; + for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n)) + count++; + buf += snprintf(buf, end - buf, " buffers: %d\n", count); + if (buf >= end) + return buf; + + count = 0; + list_for_each_entry(w, &proc->todo, entry) { + switch (w->type) { + case BINDER_WORK_TRANSACTION: + count++; + break; + default: + break; + } + } + buf += snprintf(buf, end - buf, " pending transactions: %d\n", count); + if (buf >= end) + return buf; + + buf = print_binder_stats(buf, end, " ", &proc->stats); + + return buf; +} + + +static int binder_read_proc_state(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct binder_proc *proc; + struct hlist_node *pos; + struct binder_node *node; + int len = 0; + char *buf = page; + char *end = page + PAGE_SIZE; + int do_lock = !binder_debug_no_lock; + + if (off) + return 0; + + if (do_lock) + mutex_lock(&binder_lock); + + buf += snprintf(buf, end - buf, "binder state:\n"); + + if (!hlist_empty(&binder_dead_nodes)) + buf += snprintf(buf, end - buf, "dead nodes:\n"); + hlist_for_each_entry(node, pos, &binder_dead_nodes, dead_node) { + if (buf >= end) + break; + buf = print_binder_node(buf, end, node); + } + + hlist_for_each_entry(proc, pos, &binder_procs, proc_node) { + if (buf >= end) + break; + buf = print_binder_proc(buf, end, proc, 1); + } + if (do_lock) + mutex_unlock(&binder_lock); + if (buf > page + PAGE_SIZE) + buf = page + PAGE_SIZE; + + *start = page + off; + + len = buf - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static int binder_read_proc_stats(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct binder_proc *proc; + struct hlist_node *pos; + int len = 0; + char *p = page; + int do_lock = !binder_debug_no_lock; + + if (off) + return 0; + + if (do_lock) + mutex_lock(&binder_lock); + + p += snprintf(p, PAGE_SIZE, "binder stats:\n"); + + p = print_binder_stats(p, page + PAGE_SIZE, "", &binder_stats); + + hlist_for_each_entry(proc, pos, &binder_procs, proc_node) { + if (p >= page + PAGE_SIZE) + break; + p = print_binder_proc_stats(p, page + PAGE_SIZE, proc); + } + if (do_lock) + mutex_unlock(&binder_lock); + if (p > page + PAGE_SIZE) + p = page + PAGE_SIZE; + + *start = page + off; + + len = p - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static int binder_read_proc_transactions(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct binder_proc *proc; + struct hlist_node *pos; + int len = 0; + char *buf = page; + char *end = page + PAGE_SIZE; + int do_lock = !binder_debug_no_lock; + + if (off) + return 0; + + if (do_lock) + mutex_lock(&binder_lock); + + buf += snprintf(buf, end - buf, "binder transactions:\n"); + hlist_for_each_entry(proc, pos, &binder_procs, proc_node) { + if (buf >= end) + break; + buf = print_binder_proc(buf, end, proc, 0); + } + if (do_lock) + mutex_unlock(&binder_lock); + if (buf > page + PAGE_SIZE) + buf = page + PAGE_SIZE; + + *start = page + off; + + len = buf - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static int binder_read_proc_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct binder_proc *proc = data; + int len = 0; + char *p = page; + int do_lock = !binder_debug_no_lock; + + if (off) + return 0; + + if (do_lock) + mutex_lock(&binder_lock); + p += snprintf(p, PAGE_SIZE, "binder proc state:\n"); + p = print_binder_proc(p, page + PAGE_SIZE, proc, 1); + if (do_lock) + mutex_unlock(&binder_lock); + + if (p > page + PAGE_SIZE) + p = page + PAGE_SIZE; + *start = page + off; + + len = p - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static char *print_binder_transaction_log_entry(char *buf, char *end, + struct binder_transaction_log_entry *e) +{ + buf += snprintf(buf, end - buf, + "%d: %s from %d:%d to %d:%d node %d handle %d " + "size %d:%d\n", + e->debug_id, (e->call_type == 2) ? "reply" : + ((e->call_type == 1) ? "async" : "call "), e->from_proc, + e->from_thread, e->to_proc, e->to_thread, e->to_node, + e->target_handle, e->data_size, e->offsets_size); + return buf; +} + +static int binder_read_proc_transaction_log( + char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct binder_transaction_log *log = data; + int len = 0; + int i; + char *buf = page; + char *end = page + PAGE_SIZE; + + if (off) + return 0; + + if (log->full) { + for (i = log->next; i < ARRAY_SIZE(log->entry); i++) { + if (buf >= end) + break; + buf = print_binder_transaction_log_entry(buf, end, + &log->entry[i]); + } + } + for (i = 0; i < log->next; i++) { + if (buf >= end) + break; + buf = print_binder_transaction_log_entry(buf, end, + &log->entry[i]); + } + + *start = page + off; + + len = buf - page; + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static const struct file_operations binder_fops = { + .owner = THIS_MODULE, + .poll = binder_poll, + .unlocked_ioctl = binder_ioctl, + .mmap = binder_mmap, + .open = binder_open, + .flush = binder_flush, + .release = binder_release, +}; + +static struct miscdevice binder_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "binder", + .fops = &binder_fops +}; + +static int __init binder_init(void) +{ + int ret; + + binder_deferred_workqueue = create_singlethread_workqueue("binder"); + if (!binder_deferred_workqueue) + return -ENOMEM; + + binder_proc_dir_entry_root = proc_mkdir("binder", NULL); + if (binder_proc_dir_entry_root) + binder_proc_dir_entry_proc = proc_mkdir("proc", + binder_proc_dir_entry_root); + ret = misc_register(&binder_miscdev); + if (binder_proc_dir_entry_root) { + create_proc_read_entry("state", + S_IRUGO, + binder_proc_dir_entry_root, + binder_read_proc_state, + NULL); + create_proc_read_entry("stats", + S_IRUGO, + binder_proc_dir_entry_root, + binder_read_proc_stats, + NULL); + create_proc_read_entry("transactions", + S_IRUGO, + binder_proc_dir_entry_root, + binder_read_proc_transactions, + NULL); + create_proc_read_entry("transaction_log", + S_IRUGO, + binder_proc_dir_entry_root, + binder_read_proc_transaction_log, + &binder_transaction_log); + create_proc_read_entry("failed_transaction_log", + S_IRUGO, + binder_proc_dir_entry_root, + binder_read_proc_transaction_log, + &binder_transaction_log_failed); + } + return ret; +} + +device_initcall(binder_init); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/android/binder.h b/drivers/staging/android/binder.h new file mode 100644 index 00000000000000..863ae1ad5d5580 --- /dev/null +++ b/drivers/staging/android/binder.h @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2008 Google, Inc. + * + * Based on, but no longer compatible with, the original + * OpenBinder.org binder driver interface, which is: + * + * Copyright (c) 2005 Palmsource, Inc. + * + * 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. + * + */ + +#ifndef _LINUX_BINDER_H +#define _LINUX_BINDER_H + +#include + +#define B_PACK_CHARS(c1, c2, c3, c4) \ + ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) +#define B_TYPE_LARGE 0x85 + +enum { + BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE), + BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE), + BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE), + BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE), + BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE), +}; + +enum { + FLAT_BINDER_FLAG_PRIORITY_MASK = 0xff, + FLAT_BINDER_FLAG_ACCEPTS_FDS = 0x100, +}; + +/* + * This is the flattened representation of a Binder object for transfer + * between processes. The 'offsets' supplied as part of a binder transaction + * contains offsets into the data where these structures occur. The Binder + * driver takes care of re-writing the structure type and data as it moves + * between processes. + */ +struct flat_binder_object { + /* 8 bytes for large_flat_header. */ + unsigned long type; + unsigned long flags; + + /* 8 bytes of data. */ + union { + void *binder; /* local object */ + signed long handle; /* remote object */ + }; + + /* extra data associated with local object */ + void *cookie; +}; + +/* + * On 64-bit platforms where user code may run in 32-bits the driver must + * translate the buffer (and local binder) addresses apropriately. + */ + +struct binder_write_read { + signed long write_size; /* bytes to write */ + signed long write_consumed; /* bytes consumed by driver */ + unsigned long write_buffer; + signed long read_size; /* bytes to read */ + signed long read_consumed; /* bytes consumed by driver */ + unsigned long read_buffer; +}; + +/* Use with BINDER_VERSION, driver fills in fields. */ +struct binder_version { + /* driver protocol version -- increment with incompatible change */ + signed long protocol_version; +}; + +/* This is the current protocol version. */ +#define BINDER_CURRENT_PROTOCOL_VERSION 7 + +#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) +#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t) +#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t) +#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int) +#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int) +#define BINDER_THREAD_EXIT _IOW('b', 8, int) +#define BINDER_VERSION _IOWR('b', 9, struct binder_version) + +/* + * NOTE: Two special error codes you should check for when calling + * in to the driver are: + * + * EINTR -- The operation has been interupted. This should be + * handled by retrying the ioctl() until a different error code + * is returned. + * + * ECONNREFUSED -- The driver is no longer accepting operations + * from your process. That is, the process is being destroyed. + * You should handle this by exiting from your process. Note + * that once this error code is returned, all further calls to + * the driver from any thread will return this same code. + */ + +enum transaction_flags { + TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */ + TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */ + TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */ + TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */ +}; + +struct binder_transaction_data { + /* The first two are only used for bcTRANSACTION and brTRANSACTION, + * identifying the target and contents of the transaction. + */ + union { + size_t handle; /* target descriptor of command transaction */ + void *ptr; /* target descriptor of return transaction */ + } target; + void *cookie; /* target object cookie */ + unsigned int code; /* transaction command */ + + /* General information about the transaction. */ + unsigned int flags; + pid_t sender_pid; + uid_t sender_euid; + size_t data_size; /* number of bytes of data */ + size_t offsets_size; /* number of bytes of offsets */ + + /* If this transaction is inline, the data immediately + * follows here; otherwise, it ends with a pointer to + * the data buffer. + */ + union { + struct { + /* transaction data */ + const void *buffer; + /* offsets from buffer to flat_binder_object structs */ + const void *offsets; + } ptr; + uint8_t buf[8]; + } data; +}; + +struct binder_ptr_cookie { + void *ptr; + void *cookie; +}; + +struct binder_pri_desc { + int priority; + int desc; +}; + +struct binder_pri_ptr_cookie { + int priority; + void *ptr; + void *cookie; +}; + +enum BinderDriverReturnProtocol { + BR_ERROR = _IOR('r', 0, int), + /* + * int: error code + */ + + BR_OK = _IO('r', 1), + /* No parameters! */ + + BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), + BR_REPLY = _IOR('r', 3, struct binder_transaction_data), + /* + * binder_transaction_data: the received command. + */ + + BR_ACQUIRE_RESULT = _IOR('r', 4, int), + /* + * not currently supported + * int: 0 if the last bcATTEMPT_ACQUIRE was not successful. + * Else the remote object has acquired a primary reference. + */ + + BR_DEAD_REPLY = _IO('r', 5), + /* + * The target of the last transaction (either a bcTRANSACTION or + * a bcATTEMPT_ACQUIRE) is no longer with us. No parameters. + */ + + BR_TRANSACTION_COMPLETE = _IO('r', 6), + /* + * No parameters... always refers to the last transaction requested + * (including replies). Note that this will be sent even for + * asynchronous transactions. + */ + + BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie), + BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie), + BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie), + BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie), + /* + * void *: ptr to binder + * void *: cookie for binder + */ + + BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie), + /* + * not currently supported + * int: priority + * void *: ptr to binder + * void *: cookie for binder + */ + + BR_NOOP = _IO('r', 12), + /* + * No parameters. Do nothing and examine the next command. It exists + * primarily so that we can replace it with a BR_SPAWN_LOOPER command. + */ + + BR_SPAWN_LOOPER = _IO('r', 13), + /* + * No parameters. The driver has determined that a process has no + * threads waiting to service incomming transactions. When a process + * receives this command, it must spawn a new service thread and + * register it via bcENTER_LOOPER. + */ + + BR_FINISHED = _IO('r', 14), + /* + * not currently supported + * stop threadpool thread + */ + + BR_DEAD_BINDER = _IOR('r', 15, void *), + /* + * void *: cookie + */ + BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *), + /* + * void *: cookie + */ + + BR_FAILED_REPLY = _IO('r', 17), + /* + * The the last transaction (either a bcTRANSACTION or + * a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters. + */ +}; + +enum BinderDriverCommandProtocol { + BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), + BC_REPLY = _IOW('c', 1, struct binder_transaction_data), + /* + * binder_transaction_data: the sent command. + */ + + BC_ACQUIRE_RESULT = _IOW('c', 2, int), + /* + * not currently supported + * int: 0 if the last BR_ATTEMPT_ACQUIRE was not successful. + * Else you have acquired a primary reference on the object. + */ + + BC_FREE_BUFFER = _IOW('c', 3, int), + /* + * void *: ptr to transaction data received on a read + */ + + BC_INCREFS = _IOW('c', 4, int), + BC_ACQUIRE = _IOW('c', 5, int), + BC_RELEASE = _IOW('c', 6, int), + BC_DECREFS = _IOW('c', 7, int), + /* + * int: descriptor + */ + + BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie), + BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie), + /* + * void *: ptr to binder + * void *: cookie for binder + */ + + BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc), + /* + * not currently supported + * int: priority + * int: descriptor + */ + + BC_REGISTER_LOOPER = _IO('c', 11), + /* + * No parameters. + * Register a spawned looper thread with the device. + */ + + BC_ENTER_LOOPER = _IO('c', 12), + BC_EXIT_LOOPER = _IO('c', 13), + /* + * No parameters. + * These two commands are sent as an application-level thread + * enters and exits the binder loop, respectively. They are + * used so the binder can have an accurate count of the number + * of looping threads it has available. + */ + + BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie), + /* + * void *: ptr to binder + * void *: cookie + */ + + BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie), + /* + * void *: ptr to binder + * void *: cookie + */ + + BC_DEAD_BINDER_DONE = _IOW('c', 16, void *), + /* + * void *: cookie + */ +}; + +#endif /* _LINUX_BINDER_H */ + diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c new file mode 100644 index 00000000000000..91ab822f3789a0 --- /dev/null +++ b/drivers/staging/android/logger.c @@ -0,0 +1,616 @@ +/* + * drivers/misc/logger.c + * + * A Logging Subsystem + * + * Copyright (C) 2007-2008 Google, Inc. + * + * Robert Love + * + * 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 +#include +#include "logger.h" +#include + +#include + +/* + * struct logger_log - represents a specific log, such as 'main' or 'radio' + * + * This structure lives from module insertion until module removal, so it does + * not need additional reference counting. The structure is protected by the + * mutex 'mutex'. + */ +struct logger_log { + unsigned char *buffer;/* the ring buffer itself */ + struct miscdevice misc; /* misc device representing the log */ + wait_queue_head_t wq; /* wait queue for readers */ + struct list_head readers; /* this log's readers */ + struct mutex mutex; /* mutex protecting buffer */ + size_t w_off; /* current write head offset */ + size_t head; /* new readers start here */ + size_t size; /* size of the log */ +}; + +/* + * struct logger_reader - a logging device open for reading + * + * This object lives from open to release, so we don't need additional + * reference counting. The structure is protected by log->mutex. + */ +struct logger_reader { + struct logger_log *log; /* associated log */ + struct list_head list; /* entry in logger_log's list */ + size_t r_off; /* current read head offset */ +}; + +/* logger_offset - returns index 'n' into the log via (optimized) modulus */ +#define logger_offset(n) ((n) & (log->size - 1)) + +/* + * file_get_log - Given a file structure, return the associated log + * + * This isn't aesthetic. We have several goals: + * + * 1) Need to quickly obtain the associated log during an I/O operation + * 2) Readers need to maintain state (logger_reader) + * 3) Writers need to be very fast (open() should be a near no-op) + * + * In the reader case, we can trivially go file->logger_reader->logger_log. + * For a writer, we don't want to maintain a logger_reader, so we just go + * file->logger_log. Thus what file->private_data points at depends on whether + * or not the file was opened for reading. This function hides that dirtiness. + */ +static inline struct logger_log *file_get_log(struct file *file) +{ + if (file->f_mode & FMODE_READ) { + struct logger_reader *reader = file->private_data; + return reader->log; + } else + return file->private_data; +} + +/* + * get_entry_len - Grabs the length of the payload of the next entry starting + * from 'off'. + * + * Caller needs to hold log->mutex. + */ +static __u32 get_entry_len(struct logger_log *log, size_t off) +{ + __u16 val; + + switch (log->size - off) { + case 1: + memcpy(&val, log->buffer + off, 1); + memcpy(((char *) &val) + 1, log->buffer, 1); + break; + default: + memcpy(&val, log->buffer + off, 2); + } + + return sizeof(struct logger_entry) + val; +} + +/* + * do_read_log_to_user - reads exactly 'count' bytes from 'log' into the + * user-space buffer 'buf'. Returns 'count' on success. + * + * Caller must hold log->mutex. + */ +static ssize_t do_read_log_to_user(struct logger_log *log, + struct logger_reader *reader, + char __user *buf, + size_t count) +{ + size_t len; + + /* + * We read from the log in two disjoint operations. First, we read from + * the current read head offset up to 'count' bytes or to the end of + * the log, whichever comes first. + */ + len = min(count, log->size - reader->r_off); + if (copy_to_user(buf, log->buffer + reader->r_off, len)) + return -EFAULT; + + /* + * Second, we read any remaining bytes, starting back at the head of + * the log. + */ + if (count != len) + if (copy_to_user(buf + len, log->buffer, count - len)) + return -EFAULT; + + reader->r_off = logger_offset(reader->r_off + count); + + return count; +} + +/* + * logger_read - our log's read() method + * + * Behavior: + * + * - O_NONBLOCK works + * - If there are no log entries to read, blocks until log is written to + * - Atomically reads exactly one log entry + * + * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read + * buffer is insufficient to hold next entry. + */ +static ssize_t logger_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct logger_reader *reader = file->private_data; + struct logger_log *log = reader->log; + ssize_t ret; + DEFINE_WAIT(wait); + +start: + while (1) { + prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE); + + mutex_lock(&log->mutex); + ret = (log->w_off == reader->r_off); + mutex_unlock(&log->mutex); + if (!ret) + break; + + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + + if (signal_pending(current)) { + ret = -EINTR; + break; + } + + schedule(); + } + + finish_wait(&log->wq, &wait); + if (ret) + return ret; + + mutex_lock(&log->mutex); + + /* is there still something to read or did we race? */ + if (unlikely(log->w_off == reader->r_off)) { + mutex_unlock(&log->mutex); + goto start; + } + + /* get the size of the next entry */ + ret = get_entry_len(log, reader->r_off); + if (count < ret) { + ret = -EINVAL; + goto out; + } + + /* get exactly one entry from the log */ + ret = do_read_log_to_user(log, reader, buf, ret); + +out: + mutex_unlock(&log->mutex); + + return ret; +} + +/* + * get_next_entry - return the offset of the first valid entry at least 'len' + * bytes after 'off'. + * + * Caller must hold log->mutex. + */ +static size_t get_next_entry(struct logger_log *log, size_t off, size_t len) +{ + size_t count = 0; + + do { + size_t nr = get_entry_len(log, off); + off = logger_offset(off + nr); + count += nr; + } while (count < len); + + return off; +} + +/* + * clock_interval - is a < c < b in mod-space? Put another way, does the line + * from a to b cross c? + */ +static inline int clock_interval(size_t a, size_t b, size_t c) +{ + if (b < a) { + if (a < c || b >= c) + return 1; + } else { + if (a < c && b >= c) + return 1; + } + + return 0; +} + +/* + * fix_up_readers - walk the list of all readers and "fix up" any who were + * lapped by the writer; also do the same for the default "start head". + * We do this by "pulling forward" the readers and start head to the first + * entry after the new write head. + * + * The caller needs to hold log->mutex. + */ +static void fix_up_readers(struct logger_log *log, size_t len) +{ + size_t old = log->w_off; + size_t new = logger_offset(old + len); + struct logger_reader *reader; + + if (clock_interval(old, new, log->head)) + log->head = get_next_entry(log, log->head, len); + + list_for_each_entry(reader, &log->readers, list) + if (clock_interval(old, new, reader->r_off)) + reader->r_off = get_next_entry(log, reader->r_off, len); +} + +/* + * do_write_log - writes 'len' bytes from 'buf' to 'log' + * + * The caller needs to hold log->mutex. + */ +static void do_write_log(struct logger_log *log, const void *buf, size_t count) +{ + size_t len; + + len = min(count, log->size - log->w_off); + memcpy(log->buffer + log->w_off, buf, len); + + if (count != len) + memcpy(log->buffer, buf + len, count - len); + + log->w_off = logger_offset(log->w_off + count); + +} + +/* + * do_write_log_user - writes 'len' bytes from the user-space buffer 'buf' to + * the log 'log' + * + * The caller needs to hold log->mutex. + * + * Returns 'count' on success, negative error code on failure. + */ +static ssize_t do_write_log_from_user(struct logger_log *log, + const void __user *buf, size_t count) +{ + size_t len; + + len = min(count, log->size - log->w_off); + if (len && copy_from_user(log->buffer + log->w_off, buf, len)) + return -EFAULT; + + if (count != len) + if (copy_from_user(log->buffer, buf + len, count - len)) + return -EFAULT; + + log->w_off = logger_offset(log->w_off + count); + + return count; +} + +/* + * logger_aio_write - our write method, implementing support for write(), + * writev(), and aio_write(). Writes are our fast path, and we try to optimize + * them above all else. + */ +ssize_t logger_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t ppos) +{ + struct logger_log *log = file_get_log(iocb->ki_filp); + size_t orig = log->w_off; + struct logger_entry header; + struct timespec now; + ssize_t ret = 0; + + now = current_kernel_time(); + + header.pid = current->tgid; + header.tid = current->pid; + header.sec = now.tv_sec; + header.nsec = now.tv_nsec; + header.len = min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD); + + /* null writes succeed, return zero */ + if (unlikely(!header.len)) + return 0; + + mutex_lock(&log->mutex); + + /* + * Fix up any readers, pulling them forward to the first readable + * entry after (what will be) the new write offset. We do this now + * because if we partially fail, we can end up with clobbered log + * entries that encroach on readable buffer. + */ + fix_up_readers(log, sizeof(struct logger_entry) + header.len); + + do_write_log(log, &header, sizeof(struct logger_entry)); + + while (nr_segs-- > 0) { + size_t len; + ssize_t nr; + + /* figure out how much of this vector we can keep */ + len = min_t(size_t, iov->iov_len, header.len - ret); + + /* write out this segment's payload */ + nr = do_write_log_from_user(log, iov->iov_base, len); + if (unlikely(nr < 0)) { + log->w_off = orig; + mutex_unlock(&log->mutex); + return nr; + } + + iov++; + ret += nr; + } + + mutex_unlock(&log->mutex); + + /* wake up any blocked readers */ + wake_up_interruptible(&log->wq); + + return ret; +} + +static struct logger_log *get_log_from_minor(int); + +/* + * logger_open - the log's open() file operation + * + * Note how near a no-op this is in the write-only case. Keep it that way! + */ +static int logger_open(struct inode *inode, struct file *file) +{ + struct logger_log *log; + int ret; + + ret = nonseekable_open(inode, file); + if (ret) + return ret; + + log = get_log_from_minor(MINOR(inode->i_rdev)); + if (!log) + return -ENODEV; + + if (file->f_mode & FMODE_READ) { + struct logger_reader *reader; + + reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL); + if (!reader) + return -ENOMEM; + + reader->log = log; + INIT_LIST_HEAD(&reader->list); + + mutex_lock(&log->mutex); + reader->r_off = log->head; + list_add_tail(&reader->list, &log->readers); + mutex_unlock(&log->mutex); + + file->private_data = reader; + } else + file->private_data = log; + + return 0; +} + +/* + * logger_release - the log's release file operation + * + * Note this is a total no-op in the write-only case. Keep it that way! + */ +static int logger_release(struct inode *ignored, struct file *file) +{ + if (file->f_mode & FMODE_READ) { + struct logger_reader *reader = file->private_data; + list_del(&reader->list); + kfree(reader); + } + + return 0; +} + +/* + * logger_poll - the log's poll file operation, for poll/select/epoll + * + * Note we always return POLLOUT, because you can always write() to the log. + * Note also that, strictly speaking, a return value of POLLIN does not + * guarantee that the log is readable without blocking, as there is a small + * chance that the writer can lap the reader in the interim between poll() + * returning and the read() request. + */ +static unsigned int logger_poll(struct file *file, poll_table *wait) +{ + struct logger_reader *reader; + struct logger_log *log; + unsigned int ret = POLLOUT | POLLWRNORM; + + if (!(file->f_mode & FMODE_READ)) + return ret; + + reader = file->private_data; + log = reader->log; + + poll_wait(file, &log->wq, wait); + + mutex_lock(&log->mutex); + if (log->w_off != reader->r_off) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&log->mutex); + + return ret; +} + +static long logger_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct logger_log *log = file_get_log(file); + struct logger_reader *reader; + long ret = -ENOTTY; + + mutex_lock(&log->mutex); + + switch (cmd) { + case LOGGER_GET_LOG_BUF_SIZE: + ret = log->size; + break; + case LOGGER_GET_LOG_LEN: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + if (log->w_off >= reader->r_off) + ret = log->w_off - reader->r_off; + else + ret = (log->size - reader->r_off) + log->w_off; + break; + case LOGGER_GET_NEXT_ENTRY_LEN: + if (!(file->f_mode & FMODE_READ)) { + ret = -EBADF; + break; + } + reader = file->private_data; + if (log->w_off != reader->r_off) + ret = get_entry_len(log, reader->r_off); + else + ret = 0; + break; + case LOGGER_FLUSH_LOG: + if (!(file->f_mode & FMODE_WRITE)) { + ret = -EBADF; + break; + } + list_for_each_entry(reader, &log->readers, list) + reader->r_off = log->w_off; + log->head = log->w_off; + ret = 0; + break; + } + + mutex_unlock(&log->mutex); + + return ret; +} + +static const struct file_operations logger_fops = { + .owner = THIS_MODULE, + .read = logger_read, + .aio_write = logger_aio_write, + .poll = logger_poll, + .unlocked_ioctl = logger_ioctl, + .compat_ioctl = logger_ioctl, + .open = logger_open, + .release = logger_release, +}; + +/* + * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which + * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than + * LONG_MAX minus LOGGER_ENTRY_MAX_LEN. + */ +#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \ +static unsigned char _buf_ ## VAR[SIZE]; \ +static struct logger_log VAR = { \ + .buffer = _buf_ ## VAR, \ + .misc = { \ + .minor = MISC_DYNAMIC_MINOR, \ + .name = NAME, \ + .fops = &logger_fops, \ + .parent = NULL, \ + }, \ + .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \ + .readers = LIST_HEAD_INIT(VAR .readers), \ + .mutex = __MUTEX_INITIALIZER(VAR .mutex), \ + .w_off = 0, \ + .head = 0, \ + .size = SIZE, \ +}; + +DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024) +DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024) +DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 64*1024) +DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 64*1024) + +static struct logger_log *get_log_from_minor(int minor) +{ + if (log_main.misc.minor == minor) + return &log_main; + if (log_events.misc.minor == minor) + return &log_events; + if (log_radio.misc.minor == minor) + return &log_radio; + if (log_system.misc.minor == minor) + return &log_system; + return NULL; +} + +static int __init init_log(struct logger_log *log) +{ + int ret; + + ret = misc_register(&log->misc); + if (unlikely(ret)) { + printk(KERN_ERR "logger: failed to register misc " + "device for log '%s'!\n", log->misc.name); + return ret; + } + + printk(KERN_INFO "logger: created %luK log '%s'\n", + (unsigned long) log->size >> 10, log->misc.name); + + return 0; +} + +static int __init logger_init(void) +{ + int ret; + + ret = init_log(&log_main); + if (unlikely(ret)) + goto out; + + ret = init_log(&log_events); + if (unlikely(ret)) + goto out; + + ret = init_log(&log_radio); + if (unlikely(ret)) + goto out; + + ret = init_log(&log_system); + if (unlikely(ret)) + goto out; + +out: + return ret; +} +device_initcall(logger_init); diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h new file mode 100644 index 00000000000000..2cb06e9d8f9821 --- /dev/null +++ b/drivers/staging/android/logger.h @@ -0,0 +1,49 @@ +/* include/linux/logger.h + * + * Copyright (C) 2007-2008 Google, Inc. + * Author: Robert Love + * + * 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. + * + */ + +#ifndef _LINUX_LOGGER_H +#define _LINUX_LOGGER_H + +#include +#include + +struct logger_entry { + __u16 len; /* length of the payload */ + __u16 __pad; /* no matter what, we get 2 bytes of padding */ + __s32 pid; /* generating process's pid */ + __s32 tid; /* generating process's tid */ + __s32 sec; /* seconds since Epoch */ + __s32 nsec; /* nanoseconds */ + char msg[0]; /* the entry's payload */ +}; + +#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */ +#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */ +#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */ +#define LOGGER_LOG_MAIN "log_main" /* everything else */ + +#define LOGGER_ENTRY_MAX_LEN (4*1024) +#define LOGGER_ENTRY_MAX_PAYLOAD \ + (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ + +#endif /* _LINUX_LOGGER_H */ diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c new file mode 100644 index 00000000000000..d5fb083b1c015f --- /dev/null +++ b/drivers/staging/android/lowmemorykiller.c @@ -0,0 +1,214 @@ +/* drivers/misc/lowmemorykiller.c + * + * The lowmemorykiller driver lets user-space specify a set of memory thresholds + * where processes with a range of oom_adj values will get killed. Specify the + * minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the + * number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both + * files take a comma separated list of numbers in ascending order. + * + * For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and + * "1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes + * with a oom_adj value of 8 or higher when the free memory drops below 4096 pages + * and kill processes with a oom_adj value of 0 or higher when the free memory + * drops below 1024 pages. + * + * The driver considers memory used for caches to be free, but if a large + * percentage of the cached memory is locked this can be very inaccurate + * and processes may not get killed until the normal oom killer is triggered. + * + * Copyright (C) 2007-2008 Google, Inc. + * + * 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 uint32_t lowmem_debug_level = 2; +static int lowmem_adj[6] = { + 0, + 1, + 6, + 12, +}; +static int lowmem_adj_size = 4; +static size_t lowmem_minfree[6] = { + 3 * 512, /* 6MB */ + 2 * 1024, /* 8MB */ + 4 * 1024, /* 16MB */ + 16 * 1024, /* 64MB */ +}; +static int lowmem_minfree_size = 4; + +static struct task_struct *lowmem_deathpending; + +#define lowmem_print(level, x...) \ + do { \ + if (lowmem_debug_level >= (level)) \ + printk(x); \ + } while (0) + +static int +task_notify_func(struct notifier_block *self, unsigned long val, void *data); + +static struct notifier_block task_nb = { + .notifier_call = task_notify_func, +}; + +static int +task_notify_func(struct notifier_block *self, unsigned long val, void *data) +{ + struct task_struct *task = data; + if (task == lowmem_deathpending) { + lowmem_deathpending = NULL; +#warning XXX FIXME task_free_unregister +#if 0 + task_free_unregister(&task_nb); +#endif + } + return NOTIFY_OK; +} + +static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask) +{ + struct task_struct *p; + struct task_struct *selected = NULL; + int rem = 0; + int tasksize; + int i; + int min_adj = OOM_ADJUST_MAX + 1; + int selected_tasksize = 0; + int selected_oom_adj; + int array_size = ARRAY_SIZE(lowmem_adj); + int other_free = global_page_state(NR_FREE_PAGES); + int other_file = global_page_state(NR_FILE_PAGES); + + /* + * If we already have a death outstanding, then + * bail out right away; indicating to vmscan + * that we have nothing further to offer on + * this pass. + * + */ + if (lowmem_deathpending) + return 0; + + if (lowmem_adj_size < array_size) + array_size = lowmem_adj_size; + if (lowmem_minfree_size < array_size) + array_size = lowmem_minfree_size; + for (i = 0; i < array_size; i++) { + if (other_free < lowmem_minfree[i] && + other_file < lowmem_minfree[i]) { + min_adj = lowmem_adj[i]; + break; + } + } + if (nr_to_scan > 0) + lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", + nr_to_scan, gfp_mask, other_free, other_file, + min_adj); + rem = global_page_state(NR_ACTIVE_ANON) + + global_page_state(NR_ACTIVE_FILE) + + global_page_state(NR_INACTIVE_ANON) + + global_page_state(NR_INACTIVE_FILE); + if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { + lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", + nr_to_scan, gfp_mask, rem); + return rem; + } + selected_oom_adj = min_adj; + + read_lock(&tasklist_lock); + for_each_process(p) { + struct mm_struct *mm; + struct signal_struct *sig; + int oom_adj; + + task_lock(p); + mm = p->mm; + sig = p->signal; + if (!mm || !sig) { + task_unlock(p); + continue; + } + oom_adj = sig->oom_adj; + if (oom_adj < min_adj) { + task_unlock(p); + continue; + } + tasksize = get_mm_rss(mm); + task_unlock(p); + if (tasksize <= 0) + continue; + if (selected) { + if (oom_adj < selected_oom_adj) + continue; + if (oom_adj == selected_oom_adj && + tasksize <= selected_tasksize) + continue; + } + selected = p; + selected_tasksize = tasksize; + selected_oom_adj = oom_adj; + lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n", + p->pid, p->comm, oom_adj, tasksize); + } + if (selected) { + lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n", + selected->pid, selected->comm, + selected_oom_adj, selected_tasksize); + lowmem_deathpending = selected; +#warning XXX Fix Me task_free_register +#if 0 + task_free_register(&task_nb); +#endif + force_sig(SIGKILL, selected); + rem -= selected_tasksize; + } + lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", + nr_to_scan, gfp_mask, rem); + read_unlock(&tasklist_lock); + return rem; +} + +static struct shrinker lowmem_shrinker = { + .shrink = lowmem_shrink, + .seeks = DEFAULT_SEEKS * 16 +}; + +static int __init lowmem_init(void) +{ + register_shrinker(&lowmem_shrinker); + return 0; +} + +static void __exit lowmem_exit(void) +{ + unregister_shrinker(&lowmem_shrinker); +} + +module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); +module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, + S_IRUGO | S_IWUSR); +module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, + S_IRUGO | S_IWUSR); +module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR); + +module_init(lowmem_init); +module_exit(lowmem_exit); + +MODULE_LICENSE("GPL"); + diff --git a/drivers/staging/android/ram_console.c b/drivers/staging/android/ram_console.c new file mode 100644 index 00000000000000..53f736b0ec8348 --- /dev/null +++ b/drivers/staging/android/ram_console.c @@ -0,0 +1,418 @@ +/* drivers/android/ram_console.c + * + * Copyright (C) 2007-2008 Google, Inc. + * + * 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 +#include +#include + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION +#include +#endif + +struct ram_console_buffer { + uint32_t sig; + uint32_t start; + uint32_t size; + uint8_t data[0]; +}; + +#define RAM_CONSOLE_SIG (0x43474244) /* DBGC */ + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT +static char __initdata + ram_console_old_log_init_buffer[CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE]; +#endif +static char *ram_console_old_log; +static size_t ram_console_old_log_size; + +static struct ram_console_buffer *ram_console_buffer; +static size_t ram_console_buffer_size; +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION +static char *ram_console_par_buffer; +static struct rs_control *ram_console_rs_decoder; +static int ram_console_corrected_bytes; +static int ram_console_bad_blocks; +#define ECC_BLOCK_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_DATA_SIZE +#define ECC_SIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_ECC_SIZE +#define ECC_SYMSIZE CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_SYMBOL_SIZE +#define ECC_POLY CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION_POLYNOMIAL +#endif + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION +static void ram_console_encode_rs8(uint8_t *data, size_t len, uint8_t *ecc) +{ + int i; + uint16_t par[ECC_SIZE]; + /* Initialize the parity buffer */ + memset(par, 0, sizeof(par)); + encode_rs8(ram_console_rs_decoder, data, len, par, 0); + for (i = 0; i < ECC_SIZE; i++) + ecc[i] = par[i]; +} + +static int ram_console_decode_rs8(void *data, size_t len, uint8_t *ecc) +{ + int i; + uint16_t par[ECC_SIZE]; + for (i = 0; i < ECC_SIZE; i++) + par[i] = ecc[i]; + return decode_rs8(ram_console_rs_decoder, data, par, len, + NULL, 0, NULL, 0, NULL); +} +#endif + +static void ram_console_update(const char *s, unsigned int count) +{ + struct ram_console_buffer *buffer = ram_console_buffer; +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + uint8_t *buffer_end = buffer->data + ram_console_buffer_size; + uint8_t *block; + uint8_t *par; + int size = ECC_BLOCK_SIZE; +#endif + memcpy(buffer->data + buffer->start, s, count); +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + block = buffer->data + (buffer->start & ~(ECC_BLOCK_SIZE - 1)); + par = ram_console_par_buffer + + (buffer->start / ECC_BLOCK_SIZE) * ECC_SIZE; + do { + if (block + ECC_BLOCK_SIZE > buffer_end) + size = buffer_end - block; + ram_console_encode_rs8(block, size, par); + block += ECC_BLOCK_SIZE; + par += ECC_SIZE; + } while (block < buffer->data + buffer->start + count); +#endif +} + +static void ram_console_update_header(void) +{ +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + struct ram_console_buffer *buffer = ram_console_buffer; + uint8_t *par; + par = ram_console_par_buffer + + DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE; + ram_console_encode_rs8((uint8_t *)buffer, sizeof(*buffer), par); +#endif +} + +static void +ram_console_write(struct console *console, const char *s, unsigned int count) +{ + int rem; + struct ram_console_buffer *buffer = ram_console_buffer; + + if (count > ram_console_buffer_size) { + s += count - ram_console_buffer_size; + count = ram_console_buffer_size; + } + rem = ram_console_buffer_size - buffer->start; + if (rem < count) { + ram_console_update(s, rem); + s += rem; + count -= rem; + buffer->start = 0; + buffer->size = ram_console_buffer_size; + } + ram_console_update(s, count); + + buffer->start += count; + if (buffer->size < ram_console_buffer_size) + buffer->size += count; + ram_console_update_header(); +} + +static struct console ram_console = { + .name = "ram", + .write = ram_console_write, + .flags = CON_PRINTBUFFER | CON_ENABLED, + .index = -1, +}; + +void ram_console_enable_console(int enabled) +{ + if (enabled) + ram_console.flags |= CON_ENABLED; + else + ram_console.flags &= ~CON_ENABLED; +} + +static void __init +ram_console_save_old(struct ram_console_buffer *buffer, char *dest) +{ + size_t old_log_size = buffer->size; +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + uint8_t *block; + uint8_t *par; + char strbuf[80]; + int strbuf_len; + + block = buffer->data; + par = ram_console_par_buffer; + while (block < buffer->data + buffer->size) { + int numerr; + int size = ECC_BLOCK_SIZE; + if (block + size > buffer->data + ram_console_buffer_size) + size = buffer->data + ram_console_buffer_size - block; + numerr = ram_console_decode_rs8(block, size, par); + if (numerr > 0) { +#if 0 + printk(KERN_INFO "ram_console: error in block %p, %d\n", + block, numerr); +#endif + ram_console_corrected_bytes += numerr; + } else if (numerr < 0) { +#if 0 + printk(KERN_INFO "ram_console: uncorrectable error in " + "block %p\n", block); +#endif + ram_console_bad_blocks++; + } + block += ECC_BLOCK_SIZE; + par += ECC_SIZE; + } + if (ram_console_corrected_bytes || ram_console_bad_blocks) + strbuf_len = snprintf(strbuf, sizeof(strbuf), + "\n%d Corrected bytes, %d unrecoverable blocks\n", + ram_console_corrected_bytes, ram_console_bad_blocks); + else + strbuf_len = snprintf(strbuf, sizeof(strbuf), + "\nNo errors detected\n"); + if (strbuf_len >= sizeof(strbuf)) + strbuf_len = sizeof(strbuf) - 1; + old_log_size += strbuf_len; +#endif + + if (dest == NULL) { + dest = kmalloc(old_log_size, GFP_KERNEL); + if (dest == NULL) { + printk(KERN_ERR + "ram_console: failed to allocate buffer\n"); + return; + } + } + + ram_console_old_log = dest; + ram_console_old_log_size = old_log_size; + memcpy(ram_console_old_log, + &buffer->data[buffer->start], buffer->size - buffer->start); + memcpy(ram_console_old_log + buffer->size - buffer->start, + &buffer->data[0], buffer->start); +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + memcpy(ram_console_old_log + old_log_size - strbuf_len, + strbuf, strbuf_len); +#endif +} + +static int __init ram_console_init(struct ram_console_buffer *buffer, + size_t buffer_size, char *old_buf) +{ +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + int numerr; + uint8_t *par; +#endif + ram_console_buffer = buffer; + ram_console_buffer_size = + buffer_size - sizeof(struct ram_console_buffer); + + if (ram_console_buffer_size > buffer_size) { + pr_err("ram_console: buffer %p, invalid size %zu, " + "datasize %zu\n", buffer, buffer_size, + ram_console_buffer_size); + return 0; + } + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION + ram_console_buffer_size -= (DIV_ROUND_UP(ram_console_buffer_size, + ECC_BLOCK_SIZE) + 1) * ECC_SIZE; + + if (ram_console_buffer_size > buffer_size) { + pr_err("ram_console: buffer %p, invalid size %zu, " + "non-ecc datasize %zu\n", + buffer, buffer_size, ram_console_buffer_size); + return 0; + } + + ram_console_par_buffer = buffer->data + ram_console_buffer_size; + + + /* first consecutive root is 0 + * primitive element to generate roots = 1 + */ + ram_console_rs_decoder = init_rs(ECC_SYMSIZE, ECC_POLY, 0, 1, ECC_SIZE); + if (ram_console_rs_decoder == NULL) { + printk(KERN_INFO "ram_console: init_rs failed\n"); + return 0; + } + + ram_console_corrected_bytes = 0; + ram_console_bad_blocks = 0; + + par = ram_console_par_buffer + + DIV_ROUND_UP(ram_console_buffer_size, ECC_BLOCK_SIZE) * ECC_SIZE; + + numerr = ram_console_decode_rs8(buffer, sizeof(*buffer), par); + if (numerr > 0) { + printk(KERN_INFO "ram_console: error in header, %d\n", numerr); + ram_console_corrected_bytes += numerr; + } else if (numerr < 0) { + printk(KERN_INFO + "ram_console: uncorrectable error in header\n"); + ram_console_bad_blocks++; + } +#endif + + if (buffer->sig == RAM_CONSOLE_SIG) { + if (buffer->size > ram_console_buffer_size + || buffer->start > buffer->size) + printk(KERN_INFO "ram_console: found existing invalid " + "buffer, size %d, start %d\n", + buffer->size, buffer->start); + else { + printk(KERN_INFO "ram_console: found existing buffer, " + "size %d, start %d\n", + buffer->size, buffer->start); + ram_console_save_old(buffer, old_buf); + } + } else { + printk(KERN_INFO "ram_console: no valid data in buffer " + "(sig = 0x%08x)\n", buffer->sig); + } + + buffer->sig = RAM_CONSOLE_SIG; + buffer->start = 0; + buffer->size = 0; + + register_console(&ram_console); +#ifdef CONFIG_ANDROID_RAM_CONSOLE_ENABLE_VERBOSE + console_verbose(); +#endif + return 0; +} + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT +static int __init ram_console_early_init(void) +{ + return ram_console_init((struct ram_console_buffer *) + CONFIG_ANDROID_RAM_CONSOLE_EARLY_ADDR, + CONFIG_ANDROID_RAM_CONSOLE_EARLY_SIZE, + ram_console_old_log_init_buffer); +} +#else +static int ram_console_driver_probe(struct platform_device *pdev) +{ + struct resource *res = pdev->resource; + size_t start; + size_t buffer_size; + void *buffer; + + if (res == NULL || pdev->num_resources != 1 || + !(res->flags & IORESOURCE_MEM)) { + printk(KERN_ERR "ram_console: invalid resource, %p %d flags " + "%lx\n", res, pdev->num_resources, res ? res->flags : 0); + return -ENXIO; + } + buffer_size = res->end - res->start + 1; + start = res->start; + printk(KERN_INFO "ram_console: got buffer at %zx, size %zx\n", + start, buffer_size); + buffer = ioremap(res->start, buffer_size); + if (buffer == NULL) { + printk(KERN_ERR "ram_console: failed to map memory\n"); + return -ENOMEM; + } + + return ram_console_init(buffer, buffer_size, NULL/* allocate */); +} + +static struct platform_driver ram_console_driver = { + .probe = ram_console_driver_probe, + .driver = { + .name = "ram_console", + }, +}; + +static int __init ram_console_module_init(void) +{ + int err; + err = platform_driver_register(&ram_console_driver); + return err; +} +#endif + +static ssize_t ram_console_read_old(struct file *file, char __user *buf, + size_t len, loff_t *offset) +{ + loff_t pos = *offset; + ssize_t count; + + if (pos >= ram_console_old_log_size) + return 0; + + count = min(len, (size_t)(ram_console_old_log_size - pos)); + if (copy_to_user(buf, ram_console_old_log + pos, count)) + return -EFAULT; + + *offset += count; + return count; +} + +static const struct file_operations ram_console_file_ops = { + .owner = THIS_MODULE, + .read = ram_console_read_old, +}; + +static int __init ram_console_late_init(void) +{ + struct proc_dir_entry *entry; + + if (ram_console_old_log == NULL) + return 0; +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT + ram_console_old_log = kmalloc(ram_console_old_log_size, GFP_KERNEL); + if (ram_console_old_log == NULL) { + printk(KERN_ERR + "ram_console: failed to allocate buffer for old log\n"); + ram_console_old_log_size = 0; + return 0; + } + memcpy(ram_console_old_log, + ram_console_old_log_init_buffer, ram_console_old_log_size); +#endif + entry = create_proc_entry("last_kmsg", S_IFREG | S_IRUGO, NULL); + if (!entry) { + printk(KERN_ERR "ram_console: failed to create proc entry\n"); + kfree(ram_console_old_log); + ram_console_old_log = NULL; + return 0; + } + + entry->proc_fops = &ram_console_file_ops; + entry->size = ram_console_old_log_size; + return 0; +} + +#ifdef CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT +console_initcall(ram_console_early_init); +#else +postcore_initcall(ram_console_module_init); +#endif +late_initcall(ram_console_late_init); + diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c new file mode 100644 index 00000000000000..a646107da26d56 --- /dev/null +++ b/drivers/staging/android/timed_gpio.c @@ -0,0 +1,175 @@ +/* drivers/misc/timed_gpio.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 "timed_output.h" +#include "timed_gpio.h" + + +struct timed_gpio_data { + struct timed_output_dev dev; + struct hrtimer timer; + spinlock_t lock; + unsigned gpio; + int max_timeout; + u8 active_low; +}; + +static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) +{ + struct timed_gpio_data *data = + container_of(timer, struct timed_gpio_data, timer); + + gpio_direction_output(data->gpio, data->active_low ? 1 : 0); + return HRTIMER_NORESTART; +} + +static int gpio_get_time(struct timed_output_dev *dev) +{ + struct timed_gpio_data *data = + container_of(dev, struct timed_gpio_data, dev); + + if (hrtimer_active(&data->timer)) { + ktime_t r = hrtimer_get_remaining(&data->timer); + struct timeval t = ktime_to_timeval(r); + return t.tv_sec * 1000 + t.tv_usec / 1000; + } else + return 0; +} + +static void gpio_enable(struct timed_output_dev *dev, int value) +{ + struct timed_gpio_data *data = + container_of(dev, struct timed_gpio_data, dev); + unsigned long flags; + + spin_lock_irqsave(&data->lock, flags); + + /* cancel previous timer and set GPIO according to value */ + hrtimer_cancel(&data->timer); + gpio_direction_output(data->gpio, data->active_low ? !value : !!value); + + if (value > 0) { + if (value > data->max_timeout) + value = data->max_timeout; + + hrtimer_start(&data->timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } + + spin_unlock_irqrestore(&data->lock, flags); +} + +static int timed_gpio_probe(struct platform_device *pdev) +{ + struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; + struct timed_gpio *cur_gpio; + struct timed_gpio_data *gpio_data, *gpio_dat; + int i, j, ret = 0; + + if (!pdata) + return -EBUSY; + + gpio_data = kzalloc(sizeof(struct timed_gpio_data) * pdata->num_gpios, + GFP_KERNEL); + if (!gpio_data) + return -ENOMEM; + + for (i = 0; i < pdata->num_gpios; i++) { + cur_gpio = &pdata->gpios[i]; + gpio_dat = &gpio_data[i]; + + hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + gpio_dat->timer.function = gpio_timer_func; + spin_lock_init(&gpio_dat->lock); + + gpio_dat->dev.name = cur_gpio->name; + gpio_dat->dev.get_time = gpio_get_time; + gpio_dat->dev.enable = gpio_enable; + ret = gpio_request(cur_gpio->gpio, cur_gpio->name); + if (ret >= 0) { + ret = timed_output_dev_register(&gpio_dat->dev); + if (ret < 0) + gpio_free(cur_gpio->gpio); + } + if (ret < 0) { + for (j = 0; j < i; j++) { + timed_output_dev_unregister(&gpio_data[i].dev); + gpio_free(gpio_data[i].gpio); + } + kfree(gpio_data); + return ret; + } + + gpio_dat->gpio = cur_gpio->gpio; + gpio_dat->max_timeout = cur_gpio->max_timeout; + gpio_dat->active_low = cur_gpio->active_low; + gpio_direction_output(gpio_dat->gpio, gpio_dat->active_low); + } + + platform_set_drvdata(pdev, gpio_data); + + return 0; +} + +static int timed_gpio_remove(struct platform_device *pdev) +{ + struct timed_gpio_platform_data *pdata = pdev->dev.platform_data; + struct timed_gpio_data *gpio_data = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < pdata->num_gpios; i++) { + timed_output_dev_unregister(&gpio_data[i].dev); + gpio_free(gpio_data[i].gpio); + } + + kfree(gpio_data); + + return 0; +} + +static struct platform_driver timed_gpio_driver = { + .probe = timed_gpio_probe, + .remove = timed_gpio_remove, + .driver = { + .name = TIMED_GPIO_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init timed_gpio_init(void) +{ + return platform_driver_register(&timed_gpio_driver); +} + +static void __exit timed_gpio_exit(void) +{ + platform_driver_unregister(&timed_gpio_driver); +} + +module_init(timed_gpio_init); +module_exit(timed_gpio_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("timed gpio driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/android/timed_gpio.h b/drivers/staging/android/timed_gpio.h new file mode 100644 index 00000000000000..a0e15f8be3f717 --- /dev/null +++ b/drivers/staging/android/timed_gpio.h @@ -0,0 +1,33 @@ +/* include/linux/timed_gpio.h + * + * Copyright (C) 2008 Google, Inc. + * + * 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. + * +*/ + +#ifndef _LINUX_TIMED_GPIO_H +#define _LINUX_TIMED_GPIO_H + +#define TIMED_GPIO_NAME "timed-gpio" + +struct timed_gpio { + const char *name; + unsigned gpio; + int max_timeout; + u8 active_low; +}; + +struct timed_gpio_platform_data { + int num_gpios; + struct timed_gpio *gpios; +}; + +#endif diff --git a/drivers/staging/android/timed_output.c b/drivers/staging/android/timed_output.c new file mode 100644 index 00000000000000..f373422308e016 --- /dev/null +++ b/drivers/staging/android/timed_output.c @@ -0,0 +1,123 @@ +/* drivers/misc/timed_output.c + * + * Copyright (C) 2009 Google, Inc. + * Author: Mike Lockwood + * + * 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 "timed_output.h" + +static struct class *timed_output_class; +static atomic_t device_count; + +static ssize_t enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct timed_output_dev *tdev = dev_get_drvdata(dev); + int remaining = tdev->get_time(tdev); + + return sprintf(buf, "%d\n", remaining); +} + +static ssize_t enable_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct timed_output_dev *tdev = dev_get_drvdata(dev); + int value; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + tdev->enable(tdev, value); + + return size; +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store); + +static int create_timed_output_class(void) +{ + if (!timed_output_class) { + timed_output_class = class_create(THIS_MODULE, "timed_output"); + if (IS_ERR(timed_output_class)) + return PTR_ERR(timed_output_class); + atomic_set(&device_count, 0); + } + + return 0; +} + +int timed_output_dev_register(struct timed_output_dev *tdev) +{ + int ret; + + if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time) + return -EINVAL; + + ret = create_timed_output_class(); + if (ret < 0) + return ret; + + tdev->index = atomic_inc_return(&device_count); + tdev->dev = device_create(timed_output_class, NULL, + MKDEV(0, tdev->index), NULL, tdev->name); + if (IS_ERR(tdev->dev)) + return PTR_ERR(tdev->dev); + + ret = device_create_file(tdev->dev, &dev_attr_enable); + if (ret < 0) + goto err_create_file; + + dev_set_drvdata(tdev->dev, tdev); + tdev->state = 0; + return 0; + +err_create_file: + device_destroy(timed_output_class, MKDEV(0, tdev->index)); + printk(KERN_ERR "timed_output: Failed to register driver %s\n", + tdev->name); + + return ret; +} +EXPORT_SYMBOL_GPL(timed_output_dev_register); + +void timed_output_dev_unregister(struct timed_output_dev *tdev) +{ + device_remove_file(tdev->dev, &dev_attr_enable); + device_destroy(timed_output_class, MKDEV(0, tdev->index)); + dev_set_drvdata(tdev->dev, NULL); +} +EXPORT_SYMBOL_GPL(timed_output_dev_unregister); + +static int __init timed_output_init(void) +{ + return create_timed_output_class(); +} + +static void __exit timed_output_exit(void) +{ + class_destroy(timed_output_class); +} + +module_init(timed_output_init); +module_exit(timed_output_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("timed output class driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/android/timed_output.h b/drivers/staging/android/timed_output.h new file mode 100644 index 00000000000000..ec907ab2ff5484 --- /dev/null +++ b/drivers/staging/android/timed_output.h @@ -0,0 +1,37 @@ +/* include/linux/timed_output.h + * + * Copyright (C) 2008 Google, Inc. + * + * 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. + * +*/ + +#ifndef _LINUX_TIMED_OUTPUT_H +#define _LINUX_TIMED_OUTPUT_H + +struct timed_output_dev { + const char *name; + + /* enable the output and set the timer */ + void (*enable)(struct timed_output_dev *sdev, int timeout); + + /* returns the current number of milliseconds remaining on the timer */ + int (*get_time)(struct timed_output_dev *sdev); + + /* private data */ + struct device *dev; + int index; + int state; +}; + +extern int timed_output_dev_register(struct timed_output_dev *dev); +extern void timed_output_dev_unregister(struct timed_output_dev *dev); + +#endif diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig new file mode 100644 index 00000000000000..52385914b9ae64 --- /dev/null +++ b/drivers/switch/Kconfig @@ -0,0 +1,15 @@ +menuconfig SWITCH + tristate "Switch class support" + help + Say Y here to enable switch class support. This allows + monitoring switches by userspace via sysfs and uevent. + +if SWITCH + +config SWITCH_GPIO + tristate "GPIO Swith support" + depends on GENERIC_GPIO + help + Say Y here to enable GPIO based switch support. + +endif # SWITCH diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile new file mode 100644 index 00000000000000..f7606ed4a719b9 --- /dev/null +++ b/drivers/switch/Makefile @@ -0,0 +1,4 @@ +# Switch Class Driver +obj-$(CONFIG_SWITCH) += switch_class.o +obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o + diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c new file mode 100644 index 00000000000000..e05fc25911474b --- /dev/null +++ b/drivers/switch/switch_class.c @@ -0,0 +1,174 @@ +/* + * drivers/switch/switch_class.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include + +struct class *switch_class; +static atomic_t device_count; + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *sdev = (struct switch_dev *) + dev_get_drvdata(dev); + + if (sdev->print_state) { + int ret = sdev->print_state(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%d\n", sdev->state); +} + +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct switch_dev *sdev = (struct switch_dev *) + dev_get_drvdata(dev); + + if (sdev->print_name) { + int ret = sdev->print_name(sdev, buf); + if (ret >= 0) + return ret; + } + return sprintf(buf, "%s\n", sdev->name); +} + +static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); + +void switch_set_state(struct switch_dev *sdev, int state) +{ + char name_buf[120]; + char state_buf[120]; + char *prop_buf; + char *envp[3]; + int env_offset = 0; + int length; + + if (sdev->state != state) { + sdev->state = state; + + prop_buf = (char *)get_zeroed_page(GFP_KERNEL); + if (prop_buf) { + length = name_show(sdev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(name_buf, sizeof(name_buf), + "SWITCH_NAME=%s", prop_buf); + envp[env_offset++] = name_buf; + } + length = state_show(sdev->dev, NULL, prop_buf); + if (length > 0) { + if (prop_buf[length - 1] == '\n') + prop_buf[length - 1] = 0; + snprintf(state_buf, sizeof(state_buf), + "SWITCH_STATE=%s", prop_buf); + envp[env_offset++] = state_buf; + } + envp[env_offset] = NULL; + kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); + free_page((unsigned long)prop_buf); + } else { + printk(KERN_ERR "out of memory in switch_set_state\n"); + kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); + } + } +} +EXPORT_SYMBOL_GPL(switch_set_state); + +static int create_switch_class(void) +{ + if (!switch_class) { + switch_class = class_create(THIS_MODULE, "switch"); + if (IS_ERR(switch_class)) + return PTR_ERR(switch_class); + atomic_set(&device_count, 0); + } + + return 0; +} + +int switch_dev_register(struct switch_dev *sdev) +{ + int ret; + + if (!switch_class) { + ret = create_switch_class(); + if (ret < 0) + return ret; + } + + sdev->index = atomic_inc_return(&device_count); + sdev->dev = device_create(switch_class, NULL, + MKDEV(0, sdev->index), NULL, sdev->name); + if (IS_ERR(sdev->dev)) + return PTR_ERR(sdev->dev); + + ret = device_create_file(sdev->dev, &dev_attr_state); + if (ret < 0) + goto err_create_file_1; + ret = device_create_file(sdev->dev, &dev_attr_name); + if (ret < 0) + goto err_create_file_2; + + dev_set_drvdata(sdev->dev, sdev); + sdev->state = 0; + return 0; + +err_create_file_2: + device_remove_file(sdev->dev, &dev_attr_state); +err_create_file_1: + device_destroy(switch_class, MKDEV(0, sdev->index)); + printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); + + return ret; +} +EXPORT_SYMBOL_GPL(switch_dev_register); + +void switch_dev_unregister(struct switch_dev *sdev) +{ + device_remove_file(sdev->dev, &dev_attr_name); + device_remove_file(sdev->dev, &dev_attr_state); + device_destroy(switch_class, MKDEV(0, sdev->index)); + dev_set_drvdata(sdev->dev, NULL); +} +EXPORT_SYMBOL_GPL(switch_dev_unregister); + +static int __init switch_class_init(void) +{ + return create_switch_class(); +} + +static void __exit switch_class_exit(void) +{ + class_destroy(switch_class); +} + +module_init(switch_class_init); +module_exit(switch_class_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("Switch class driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c new file mode 100644 index 00000000000000..b5f98ca0b2e115 --- /dev/null +++ b/drivers/switch/switch_gpio.c @@ -0,0 +1,171 @@ +/* + * drivers/switch/switch_gpio.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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 +#include +#include + +struct gpio_switch_data { + struct switch_dev sdev; + unsigned gpio; + const char *name_on; + const char *name_off; + const char *state_on; + const char *state_off; + int irq; + struct work_struct work; +}; + +static void gpio_switch_work(struct work_struct *work) +{ + int state; + struct gpio_switch_data *data = + container_of(work, struct gpio_switch_data, work); + + state = gpio_get_value(data->gpio); + switch_set_state(&data->sdev, state); +} + +static irqreturn_t gpio_irq_handler(int irq, void *dev_id) +{ + struct gpio_switch_data *switch_data = + (struct gpio_switch_data *)dev_id; + + schedule_work(&switch_data->work); + return IRQ_HANDLED; +} + +static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) +{ + struct gpio_switch_data *switch_data = + container_of(sdev, struct gpio_switch_data, sdev); + const char *state; + if (switch_get_state(sdev)) + state = switch_data->state_on; + else + state = switch_data->state_off; + + if (state) + return sprintf(buf, "%s\n", state); + return -1; +} + +static int gpio_switch_probe(struct platform_device *pdev) +{ + struct gpio_switch_platform_data *pdata = pdev->dev.platform_data; + struct gpio_switch_data *switch_data; + int ret = 0; + + if (!pdata) + return -EBUSY; + + switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL); + if (!switch_data) + return -ENOMEM; + + switch_data->sdev.name = pdata->name; + switch_data->gpio = pdata->gpio; + switch_data->name_on = pdata->name_on; + switch_data->name_off = pdata->name_off; + switch_data->state_on = pdata->state_on; + switch_data->state_off = pdata->state_off; + switch_data->sdev.print_state = switch_gpio_print_state; + + ret = switch_dev_register(&switch_data->sdev); + if (ret < 0) + goto err_switch_dev_register; + + ret = gpio_request(switch_data->gpio, pdev->name); + if (ret < 0) + goto err_request_gpio; + + ret = gpio_direction_input(switch_data->gpio); + if (ret < 0) + goto err_set_gpio_input; + + INIT_WORK(&switch_data->work, gpio_switch_work); + + switch_data->irq = gpio_to_irq(switch_data->gpio); + if (switch_data->irq < 0) { + ret = switch_data->irq; + goto err_detect_irq_num_failed; + } + + ret = request_irq(switch_data->irq, gpio_irq_handler, + IRQF_TRIGGER_LOW, pdev->name, switch_data); + if (ret < 0) + goto err_request_irq; + + /* Perform initial detection */ + gpio_switch_work(&switch_data->work); + + return 0; + +err_request_irq: +err_detect_irq_num_failed: +err_set_gpio_input: + gpio_free(switch_data->gpio); +err_request_gpio: + switch_dev_unregister(&switch_data->sdev); +err_switch_dev_register: + kfree(switch_data); + + return ret; +} + +static int __devexit gpio_switch_remove(struct platform_device *pdev) +{ + struct gpio_switch_data *switch_data = platform_get_drvdata(pdev); + + cancel_work_sync(&switch_data->work); + gpio_free(switch_data->gpio); + switch_dev_unregister(&switch_data->sdev); + kfree(switch_data); + + return 0; +} + +static struct platform_driver gpio_switch_driver = { + .probe = gpio_switch_probe, + .remove = __devexit_p(gpio_switch_remove), + .driver = { + .name = "switch-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_switch_init(void) +{ + return platform_driver_register(&gpio_switch_driver); +} + +static void __exit gpio_switch_exit(void) +{ + platform_driver_unregister(&gpio_switch_driver); +} + +module_init(gpio_switch_init); +module_exit(gpio_switch_exit); + +MODULE_AUTHOR("Mike Lockwood "); +MODULE_DESCRIPTION("GPIO Switch driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 6a58cb1330c1dc..9dbca1c53d21d3 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -60,6 +60,7 @@ config USB_ARCH_HAS_EHCI default y if PPC_83xx default y if SOC_AU1200 default y if ARCH_IXP4XX + default y if ARCH_MMP default y if ARCH_W90X900 default y if ARCH_AT91SAM9G45 default y if ARCH_MXC diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 80b4008c89baf0..1726d12cf5393a 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -4,6 +4,7 @@ # Object files in subdirectories +obj-$(CONFIG_USB_OTG) += otg/ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_MON) += mon/ diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 7e594449600e00..aded7bb22ad5c6 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -108,7 +108,7 @@ config USB_SUSPEND config USB_OTG bool - depends on USB && EXPERIMENTAL + depends on USB && EXPERIMENTAL && PM depends on USB_SUSPEND default n diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index a3cdb09734abc5..655809dcabda48 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -240,6 +240,10 @@ struct hc_driver { void (*relinquish_port)(struct usb_hcd *, int); /* has a port been handed over to a companion? */ int (*port_handed_over)(struct usb_hcd *, int); +#ifdef CONFIG_USB_OTG + int (*disconnect)(struct usb_hcd *); + int (*connect)(struct usb_hcd *, struct usb_device *udev); +#endif /* CLEAR_TT_BUFFER completion callback */ void (*clear_tt_buffer_complete)(struct usb_hcd *, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0940ccd6f4f4fb..cf97f45721c50d 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -128,7 +128,11 @@ MODULE_PARM_DESC(initial_descriptor_timeout, * otherwise the new scheme is used. If that fails and "use_both_schemes" * is set, then the driver will make another attempt, using the other scheme. */ +#ifdef CONFIG_CPU_PXA168 +static int old_scheme_first = 1; +#else static int old_scheme_first = 0; +#endif module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(old_scheme_first, "start with the old device initialization scheme"); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 11a3e0fa433181..c5f7558d75817c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -191,7 +191,6 @@ config USB_GADGET_OMAP boolean "OMAP USB Device Controller" depends on ARCH_OMAP select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG - select USB_OTG_UTILS if ARCH_OMAP help Many Texas Instruments OMAP processors have flexible full speed USB device controllers, with support for up to 30 @@ -291,6 +290,43 @@ config USB_PXA27X default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_PXA_U2O + bool "PXA9xx Processor USB 2.0 controller" + select USB_GADGET_DUALSPEED + help + PXA9xx Processor series include a high speed USB 2.0 device + controller, which support high speed and full speed USB Peripheral. + +config USB_PXA_U2O + tristate + depends on USB_GADGET_PXA_U2O + default USB_GADGET + select USB_GADGET_SELECTED + +config USB_COMPOSITE + boolean "Multiple gadget drivers support (EXPERIMENTAL)" + depends on USB_GADGET_PXA_U2O + default n + help + Multiple gadget drivers support in the PXA9xx controller driver. + user can insmod several gadget drivers to implement more + than one function on the device. There are several restrictions + to such gadget drivers, and maybe modifications to the host driver. + + supported gadget drivers combination(connect to linux host machine): + CDC ACM(or serial) + CDC Ethernet + mass storage + +config USB_OTG + boolean "OTG Support" + depends on USB_EHCI_HCD && USB_GADGET_PXA_U2O + default n + select USB_OTG_PXA3XX + select USB_EHCI_PXA_U2O + help + +config USB_OTG_PXA3XX + depends on USB_OTG + config USB_GADGET_S3C_HSOTG boolean "S3C HS/OtG USB Device controller" depends on S3C_DEV_USB_HSOTG @@ -353,7 +389,7 @@ config USB_S3C2410_DEBUG # musb builds in ../musb along with host support config USB_GADGET_MUSB_HDRC - boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)" + boolean "Inventra HDRC USB Peripheral (TI, ...)" depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG) select USB_GADGET_DUALSPEED select USB_GADGET_SELECTED @@ -423,24 +459,6 @@ config USB_FSL_QE default USB_GADGET select USB_GADGET_SELECTED -config USB_GADGET_CI13XXX - boolean "MIPS USB CI13xxx" - depends on PCI - select USB_GADGET_DUALSPEED - help - MIPS USB IP core family device controller - Currently it only supports IP part number CI13412 - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "ci13xxx_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_CI13XXX - tristate - depends on USB_GADGET_CI13XXX - default USB_GADGET - select USB_GADGET_SELECTED - config USB_GADGET_NET2280 boolean "NetChip 228x" depends on PCI @@ -798,6 +816,14 @@ config USB_G_PRINTER For more information, see Documentation/usb/gadget_printer.txt which includes sample code for accessing the device file. +config USB_ANDROID + tristate "Android Gadget" + help + The Android gadget provides mass storage and adb transport. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_android". + config USB_CDC_COMPOSITE tristate "CDC Composite Device (Ethernet and ACM)" depends on NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 43b51da8d7278f..82fdda33c605db 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -26,6 +26,11 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o +obj-$(CONFIG_USB_PXA_U2O) += mv_gadget.o pxa_comp.o +obj-$(CONFIG_USB_PXA_U2O) += mv/ +obj-$(CONFIG_MULTIPLE_GADGET_DRIVERS) += epautoconf.o + +C_UTILS = composite.o usbstring.o config.o epautoconf.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o @@ -42,6 +47,7 @@ g_file_storage-objs := file_storage.o g_mass_storage-objs := mass_storage.o g_printer-objs := printer.o g_cdc-objs := cdc2.o +g_android-objs := android.o f_adb.o f_mass_storage.o g_multi-objs := multi.o g_nokia-objs := nokia.o @@ -55,6 +61,7 @@ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o +obj-$(CONFIG_USB_ANDROID) += g_android.o obj-$(CONFIG_USB_G_MULTI) += g_multi.o obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c new file mode 100644 index 00000000000000..95883ac18904e9 --- /dev/null +++ b/drivers/usb/gadget/android.c @@ -0,0 +1,345 @@ +/* + * Gadget Driver for Android + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "f_mass_storage.h" +#include "f_adb.h" + +#include "gadget_chips.h" + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "composite.c" + +MODULE_AUTHOR("Mike Lockwood"); +MODULE_DESCRIPTION("Android Composite USB Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); + +static const char longname[] = "Gadget Android"; + +/* Default vendor and product IDs, overridden by platform data */ +#define VENDOR_ID 0x0bb4 +#define PRODUCT_ID 0x0c01 +#define ADB_PRODUCT_ID 0x0c02 + +struct android_dev { + struct usb_gadget *gadget; + struct usb_composite_dev *cdev; + + int product_id; + int adb_product_id; + int version; + + int adb_enabled; + int nluns; +}; + +static atomic_t adb_enable_excl; +static struct android_dev *_android_dev; + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +/* String Table */ +static struct usb_string strings_dev[] = { + /* These dummy values should be overridden by platform data */ + [STRING_MANUFACTURER_IDX].s = "Android", + [STRING_PRODUCT_IDX].s = "Android", + [STRING_SERIAL_IDX].s = "0123456789ABCDEF", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof(device_desc), + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .idVendor = __constant_cpu_to_le16(VENDOR_ID), + .idProduct = __constant_cpu_to_le16(PRODUCT_ID), + .bcdDevice = __constant_cpu_to_le16(0xffff), + .bNumConfigurations = 1, +}; + +static int __init android_bind_config(struct usb_configuration *c) +{ + struct android_dev *dev = _android_dev; + int ret; + printk(KERN_DEBUG "android_bind_config\n"); + + ret = mass_storage_function_add(dev->cdev, c, dev->nluns); + if (ret) + return ret; + return adb_function_add(dev->cdev, c); +} + +static struct usb_configuration android_config_driver = { + .label = "android", + .bind = android_bind_config, + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0xFA, /* 500ma */ +}; + +static int __init android_bind(struct usb_composite_dev *cdev) +{ + struct android_dev *dev = _android_dev; + struct usb_gadget *gadget = cdev->gadget; + int gcnum; + int id; + int ret; + + printk(KERN_INFO "android_bind\n"); + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_MANUFACTURER_IDX].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_PRODUCT_IDX].id = id; + device_desc.iProduct = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_dev[STRING_SERIAL_IDX].id = id; + device_desc.iSerialNumber = id; + + if (gadget->ops->wakeup) + android_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + + /* register our configuration */ + ret = usb_add_config(cdev, &android_config_driver); + if (ret) { + printk(KERN_ERR "usb_add_config failed\n"); + return ret; + } + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + /* gadget zero is so simple (for now, no altsettings) that + * it SHOULD NOT have problems with bulk-capable hardware. + * so just warn about unrcognized controllers -- don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ + pr_warning("%s: controller '%s' not recognized\n", + longname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16(0x9999); + } + + usb_gadget_set_selfpowered(gadget); + dev->cdev = cdev; + + return 0; +} + +static struct usb_composite_driver android_usb_driver = { + .name = "android_usb", + .dev = &device_desc, + .strings = dev_strings, + .bind = android_bind, +}; + +static void enable_adb(struct android_dev *dev, int enable) +{ + if (enable != dev->adb_enabled) { + dev->adb_enabled = enable; + adb_function_enable(enable); + + /* set product ID to the appropriate value */ + if (enable) + device_desc.idProduct = + __constant_cpu_to_le16(dev->adb_product_id); + else + device_desc.idProduct = + __constant_cpu_to_le16(dev->product_id); + if (dev->cdev) + dev->cdev->desc.idProduct = device_desc.idProduct; + + /* force reenumeration */ + if (dev->cdev && dev->cdev->gadget && + dev->cdev->gadget->speed != USB_SPEED_UNKNOWN) { + usb_gadget_disconnect(dev->cdev->gadget); + msleep(10); + usb_gadget_connect(dev->cdev->gadget); + } + } +} + +static int adb_enable_open(struct inode *ip, struct file *fp) +{ + if (atomic_inc_return(&adb_enable_excl) != 1) { + atomic_dec(&adb_enable_excl); + return -EBUSY; + } + + printk(KERN_INFO "enabling adb\n"); + enable_adb(_android_dev, 1); + + return 0; +} + +static int adb_enable_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "disabling adb\n"); + enable_adb(_android_dev, 0); + atomic_dec(&adb_enable_excl); + return 0; +} + +static struct file_operations adb_enable_fops = { + .owner = THIS_MODULE, + .open = adb_enable_open, + .release = adb_enable_release, +}; + +static struct miscdevice adb_enable_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "android_adb_enable", + .fops = &adb_enable_fops, +}; + +static int __init android_probe(struct platform_device *pdev) +{ + struct android_usb_platform_data *pdata = pdev->dev.platform_data; + struct android_dev *dev = _android_dev; + + printk(KERN_INFO "android_probe pdata: %p\n", pdata); + + if (pdata) { + if (pdata->vendor_id) + device_desc.idVendor = + __constant_cpu_to_le16(pdata->vendor_id); + if (pdata->product_id) { + dev->product_id = pdata->product_id; + device_desc.idProduct = + __constant_cpu_to_le16(pdata->product_id); + } + if (pdata->adb_product_id) + dev->adb_product_id = pdata->adb_product_id; + if (pdata->version) + dev->version = pdata->version; + + if (pdata->product_name) + strings_dev[STRING_PRODUCT_IDX].s = pdata->product_name; + if (pdata->manufacturer_name) + strings_dev[STRING_MANUFACTURER_IDX].s = + pdata->manufacturer_name; + if (pdata->serial_number) + strings_dev[STRING_SERIAL_IDX].s = pdata->serial_number; + dev->nluns = pdata->nluns; + } + + return 0; +} + +static struct platform_driver android_platform_driver = { + .driver = { .name = "android_usb", }, + .probe = android_probe, +}; + +static int __init init(void) +{ + struct android_dev *dev; + int ret; + + printk(KERN_INFO "android init\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* set default values, which should be overridden by platform data */ + dev->product_id = PRODUCT_ID; + dev->adb_product_id = ADB_PRODUCT_ID; + _android_dev = dev; + + ret = platform_driver_register(&android_platform_driver); + if (ret) + return ret; + ret = misc_register(&adb_enable_device); + if (ret) { + platform_driver_unregister(&android_platform_driver); + return ret; + } + ret = usb_composite_register(&android_usb_driver); + if (ret) { + misc_deregister(&adb_enable_device); + platform_driver_unregister(&android_platform_driver); + } + return ret; +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&android_usb_driver); + misc_deregister(&adb_enable_device); + platform_driver_unregister(&android_platform_driver); + kfree(_android_dev); + _android_dev = NULL; +} +module_exit(cleanup); diff --git a/drivers/usb/gadget/f_adb.c b/drivers/usb/gadget/f_adb.c new file mode 100644 index 00000000000000..6ca53273ada84c --- /dev/null +++ b/drivers/usb/gadget/f_adb.c @@ -0,0 +1,668 @@ +/* + * Gadget Driver for Android ADB + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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. + * + */ + +/* #define DEBUG */ +/* #define VERBOSE_DEBUG */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "f_adb.h" + +#define BULK_BUFFER_SIZE 4096 + +/* number of rx and tx requests to allocate */ +#define RX_REQ_MAX 4 +#define TX_REQ_MAX 4 + +static const char shortname[] = "android_adb"; + +struct adb_dev { + struct usb_function function; + struct usb_composite_dev *cdev; + spinlock_t lock; + + struct usb_ep *ep_in; + struct usb_ep *ep_out; + + int online; + int error; + + atomic_t read_excl; + atomic_t write_excl; + atomic_t open_excl; + + struct list_head tx_idle; + struct list_head rx_idle; + struct list_head rx_done; + + wait_queue_head_t read_wq; + wait_queue_head_t write_wq; + + /* the request we're currently reading from */ + struct usb_request *read_req; + unsigned char *read_buf; + unsigned read_count; +}; + +static struct usb_interface_descriptor adb_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xFF, + .bInterfaceSubClass = 0x42, + .bInterfaceProtocol = 1, +}; + +static struct usb_endpoint_descriptor adb_highspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_highspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor adb_fullspeed_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor adb_fullspeed_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_descriptor_header *fs_adb_descs[] = { + (struct usb_descriptor_header *) &adb_interface_desc, + (struct usb_descriptor_header *) &adb_fullspeed_in_desc, + (struct usb_descriptor_header *) &adb_fullspeed_out_desc, + NULL, +}; + +static struct usb_descriptor_header *hs_adb_descs[] = { + (struct usb_descriptor_header *) &adb_interface_desc, + (struct usb_descriptor_header *) &adb_highspeed_in_desc, + (struct usb_descriptor_header *) &adb_highspeed_out_desc, + NULL, +}; + +/* used when adb function is disabled */ +static struct usb_descriptor_header *null_adb_descs[] = { + NULL, +}; + + +/* temporary variable used between adb_open() and adb_gadget_bind() */ +static struct adb_dev *_adb_dev; + +static inline struct adb_dev *func_to_dev(struct usb_function *f) +{ + return container_of(f, struct adb_dev, function); +} + + +static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size) +{ + struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); + if (!req) + return NULL; + + /* now allocate buffers for the requests */ + req->buf = kmalloc(buffer_size, GFP_KERNEL); + if (!req->buf) { + usb_ep_free_request(ep, req); + return NULL; + } + + return req; +} + +static void adb_request_free(struct usb_request *req, struct usb_ep *ep) +{ + if (req) { + kfree(req->buf); + usb_ep_free_request(ep, req); + } +} + +static inline int _lock(atomic_t *excl) +{ + if (atomic_inc_return(excl) == 1) { + return 0; + } else { + atomic_dec(excl); + return -1; + } +} + +static inline void _unlock(atomic_t *excl) +{ + atomic_dec(excl); +} + +/* add a request to the tail of a list */ +void req_put(struct adb_dev *dev, struct list_head *head, + struct usb_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&req->list, head); + spin_unlock_irqrestore(&dev->lock, flags); +} + +/* remove a request from the head of a list */ +struct usb_request *req_get(struct adb_dev *dev, struct list_head *head) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&dev->lock, flags); + if (list_empty(head)) { + req = 0; + } else { + req = list_first_entry(head, struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&dev->lock, flags); + return req; +} + +static void adb_complete_in(struct usb_ep *ep, struct usb_request *req) +{ + struct adb_dev *dev = _adb_dev; + + if (req->status != 0) + dev->error = 1; + + req_put(dev, &dev->tx_idle, req); + + wake_up(&dev->write_wq); +} + +static void adb_complete_out(struct usb_ep *ep, struct usb_request *req) +{ + struct adb_dev *dev = _adb_dev; + + if (req->status != 0) { + dev->error = 1; + req_put(dev, &dev->rx_idle, req); + } else { + req_put(dev, &dev->rx_done, req); + } + + wake_up(&dev->read_wq); +} + +static int __init create_bulk_endpoints(struct adb_dev *dev, + struct usb_endpoint_descriptor *in_desc, + struct usb_endpoint_descriptor *out_desc) +{ + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + struct usb_ep *ep; + int i; + + DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); + + ep = usb_ep_autoconfig(cdev->gadget, in_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); + dev->ep_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, out_desc); + if (!ep) { + DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); + return -ENODEV; + } + DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name); + dev->ep_out = ep; + + /* now allocate requests for our endpoints */ + for (i = 0; i < RX_REQ_MAX; i++) { + req = adb_request_new(dev->ep_out, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_out; + req_put(dev, &dev->rx_idle, req); + } + + for (i = 0; i < TX_REQ_MAX; i++) { + req = adb_request_new(dev->ep_in, BULK_BUFFER_SIZE); + if (!req) + goto fail; + req->complete = adb_complete_in; + req_put(dev, &dev->tx_idle, req); + } + + return 0; + +fail: + printk(KERN_ERR "adb_bind() could not allocate requests\n"); + return -1; +} + +static ssize_t adb_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct adb_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req; + int r = count, xfer; + int ret; + + DBG(cdev, "adb_read(%d)\n", count); + + if (_lock(&dev->read_excl)) + return -EBUSY; + + /* we will block until we're online */ + while (!(dev->online || dev->error)) { + DBG(cdev, "adb_read: waiting for online state\n"); + ret = wait_event_interruptible(dev->read_wq, + (dev->online || dev->error)); + if (ret < 0) { + _unlock(&dev->read_excl); + return ret; + } + } + + while (count > 0) { + if (dev->error) { + DBG(cdev, "adb_read dev->error\n"); + r = -EIO; + break; + } + + /* if we have idle read requests, get them queued */ + while ((req = req_get(dev, &dev->rx_idle))) { +requeue_req: + req->length = BULK_BUFFER_SIZE; + ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC); + + if (ret < 0) { + r = -EIO; + dev->error = 1; + req_put(dev, &dev->rx_idle, req); + goto fail; + } else { + DBG(cdev, "rx %p queue\n", req); + } + } + + /* if we have data pending, give it to userspace */ + if (dev->read_count > 0) { + if (dev->read_count < count) + xfer = dev->read_count; + else + xfer = count; + + if (copy_to_user(buf, dev->read_buf, xfer)) { + r = -EFAULT; + break; + } + dev->read_buf += xfer; + dev->read_count -= xfer; + buf += xfer; + count -= xfer; + + /* if we've emptied the buffer, release the request */ + if (dev->read_count == 0) { + req_put(dev, &dev->rx_idle, dev->read_req); + dev->read_req = 0; + } + continue; + } + + /* wait for a request to complete */ + req = 0; + ret = wait_event_interruptible(dev->read_wq, + ((req = req_get(dev, &dev->rx_done)) || dev->error)); + if (req != 0) { + /* if we got a 0-len one we need to put it back into + ** service. if we made it the current read req we'd + ** be stuck forever + */ + if (req->actual == 0) + goto requeue_req; + + dev->read_req = req; + dev->read_count = req->actual; + dev->read_buf = req->buf; + DBG(cdev, "rx %p %d\n", req, req->actual); + } + + if (ret < 0) { + r = ret; + break; + } + } + +fail: + _unlock(&dev->read_excl); + DBG(cdev, "adb_read returning %d\n", r); + return r; +} + +static ssize_t adb_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct adb_dev *dev = fp->private_data; + struct usb_composite_dev *cdev = dev->cdev; + struct usb_request *req = 0; + int r = count, xfer; + int ret; + + DBG(cdev, "adb_write(%d)\n", count); + + if (_lock(&dev->write_excl)) + return -EBUSY; + + while (count > 0) { + if (dev->error) { + DBG(cdev, "adb_write dev->error\n"); + r = -EIO; + break; + } + + /* get an idle tx request to use */ + req = 0; + ret = wait_event_interruptible(dev->write_wq, + ((req = req_get(dev, &dev->tx_idle)) || dev->error)); + + if (ret < 0) { + r = ret; + break; + } + + if (req != 0) { + if (count > BULK_BUFFER_SIZE) + xfer = BULK_BUFFER_SIZE; + else + xfer = count; + if (copy_from_user(req->buf, buf, xfer)) { + r = -EFAULT; + break; + } + + req->length = xfer; + ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC); + if (ret < 0) { + DBG(cdev, "adb_write: xfer error %d\n", ret); + dev->error = 1; + r = -EIO; + break; + } + + buf += xfer; + count -= xfer; + + /* zero this so we don't try to free it on error exit */ + req = 0; + } + } + + if (req) + req_put(dev, &dev->tx_idle, req); + + _unlock(&dev->write_excl); + DBG(cdev, "adb_write returning %d\n", r); + return r; +} + +static int adb_open(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "adb_open\n"); + if (_lock(&_adb_dev->open_excl)) + return -EBUSY; + + fp->private_data = _adb_dev; + + /* clear the error latch */ + _adb_dev->error = 0; + + return 0; +} + +static int adb_release(struct inode *ip, struct file *fp) +{ + printk(KERN_INFO "adb_release\n"); + _unlock(&_adb_dev->open_excl); + return 0; +} + +/* file operations for ADB device /dev/android_adb */ +static struct file_operations adb_fops = { + .owner = THIS_MODULE, + .read = adb_read, + .write = adb_write, + .open = adb_open, + .release = adb_release, +}; + +static struct miscdevice adb_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = shortname, + .fops = &adb_fops, +}; + +static int __init +adb_function_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct adb_dev *dev = func_to_dev(f); + int id; + int ret; + + dev->cdev = cdev; + DBG(cdev, "adb_function_bind dev: %p\n", dev); + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + adb_interface_desc.bInterfaceNumber = id; + + /* allocate endpoints */ + ret = create_bulk_endpoints(dev, &adb_fullspeed_in_desc, + &adb_fullspeed_out_desc); + if (ret) + return ret; + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + adb_highspeed_in_desc.bEndpointAddress = + adb_fullspeed_in_desc.bEndpointAddress; + adb_highspeed_out_desc.bEndpointAddress = + adb_fullspeed_out_desc.bEndpointAddress; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, dev->ep_in->name, dev->ep_out->name); + return 0; +} + +static void +adb_function_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct adb_dev *dev = func_to_dev(f); + struct usb_request *req; + + spin_lock_irq(&dev->lock); + + while ((req = req_get(dev, &dev->rx_idle))) + adb_request_free(req, dev->ep_out); + while ((req = req_get(dev, &dev->tx_idle))) + adb_request_free(req, dev->ep_in); + + dev->online = 0; + dev->error = 1; + spin_unlock_irq(&dev->lock); + + misc_deregister(&adb_device); + kfree(_adb_dev); + _adb_dev = NULL; +} + +static int adb_function_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct adb_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = f->config->cdev; + int ret; + + DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt); + ret = usb_ep_enable(dev->ep_in, + ep_choose(cdev->gadget, + &adb_highspeed_in_desc, + &adb_fullspeed_in_desc)); + if (ret) + return ret; + ret = usb_ep_enable(dev->ep_out, + ep_choose(cdev->gadget, + &adb_highspeed_out_desc, + &adb_fullspeed_out_desc)); + if (ret) { + usb_ep_disable(dev->ep_in); + return ret; + } + dev->online = 1; + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + return 0; +} + +static void adb_function_disable(struct usb_function *f) +{ + struct adb_dev *dev = func_to_dev(f); + struct usb_composite_dev *cdev = dev->cdev; + + DBG(cdev, "adb_function_disable\n"); + dev->online = 0; + dev->error = 1; + usb_ep_disable(dev->ep_in); + usb_ep_disable(dev->ep_out); + + /* readers may be blocked waiting for us to go online */ + wake_up(&dev->read_wq); + + VDBG(cdev, "%s disabled\n", dev->function.name); +} + +int __init adb_function_add(struct usb_composite_dev *cdev, + struct usb_configuration *c) +{ + struct adb_dev *dev; + int ret; + + printk(KERN_INFO "adb_function_add\n"); + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + spin_lock_init(&dev->lock); + + init_waitqueue_head(&dev->read_wq); + init_waitqueue_head(&dev->write_wq); + + atomic_set(&dev->open_excl, 0); + atomic_set(&dev->read_excl, 0); + atomic_set(&dev->write_excl, 0); + + INIT_LIST_HEAD(&dev->rx_idle); + INIT_LIST_HEAD(&dev->rx_done); + INIT_LIST_HEAD(&dev->tx_idle); + + dev->cdev = cdev; + dev->function.name = "adb"; + dev->function.descriptors = null_adb_descs; + dev->function.hs_descriptors = null_adb_descs; + dev->function.bind = adb_function_bind; + dev->function.unbind = adb_function_unbind; + dev->function.set_alt = adb_function_set_alt; + dev->function.disable = adb_function_disable; + + /* _adb_dev must be set before calling usb_gadget_register_driver */ + _adb_dev = dev; + + ret = misc_register(&adb_device); + if (ret) + goto err1; + ret = usb_add_function(c, &dev->function); + if (ret) + goto err2; + + return 0; + +err2: + misc_deregister(&adb_device); +err1: + kfree(dev); + printk(KERN_ERR "adb gadget driver failed to initialize\n"); + return ret; +} + +void adb_function_enable(int enable) +{ + struct adb_dev *dev = _adb_dev; + + if (dev) { + DBG(dev->cdev, "adb_function_enable(%s)\n", + enable ? "true" : "false"); + + if (enable) { + dev->function.descriptors = fs_adb_descs; + dev->function.hs_descriptors = hs_adb_descs; + } else { + dev->function.descriptors = null_adb_descs; + dev->function.hs_descriptors = null_adb_descs; + } + } +} + diff --git a/drivers/usb/gadget/f_adb.h b/drivers/usb/gadget/f_adb.h new file mode 100644 index 00000000000000..4854ff6297975e --- /dev/null +++ b/drivers/usb/gadget/f_adb.h @@ -0,0 +1,25 @@ +/* + * Gadget Driver for Android ADB + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * 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. + * + */ + +#ifndef __F_ADB_H +#define __F_ADB_H + +int adb_function_add(struct usb_composite_dev *cdev, + struct usb_configuration *c); +void adb_function_enable(int enable); + +#endif /* __F_ADB_H */ diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/f_mass_storage.h new file mode 100644 index 00000000000000..8e63ac0cbe9316 --- /dev/null +++ b/drivers/usb/gadget/f_mass_storage.h @@ -0,0 +1,52 @@ +/* + * drivers/usb/gadget/f_mass_storage.h + * + * Function Driver for USB Mass Storage + * + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood + * + * Based heavily on the file_storage gadget driver in + * drivers/usb/gadget/file_storage.c and licensed under the same terms: + * + * Copyright (C) 2003-2007 Alan Stern + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __F_MASS_STORAGE_H +#define __F_MASS_STORAGE_H + +int mass_storage_function_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, int nluns); + +#endif /* __F_MASS_STORAGE_H */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e511fec9f26d8d..df97b09aedc949 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -120,10 +120,10 @@ #define gadget_is_fsl_qe(g) 0 #endif -#ifdef CONFIG_USB_GADGET_CI13XXX -#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name)) +#ifdef CONFIG_USB_GADGET_PXA_U2O +#define gadget_is_pxau2o(g) !strcmp("pxa-u2o", (g)->name) #else -#define gadget_is_ci13xxx(g) 0 +#define gadget_is_pxau2o(g) 0 #endif // CONFIG_USB_GADGET_SX2 @@ -192,7 +192,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x21; else if (gadget_is_fsl_qe(gadget)) return 0x22; - else if (gadget_is_ci13xxx(gadget)) + else if (gadget_is_pxau2o(gadget)) return 0x23; else if (gadget_is_langwell(gadget)) return 0x24; diff --git a/drivers/usb/gadget/mv/Makefile b/drivers/usb/gadget/mv/Makefile new file mode 100644 index 00000000000000..f40679532b9201 --- /dev/null +++ b/drivers/usb/gadget/mv/Makefile @@ -0,0 +1,5 @@ +# +# USB device controller drivers +# +obj-$(CONFIG_USB_PXA_U2O) += mvUsbDevMain.o mvUsbDevCh9.o mvUsbDevUtl.o mvUsbHsDevMain.o mvUsbDevSend.o mvUsbDevRecv.o mvUsbHsDevCncl.o mvUsbHsDevUtl.o + diff --git a/drivers/usb/gadget/mv/mvUsbCh9.h b/drivers/usb/gadget/mv/mvUsbCh9.h new file mode 100644 index 00000000000000..c5138779ade462 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbCh9.h @@ -0,0 +1,125 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbCh9_h__ +#define __mvUsbCh9_h__ + +#include "mvUsbTypes.h" +/*----------------------------------------------------------------** +** Chapter 9.4 Standard Device Requests -- all devices ** +** See Table 9-3 p. 250 of USB 2.0 spec for combinations ** +** of request type bitfields with requests, WVALUE, WINDEX etc. ** +**----------------------------------------------------------------*/ +#define REQ_RECIP_MASK 0x1f +#define REQ_RECIP_DEVICE 0x00 +#define REQ_RECIP_INTERFACE 0x01 +#define REQ_RECIP_ENDPOINT 0x02 +#define REQ_RECIP_OTHER 0x03 + +/* Also for class requests set the following bit */ +#define REQ_TYPE_OFFSET 5 +#define REQ_TYPE_MASK (0x03 << REQ_TYPE_OFFSET) +#define REQ_TYPE_STANDARD (0x00 << REQ_TYPE_OFFSET) +#define REQ_TYPE_CLASS (0x01 << REQ_TYPE_OFFSET) +#define REQ_TYPE_VENDOR (0x02 << REQ_TYPE_OFFSET) +#define REQ_TYPE_RESERVED (0x03 << REQ_TYPE_OFFSET) + +/* Combine one of the 3 above with one of the following 2 */ +#define REQ_DIR_OFFSET 7 +#define REQ_DIR_IN (1 << REQ_DIR_OFFSET) +#define REQ_DIR_OUT (0 << REQ_DIR_OFFSET) + +/* Standard USB requests, see Chapter 9 */ +#define REQ_GET_STATUS 0 +#define REQ_CLEAR_FEATURE 1 +#define REQ_SET_FEATURE 3 +#define REQ_SET_ADDRESS 5 +#define REQ_GET_DESCRIPTOR 6 +#define REQ_SET_DESCRIPTOR 7 +#define REQ_GET_CONFIGURATION 8 +#define REQ_SET_CONFIGURATION 9 +#define REQ_GET_INTERFACE 10 +#define REQ_SET_INTERFACE 11 +#define REQ_SYNCH_FRAME 12 + +#define DESC_TYPE_DEVICE 0x1 +#define DESC_TYPE_CONFIG 0x2 +#define DESC_TYPE_STRING 0x3 +#define DESC_TYPE_INTERFACE 0x4 +#define DESC_TYPE_ENDPOINT 0x5 +#define DESC_TYPE_QUALIFIER 0x6 +#define DESC_TYPE_OTHER_SPEED 0x7 +#define DESC_TYPE_INTF_POWER 0x8 +#define DESC_TYPE_OTG 0x9 + +/******************************************************************* +** +** Values specific to CLEAR FEATURE commands (must go to common.h later) +*/ + +#define ENDPOINT_HALT 0 +#define DEVICE_SELF_POWERED 0 +#define DEVICE_REMOTE_WAKEUP 1 +#define DEVICE_TEST_MODE 2 + + +/* States of device instances on the device list */ + +/* initial device state */ +#define DEVSTATE_INITIAL 0x00 + +/* device descriptor [0..7]*/ +#define DEVSTATE_DEVDESC8 0x01 + +/* address set */ +#define DEVSTATE_ADDR_SET 0x02 + +/* full device descriptor */ +#define DEVSTATE_DEV_DESC 0x03 + +/* config descriptor [0..7] */ +#define DEVSTATE_GET_CFG9 0x04 + +/* config set */ +#define DEVSTATE_SET_CFG 0x05 + +/* full config desc. read in */ +#define DEVSTATE_CFG_READ 0x06 + +/* application callbacks */ +#define DEVSTATE_APP_CALL 0x07 + +/* Select interface done */ +#define DEVSTATE_SET_INTF 0x08 + +#define DEVSTATE_ENUM_OK 0x09 + +#define DEVSTATE_CHK_OTG 0x0A + +/* Event codes for attach/detach etc. callback */ +#define USB_ATTACH_EVENT 1 /* device attach */ +#define USB_DETACH_EVENT 2 /* device detach */ +#define USB_CONFIG_EVENT 3 /* device reconfigured */ +#define USB_INTF_EVENT 4 /* device interface selected */ + +#endif /* __mvUsbCh9_h__ */ + +/* EOF */ + diff --git a/drivers/usb/gadget/mv/mvUsbCore.h b/drivers/usb/gadget/mv/mvUsbCore.h new file mode 100644 index 00000000000000..056e9d099bba2b --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbCore.h @@ -0,0 +1,757 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbCore_h__ +#define __mvUsbCore_h__ + +#include "mvUsbTypes.h" + +/* VUSBHS specific defines */ +#define VUSBHS_MAX_PORTS (8) +#define EHCI_CAP_LEN_MASK (0x000000FF) +#define EHCI_DATA_STRUCTURE_BASE_ADDRESS (0) + +/* Command Register Bit Masks */ +#define EHCI_CMD_RUN_STOP (0x00000001) +#define EHCI_CMD_CTRL_RESET (0x00000002) +#define EHCI_CMD_SETUP_TRIPWIRE_SET (0x00002000) +#define EHCI_CMD_SETUP_TRIPWIRE_CLEAR ~EHCI_CMD_SETUP_TRIPWIRE_SET + +#define EHCI_CMD_ATDTW_TRIPWIRE_SET (0x00004000) +#define EHCI_CMD_ATDTW_TRIPWIRE_CLEAR ~EHCI_CMD_ATDTW_TRIPWIRE_SET + +/*bit 15,3,2 are for frame list size */ +#define EHCI_CMD_FRAME_SIZE_1024 (0x00000000) /* 000 */ +#define EHCI_CMD_FRAME_SIZE_512 (0x00000004) /* 001 */ +#define EHCI_CMD_FRAME_SIZE_256 (0x00000008) /* 010 */ +#define EHCI_CMD_FRAME_SIZE_128 (0x0000000C) /* 011 */ +#define EHCI_CMD_FRAME_SIZE_64 (0x00008000) /* 100 */ +#define EHCI_CMD_FRAME_SIZE_32 (0x00008004) /* 101 */ +#define EHCI_CMD_FRAME_SIZE_16 (0x00008008) /* 110 */ +#define EHCI_CMD_FRAME_SIZE_8 (0x0000800C) /* 111 */ + +/* Hardware Rev 4.0 related change */ +/* Mode Register Bit Masks */ +#define VUSBHS_MODE_CTRL_MODE_IDLE (0x00000000) +#define VUSBHS_MODE_CTRL_MODE_DEV (0x00000002) +#define VUSBHS_MODE_CTRL_MODE_HOST (0x00000003) +#define VUSBHS_MODE_BIG_ENDIAN (0x00000004) +#define VUSBHS_MODE_SETUP_LOCK_DISABLE (0x00000008) +#define VUSBHS_MODE_STREAM_DISABLE (0x00000010) + +/* Interrupt Enable Register Bit Masks */ +#define EHCI_INTR_INT_EN (0x00000001) +#define EHCI_INTR_ERR_INT_EN (0x00000002) +#define EHCI_INTR_PORT_CHANGE_DETECT_EN (0x00000004) + +#define EHCI_INTR_ASYNC_ADV_AAE (0x00000020) +#define EHCI_INTR_ASYNC_ADV_AAE_ENABLE (0x00000020) /* | with this to enable */ +#define EHCI_INTR_ASYNC_ADV_AAE_DISABLE (0xFFFFFFDF) /* & with this to disable */ + +#define EHCI_INTR_RESET_EN (0x00000040) +#define EHCI_INTR_SOF_UFRAME_EN (0x00000080) +#define EHCI_INTR_DEVICE_SUSPEND (0x00000100) + +/* Interrupt Status Register Masks */ +#define EHCI_STS_SOF (0x00000080) +#define EHCI_STS_RESET (0x00000040) +#define EHCI_STS_SEI (0x00000010) +#define EHCI_STS_PORT_CHANGE (0x00000004) +#define EHCI_STS_ERR (0x00000002) +#define EHCI_STS_INT (0x00000001) +#define EHCI_STS_SUSPEND (0x00000100) +#define EHCI_STS_HC_HALTED (0x00001000) + +/* Endpoint Queue Head Bit Masks */ +#define VUSB_EP_QUEUE_HEAD_IOS (0x00008000) +#define VUSB_EP_QUEUE_HEAD_IOC (0x00008000) +#define VUSB_EP_QUEUE_HEAD_INT (0x00000100) +#define VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE (0x00000001) +#define VUSB_EP_QUEUE_HEAD_MAX_PKT_LEN_POS (16) +#define VUSB_EP_QUEUE_HEAD_ZERO_LEN_TER_SEL (0x20000000) +#define VUSB_EP_QUEUE_HEAD_MULT_POS (30) +#define VUSB_EP_MAX_LENGTH_TRANSFER (0x4000) + +#define VUSB_EP_QUEUE_HEAD_STATUS_ACTIVE (0x00000080) + +#define VUSBHS_TD_NEXT_TERMINATE (0x00000001) +#define VUSBHS_TD_IOC (0x00008000) +#define VUSBHS_TD_STATUS_ACTIVE (0x00000080) +#define VUSBHS_TD_STATUS_HALTED (0x00000040) +#define VUSBHS_TD_RESERVED_FIELDS (0x00007F00) +#define VUSBHS_TD_ERROR_MASK (0x68) +#define VUSBHS_TD_ADDR_MASK (0xFFFFFFE0) +#define VUSBHS_TD_LENGTH_BIT_POS (16) + +#define EHCI_EPCTRL_TX_ALL_MASK (0xFFFF0000) +#define EHCI_EPCTRL_RX_ALL_MASK (0x0000FFFF) + +#define EHCI_EPCTRL_TX_DATA_TOGGLE_RST (0x00400000) +#define EHCI_EPCTRL_TX_EP_STALL (0x00010000) +#define EHCI_EPCTRL_RX_EP_STALL (0x00000001) +#define EHCI_EPCTRL_RX_DATA_TOGGLE_RST (0x00000040) +#define EHCI_EPCTRL_RX_ENABLE (0x00000080) +#define EHCI_EPCTRL_TX_ENABLE (0x00800000) +#define EHCI_EPCTRL_CONTROL (0x00000000) +#define EHCI_EPCTRL_ISOCHRONOUS (0x00040000) +#define EHCI_EPCTRL_BULK (0x00080000) +#define EHCI_EPCTRL_INT (0x000C0000) +#define EHCI_EPCTRL_TX_TYPE (0x000C0000) +#define EHCI_EPCTRL_RX_TYPE (0x0000000C) +#define EHCI_EPCTRL_DATA_TOGGLE_INHIBIT (0x00000020) +#define EHCI_EPCTRL_TX_EP_TYPE_SHIFT (18) +#define EHCI_EPCTRL_RX_EP_TYPE_SHIFT (2) + +/* set bit 24 (PFSC) in PORTSCX register */ +#define EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT (0x01000000) +/* set bit 23 (PHCD) in PORTSCX register */ +#define EHCI_PORTSCX_PHY_CLOCK_DISABLE (0x00800000) +#define EHCI_PORTSCX_PAR_XCVR_SELECT (0xC0000000) +#define EHCI_PORTSCX_PORT_POWER (0x00001000) +#define EHCI_PORTSCX_LINE_STATUS_BITS (0x00000C00) +#define EHCI_PORTSCX_LINE_STATUS_SE0 (0x00000000) +#define EHCI_PORTSCX_LINE_STATUS_KSTATE (0x00000400) +#define EHCI_PORTSCX_LINE_STATUS_JSTATE (0x00000800) +#define EHCI_PORTSCX_PORT_HIGH_SPEED (0x00000200) +#define EHCI_PORTSCX_PORT_RESET (0x00000100) +#define EHCI_PORTSCX_PORT_SUSPEND (0x00000080) +#define EHCI_PORTSCX_PORT_FORCE_RESUME (0x00000040) +#define EHCI_PORTSCX_PORT_EN_DIS_CHANGE (0x00000008) +#define EHCI_PORTSCX_PORT_ENABLE (0x00000004) +#define EHCI_PORTSCX_CONNECT_STATUS_CHANGE (0x00000002) +#define EHCI_PORTSCX_CURRENT_CONNECT_STATUS (0x00000001) + +#define VUSBHS_PORTSCX_PORT_SPEED_FULL (0x00000000) +#define VUSBHS_PORTSCX_PORT_SPEED_LOW (0x04000000) +#define VUSBHS_PORTSCX_PORT_SPEED_HIGH (0x08000000) +#define VUSBHS_SPEED_MASK (0x0C000000) +#define VUSBHS_SPEED_BIT_POS (26) + +#define EHCI_PORTSCX_W1C_BITS (0x2A) +#define VUSB_EP_QH_PACKET_SIZE (0x3FFF0000) +#define VUSB_EP_TR_PACKET_SIZE (0x7FFF0000) + +// These are not EHCI defined, but named so for consistency +// // and are for selecting the transceiver (XCVR) type +// //------------------------------------------------------------- +#define EHCI_PORTSCX_UTMI_XCVR_SELECT (0x00000000) +#define EHCI_PORTSCX_ULPI_XCVR_SELECT (0x80000000) +#define EHCI_PORTSCX_SERIAL_XCVR_SELECT (0xC0000000) + +#define VUSBHS_FRINDEX_MS_MASK (0xFFFFFFF8) +#define VUSBHS_ADDRESS_BIT_SHIFT (25) + +#define VUSB20_DCC_MAX_ENDPTS_SUPPORTED (0x1F) +#define VUSB20_DCC_HOST_MODE_MASK (1 << 8) +#define VUSB20_DCC_DEVICE_MODE_MASK (1 << 7) + +#define EHCI_HCC_PARAMS_64_BIT_ADDR_CAP (0x01) +#define EHCI_HCC_PARAMS_PGM_FRM_LIST_FLAG (0x02) +#define EHCI_HCC_PARAMS_ASYNC_PARK_CAP (0x04) +#define EHCI_HCC_PARAMS_ISOCH_SCHED_THRESHOLD (0xF0) +#define EHCI_HCC_PARAMS_ISOCH_FRAME_CACHED (0x80) + +#define VUSB20_HCS_PARAMS_PORT_POWER_CONTROL_FLAG (0x10) + +#define VUSB20_HOST_INTR_EN_BITS (0x37) + +#define VUSB20_DEFAULT_PERIODIC_FRAME_LIST_SIZE (1024) +#define VUSB20_NEW_PERIODIC_FRAME_LIST_BITS (2) +#define EHCI_FRAME_LIST_ELEMENT_POINTER_T_BIT (0x01) +#define EHCI_ITD_T_BIT (0x01) +#define EHCI_SITD_T_BIT (0x01) +#define EHCI_QUEUE_HEAD_POINTER_T_BIT (0x01) + + +/************************************************************ +Split transatcions specific defines +************************************************************/ +#define EHCI_START_SPLIT_MAX_BUDGET 188 + +#define EHCI_ELEMENT_TYPE_ITD (0x00) +#define EHCI_ELEMENT_TYPE_QH (0x02) +#define EHCI_ELEMENT_TYPE_SITD (0x04) +#define EHCI_ELEMENT_TYPE_FSTN (0x06) +#define EHCI_ELEMENT_TYPE_MASK (0x06) + +#define EHCI_FRAME_LIST_ELEMENT_TYPE_ITD (0x00) +#define EHCI_FRAME_LIST_ELEMENT_TYPE_QH (0x01) +#define EHCI_FRAME_LIST_ELEMENT_TYPE_SITD (0x02) +#define EHCI_FRAME_LIST_ELEMENT_TYPE_FSTN (0x03) +#define EHCI_FRAME_LIST_ELEMENT_TYPE_BIT_POS (1) + + +#define EHCI_QH_ELEMENT_TYPE_ITD (0x00) +#define EHCI_QH_ELEMENT_TYPE_QH (0x01) +#define EHCI_QH_ELEMENT_TYPE_SITD (0x02) +#define EHCI_QH_ELEMENT_TYPE_FSTN (0x03) + +#define EHCI_QH_ELEMENT_TYPE_BIT_POS (1) + +#define EHCI_QTD_PID_OUT_TOKEN (0x000) +#define EHCI_QTD_PID_IN_TOKEN (0x100) +#define EHCI_QTD_PID_SETUP_TOKEN (0x200) +#define EHCI_QTD_IOC (0x8000) +#define EHCI_QTD_STATUS_ACTIVE (0x0080) +#define EHCI_QTD_STATUS_HALTED (0x0040) +#define EHCI_QTD_PID_SETUP (0x0200) +#define EHCI_QTD_PID_IN (0x0100) +#define EHCI_QTD_PID_OUT (0x0000) +#define EHCI_QTD_LENGTH_BIT_POS (16) +#define EHCI_QTD_DATA_TOGGLE (0x80000000) +#define EHCI_QTD_DATA_TOGGLE_BIT_POS (31) +#define EHCI_QTD_LENGTH_BIT_MASK (0x7FFF0000) +#define EHCI_QTD_ERROR_BITS_MASK (0x0000003E) +#define EHCI_QTD_DEFAULT_CERR_VALUE (0xC00) + +#define EHCI_SETUP_TOKEN (2) +#define EHCI_OUT_TOKEN (0) +#define EHCI_IN_TOKEN (1) + +#define EHCI_QTD_T_BIT (0x01) + +#define EHCI_QH_ENDPOINT_SPEED_FULL (0x00) +#define EHCI_QH_ENDPOINT_SPEED_LOW (0x01) +#define EHCI_QH_ENDPOINT_SPEED_HIGH (0x02) +#define EHCI_QH_ENDPOINT_SPEED_RESERVED (0x03) + +#define EHCI_ITD_LENGTH_BIT_POS (16) +#define EHCI_ITD_IOC_BIT (0x00008000) +#define EHCI_ITD_ACTIVE_BIT (0x80000000) +#define EHCI_ITD_PG_SELECT_BIT_POS (12) +#define EHCI_ITD_DIRECTION_BIT_POS (11) +#define EHCI_ITD_EP_BIT_POS (8) +#define EHCI_ITD_STATUS (0xF0000000) +#define EHCI_ITD_STATUS_ACTIVE (0x80000000) /*bit 4 = 1000*/ +#define EHCI_ITD_STATUS_DATA_BUFFER_ERR (0x40000000) /*bit 3 = 0100*/ +#define EHCI_ITD_STATUS_BABBLE_ERROR (0x20000000) /*bit 2 = 0010*/ +#define EHCI_ITD_STATUS_TRANSACTION_ERR (0x10000000) /*bit 4 = 0001*/ + +#define EHCI_ITD_LENGTH_TRANSMITTED (0x0FFF0000) +#define EHCI_ITD_BUFFER_OFFSET (0x00000FFF) +#define EHCI_ITD_PAGE_NUMBER (0x00007000) +#define EHCI_ITD_BUFFER_POINTER (0xFFFFF000) +#define EHCI_ITD_MULTI_TRANSACTION_BITS (0x00000003) + + + +/* SITD position bits */ +#define EHCI_SITD_DIRECTION_BIT_POS (31) +#define EHCI_SITD_PORT_NUMBER_BIT_POS (24) +#define EHCI_SITD_HUB_ADDR_BIT_POS (16) +#define EHCI_SITD_EP_ADDR_BIT_POS (8) + +#define EHCI_SITD_COMPLETE_SPLIT_MASK_BIT_POS (8) + +#define EHCI_SITD_IOC_BIT_SET (0x80000000) +#define EHCI_SITD_PAGE_SELECT_BIT_POS (30) +#define EHCI_SITD_TRANSFER_LENGTH_BIT_POS (16) +#define EHCI_SITD_STATUS_ACTIVE (0x80) + +#define EHCI_SITD_STATUS (0xFF) +#define EHCI_SITD_LENGTH_TRANSMITTED (0x03FF0000) +#define EHCI_SITD_BUFFER_OFFSET (0x00000FFF) +#define EHCI_SITD_PAGE_NUMBER (0x40000000) +#define EHCI_SITD_BUFFER_POINTER (0xFFFFF000) + + + +#define EHCI_SITD_BUFFER_PTR_BIT_POS (12) +#define EHCI_SITD_TP_BIT_POS (3) +#define EHCI_SITD_TP_ALL (0) +#define EHCI_SITD_TP_BEGIN (1) +#define EHCI_SITD_TP_MID (2) +#define EHCI_SITD_TP_END (3) + + + +/* Interrupt enable bit masks */ +#define EHCI_IER_ASYNCH_ADVANCE (0x00000020) +#define EHCI_IER_HOST_SYS_ERROR (0x00000010) +#define EHCI_IER_FRAME_LIST_ROLLOVER (0x00000008) +#define EHCI_IER_PORT_CHANGE (0x00000004) +#define EHCI_IER_USB_ERROR (0x00000002) +#define EHCI_IER_USB_INTERRUPT (0x00000001) + +/* Interrupt status bit masks */ +#define EHCI_STS_RECLAIMATION (0x00002000) +#define EHCI_STS_SOF_COUNT (0x00000080) +#define EHCI_STS_ASYNCH_ADVANCE (0x00000020) +#define EHCI_STS_HOST_SYS_ERROR (0x00000010) +#define EHCI_STS_FRAME_LIST_ROLLOVER (0x00000008) +#define EHCI_STS_PORT_CHANGE (0x00000004) +#define EHCI_STS_USB_ERROR (0x00000002) +#define EHCI_STS_USB_INTERRUPT (0x00000001) + +/* Status bit masks */ +#define EHCI_STS_ASYNCH_SCHEDULE (0x00008000) +#define EHCI_STS_PERIODIC_SCHEDULE (0x00004000) +#define EHCI_STS_RECLAMATION (0x00002000) +#define EHCI_STS_HC_HALTED (0x00001000) + +/* USB command bit masks */ +#define EHCI_USBCMD_ASYNC_SCHED_ENABLE (0x00000020) +#define EHCI_USBCMD_PERIODIC_SCHED_ENABLE (0x00000010) + +#define EHCI_HCS_PARAMS_N_PORTS (0x0F) + +#define VUSB_HS_DELAY (3500) + +#define EHCI_QH_EP_NUM_MASK (0x0F00) +#define EHCI_QH_EP_NUM_BITS_POS (8) +#define EHCI_QH_DEVICE_ADDRESS_MASK (0x7F) +#define EHCI_QH_SPEED_BITS_POS (12) +#define EHCI_QH_MAX_PKT_SIZE_BITS_POS (16) +#define EHCI_QH_NAK_COUNT_RL_BITS_POS (28) +#define EHCI_QH_EP_CTRL_FLAG_BIT_POS (27) +#define EHCI_QH_HEAD_RECLAMATION_BIT_POS (15) +#define EHCI_QH_DTC_BIT_POS (14) +#define EHCI_QH_HIGH_BW_MULT_BIT_POS (30) +#define EHCI_QH_HUB_PORT_NUM_BITS_POS (23) +#define EHCI_QH_HUB_ADDR_BITS_POS (16) +#define EHCI_QH_SPLIT_COMPLETION_MASK_BITS_POS (8) +#define EHCI_QH_SPLIT_COMPLETION_MASK (0xFF00) +#define EHCI_QH_INTR_SCHED_MASK (0xFF) +#define EHCI_QH_INACTIVATE_NEXT_TR_BIT_POS (7) +#define EHCI_QH_HORIZ_PHY_ADDRESS_MASK (0xFFFFFFE0) +#define EHCI_QH_TR_OVERLAY_DT_BIT (0x80000000) + +#define EHCI_SITD_SPLIT_COMPLETION_MASK_BITS_POS (8) + +#define EHCI_INTR_NO_THRESHOLD_IMMEDIATE (0x00010000) +#define EHCI_NEW_PERIODIC_FRAME_LIST_SIZE (1024) +#define EHCI_FRAME_LIST_SIZE_BITS_POS (2) +#define EHCI_HORIZ_PHY_ADDRESS_MASK (0xFFFFFFE0) + +#define DEFAULT_MAX_NAK_COUNT (15) + +/* OTG Status and control register bit masks */ + +/* OTG interrupt enable bit masks */ +#define VUSBHS_OTGSC_INTERRUPT_ENABLE_BITS_MASK (0x5F000000) +#define VUSBHS_OTGSC_DPIE (0x40000000) /* Data-line pulsing IE */ +#define VUSBHS_OTGSC_1MSIE (0x20000000) +#define VUSBHS_OTGSC_BSEIE (0x10000000) /* B-session end IE */ +#define VUSBHS_OTGSC_BSVIE (0x08000000) /* B-session valid IE */ +#define VUSBHS_OTGSC_ASVIE (0x04000000) /* A-session valid IE */ +#define VUSBHS_OTGSC_AVVIE (0x02000000) /* A-V-bus valid IE */ +#define VUSBHS_OTGSC_IDIE (0x01000000) /* OTG ID IE */ + +/* OTG interrupt status bit masks */ +#define VUSBHS_OTGSC_INTERRUPT_STATUS_BITS_MASK (0x005F0000) +#define VUSBHS_OTGSC_DPIS (0x00400000) /* Data-line pulsing IS */ +#define VUSBHS_OTGSC_1MSIS (0x00200000) +#define VUSBHS_OTGSC_BSEIS (0x00100000) /* B-session end IS */ +#define VUSBHS_OTGSC_BSVIS (0x00080000) /* B-session valid IS */ +#define VUSBHS_OTGSC_ASVIS (0x00040000) /* A-session valid IS */ +#define VUSBHS_OTGSC_AVVIS (0x00020000) /* A-Vbus valid IS */ +#define VUSBHS_OTGSC_IDIS (0x00010000) /* OTG ID IS */ + +/* OTG status bit masks */ +#define VUSBHS_OTGSC_DPS (0x00004000) +#define VUSBHS_OTGSC_BSE (0x00001000) /* B-session end */ +#define VUSBHS_OTGSC_BSV (0x00000800) /* B-session valid */ +#define VUSBHS_OTGSC_ASV (0x00000400) /* A-session valid */ +#define VUSBHS_OTGSC_AVV (0x00000200) /* A-Vbus Valid */ +#define VUSBHS_OTGSC_ID (0x00000100) /* OTG ID */ + +/* OTG control bit masks */ +#define VUSBHS_OTGSC_CTL_BITS (0x2F) +#define VUSBHS_OTGSC_HABA (0x00000080) /* hardware assisted data pulse bits*/ +#define VUSBHS_OTGSC_HADP (0x00000040) /* hardware assisted data pulse bits*/ + +#ifdef PATCH_3 +/* the following change is to be compatable with 4.0 revision of +hardware. Enable the following switch in config.mk to enable the +changes. */ + + /* WEB20040409 below line changed from VUSBHS_OTGSC_B_HOST_EN to VUSBHS_OTGSC_IDPU + to reflect change in usbhs4.0 B_HOST_EN has not been used quite some time */ + #define VUSBHS_OTGSC_IDPU (0x00000020) /* ID pull enable */ +#else + #define VUSBHS_OTGSC_B_HOST_EN (0x00000020) /* B_host_enable */ +#endif + +#define VUSBHS_OTGSC_DP (0x00000010) /* Data-pulsing */ +#define VUSBHS_OTGSC_OT (0x00000008) /* OTG termination */ +#if 0 + #define VUSBHS_OTGSC_VO (0x00000004) /* Vbus on */ +#endif + +#define VUSBHS_OTGSC_HAAR (0x00000004) /* Auto reset bit*/ + +#define VUSBHS_OTGSC_VC (0x00000002) /* Vbus charge */ +#define VUSBHS_OTGSC_VD (0x00000001) /* Vbus discharge */ + +typedef uint_32 USB_REGISTER, _PTR_ USB_REGISTER_PTR; + +/* The VUSB register structure */ +typedef struct { + union { + struct { + volatile USB_REGISTER CAPLENGTH_HCIVER; + volatile USB_REGISTER HCS_PARAMS; /* HC structural parameters */ + volatile USB_REGISTER HCC_PARAMS; /* HC Capability Parameters*/ + volatile USB_REGISTER RESERVED1[5]; + volatile USB_REGISTER DCI_VERSION; /* DC version number and reserved 16 bits */ + volatile USB_REGISTER DCC_PARAMS; /* DC Capability Parameters */ + } CAPABILITY_REGISTERS; + + struct { + volatile USB_REGISTER USB_CMD; /* Command register */ + volatile USB_REGISTER USB_STS; /* Status register */ + volatile USB_REGISTER USB_INTR; /* Interrupt enable */ + volatile USB_REGISTER USB_FRINDEX; /* Frame index */ +#if 0 + volatile USB_REGISTER CTRLDSSEGMENT; /* 4G segment selector */ +#else + volatile USB_REGISTER RESERVED2[1]; +#endif + volatile USB_REGISTER DEVICE_ADDR; /* Device Address */ + + volatile USB_REGISTER EP_LIST_ADDR; /* Endpoint List Address */ + +#if 1 +//xj add + + volatile USB_REGISTER TT_CTRL; /* HOST TT status and control */ + volatile USB_REGISTER BURST_SIZE; /* Programmable Burst Size */ + volatile USB_REGISTER TX_FILL_TUNING; /* Host Transmit Pre-Buffer Packet Tuning */ + + volatile USB_REGISTER RESERVED0[4]; + + volatile USB_REGISTER EP_NAK; /* Endpoint NAK */ + volatile USB_REGISTER EP_NAK_EN; /* Endpoint NAK Enable */ +#else + volatile USB_REGISTER RESERVED0[9]; +#endif + volatile USB_REGISTER CONFIG_FLAG; /* Configured Flag register */ + volatile USB_REGISTER PORTSCX[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ + volatile USB_REGISTER OTGSC; + volatile USB_REGISTER USB_MODE; /* USB Host/Device mode */ + volatile USB_REGISTER ENDPT_SETUP_STAT; /* Endpoint Setup Status */ + volatile USB_REGISTER ENDPTPRIME; /* Endpoint Initialize */ + volatile USB_REGISTER ENDPTFLUSH; /* Endpoint De-initialize */ + volatile USB_REGISTER ENDPTSTATUS; /* Endpoint Status */ + volatile USB_REGISTER ENDPTCOMPLETE; /* Endpoint Interrupt On Complete */ + volatile USB_REGISTER ENDPTCTRLX[16]; /* Endpoint Control, where x = 0.. 15 */ +//xj add + volatile USB_REGISTER MCR; /* Mux Control */ + volatile USB_REGISTER ISR; /* Interrupt Status */ + volatile USB_REGISTER IER; /* Interrupt Enable */ +//xj add end + } OPERATIONAL_DEVICE_REGISTERS; + +#if 0 + xj del + struct { + volatile USB_REGISTER USB_CMD; /* Command register */ + volatile USB_REGISTER USB_STS; /* Status register */ + volatile USB_REGISTER USB_INTR; /* Interrupt enable */ + volatile USB_REGISTER USB_FRINDEX; /* Frame index */ + volatile USB_REGISTER CTRLDSSEGMENT; /* 4G segment selector */ + volatile USB_REGISTER PERIODIC_LIST_BASE_ADDR; /* Periodic schedule list */ + volatile USB_REGISTER CURR_ASYNC_LIST_ADDR; /* Current Asynch schedule list */ + volatile USB_REGISTER ASYNCTTSTS; /* Async buffer in embedded TT control */ + volatile USB_REGISTER RESERVED0[8]; + volatile USB_REGISTER CONFIG_FLAG; /* Configured Flag register */ + volatile USB_REGISTER PORTSCX[VUSBHS_MAX_PORTS]; /* Port Status/Control x, x = 1..8 */ + + volatile USB_REGISTER OTGSC; /* OTG status and control register */ + volatile USB_REGISTER USB_MODE; /* USB Host/Device mode */ + } OPERATIONAL_HOST_REGISTERS; +#endif + } REGISTERS; +} VUSB20_REG_STRUCT, _PTR_ VUSB20_REG_STRUCT_PTR; + +typedef struct { + volatile uint_32 MAX_PKT_LENGTH; /* Bits 16..26 Bit 15 is Interrupt + ** On Setup + */ + volatile uint_32 CURR_DTD_PTR; /* Current dTD Pointer */ + volatile uint_32 NEXT_DTD_PTR; /* Next dTD Pointer */ + volatile uint_32 SIZE_IOC_INT_STS; /* Total bytes (16..30), IOC (15), + ** INT (8), STS (0-7) + */ + volatile uint_32 BUFF_PTR0; /* Buffer pointer Page 0 (12-31) */ + volatile uint_32 BUFF_PTR1; /* Buffer pointer Page 1 (12-31) */ + volatile uint_32 BUFF_PTR2; /* Buffer pointer Page 2 (12-31) */ + volatile uint_32 BUFF_PTR3; /* Buffer pointer Page 3 (12-31) */ + volatile uint_32 BUFF_PTR4; /* Buffer pointer Page 4 (12-31) */ + volatile uint_32 RESERVED1; + volatile uint_8 SETUP_BUFFER[8]; /* 8 bytes of setup data that follows + ** the Setup PID + */ + volatile uint_32 RESERVED2[4]; +} VUSB20_EP_QUEUE_HEAD_STRUCT, _PTR_ VUSB20_EP_QUEUE_HEAD_STRUCT_PTR; + +typedef struct { + pointer PRIVATE; + void (_CODE_PTR_ FREE)(pointer); + pointer XD_FOR_THIS_DTD; +} SCRATCH_STRUCT, _PTR_ SCRATCH_STRUCT_PTR; + +typedef struct ep_tr_struct { + volatile uint_32 NEXT_TR_ELEM_PTR; /* Memory address of next + ** dTD to be processed (5-31) + ** and the T (bit 0) indicating + ** pointer validity + */ + volatile uint_32 SIZE_IOC_STS; /* total bytes (16-30), + ** IOC (15), Status (0-7) + */ + volatile uint_32 BUFF_PTR0; /* Buffer pointer Page 0 */ + volatile uint_32 BUFF_PTR1; /* Buffer pointer Page 1 */ + volatile uint_32 BUFF_PTR2; /* Buffer pointer Page 2 */ + volatile uint_32 BUFF_PTR3; /* Buffer pointer Page 3 */ + volatile uint_32 BUFF_PTR4; /* Buffer pointer Page 4 */ + volatile SCRATCH_STRUCT_PTR SCRATCH_PTR; +} VUSB20_EP_TR_STRUCT, _PTR_ VUSB20_EP_TR_STRUCT_PTR; + +typedef struct { + uint_32 NEXT_LINK_PTR; /* (5-31) Memory address of + ** next schedule data structure + ** item Type (1..2 ) and the + ** T (bit 0) indicating pointer + ** validity + */ + uint_32 TR_STATUS_CTL_LIST[8]; /* bits 31-28: Status, + ** bits 27-16: Tr X length + ** bit 15: Int on complete + ** bits 14-12: Page Select + ** bits 11-0: Tr X offset + */ + uint_32 BUFFER_PAGE_PTR_LIST[7]; /* bits 31-12 4K aligned pointer + ** to physical memory + ** bits 11-8 endpoint no. + ** bit 7: reserved + ** bits 6-0 device address*/ + SCRATCH_STRUCT_PTR SCRATCH_PTR; + pointer PIPE_DESCR_FOR_THIS_ITD; + pointer PIPE_TR_DESCR_FOR_THIS_ITD; + uint_32_ptr frame_list_ptr; + uint_32 number_of_transactions; + /* 32-byte aligned structures */ + uint_32 RESERVED[11]; +} EHCI_ITD_STRUCT, _PTR_ EHCI_ITD_STRUCT_PTR; + +typedef struct { + uint_32 NEXT_LINK_PTR; /* (5-31) Memory address of + ** next schedule data structure + ** item Type (1..2 ) and the + ** T (bit 0) indicating pointer + ** validity + */ + uint_32 EP_CAPAB_CHARAC; /* bits 31: Direction (I/O), + ** bits 30-24: Port number + ** bit 23: reserved + ** bits 22-16: Hub address + ** bits 15-12: Reserved + ** bits 11-8: Endpoint number + ** bit 7: reserved + ** bits 6-0: device address + */ + uint_32 UFRAME_SCHED_CTL; /* bits 31-16: reserved + ** bits 15-8: Split completion mask + ** bits 7-0: Split start mask + */ + uint_32 TRANSFER_STATE; /* bit 31: int on complete + ** bit 30: Page Select + ** bits 29-26: Reserved + ** bits 25-16: total bytes to + ** transfer + ** bits 15-8: uframe + ** complete-split progress mask + ** bits 7-0: status + */ + uint_32 BUFFER_PTR_0; /* bits 31-12: 4K aligned pointer + ** to physical memory + ** bits 11-0: Current offset + */ + uint_32 BUFFER_PTR_1; /* bits 31-12: 4K aligned pointer + ** to physical memory + ** bits 11-5 reserved + ** bits 4-3 tr position + ** bits 2-0 tr count + */ + uint_32 BACK_LINK_PTR; /* bits 31-5 back pointer points to sITD + ** bits 4-1: reserved + ** bit 0: terminate + */ + SCRATCH_STRUCT_PTR SCRATCH_PTR; + pointer PIPE_DESCR_FOR_THIS_SITD; + pointer PIPE_TR_DESCR_FOR_THIS_SITD; + uint_32_ptr frame_list_ptr; + + /* align to 16 word boundry */ + uint_32 RESERVED[5]; + +} EHCI_SITD_STRUCT, _PTR_ EHCI_SITD_STRUCT_PTR; + +typedef struct { + uint_32 NEXT_QTD_PTR; /* (5-31) Memory address of + ** next qTD to be processed + ** (4..1) reserved + ** T (bit 0) indicating pointer + ** validity + */ + uint_32 ALT_NEXT_QTD_PTR; /* bits 31-5: alternate next + ** qTD if the above one encounters + ** a short packet + ** (4..1) reserved + ** T (bit 0) indicating pointer + ** validity + */ + uint_32 TOKEN; /* bits 31: data toggle + ** bits 30-16: Total bytes to transfer + ** bit 15: Interrupt on Complete + ** bits 14-12: Current page + ** bits 11-10: Error Counter + ** bits 9-8: PID code + ** bits 7-0: status + */ + uint_32 BUFFER_PTR_0; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: Current Offset + */ + uint_32 BUFFER_PTR_1; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + uint_32 BUFFER_PTR_2; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + uint_32 BUFFER_PTR_3; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + uint_32 BUFFER_PTR_4; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + SCRATCH_STRUCT_PTR SCRATCH_PTR; + pointer PIPE_DESCR_FOR_THIS_QTD; + pointer TR_FOR_THIS_QTD; + uint_32 RESERVED[5]; +} EHCI_QTD_STRUCT, _PTR_ EHCI_QTD_STRUCT_PTR; + +typedef struct { + uint_32 HORIZ_LINK_PTR; /* (5-31) Memory address of + ** next data object to be processed + ** (4..3) reserved + ** (2..1) type of the item + ** T (bit 0) indicating pointer + ** validity + */ + uint_32 EP_CAPAB_CHARAC1; /* bits 31-28: NAK count reload, + ** bit 27: Control endpoint flag + ** bit 26-16: Maximum packet length + ** bit 15: Head of reclamation + ** list flag + ** bit 14: data toggle control + ** bits 13-12: endpoint speed + ** bit 11-8: endpoint number + ** bits 7: Inactivate on next tr + ** bits 6-0: Device address + */ + uint_32 EP_CAPAB_CHARAC2; /* bits 31-30: High-BW pipe + ** Multiplier, + ** bit 29-23: Port number + ** bit 22-16: Hub address + ** bit 15-8: Split completion mask + ** bit 7-0: Interrupt schedule mask + */ + uint_32 CURR_QTD_LINK_PTR;/* bits 31-5: physical memory address + ** of the current xaction processed + */ + uint_32 NEXT_QTD_LINK_PTR;/* bits 31-5: physical memory address + ** of the current xaction processed + ** bit 0: Terminate bit + */ + uint_32 ALT_NEXT_QTD_LINK_PTR; /* bits 31-5: physical memory address + ** of the current xaction processed + ** bits 4-1: NAK counter + ** bit 0: Terminate bit + */ + uint_32 STATUS; /* bit 31: data-toggle + ** bits 30-16: total bytes to transfer + ** bit 15: Interrupt on complete + ** bits 11-10: Error counter + ** bit 0: Ping state/Err + ** physical memory address + ** bit 11-0: reserved + */ + uint_32 BUFFER_PTR_0; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + uint_32 BUFFER_PTR_1; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 7-0: Split-transaction, + ** complete-split progress + */ + uint_32 BUFFER_PTR_2; /* bits 31-12: 4K-page aligned + ** physical memory address + ** bits 11-5: S-bytes + ** bits 4-0: Split-transaction + ** frame tag + */ + uint_32 BUFFER_PTR_3; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + uint_32 BUFFER_PTR_4; /* bit 31-12: 4K-page aligned + ** physical memory address + ** bit 11-0: reserved + */ + SCRATCH_STRUCT_PTR SCRATCH_PTR; + pointer PIPE_DESCR_FOR_THIS_QH; + uint_32 RESERVED[18]; +} EHCI_QH_STRUCT, _PTR_ EHCI_QH_STRUCT_PTR; + +typedef struct { + uint_32 NORMAL_PATH_LINK_PTR; /* (5-31) Memory address of + ** next data object to be processed + ** in the periodic list + ** bits 4-3: reserved + ** (2..1) type of the item + ** T (bit 0) indicating pointer + ** validity + */ + uint_32 BACK_PATH_LINK_PTR; /* bits 31-5: Memory address of + ** the queue head, + ** bit 4-3: reserved + ** (2..1) type of the item + ** T (bit 0) indicating pointer + ** validity + */ + SCRATCH_STRUCT_PTR SCRATCH_PTR; + /* 32-bytes aligned */ + uint_32 RESERVED[6]; +} EHCI_FSTN_STRUCT, _PTR_ EHCI_FSTN_STRUCT_PTR; + +typedef uint_32 EHCI_FRAME_LIST_ELEMENT_POINTER; + +#endif /* __mvUsbCore_h__ */ +/* EOF */ + + + diff --git a/drivers/usb/gadget/mv/mvUsbDebug.h b/drivers/usb/gadget/mv/mvUsbDebug.h new file mode 100644 index 00000000000000..65c0a31f0264e1 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDebug.h @@ -0,0 +1,112 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbDebug_h__ +#define __mvUsbDebug_h__ + +#include "mvUsbTypes.h" + +#define MV_USB_RT_DEBUG + +/************************************************************ +The following array is used to make a run time trace route +inside the USB stack. +*************************************************************/ + +#define ARC_DEBUG_FLAG_ANY 0x00000000 + +#define ARC_DEBUG_FLAG_TRACE 0x00000001 +#define ARC_DEBUG_FLAG_CTRL 0x00000002 +#define ARC_DEBUG_FLAG_RX 0x00000004 +#define ARC_DEBUG_FLAG_TX 0x00000008 +#define ARC_DEBUG_FLAG_STALL 0x00000010 +#define ARC_DEBUG_FLAG_STATUS 0x00000020 +#define ARC_DEBUG_FLAG_TRANSFER 0x00000040 +#define ARC_DEBUG_FLAG_INIT 0x00000080 +#define ARC_DEBUG_FLAG_ISR 0x00000100 +#define ARC_DEBUG_FLAG_ERROR 0x00000200 +#define ARC_DEBUG_FLAG_ADDR 0x00000400 +#define ARC_DEBUG_FLAG_DUMP 0x00000800 +#define ARC_DEBUG_FLAG_SETUP 0x00001000 +#define ARC_DEBUG_FLAG_CLASS 0x00002000 +#define ARC_DEBUG_FLAG_SPEED 0x00004000 +#define ARC_DEBUG_FLAG_RESET 0x00008000 +#define ARC_DEBUG_FLAG_SUSPEND 0x00010000 +#define ARC_DEBUG_FLAG_RESUME 0x00020000 +#define ARC_DEBUG_FLAG_EP0 0x00040000 +#define ARC_DEBUG_FLAG_EP1 0x00080000 +#define ARC_DEBUG_FLAG_STATS 0x00100000 + + +#define ARC_DEBUG_FLAG_ALL 0xffffffff + +extern uint_32 usbDebugFlags; + +#ifdef MV_USB_RT_DEBUG +# define ARC_DEBUG_CODE(flags, code) \ + if( (usbDebugFlags & (flags)) == (flags) ) \ + code +#else +# define ARC_DEBUG_CODE(flags, code) +#endif + +#define MV_USB_TRACE_PRINT + +#if defined(MV_USB_TRACE_LOG) + +#define TRACE_ARRAY_SIZE 400 +#define MAX_STRING_SIZE 132 + +extern uint_16 DEBUG_TRACE_ARRAY_COUNTER; +extern char DEBUG_TRACE_ARRAY[TRACE_ARRAY_SIZE][MAX_STRING_SIZE]; + +#define ARC_DEBUG_TRACE(flags, format, x...) \ +{ \ + if( (usbDebugFlags & (flags)) == (flags)) \ + { \ + USB_sprintf(DEBUG_TRACE_ARRAY[DEBUG_TRACE_ARRAY_COUNTER], format, ##x); \ + DEBUG_TRACE_ARRAY_COUNTER++; \ + if(DEBUG_TRACE_ARRAY_COUNTER >= TRACE_ARRAY_SIZE) \ + {DEBUG_TRACE_ARRAY_COUNTER = 0;} \ + } \ +} + +#elif defined(MV_USB_TRACE_PRINT) + +# define ARC_DEBUG_TRACE(flags, format, x...) \ + if((usbDebugFlags & (flags)) == (flags)) \ + USB_printf(format, ##x) + +/*if trace switch is not enabled define debug log trace to empty*/ +#else +# define ARC_DEBUG_TRACE(flags, fromat, x...) +#endif + + +/************************************************************ +The following are global data structures that can be used +to copy data from stack on run time. This structure can +be analyzed at run time to see the state of various other +data structures in the memory. +*************************************************************/ + +#endif /* __mvUsbDebug_h__ */ +/* EOF */ + diff --git a/drivers/usb/gadget/mv/mvUsbDefs.h b/drivers/usb/gadget/mv/mvUsbDefs.h new file mode 100644 index 00000000000000..4aded4e2e97fba --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDefs.h @@ -0,0 +1,140 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbDefs_h__ +#define __mvUsbDefs_h__ + +#include "mvUsbTypes.h" + +/* Host specific */ +#define USB_DEBOUNCE_DELAY (101) +#define USB_RESET_RECOVERY_DELAY (11) +#define USB_RESET_DELAY (60) + +/* Error codes */ +#define USB_OK (0x00) +#define USBERR_ALLOC (0x81) +#define USBERR_BAD_STATUS (0x82) +#define USBERR_CLOSED_SERVICE (0x83) +#define USBERR_OPEN_SERVICE (0x84) +#define USBERR_TRANSFER_IN_PROGRESS (0x85) +#define USBERR_ENDPOINT_STALLED (0x86) +#define USBERR_ALLOC_STATE (0x87) +#define USBERR_DRIVER_INSTALL_FAILED (0x88) +#define USBERR_DRIVER_NOT_INSTALLED (0x89) +#define USBERR_INSTALL_ISR (0x8A) +#define USBERR_INVALID_DEVICE_NUM (0x8B) +#define USBERR_ALLOC_SERVICE (0x8C) +#define USBERR_INIT_FAILED (0x8D) +#define USBERR_SHUTDOWN (0x8E) +#define USBERR_INVALID_PIPE_HANDLE (0x8F) +#define USBERR_OPEN_PIPE_FAILED (0x90) +#define USBERR_INIT_DATA (0x91) +#define USBERR_SRP_REQ_INVALID_STATE (0x92) +#define USBERR_TX_FAILED (0x93) +#define USBERR_RX_FAILED (0x94) +#define USBERR_EP_INIT_FAILED (0x95) +#define USBERR_EP_DEINIT_FAILED (0x96) +#define USBERR_TR_FAILED (0x97) +#define USBERR_BANDWIDTH_ALLOC_FAILED (0x98) +#define USBERR_INVALID_NUM_OF_ENDPOINTS (0x99) + +#define USBERR_DEVICE_NOT_FOUND (0xC0) +#define USBERR_DEVICE_BUSY (0xC1) +#define USBERR_NO_DEVICE_CLASS (0xC3) +#define USBERR_UNKNOWN_ERROR (0xC4) +#define USBERR_INVALID_BMREQ_TYPE (0xC5) +#define USBERR_GET_MEMORY_FAILED (0xC6) +#define USBERR_INVALID_MEM_TYPE (0xC7) +#define USBERR_NO_DESCRIPTOR (0xC8) +#define USBERR_NULL_CALLBACK (0xC9) +#define USBERR_NO_INTERFACE (0xCA) +#define USBERR_INVALID_CFIG_NUM (0xCB) +#define USBERR_INVALID_ANCHOR (0xCC) +#define USBERR_INVALID_REQ_TYPE (0xCD) + +/* Error Codes for lower-layer */ +#define USBERR_ALLOC_EP_QUEUE_HEAD (0xA8) +#define USBERR_ALLOC_TR (0xA9) +#define USBERR_ALLOC_DTD_BASE (0xAA) +#define USBERR_CLASS_DRIVER_INSTALL (0xAB) + + +/* Pipe Types */ +#define USB_ISOCHRONOUS_PIPE (0x01) +#define USB_INTERRUPT_PIPE (0x02) +#define USB_CONTROL_PIPE (0x03) +#define USB_BULK_PIPE (0x04) + +#define ARC_USB_STATE_UNKNOWN (0xff) +#define ARC_USB_STATE_POWERED (0x03) +#define ARC_USB_STATE_DEFAULT (0x02) +#define ARC_USB_STATE_ADDRESS (0x01) +#define ARC_USB_STATE_CONFIG (0x00) +#define ARC_USB_STATE_SUSPEND (0x80) + +#define ARC_USB_SELF_POWERED (0x01) +#define ARC_USB_REMOTE_WAKEUP (0x02) + +/* Bus Control values */ +#define ARC_USB_NO_OPERATION (0x00) +#define ARC_USB_ASSERT_BUS_RESET (0x01) +#define ARC_USB_DEASSERT_BUS_RESET (0x02) +#define ARC_USB_ASSERT_RESUME (0x03) +#define ARC_USB_DEASSERT_RESUME (0x04) +#define ARC_USB_SUSPEND_SOF (0x05) +#define ARC_USB_RESUME_SOF (0x06) + +/* possible values of XD->bStatus */ +#define ARC_USB_STATUS_IDLE (0) +#define ARC_USB_STATUS_TRANSFER_ACCEPTED (1) +#define ARC_USB_STATUS_TRANSFER_PENDING (2) +#define ARC_USB_STATUS_TRANSFER_IN_PROGRESS (3) +#define ARC_USB_STATUS_ERROR (4) +#define ARC_USB_STATUS_DISABLED (5) +#define ARC_USB_STATUS_STALLED (6) +#define ARC_USB_STATUS_TRANSFER_QUEUED (7) + +#define ARC_USB_RECV (0) +#define ARC_USB_SEND (1) + +#define ARC_USB_DEVICE_DONT_ZERO_TERMINATE (0x1) + +#define ARC_USB_SETUP_DATA_XFER_DIRECTION (0x80) + +#define ARC_USB_SPEED_FULL (0) +#define ARC_USB_SPEED_LOW (1) +#define ARC_USB_SPEED_HIGH (2) + +#define ARC_USB_MAX_PKTS_PER_UFRAME (0x6) + +/* USB 1.1 Setup Packet */ +typedef struct setup_struct { + uint_8 REQUESTTYPE; + uint_8 REQUEST; + uint_16 VALUE; + uint_16 INDEX; + uint_16 LENGTH; +} SETUP_STRUCT, _PTR_ SETUP_STRUCT_PTR; + +#endif /* __mvUsbDefs_h__ */ + +/* EOF */ + diff --git a/drivers/usb/gadget/mv/mvUsbDesc.h b/drivers/usb/gadget/mv/mvUsbDesc.h new file mode 100644 index 00000000000000..ef53f2c5eaf247 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDesc.h @@ -0,0 +1,163 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbDesc_h__ +#define __mvUsbDesc_h__ + +#include "mvUsbTypes.h" + +typedef struct usb_device_descriptor +{ + uint_8 bLength; /* Descriptor size in bytes = 18 */ + uint_8 bDescriptorType; /* DEVICE descriptor type = 1 */ + uint_8 bcdUSD[2]; /* USB spec in BCD, e.g. 0x0200 */ + uint_8 bDeviceClass; /* Class code, if 0 see interface */ + uint_8 bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */ + uint_8 bDeviceProtocol; /* Protocol, if 0 see interface */ + uint_8 bMaxPacketSize; /* Endpoint 0 max. size */ + uint_8 idVendor[2]; /* Vendor ID per USB-IF */ + uint_8 idProduct[2]; /* Product ID per manufacturer */ + uint_8 bcdDevice[2]; /* Device release # in BCD */ + uint_8 iManufacturer; /* Index to manufacturer string */ + uint_8 iProduct; /* Index to product string */ + uint_8 iSerialNumber; /* Index to serial number string */ + uint_8 bNumConfigurations; /* Number of possible configurations */ +} DEVICE_DESCRIPTOR, _PTR_ DEVICE_DESCRIPTOR_PTR; + +typedef struct usb_configuration_descriptor +{ + uint_8 bLength; /* Descriptor size in bytes = 9 */ + uint_8 bDescriptorType; /* CONFIGURATION type = 2 or 7 */ + uint_8 wTotalLength[2]; /* Length of concatenated descriptors */ + uint_8 bNumInterfaces; /* Number of interfaces, this config. */ + uint_8 bConfigurationValue; /* Value to set this config. */ + uint_8 iConfig; /* Index to configuration string */ + uint_8 bmAttributes; /* Config. characteristics */ + #define CONFIG_RES7 (0x80) /* Reserved, always = 1 */ + #define CONFIG_SELF_PWR (0x40) /* Self-powered device */ + #define CONFIG_WAKEUP (0x20) /* Remote wakeup */ + uint_8 bMaxPower; /* Max.power from bus, 2mA units */ +} CONFIGURATION_DESCRIPTOR, _PTR_ CONFIGURATION_DESCRIPTOR_PTR; + +typedef struct usb_interface_descriptor +{ + uint_8 bLength; /* Descriptor size in bytes = 9 */ + uint_8 bDescriptorType; /* INTERFACE descriptor type = 4 */ + uint_8 bInterfaceNumber; /* Interface no.*/ + uint_8 bAlternateSetting; /* Value to select this IF */ + uint_8 bNumEndpoints; /* Number of endpoints excluding 0 */ + uint_8 bInterfaceClass; /* Class code, 0xFF = vendor */ + uint_8 bInterfaceSubClass; /* Sub-Class code, 0 if class = 0 */ + uint_8 bInterfaceProtocol; /* Protocol, 0xFF = vendor */ + uint_8 iInterface; /* Index to interface string */ +} INTERFACE_DESCRIPTOR, _PTR_ INTERFACE_DESCRIPTOR_PTR; + +typedef struct usb_endpoint_descriptor +{ + uint_8 bLength; /* Descriptor size in bytes = 7 */ + uint_8 bDescriptorType; /* ENDPOINT descriptor type = 5 */ + uint_8 bEndpointAddress; /* Endpoint # 0 - 15 | IN/OUT */ + #define IN_ENDPOINT (0x80) /* IN endpoint, device to host */ + #define OUT_ENDPOINT (0x00) /* OUT endpoint, host to device */ + #define ENDPOINT_MASK (0x0F) /* Mask endpoint # */ + uint_8 bmAttributes; /* Transfer type */ + #define CONTROL_ENDPOINT (0x00) /* Control transfers */ + #define ISOCH_ENDPOINT (0x01) /* Isochronous transfers */ + #define BULK_ENDPOINT (0x02) /* Bulk transfers */ + #define IRRPT_ENDPOINT (0x03) /* Interrupt transfers */ + #define EP_TYPE_MASK (0x03) /* Mask type bits */ + /* Following must be zero except for isochronous endpoints */ + #define ISOCH_NOSYNC (0x00) /* No synchronization */ + #define ISOCH_ASYNC (0x04) /* Asynchronous */ + #define ISOCH_ADAPT (0x08) /* Adaptive */ + #define ISOCH_SYNCH (0x0C) /* Synchrounous */ + #define ISOCH_DATA (0x00) /* Data endpoint */ + #define ISOCH_FEEDBACK (0x10) /* Feedback endpoint */ + #define ISOCH_IMPLICIT (0x20) /* Implicit feedback */ + #define ISOCH_RESERVED (0x30) /* Reserved */ + uint_8 wMaxPacketSize[2]; /* Bits 10:0 = max. packet size */ + /* For high-speed interrupt or isochronous only, additional + ** transaction opportunities per microframe follow.*/ + #define PACKET_SIZE_MASK (0x7FF) /* packet size bits */ + #define NO_ADDITONAL (0x0000) /* 1 / microframe */ + #define ONE_ADDITIONAL (0x0800) /* 2 / microframe */ + #define TWO_ADDITIONAL (0x1000) /* 3 / microframe */ + #define ADDITIONAL_MASK (ONE_ADDITIONAL | TWO_ADDITIONAL) + uint_8 iInterval; /* Polling interval in (micro) frames */ +} ENDPOINT_DESCRIPTOR, _PTR_ ENDPOINT_DESCRIPTOR_PTR; + +typedef struct usb_qualifier_descriptor +{ + uint_8 bLength; /* Descriptor size in bytes = 10 */ + uint_8 bDescriptorType; /* DEVICE QUALIFIER type = 6 */ + uint_8 bcdUSD[2]; /* USB spec in BCD, e.g. 0x0200 */ + uint_8 bDeviceClass; /* Class code, if 0 see interface */ + uint_8 bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */ + uint_8 bDeviceProtocol; /* Protocol, if 0 see interface */ + uint_8 bMaxPacketSize; /* Endpoint 0 max. size */ + uint_8 bNumConfigurations; /* Number of possible configurations */ + uint_8 bReserved; /* Reserved = 0 */ +} QUALIFIER_DESCRIPTOR, _PTR_ QUALIFIER_DESCRIPTOR_PTR; + +/* Other-Config type 7 fields are identical to type 2 above */ + +/* Interface-Power descriptor type 8 not used in this version */ + +typedef struct usb_otg_descriptor +{ + uint_8 bLength; /* Descriptor size in bytes = 9 */ + uint_8 bDescriptorType; /* CONFIGURATION type = 2 or 7 */ + uint_8 bmAttributes; /* OTG characteristics */ + #define OTG_SRP_SUPPORT (0x01) /* Supports SRP */ + #define OTG_HNP_SUPPORT (0x02) /* Supports HNP */ +} OTG_DESCRIPTOR, _PTR_ OTG_DESCRIPTOR_PTR; + +typedef union descriptor_union +{ + uint_32 word; + uint_8_ptr bufr; + pointer pntr; + DEVICE_DESCRIPTOR_PTR dvic; + CONFIGURATION_DESCRIPTOR_PTR cfig; + INTERFACE_DESCRIPTOR_PTR intf; + ENDPOINT_DESCRIPTOR_PTR ndpt; + QUALIFIER_DESCRIPTOR_PTR qual; + OTG_DESCRIPTOR_PTR otg; +} DESCRIPTOR_UNION, _PTR_ DESCRIPTOR_UNION_PTR; + +/* Prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern uint_32 usb_host_init(uint_8, uint_32, + _usb_host_handle _PTR_); +extern uint_32 _usb_host_open_pipe(_usb_host_handle, + PIPE_INIT_PARAM_STRUCT_PTR, _usb_pipe_handle _PTR_ ); + +#ifdef __cplusplus +} +#endif + +#endif /* __mvUsbDesc_h__ */ + +/* EOF */ + diff --git a/drivers/usb/gadget/mv/mvUsbDevApi.h b/drivers/usb/gadget/mv/mvUsbDevApi.h new file mode 100644 index 00000000000000..ae5fbfe8eae97c --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevApi.h @@ -0,0 +1,164 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbDevApi_h__ +#define __mvUsbDevApi_h__ + +//#include + +#include "mvUsbTypes.h" +#include "mvUsbDebug.h" +#include "mvUsbDefs.h" + +#define ARC_USB_MAX_ENDPOINTS (16) + +#define MAX_EP_TR_DESCRS (64) +// xj modify to meet serial queue size 32 for default +//#define MAX_XDS_FOR_TR_CALLS (32) +#define MAX_XDS_FOR_TR_CALLS (64) +#define MAX_USB_DEVICES (1) + +/*Assumption here is that all control endpoints are sequential 0,1,.. +if they are not you need to modify the tr_complete routine to handle that */ +#define USB_MAX_CONTROL_ENDPOINTS (1) + +#define USB_MAX_CTRL_PAYLOAD (64) + + +/* Endpoint types */ +#define ARC_USB_CONTROL_ENDPOINT (0) +#define ARC_USB_ISOCHRONOUS_ENDPOINT (1) +#define ARC_USB_BULK_ENDPOINT (2) +#define ARC_USB_INTERRUPT_ENDPOINT (3) + +/* Informational Request/Set Types */ +#define ARC_USB_STATUS_DEVICE_STATE (0x01) +#define ARC_USB_STATUS_INTERFACE (0x02) +#define ARC_USB_STATUS_ADDRESS (0x03) +#define ARC_USB_STATUS_CURRENT_CONFIG (0x04) +#define ARC_USB_STATUS_SOF_COUNT (0x05) +#define ARC_USB_STATUS_DEVICE (0x06) +#define ARC_USB_STATUS_TEST_MODE (0x07) +#define ARC_USB_FORCE_FULL_SPEED (0x08) +#define ARC_USB_PHY_LOW_POWER_SUSPEND (0x09) + +#define ARC_USB_STATUS_ENDPOINT_NUMBER_MASK (0x000F) +#define ARC_USB_STATUS_ENDPOINT_DIR_MASK (0x0080) + +#define ARC_USB_TEST_MODE_TEST_PACKET (0x0400) + +/* Available service types */ +/* Services 0 through 15 are reserved for endpoints */ +#define ARC_USB_SERVICE_EP0 (0x00) +#define ARC_USB_SERVICE_EP1 (0x01) +#define ARC_USB_SERVICE_EP2 (0x02) +#define ARC_USB_SERVICE_EP3 (0x03) +#define ARC_USB_SERVICE_BUS_RESET (0x10) +#define ARC_USB_SERVICE_SUSPEND (0x11) +#define ARC_USB_SERVICE_SOF (0x12) +#define ARC_USB_SERVICE_RESUME (0x13) +#define ARC_USB_SERVICE_SLEEP (0x14) +#define ARC_USB_SERVICE_SPEED_DETECTION (0x15) +#define ARC_USB_SERVICE_ERROR (0x16) +#define ARC_USB_SERVICE_STALL (0x17) + +typedef pointer _usb_device_handle; +typedef void (*USB_SERVICE_FUNC)(void* handle, uint_8, boolean, uint_8, + uint_8_ptr, uint_32, uint_8); + +#ifdef __cplusplus +extern "C" { +#endif + +void _usb_dci_vusb20_isr(void* handle); + +void _usb_device_set_bsp_funcs(USB_IMPORT_FUNCS* pBspFuncs); + +uint_8 _usb_device_init(uint_8 devNo, void** pHandle); + +uint_8 _usb_device_get_max_endpoint(void* handle); + +uint_8 _usb_device_get_dev_num(void* handle); + +void _usb_device_shutdown(void* handle); + +void _usb_device_stop(void* handle); +void _usb_device_start(void* handle); + +uint_8 _usb_device_init_endpoint(void* handle, uint_8 ep_num, uint_16 max_pkt_size, + uint_8 direction, uint_8 type, uint_8 flag); +uint_8 _usb_device_deinit_endpoint(void* handle, uint_8 ep_num, uint_8 direction); + +uint_8 _usb_device_recv_data(void* handle, uint_8 ep_num, uint_8* buf_ptr, uint_32 size); +uint_8 _usb_device_send_data(void* handle, uint_8 ep_num, uint_8* buf_ptr, uint_32 size); +uint_8 _usb_device_cancel_transfer(void* handle, uint_8 ep_num, uint_8 direction); +uint_8 _usb_device_get_transfer_status(void* handle, uint_8 ep_num, uint_8 direction); +void _usb_device_stall_endpoint(void* handle, uint_8 ep_num, uint_8 direction); +void _usb_device_unstall_endpoint(void* handle, uint_8 ep_num, uint_8 direction); +uint_8 _usb_device_is_endpoint_stalled(void* handle, uint_8 ep_num, uint_8 direction); +void _usb_device_assert_resume(void* handle); +uint_8 _usb_device_get_status(void* handle, uint_8 component, uint_16* status_ptr); +uint_8 _usb_device_set_status(void* handle, uint_8 component, uint_16 setting); +void _usb_device_read_setup_data(void* handle, uint_8 ep_num, uint_8* buf_ptr); + +uint_8 _usb_device_register_service(void* handle, uint_8 type, USB_SERVICE_FUNC serviceFunc); + +uint_8 _usb_device_unregister_service(void* handle, uint_8 type); + + + +/* These functions that implement USB 2.0 standard Chapter 9 Setup requests */ +void mvUsbCh9GetStatus(void* handle, boolean setup, + SETUP_STRUCT* ctrl_req); + +void mvUsbCh9ClearFeature(void* handle, boolean setup, + SETUP_STRUCT* setup_ptr); + +void mvUsbCh9SetFeature(void* handle, boolean setup, + SETUP_STRUCT* setup_ptr); + +void mvUsbCh9SetAddress(void* handle, boolean setup, + SETUP_STRUCT* setup_ptr); + +/* DEBUG Functions */ +void _usb_dci_vusb20_set_test_mode(void* handle, uint_16 testMode); + +void _usb_debug_set_flags(uint_32 flags); +uint_32 _usb_debug_get_flags(void); + +void _usb_debug_init_trace_log(void); +void _usb_debug_print_trace_log(void); + +void _usb_regs(void* usbHandle); +void _usb_status(void* usbHandle); +void _usb_stats(void* usbHandle); +void _usb_clear_stats(void* usbHandle); +void _usb_ep_status(void* usbHandle, int ep_num, int direction); + +extern void dump_epin(int); + +#ifdef __cplusplus +} +#endif + +#endif /* __mvUsbDevApi_h__ */ +/* EOF */ + + diff --git a/drivers/usb/gadget/mv/mvUsbDevCh9.c b/drivers/usb/gadget/mv/mvUsbDevCh9.c new file mode 100644 index 00000000000000..f28df9ccae90be --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevCh9.c @@ -0,0 +1,302 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" +#include "mvUsbCh9.h" + +static volatile boolean ENTER_TEST_MODE = FALSE; +static volatile uint_16 test_mode_index = 0; + + +void mvUsbCh9GetStatus(_usb_device_handle handle, boolean setup, + SETUP_STRUCT* ctrl_req) +{ /* Body */ + uint_8 endpoint, direction; + uint_16 usb_status; + USB_DEV_STATE_STRUCT* usb_dev_ptr = (USB_DEV_STATE_STRUCT*)handle; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SETUP, "%s: setup=%d\n", __FUNCTION__, (int)setup); + + if(!setup) + return; + + switch (ctrl_req->REQUESTTYPE) + { + case (REQ_DIR_IN | REQ_RECIP_DEVICE): + /* Device request */ + _usb_device_get_status(handle, ARC_USB_STATUS_DEVICE, &usb_status); + break; + + case (REQ_DIR_IN | REQ_RECIP_INTERFACE): + /* Interface request */ + _usb_device_get_status(handle, ARC_USB_STATUS_INTERFACE, &usb_status); + break; + + case (REQ_DIR_IN | REQ_RECIP_ENDPOINT): + /* Endpoint request */ + endpoint = ctrl_req->INDEX & ARC_USB_STATUS_ENDPOINT_NUMBER_MASK; + if( (ctrl_req->INDEX & (1 << REQ_DIR_OFFSET)) == REQ_DIR_IN) + direction = ARC_USB_SEND; + else + direction = ARC_USB_RECV; + + usb_status = _usb_device_is_endpoint_stalled(handle, endpoint, direction); + break; + + default: + /* Unknown request */ + USB_printf("GetStatus: Unknown request type 0x%x\n", ctrl_req->REQUESTTYPE); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endswitch */ + + /* Send the requested data */ + *usb_dev_ptr->STATUS_PTR = USB_16BIT_LE(usb_status); + _usb_device_send_data(handle, 0, (uint_8_ptr)usb_dev_ptr->STATUS_PTR, sizeof(uint_16)); + + /* status phase */ + _usb_device_recv_data(handle, 0, NULL, 0); + + return; +} /* Endbody */ + +void mvUsbCh9ClearFeature(_usb_device_handle handle, boolean setup, + SETUP_STRUCT* setup_ptr) +{ /* Body */ + uint_8 endpoint, direction; + uint_16 usb_status; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SETUP, "%s: setup=%d\n", __FUNCTION__, (int)setup); + + _usb_device_get_status(handle, ARC_USB_STATUS_DEVICE_STATE, &usb_status); + if ((usb_status != ARC_USB_STATE_CONFIG) && (usb_status != ARC_USB_STATE_ADDRESS)) + { + USB_printf("ClearFeature: Wrong USB state %d\n", usb_status); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endif */ + + if(!setup) + return; + + switch (setup_ptr->REQUESTTYPE) + { + case (REQ_DIR_OUT | REQ_RECIP_DEVICE): + /* DEVICE */ + switch(setup_ptr->VALUE) + { + case DEVICE_REMOTE_WAKEUP: + /* clear remote wakeup */ + _usb_device_get_status(handle, ARC_USB_STATUS_DEVICE, &usb_status); + usb_status &= ~ARC_USB_REMOTE_WAKEUP; + _usb_device_set_status(handle, ARC_USB_STATUS_DEVICE, usb_status); + USB_printf("Clear REMOTE_WAKEUP feature\n"); + break; + + case DEVICE_TEST_MODE: + /* Exit Test Mode */ + _usb_device_set_status(handle, ARC_USB_STATUS_TEST_MODE, 0); + break; + + default: + USB_printf("ClearFeature: Unknown Device feature %d\n", + setup_ptr->VALUE); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endif */ + break; + + case (REQ_DIR_OUT | REQ_RECIP_ENDPOINT): + /* ENDPOINT */ + if (setup_ptr->VALUE != ENDPOINT_HALT) + { + USB_printf("ClearFeature: Wrong Endpoint feature %d\n", + setup_ptr->VALUE); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endif */ + + endpoint = setup_ptr->INDEX & ARC_USB_STATUS_ENDPOINT_NUMBER_MASK; + if( (setup_ptr->INDEX & (1 << REQ_DIR_OFFSET)) == REQ_DIR_IN) + direction = ARC_USB_SEND; + else + direction = ARC_USB_RECV; + + _usb_device_unstall_endpoint(handle, endpoint, direction); + break; + + default: + USB_printf("ClearFeature: Unknown REQUEST_TYPE %d\n", + setup_ptr->REQUESTTYPE); + + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endswitch */ + + /* status phase */ + _usb_device_send_data(handle, 0, 0, 0); +} + +void mvUsbCh9SetFeature(_usb_device_handle handle, boolean setup, + SETUP_STRUCT* setup_ptr) +{ + uint_16 usb_status; + uint_8 endpoint, direction; + USB_DEV_STATE_STRUCT* usb_dev_ptr = (USB_DEV_STATE_STRUCT*)handle; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SETUP, "%s: setup=%d\n", __FUNCTION__, (int)setup); + + if (setup) + { + switch (setup_ptr->REQUESTTYPE) + { + case (REQ_DIR_OUT | REQ_RECIP_DEVICE): + /* DEVICE */ + switch (setup_ptr->VALUE) + { + case DEVICE_REMOTE_WAKEUP: + /* set remote wakeup */ + _usb_device_get_status(handle, ARC_USB_STATUS_DEVICE, &usb_status); + usb_status |= ARC_USB_REMOTE_WAKEUP; + _usb_device_set_status(handle, ARC_USB_STATUS_DEVICE, usb_status); + USB_printf("Set REMOTE_WAKEUP feature\n"); + break; + + case DEVICE_TEST_MODE: + /* Test Mode */ + if( (setup_ptr->INDEX & 0x00FF) || (usb_dev_ptr->SPEED != ARC_USB_SPEED_HIGH) ) + { + USB_printf("SetFeature: Wrong Test mode parameters: mode=%d, speed=%d\n", + (setup_ptr->INDEX & 0x00FF), usb_dev_ptr->SPEED); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endif */ + + _usb_device_get_status(handle, ARC_USB_STATUS_DEVICE_STATE, &usb_status); + if( (usb_status == ARC_USB_STATE_CONFIG) || + (usb_status == ARC_USB_STATE_ADDRESS) || + (usb_status == ARC_USB_STATE_DEFAULT)) + { + /* wait with Set Test mode */ + ENTER_TEST_MODE = TRUE; + test_mode_index = (setup_ptr->INDEX & 0xFF00); + USB_printf("SetFeature: Prepare for Test mode 0x%x\n", test_mode_index); + } + else + { + USB_printf("SetFeature: Wrong USB state for Test mode: state=%d\n", + usb_status); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endif */ + break; + + default: + USB_printf("SetFeature: Unknown Device feature %d\n", + setup_ptr->VALUE); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endswitch */ + break; + + case (REQ_DIR_OUT | REQ_RECIP_ENDPOINT): + /* ENDPOINT */ + if (setup_ptr->VALUE != ENDPOINT_HALT) + { + USB_printf("SetFeature: Unknown Endpoint feature %d\n", + setup_ptr->VALUE); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endif */ + + endpoint = setup_ptr->INDEX & ARC_USB_STATUS_ENDPOINT_NUMBER_MASK; + if( (setup_ptr->INDEX & (1 << REQ_DIR_OFFSET)) == REQ_DIR_IN) + direction = ARC_USB_SEND; + else + direction = ARC_USB_RECV; + + _usb_device_stall_endpoint(handle, endpoint, direction); + break; + + default: + USB_printf("SetFeature: Unknown REQUEST_TYPE %d\n", + setup_ptr->REQUESTTYPE); + + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } /* Endswitch */ + + /* status phase */ + _usb_device_send_data(handle, 0, 0, 0); + } + else + { + if (ENTER_TEST_MODE) + { + /* Enter Test Mode */ + USB_printf("SetFeature: Activate Test mode 0x%x\n", test_mode_index); + _usb_device_set_status(handle, ARC_USB_STATUS_TEST_MODE, test_mode_index); + } /* Endif */ + } /* Endif */ +} + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : ch9SetAddress +* Returned Value : None +* Comments : +* Chapter 9 SetAddress command +* We setup a TX packet of 0 length ready for the IN token +* Once we get the TOK_DNE interrupt for the IN token, then +* we change the ADDR register and go to the ADDRESS state. +* +*END*--------------------------------------------------------------------*/ +void mvUsbCh9SetAddress(_usb_device_handle handle, + boolean setup, SETUP_STRUCT* setup_ptr) +{ /* Body */ + static uint_8 new_address; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ADDR, "usbDisk %s: setup=%d, address=%d\n", + __FUNCTION__, (int)setup, setup_ptr->VALUE); + + if (setup) + { + new_address = setup_ptr->VALUE; + /******************************************************* + * if hardware assitance is enabled for set_address (see + * hardware rev for details) we need to do the set_address + * before queuing the status phase. + *******************************************************/ +#ifdef SET_ADDRESS_HARDWARE_ASSISTANCE + _usb_device_set_status(handle, ARC_USB_STATUS_ADDRESS, new_address); +#endif + /* ack */ + _usb_device_send_data(handle, 0, 0, 0); + } + else + { +#ifndef SET_ADDRESS_HARDWARE_ASSISTANCE + _usb_device_set_status(handle, ARC_USB_STATUS_ADDRESS, new_address); +#endif + _usb_device_set_status(handle, ARC_USB_STATUS_DEVICE_STATE, ARC_USB_STATE_ADDRESS); + } +} + diff --git a/drivers/usb/gadget/mv/mvUsbDevMain.c b/drivers/usb/gadget/mv/mvUsbDevMain.c new file mode 100644 index 00000000000000..76cda2cfbac93a --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevMain.c @@ -0,0 +1,772 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" + +#include + +USB_IMPORT_FUNCS* global_import_funcs = NULL; + +#ifdef USB_UNDERRUN_WA +USB_WA_FUNCS* global_wa_funcs = NULL; +int global_wa_threshold = 64; +int global_wa_sram_parts = 2; +#endif /* USB_UNDERRUN_WA */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_cleanup +* Returned Value : void +* Comments : +* Cleanup allocated structures. +* +*END*-----------------------------------------------------------------*/ + +static void _usb_device_cleanup(USB_DEV_STATE_STRUCT_PTR usb_dev_ptr) +{ + /* Free all internal transfer descriptors */ + if(usb_dev_ptr->XD_BASE != NULL) + { + USB_memfree((pointer)usb_dev_ptr->XD_BASE); + } + + /* Free all XD scratch memory */ + if(usb_dev_ptr->XD_SCRATCH_STRUCT_BASE != NULL) + { + USB_memfree((pointer)usb_dev_ptr->XD_SCRATCH_STRUCT_BASE); + } + /* Free the temp ep init XD */ + if(usb_dev_ptr->TEMP_XD_PTR != NULL) + { + USB_memfree((pointer)usb_dev_ptr->TEMP_XD_PTR); + } + + if(usb_dev_ptr->STATUS_UNAIGNED_PTR != NULL) + USB_memfree((pointer)usb_dev_ptr->STATUS_UNAIGNED_PTR); + + if(usb_dev_ptr->TEST_PKT_UNAIGNED_PTR != NULL) + USB_memfree((pointer)usb_dev_ptr->TEST_PKT_UNAIGNED_PTR); + + /* Free the USB state structure */ + USB_memfree((pointer)usb_dev_ptr); +} + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_free_XD +* Returned Value : void +* Comments : +* Enqueues a XD onto the free XD ring. +* +*END*-----------------------------------------------------------------*/ + +void _usb_device_free_XD + ( + /* [IN] the dTD to enqueue */ + pointer xd_ptr + ) +{ /* Body */ + int lockKey; + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)(((XD_STRUCT_PTR)xd_ptr)->SCRATCH_PTR->PRIVATE); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRACE, "free_XD: xd_ptr=0x%x\n", (unsigned)xd_ptr); + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.free_XD_count++)); + + /* + ** This function can be called from any context, and it needs mutual + ** exclusion with itself. + */ + + lockKey = USB_lock(); + + /* + ** Add the XD to the free XD queue (linked via PRIVATE) and + ** increment the tail to the next descriptor + */ + USB_XD_QADD(usb_dev_ptr->XD_HEAD, usb_dev_ptr->XD_TAIL, (XD_STRUCT_PTR)xd_ptr); + usb_dev_ptr->XD_ENTRIES++; + + USB_unlock(lockKey); + +} /* Endbody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_set_bsp_funcs +* Returned Value : NONE +* Comments : +* Set pointer to structure of imported BSP functions +* +*END*-----------------------------------------------------------------*/ +void _usb_device_set_bsp_funcs(USB_IMPORT_FUNCS* pBspFuncs) +{ + static boolean isFirst = TRUE; + + if(isFirst) + { + global_import_funcs = pBspFuncs; + _usb_debug_init_trace_log(); + isFirst = FALSE; + } +} + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_get_max_endpoint +* Returned Value : handle or NULL +* Comments : +* Return maximum number of endpoints supportedby USB device +* (for DEBUG only) +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_get_max_endpoint(_usb_device_handle handle) +{ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + return usb_dev_ptr->MAX_ENDPOINTS; +} + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_get_dev_num +* Returned Value : handle or NULL +* Comments : +* Return unique USB device number +* (for DEBUG only) +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_get_dev_num(_usb_device_handle handle) +{ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + return usb_dev_ptr->DEV_NUM; +} + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_init +* Returned Value : USB_OK or error code +* Comments : +* Initializes the USB device specific data structures and calls +* the low-level device controller chip initialization routine. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_init + ( + /* [IN] the USB device controller to initialize */ + uint_8 devnum, + + /* [OUT] the USB_USB_dev_initialize state structure */ + _usb_device_handle* handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + XD_STRUCT_PTR xd_ptr; + uint_8 i, error; + SCRATCH_STRUCT_PTR temp_scratch_ptr; + + /* global_import_funcs must be initailized before */ + if(global_import_funcs == NULL) + return USBERR_INIT_FAILED; + + if (devnum > MAX_USB_DEVICES) + { + USB_printf("_usb_device_init, error invalid device number"); + return USBERR_INVALID_DEVICE_NUM; + } /* Endif */ + + /* Allocate memory for the state structure */ + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)USB_memalloc(sizeof(USB_DEV_STATE_STRUCT)); + if (usb_dev_ptr == NULL) + { + USB_printf("_usb_device_init, malloc of %d bytes for USB_DEV_STATE_STRUCT failed\n", + sizeof(USB_DEV_STATE_STRUCT)); + return USBERR_ALLOC_STATE; + } /* Endif */ + + /* Zero out the internal USB state structure */ + USB_memzero(usb_dev_ptr, sizeof(USB_DEV_STATE_STRUCT)); + + usb_dev_ptr->DEV_NUM = devnum; + + /* Multiple devices will have different base addresses and + ** interrupt vectors (For future) + */ + usb_dev_ptr->USB_STATE = ARC_USB_STATE_UNKNOWN; + + /* Allocate MAX_XDS_FOR_TR_CALLS */ + xd_ptr = (XD_STRUCT_PTR)USB_memalloc(sizeof(XD_STRUCT) * MAX_XDS_FOR_TR_CALLS); + if (xd_ptr == NULL) + { + _usb_device_cleanup(usb_dev_ptr); + USB_printf("_usb_device_init, malloc of %d bytes for %d XD_STRUCT failed\n", + sizeof(XD_STRUCT) * MAX_XDS_FOR_TR_CALLS, MAX_XDS_FOR_TR_CALLS); + return USBERR_ALLOC_TR; + } /* Endif */ + + usb_dev_ptr->XD_BASE = xd_ptr; + + _usb_clear_stats(usb_dev_ptr); + + USB_memzero(xd_ptr, sizeof(XD_STRUCT) * MAX_XDS_FOR_TR_CALLS); + + /* Allocate memory for internal scratch structure */ + usb_dev_ptr->XD_SCRATCH_STRUCT_BASE = (SCRATCH_STRUCT_PTR) + USB_memalloc(sizeof(SCRATCH_STRUCT) * MAX_XDS_FOR_TR_CALLS); + if (usb_dev_ptr->XD_SCRATCH_STRUCT_BASE == NULL) + { + _usb_device_cleanup(usb_dev_ptr); + USB_printf("_usb_device_init, malloc of %d bytes for %d XD_STRUCT failed\n", + sizeof(SCRATCH_STRUCT) * MAX_XDS_FOR_TR_CALLS, MAX_XDS_FOR_TR_CALLS); + return USBERR_ALLOC; + } /* Endif */ + + temp_scratch_ptr = usb_dev_ptr->XD_SCRATCH_STRUCT_BASE; + usb_dev_ptr->XD_HEAD = NULL; + usb_dev_ptr->XD_TAIL = NULL; + usb_dev_ptr->XD_ENTRIES = 0; + + /* Enqueue all the XDs */ + for (i=0;iSCRATCH_PTR = temp_scratch_ptr; + xd_ptr->SCRATCH_PTR->FREE = _usb_device_free_XD; + xd_ptr->SCRATCH_PTR->PRIVATE = (pointer)usb_dev_ptr; + _usb_device_free_XD((pointer)xd_ptr); + xd_ptr++; + temp_scratch_ptr++; + } /* Endfor */ + + usb_dev_ptr->TEMP_XD_PTR = (XD_STRUCT_PTR)USB_memalloc(sizeof(XD_STRUCT)); + if(usb_dev_ptr->TEMP_XD_PTR == NULL) + { + USB_printf("_usb_device_init, malloc of %d bytes for TEMP_XD_STRUCT failed\n", + sizeof(XD_STRUCT)); + _usb_device_cleanup(usb_dev_ptr); + return USBERR_ALLOC; + } + USB_memzero(usb_dev_ptr->TEMP_XD_PTR, sizeof(XD_STRUCT)); + + /* Allocate 2 bytes for USB_STATUS to be sent over USB, so Cache line aligned */ + usb_dev_ptr->STATUS_UNAIGNED_PTR = (uint_8*)USB_memalloc(sizeof(uint_16) + PSP_CACHE_LINE_SIZE); + if(usb_dev_ptr->STATUS_UNAIGNED_PTR == NULL) + { + USB_printf("_usb_device_init, malloc of %d bytes for USB_STATUS failed\n", + sizeof(uint_16) + PSP_CACHE_LINE_SIZE); + _usb_device_cleanup(usb_dev_ptr); + return USBERR_ALLOC; + } + USB_memzero(usb_dev_ptr->STATUS_UNAIGNED_PTR, sizeof(uint_16) + PSP_CACHE_LINE_SIZE); + usb_dev_ptr->STATUS_PTR = (uint_16*)USB_CACHE_ALIGN((uint_32)usb_dev_ptr->STATUS_UNAIGNED_PTR); + + /* Allocate 53 bytes for USB Test packet to be sent over USB, so Cache line aligned */ + usb_dev_ptr->TEST_PKT_UNAIGNED_PTR = (uint_8*)USB_memalloc(USB_TEST_MODE_TEST_PACKET_LENGTH + PSP_CACHE_LINE_SIZE); + if(usb_dev_ptr->TEST_PKT_UNAIGNED_PTR == NULL) + { + USB_printf("_usb_device_init, malloc of %d bytes for USB Test packet failed\n", + USB_TEST_MODE_TEST_PACKET_LENGTH + PSP_CACHE_LINE_SIZE); + _usb_device_cleanup(usb_dev_ptr); + return USBERR_ALLOC; + } + USB_memzero(usb_dev_ptr->TEST_PKT_UNAIGNED_PTR, USB_TEST_MODE_TEST_PACKET_LENGTH + PSP_CACHE_LINE_SIZE); + usb_dev_ptr->TEST_PKT_PTR = (uint_8*)USB_CACHE_ALIGN((uint_32)usb_dev_ptr->TEST_PKT_UNAIGNED_PTR); + + + /* Initialize the USB controller chip */ + error = _usb_dci_vusb20_init(devnum, usb_dev_ptr); + if (error) + { + _usb_device_cleanup(usb_dev_ptr); + USB_printf("_usb_device_init, init failed"); + return USBERR_INIT_FAILED; + } /* Endif */ + + USB_printf("device_init: pDev=0x%x, pXD(%d)=0x%x, pSCRATCH(%d)=0x%x, pTempXD=0x%x\n", + (unsigned)usb_dev_ptr, MAX_XDS_FOR_TR_CALLS, (unsigned)usb_dev_ptr->XD_BASE, + MAX_XDS_FOR_TR_CALLS, (unsigned)usb_dev_ptr->XD_SCRATCH_STRUCT_BASE, + (unsigned)usb_dev_ptr->TEMP_XD_PTR); + + *handle = usb_dev_ptr; + return USB_OK; +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_shutdown +* Returned Value : USB_OK or error code +* Comments : +* Shutdown an initialized USB device +* +*END*-----------------------------------------------------------------*/ +void _usb_device_shutdown(_usb_device_handle handle) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + SERVICE_STRUCT_PTR service_ptr; + int ep; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_CTRL, "shutdown\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + for(ep=0; ep<(usb_dev_ptr->MAX_ENDPOINTS); ep++) + { + /* Cancel all transfers on all endpoints */ + while(_usb_device_get_transfer_status(handle, ep, ARC_USB_RECV) != + ARC_USB_STATUS_IDLE) + { + _usb_device_cancel_transfer(handle, ep, ARC_USB_RECV); + } + while(_usb_device_get_transfer_status(handle, ep, ARC_USB_SEND) != + ARC_USB_STATUS_IDLE) + { + _usb_device_cancel_transfer(handle, ep, ARC_USB_SEND); + } + } + _usb_dci_vusb20_shutdown(usb_dev_ptr); + + /* Free all the Callback function structure memory */ + for( service_ptr = usb_dev_ptr->SERVICE_HEAD_PTR; service_ptr; + service_ptr = service_ptr->NEXT) + { + USB_printf("_usb_device_shutdown: free service_ptr = 0x%x\n", + service_ptr); + USB_memfree(service_ptr); + } + usb_dev_ptr->SERVICE_HEAD_PTR = NULL; + + _usb_device_cleanup(usb_dev_ptr); +} /* EndBody */ + + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_device_register_service +* Returned Value : USB_OK or error code +* Comments : +* Registers a callback routine for a specified event or endpoint. +* +*END*--------------------------------------------------------------------*/ +uint_8 _usb_device_register_service + ( + /* [IN] Handle to the USB device */ + _usb_device_handle handle, + + /* [IN] type of event or endpoint number to service */ + uint_8 type, + + /* [IN] Pointer to the service's callback function */ + void(_CODE_PTR_ service)(pointer, uint_8, boolean, uint_8, uint_8_ptr, uint_32, uint_8) + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + SERVICE_STRUCT_PTR service_ptr; + SERVICE_STRUCT_PTR _PTR_ search_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + /* Needs mutual exclusion */ + lockKey = USB_lock(); + + /* Search for an existing entry for type */ + for (search_ptr = &usb_dev_ptr->SERVICE_HEAD_PTR; + *search_ptr; + search_ptr = &(*search_ptr)->NEXT) + { + if ((*search_ptr)->TYPE == type) + { + /* Found an existing entry */ + USB_unlock(lockKey); + USB_printf("_usb_device_register_service, service %d already opened\n"); + return USBERR_OPEN_SERVICE; + } /* Endif */ + } /* Endfor */ + + /* No existing entry found - create a new one */ + service_ptr = (SERVICE_STRUCT_PTR)USB_memalloc(sizeof(SERVICE_STRUCT)); + if (!service_ptr) + { + USB_unlock(lockKey); + USB_printf("_usb_device_register_service, malloc for %d bytes failed\n", + sizeof(SERVICE_STRUCT)); + return USBERR_ALLOC; + } /* Endif */ + + service_ptr->TYPE = type; + service_ptr->SERVICE = service; + service_ptr->NEXT = NULL; + *search_ptr = service_ptr; + + USB_unlock(lockKey); + + return USB_OK; +} /* EndBody */ + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_device_unregister_service +* Returned Value : USB_OK or error code +* Comments : +* Unregisters a callback routine for a specified event or endpoint. +* +*END*--------------------------------------------------------------------*/ +uint_8 _usb_device_unregister_service + ( + /* [IN] Handle to the USB device */ + _usb_device_handle handle, + + /* [IN] type of event or endpoint number to service */ + uint_8 type + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + SERVICE_STRUCT_PTR service_ptr; + SERVICE_STRUCT_PTR _PTR_ search_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + /* Needs mutual exclusion */ + lockKey = USB_lock(); + + /* Search for an existing entry for type */ + for (search_ptr = &usb_dev_ptr->SERVICE_HEAD_PTR; + *search_ptr; + search_ptr = &(*search_ptr)->NEXT) + { + if ((*search_ptr)->TYPE == type) { + /* Found an existing entry - delete it */ + break; + } /* Endif */ + } /* Endfor */ + + /* No existing entry found */ + if (!*search_ptr) + { + USB_unlock(lockKey); + USB_printf("_usb_device_unregister_service, no service found\n"); + return USBERR_CLOSED_SERVICE; + } /* Endif */ + + service_ptr = *search_ptr; + *search_ptr = service_ptr->NEXT; + + USB_memfree((pointer)service_ptr); + + USB_unlock(lockKey); + + return USB_OK; + +} /* EndBody */ + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_device_call_service +* Returned Value : USB_OK or error code +* Comments : +* Calls the appropriate service for the specified type, if one is +* registered. Used internally only. +* +*END*--------------------------------------------------------------------*/ +uint_8 _usb_device_call_service + ( + /* [IN] Handle to the USB device */ + _usb_device_handle handle, + + /* [OUT] Type of service or endpoint */ + uint_8 type, + + /* [OUT] Is it a Setup transfer? */ + boolean setup, + + /* [OUT] Direction of transmission; is it a Transmit? */ + boolean direction, + + /* [OUT] Pointer to the data */ + uint_8_ptr buffer_ptr, + + /* [OUT] Number of bytes in transmission */ + uint_32 length, + + /* [OUT] Any errors */ + uint_8 errors + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + SERVICE_STRUCT _PTR_ service_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + /* Needs mutual exclusion */ + lockKey = USB_lock(); + + /* Search for an existing entry for type */ + for (service_ptr = usb_dev_ptr->SERVICE_HEAD_PTR; + service_ptr; + service_ptr = service_ptr->NEXT) + { + if (service_ptr->TYPE == type) + { + service_ptr->SERVICE(handle, type, setup, direction, buffer_ptr, length, errors); + USB_unlock(lockKey); + + return USB_OK; + } /* Endif */ + + } /* Endfor */ + + USB_unlock(lockKey); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_CTRL, "_usb_device_call_service, service %d is closed\n", type); + + return USBERR_CLOSED_SERVICE; +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_init_endpoint +* Returned Value : USB_OK or error code +* Comments : +* Initializes the endpoint and the data structures associated with the +* endpoint +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_init_endpoint + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] MAX Packet size for this endpoint */ + uint_16 max_pkt_size, + + /* [IN] Direction */ + uint_8 direction, + + /* [IN] Type of Endpoint */ + uint_8 type, + + /* [IN] After all data is transfered, should we terminate the transfer + ** with a zero length packet if the last packet size == MAX_PACKET_SIZE? + */ + uint_8 flag + ) +{ /* Body */ + + int lockKey; + uint_8 error = 0; + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + /* Initialize the transfer descriptor */ + usb_dev_ptr->TEMP_XD_PTR->EP_NUM = ep_num; + usb_dev_ptr->TEMP_XD_PTR->BDIRECTION = direction; + usb_dev_ptr->TEMP_XD_PTR->WMAXPACKETSIZE = max_pkt_size; + usb_dev_ptr->TEMP_XD_PTR->EP_TYPE = type; + usb_dev_ptr->TEMP_XD_PTR->DONT_ZERO_TERMINATE = flag; + usb_dev_ptr->TEMP_XD_PTR->MAX_PKTS_PER_UFRAME = + ((flag & ARC_USB_MAX_PKTS_PER_UFRAME) >> 1); + + lockKey = USB_lock(); + error = _usb_dci_vusb20_init_endpoint(handle, usb_dev_ptr->TEMP_XD_PTR); + USB_unlock(lockKey); + + return error; + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_deinit_endpoint +* Returned Value : USB_OK or error code +* Comments : +* Disables the endpoint and the data structures associated with the +* endpoint +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_deinit_endpoint + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] Direction */ + uint_8 direction + ) +{ /* Body */ + int lockKey; + uint_8 error = 0; + lockKey = USB_lock(); + + error = _usb_dci_vusb20_deinit_endpoint(handle, ep_num, direction); + + USB_unlock(lockKey); + + return error; +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_get_transfer_status +* Returned Value : Status of the transfer +* Comments : +* returns the status of the transaction on the specified endpoint. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_get_transfer_status + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + uint_8 status; + int lockKey; + + lockKey = USB_lock(); + + status = _usb_dci_vusb20_get_transfer_status(handle, ep_num, direction); + + USB_unlock(lockKey); + + /* Return the status of the last queued transfer */ + return (status); + +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_read_setup_data +* Returned Value : USB_OK or error code +* Comments : +* Reads the setup data from the hardware +* +*END*-----------------------------------------------------------------*/ +void _usb_device_read_setup_data + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] buffer for receiving Setup packet */ + uint_8_ptr buff_ptr + ) +{ /* Body */ + int lockKey; + + lockKey = USB_lock(); + + _usb_dci_vusb20_get_setup_data(handle, ep_num, buff_ptr); + + USB_unlock(lockKey); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_cancel_transfer +* Returned Value : USB_OK or error code +* Comments : +* returns the status of the transaction on the specified endpoint. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_cancel_transfer + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + uint_8 error = USB_OK; + int lockKey; + + lockKey = USB_lock(); + + /* Cancel transfer on the specified endpoint for the specified + ** direction + */ + error = _usb_dci_vusb20_cancel_transfer(handle, ep_num, direction); + + USB_unlock(lockKey); + + return error; +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_stop +* Returned Value : None +* Comments : +* Stop USB device +* +*END*-----------------------------------------------------------------*/ +void _usb_device_stop(_usb_device_handle handle) +{ + int lockKey; + + lockKey = USB_lock(); + _usb_dci_vusb20_stop(handle); + USB_unlock(lockKey); +} + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_start +* Returned Value : None +* Comments : +* Start USB device +* +*END*-----------------------------------------------------------------*/ +void _usb_device_start(_usb_device_handle handle) +{ + int lockKey; + + lockKey = USB_lock(); + _usb_dci_vusb20_start(handle); + USB_unlock(lockKey); +} + diff --git a/drivers/usb/gadget/mv/mvUsbDevPrv.h b/drivers/usb/gadget/mv/mvUsbDevPrv.h new file mode 100644 index 00000000000000..b3af624f00e801 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevPrv.h @@ -0,0 +1,270 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbDevPrv_h__ +#define __mvUsbDevPrv_h__ + +#include "mvUsbCore.h" + +//xj add +#define SET_ADDRESS_HARDWARE_ASSISTANCE 1 + +#define USB_TEST_MODE_TEST_PACKET_LENGTH (53) + + +#define USB_XD_QADD(head,tail,XD) \ + if ((head) == NULL) { \ + (head) = (XD); \ + } else { \ + (tail)->SCRATCH_PTR->PRIVATE = (XD); \ + } /* Endif */ \ + (tail) = (XD); \ + (XD)->SCRATCH_PTR->PRIVATE = NULL + +#define USB_XD_QGET(head,tail,XD) \ + (XD) = (head); \ + if (head) { \ + (head) = (XD_STRUCT_PTR)((head)->SCRATCH_PTR->PRIVATE); \ + if ((head) == NULL) { \ + (tail) = NULL; \ + } /* Endif */ \ + } /* Endif */ + +#define EHCI_DTD_QADD(head,tail,dTD) \ + if ((head) == NULL) { \ + (head) = (dTD); \ + } else { \ + (tail)->SCRATCH_PTR->PRIVATE = (void *) (dTD); \ + } /* Endif */ \ + (tail) = (dTD); \ + (dTD)->SCRATCH_PTR->PRIVATE = NULL + +#define EHCI_DTD_QGET(head,tail,dTD) \ + (dTD) = (head); \ + if (head) { \ + (head) = (head)->SCRATCH_PTR->PRIVATE; \ + if ((head) == NULL) { \ + (tail) = NULL; \ + } /* Endif */ \ + } /* Endif */ + +/*************************************** +** +** Data structures +** +*/ + +typedef struct +{ + uint_32 usb_isr_count; + uint_32 usb_reset_count; + uint_32 usb_send_count; + uint_32 usb_recv_count; + uint_32 usb_setup_count; + uint_32 free_XD_count; + uint_32 free_dTD_count; + uint_32 usb_cancel_count; + uint_32 usb_add_count; + uint_32 usb_add_not_empty_count; + uint_32 usb_empty_isr_count; + uint_32 usb_empty_complete_count; + uint_32 usb_read_setup_count; + uint_32 usb_complete_isr_count; + uint_32 usb_complete_count; + uint_32 usb_complete_max_count; + uint_32 usb_port_change_count; + uint_32 usb_suspend_count; + uint_32 usb_complete_ep_count[ARC_USB_MAX_ENDPOINTS*2]; + +} USB_STATS; + + + +/* Callback function storage structure */ +typedef struct service_struct +{ + uint_8 TYPE; + void (_CODE_PTR_ SERVICE)(pointer, uint_8, boolean, uint_8, uint_8_ptr, uint_32, uint_8); + struct service_struct _PTR_ NEXT; + +} SERVICE_STRUCT, _PTR_ SERVICE_STRUCT_PTR; + +typedef struct xd_struct +{ + uint_8 EP_NUM; /* Endpoint number */ + uint_8 BDIRECTION; /* Direction : Send/Receive */ + uint_8 EP_TYPE; /* Type of the endpoint: Ctrl, Isoch, Bulk, Int */ + uint_8 BSTATUS; /* Current transfer status */ + uint_8_ptr WSTARTADDRESS; /* Address of first byte */ + uint_32 WTOTALLENGTH; /* Number of bytes to send/recv */ + uint_32 WSOFAR; /* Number of bytes recv'd so far */ + uint_16 WMAXPACKETSIZE; /* Max Packet size */ + boolean DONT_ZERO_TERMINATE; + uint_8 MAX_PKTS_PER_UFRAME; + SCRATCH_STRUCT *SCRATCH_PTR; +} XD_STRUCT, _PTR_ XD_STRUCT_PTR; + +/* The USB Device State Structure */ +typedef struct +{ + boolean BUS_RESETTING; /* Device is + ** being reset */ + volatile VUSB20_REG_STRUCT_PTR CAP_REGS_PTR; /* Capabilities registers */ + + volatile VUSB20_REG_STRUCT_PTR DEV_PTR; /* Device Controller + ** Register base + ** address */ + + SERVICE_STRUCT_PTR SERVICE_HEAD_PTR; /* Head struct + ** address of + ** registered services + */ + XD_STRUCT_PTR TEMP_XD_PTR; /* Temp xd for ep init */ + XD_STRUCT_PTR XD_BASE; + XD_STRUCT_PTR XD_HEAD; /* Head Transaction + ** descriptors + */ + XD_STRUCT_PTR XD_TAIL; /* Tail Transaction + ** descriptors + */ + uint_32 XD_ENTRIES; + uint_8* EP_QUEUE_HEAD_BASE; + uint_32 EP_QUEUE_HEAD_PHYS; + uint_32 EP_QUEUE_HEAD_SIZE; + VUSB20_EP_QUEUE_HEAD_STRUCT_PTR EP_QUEUE_HEAD_PTR; /* Endpoint Queue head */ + + uint_8* DTD_BASE_PTR; /* Device transfer descriptor pool address */ + uint_32 DTD_BASE_PHYS; + uint_32 DTD_SIZE; + VUSB20_EP_TR_STRUCT_PTR DTD_ALIGNED_BASE_PTR;/* Aligned transfer descriptor pool address */ + + VUSB20_EP_TR_STRUCT_PTR DTD_HEAD; + VUSB20_EP_TR_STRUCT_PTR DTD_TAIL; + VUSB20_EP_TR_STRUCT_PTR EP_DTD_HEADS[ARC_USB_MAX_ENDPOINTS * 2]; + VUSB20_EP_TR_STRUCT_PTR EP_DTD_TAILS[ARC_USB_MAX_ENDPOINTS * 2]; + SCRATCH_STRUCT_PTR XD_SCRATCH_STRUCT_BASE; + + + SCRATCH_STRUCT_PTR SCRATCH_STRUCT_BASE; + + uint_16 USB_STATE; + uint_16 USB_DEVICE_STATE; + uint_16 USB_SOF_COUNT; + uint_16 DTD_ENTRIES; + uint_16 ERRORS; + uint_16 ERROR_STATE; + uint_16 USB_DEV_STATE_B4_SUSPEND; + uint_8 DEV_NUM; /* USB device number + ** on the board + */ + uint_8 SPEED; /* Low Speed, + ** High Speed, + ** Full Speed + */ + uint_8 MAX_ENDPOINTS; /* Max endpoints + ** supported by this + ** device + */ + + uint_8 USB_CURR_CONFIG; + uint_8 DEVICE_ADDRESS; + uint_8 FORCE_FS; + USB_STATS STATS; + + uint_8* STATUS_UNAIGNED_PTR; + uint_16* STATUS_PTR; + + uint_8* TEST_PKT_UNAIGNED_PTR; + uint_8* TEST_PKT_PTR; + +} USB_DEV_STATE_STRUCT, _PTR_ USB_DEV_STATE_STRUCT_PTR; + +/* ONLY For data bases allocated by the driver (when PHYS and VIRT bases are known) */ +#define USB_EP_QH_VIRT_TO_PHYS(handle, virtAddr) \ + (((virtAddr) == NULL) ? 0 : ((handle)->EP_QUEUE_HEAD_PHYS + \ + ((uint_32)(virtAddr) - (uint_32)(handle)->EP_QUEUE_HEAD_BASE))) + +#define USB_DTD_VIRT_TO_PHYS(handle, virtAddr) \ + (((virtAddr) == NULL) ? 0 : ((handle)->DTD_BASE_PHYS + \ + ((uint_32)(virtAddr) - (uint_32)(handle)->DTD_BASE_PTR))) + +#define USB_DTD_PHYS_TO_VIRT(handle, physAddr) \ + (((physAddr) == 0) ? NULL : ((handle)->DTD_BASE_PTR + \ + ((physAddr) - (handle)->DTD_BASE_PHYS))) + + +/*************************************** +** +** Prototypes +** +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern uint_8 _usb_device_call_service(void* handle, uint_8, boolean, + boolean, uint_8_ptr, uint_32, uint_8); + +extern uint_8 _usb_dci_vusb20_init(uint_8, _usb_device_handle); +extern void _usb_device_free_XD(pointer); +extern void _usb_dci_vusb20_free_dTD(pointer); +extern uint_8 _usb_dci_vusb20_add_dTD(_usb_device_handle, XD_STRUCT_PTR); +extern uint_8 _usb_dci_vusb20_cancel_transfer(_usb_device_handle, uint_8, uint_8); +extern uint_8 _usb_dci_vusb20_get_transfer_status(_usb_device_handle, uint_8, uint_8); +extern XD_STRUCT_PTR _usb_dci_vusb20_get_transfer_details(_usb_device_handle, uint_8, uint_8); +extern void _usb_dci_vusb20_process_tr_complete(_usb_device_handle); +extern void _usb_dci_vusb20_process_reset(_usb_device_handle); +extern void _usb_dci_vusb20_process_tr_complete(_usb_device_handle); +extern void _usb_dci_vusb20_process_suspend(_usb_device_handle); +extern void _usb_dci_vusb20_process_SOF(_usb_device_handle); +extern void _usb_dci_vusb20_process_port_change(_usb_device_handle); +extern void _usb_dci_vusb20_process_error(_usb_device_handle); +extern void _usb_dci_vusb20_shutdown(_usb_device_handle); +extern void _usb_dci_vusb20_set_speed_full(_usb_device_handle, uint_8); +extern void _usb_dci_vusb20_suspend_phy(_usb_device_handle, uint_8); +extern void _usb_dci_vusb20_hnp_shutdown(void); +extern void _usb_dci_vusb20_set_address(_usb_device_handle, uint_8); +extern void _usb_dci_vusb20_get_setup_data(_usb_device_handle, uint_8, uint_8_ptr); +extern void _usb_dci_vusb20_assert_resume(_usb_device_handle); +extern uint_8 _usb_dci_vusb20_init_endpoint(_usb_device_handle, XD_STRUCT_PTR); +extern void _usb_dci_vusb20_stall_endpoint(_usb_device_handle, uint_8, uint_8); +extern void _usb_dci_vusb20_unstall_endpoint(_usb_device_handle, uint_8, uint_8); +extern uint_8 _usb_dci_vusb20_is_endpoint_stalled(_usb_device_handle, uint_8, uint_8); +extern uint_8 _usb_dci_vusb20_deinit_endpoint(_usb_device_handle, uint_8, uint_8); +extern void _usb_dci_vusb20_chip_initialize(_usb_device_handle); +extern void _usb_dci_vusb20_stop(_usb_device_handle handle); +extern void _usb_dci_vusb20_start(_usb_device_handle handle); + +#if defined(USB_UNDERRUN_WA) + +extern uint_8* usbSramBase; +extern int usbSramSize; + +void _usb_reset_send_queue(void); +void usbSendComplete(void* handle, uint_8 type, boolean setup, uint_8 dir, + uint_8_ptr buffer, uint_32 length, uint_8 error); +#endif /* USB_UNDERRUN_WA */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/usb/gadget/mv/mvUsbDevRecv.c b/drivers/usb/gadget/mv/mvUsbDevRecv.c new file mode 100644 index 00000000000000..492a6db9b9cdfa --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevRecv.c @@ -0,0 +1,100 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_recv_data +* Returned Value : USB_OK or error code +* Comments : +* Receives data on a specified endpoint. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_recv_data + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] buffer to receive data */ + uint_8_ptr buff_ptr, + + /* [IN] length of the transfer */ + uint_32 size + ) +{ /* Body */ + int lockKey; + uint_8 error = USB_OK; + XD_STRUCT_PTR xd_ptr; + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_RX, "recv_data: ep=%d, buf_ptr=0x%x, size=%d\n", + ep_num, (unsigned)buff_ptr, (int)size); + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_recv_count++)); + + if(buff_ptr != NULL) + USB_dcache_inv((pointer)buff_ptr,size); + + lockKey = USB_lock(); + + if (!usb_dev_ptr->XD_ENTRIES) + { + USB_unlock(lockKey); + USB_printf("_usb_device_recv_data, transfer in progress\n"); + return ARC_USB_STATUS_TRANSFER_IN_PROGRESS; + } /* Endif */ + + /* Get a transfer descriptor for the specified endpoint + ** and direction + */ + USB_XD_QGET(usb_dev_ptr->XD_HEAD, usb_dev_ptr->XD_TAIL, xd_ptr); + + usb_dev_ptr->XD_ENTRIES--; + + /* Initialize the new transfer descriptor */ + xd_ptr->EP_NUM = ep_num; + xd_ptr->BDIRECTION = ARC_USB_RECV; + xd_ptr->WTOTALLENGTH = size; + xd_ptr->WSOFAR = 0; + xd_ptr->WSTARTADDRESS = buff_ptr; + + xd_ptr->BSTATUS = ARC_USB_STATUS_TRANSFER_ACCEPTED; + + error = _usb_dci_vusb20_add_dTD(handle, xd_ptr); + + USB_unlock(lockKey); + + if (error) + { + USB_printf("_usb_device_recv_data, receive failed\n"); + return USBERR_RX_FAILED; + } /* Endif */ + + return error; + +} /* EndBody */ + diff --git a/drivers/usb/gadget/mv/mvUsbDevSend.c b/drivers/usb/gadget/mv/mvUsbDevSend.c new file mode 100644 index 00000000000000..62511df16e0271 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevSend.c @@ -0,0 +1,377 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" +#include "../mvOs.h" + +#if defined(USB_UNDERRUN_WA) + +typedef struct +{ + uint_8* buff_ptr[MAX_XDS_FOR_TR_CALLS]; + uint_32 size[MAX_XDS_FOR_TR_CALLS]; + uint_8 ep_num[MAX_XDS_FOR_TR_CALLS]; + int head; + int tail; + int tail_dma; + int num; + int num_dma; + +} USB_SEND_QUEUE; + +uint_8* usbSramBase; +int usbSramSize; +int usbSramPartSize; +USB_SEND_QUEUE usbSendQueue; + +uint_32 usbSentSize = 0; +uint_32 usbDmaSize = 0; + +#define S_FREE 0 +#define S_BUSY 1 + +uint_32 dma_index = 0; +uint_32 sent_index = 0; +uint_32 sram_parts[USB_SRAM_MAX_PARTS]; + + +void _usb_reset_send_queue(void) +{ + int i; + + usbSendQueue.num = 0; + usbSendQueue.num_dma = 0; + usbSendQueue.head = 0; + usbSendQueue.tail = 0; + usbSendQueue.tail_dma = 0; + for(i=0; i= usbSendQueue.size[tail_dma]) + { + /* Remove from the usbSendQueues */ + num_dma--; + tail_dma++; + if(tail_dma == MAX_XDS_FOR_TR_CALLS) + tail_dma = 0; + + usbSendQueue.tail_dma = tail_dma; + usbSendQueue.num_dma = num_dma; + usbDmaSize = 0; + + if(num_dma == 0) + break; + } + + buff_ptr = usbSendQueue.buff_ptr[tail_dma] + usbDmaSize; + size = MIN(usbSramPartSize, (usbSendQueue.size[tail_dma] - usbDmaSize) ); + + usbDmaSize += size; + + if(size > global_wa_threshold) + { + tmp_buff = buff_ptr; + buff_ptr = (uint_8*)((int)usbSramBase + (dma_index * usbSramPartSize)); + USB_idma_copy(buff_ptr, tmp_buff, size); + + sram_parts[dma_index] = S_BUSY; + dma_index++; + if(dma_index == global_wa_sram_parts) + dma_index = 0; + } + + + /* Get a transfer descriptor */ + USB_XD_QGET(usb_dev_ptr->XD_HEAD, usb_dev_ptr->XD_TAIL, xd_ptr); + + usb_dev_ptr->XD_ENTRIES--; + USB_dcache_flush((pointer)buff_ptr, size); + + /* Initialize the new transfer descriptor */ + xd_ptr->EP_NUM = usbSendQueue.ep_num[tail_dma]; + xd_ptr->BDIRECTION = ARC_USB_SEND; + xd_ptr->WTOTALLENGTH = size; + xd_ptr->WSOFAR = 0; + xd_ptr->WSTARTADDRESS = buff_ptr; + xd_ptr->BSTATUS = ARC_USB_STATUS_TRANSFER_ACCEPTED; + + error = _usb_dci_vusb20_add_dTD(handle, xd_ptr); + + if(error) + break; + } + + return error; +} + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : usbSendComplete +* Returned Value : None +* Comments : +* Callback for send transfer complete event. +* +*END*-----------------------------------------------------------------*/ +void usbSendComplete(void* handle, uint_8 type, boolean setup, uint_8 dir, + uint_8_ptr buffer, uint_32 length, uint_8 error) +{ + /* Check if this complete is one from the sendQueue */ + if( (usbSendQueue.ep_num[usbSendQueue.tail] == type) && + (usbSendQueue.num > 0) ) + { + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + uint_8* buff_ptr; + uint_32 size; + int num, tail; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + tail = usbSendQueue.tail; + num = usbSendQueue.num; + buff_ptr = usbSendQueue.buff_ptr[tail]; + size = usbSendQueue.size[tail]; +/* + USB_printf("usbSendComplete: num=%d, tail=%d, usbSentSize=%d, type=%d, length=%d (%d), buff=%p (%p)\n", + num, tail, usbSentSize, type, length, usbSendQueue.size[tail], + buffer, usbSendQueue.buff_ptr[tail]); +*/ + usbSentSize += length; + + /* if the buffer was on the SRAM */ + if( ((unsigned)buffer >= (unsigned)usbSramBase) && + ((unsigned)buffer < ((unsigned)usbSramBase + (usbSramPartSize * global_wa_sram_parts))) ) + { + sram_parts[sent_index] = S_FREE; + sent_index++; + if(sent_index == global_wa_sram_parts) + sent_index = 0; + } + + if(usbSentSize >= usbSendQueue.size[tail]) + { + /* Remove from the usbSendQueues */ + num--; + tail++; + if(tail == MAX_XDS_FOR_TR_CALLS) + tail = 0; + + usbSendQueue.tail = tail; + usbSendQueue.num = num; + usbSentSize = 0; + + /* Call complete callback */ + _usb_device_call_service(handle, type, setup, dir, + buff_ptr, size, error); + + if(num == 0) + return; + } + + error = _usb_prepare_to_send(handle); + if (error) + { + USB_printf("usbSendComplete, add_dTD failed\n"); + } + + } + else + { + /* Call complete callback */ + _usb_device_call_service(handle, type, setup, dir, + buffer, length, error); + } +} +#endif /* USB_UNDERRUN_WA */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_send_data +* Returned Value : USB_OK or error code +* Comments : +* Sends data on a specified endpoint. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_send_data + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] buffer to send */ + uint_8_ptr buff_ptr, + + /* [IN] length of the transfer */ + uint_32 size + ) +{ /* Body */ + int lockKey; + uint_8 error = 0; + XD_STRUCT_PTR xd_ptr; + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + boolean toSend = TRUE; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TX, + "send_data: handle=%p, ep=%d, pBuf=0x%x, size=%d, EP_QH=%p\n", + handle, ep_num, (unsigned)buff_ptr, (int)size, usb_dev_ptr->EP_QUEUE_HEAD_PTR); + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_send_count++)); + + lockKey = USB_lock(); + + if (!usb_dev_ptr->XD_ENTRIES) + { + USB_unlock(lockKey); + USB_printf("_usb_device_send_data, transfer in progress\n"); + printk(KERN_DEBUG "_usb_device_send_data, transfer in progress\n"); + return ARC_USB_STATUS_TRANSFER_IN_PROGRESS; + } /* Endif */ + +#if defined(USB_UNDERRUN_WA) + { + int head; + VUSB20_EP_QUEUE_HEAD_STRUCT* ep_queue_head_ptr; + + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + + 2*ep_num + ARC_USB_SEND; + + if( ((ep_queue_head_ptr->MAX_PKT_LENGTH >> 16) & 0x7FF) > global_wa_threshold) + { + /* Only Endpoints with maxPktSize more than 128 bytes need special processing */ + if( (size > global_wa_threshold) || + (usbSendQueue.num != 0) ) + { + + USB_printf("_usb_device_send_data: ep_num=%d, maxPktSize=%d, size=%d\n", + ep_num, (ep_queue_head_ptr->MAX_PKT_LENGTH >> 16) & 0x7FF, size); + + /* Check if usbSendQueue is not Full */ + if(usbSendQueue.num == MAX_XDS_FOR_TR_CALLS) + { + USB_printf("ep=%d: usbSendQueue is FULL\n", ep_num); + printk(KERN_DEBUG "ep=%d: usbSendQueue is FULL\n", ep_num); + USB_unlock(lockKey); + return USBERR_TX_FAILED; + } + + /* Add to usbSendQueu */ + head = usbSendQueue.head; + + usbSendQueue.num++; + usbSendQueue.num_dma++; + usbSendQueue.size[head] = size; + usbSendQueue.buff_ptr[head] = buff_ptr; + usbSendQueue.ep_num[head] = ep_num; + + head++; + if(head == MAX_XDS_FOR_TR_CALLS) + head = 0; + + usbSendQueue.head = head; + + /* Process first usbSendQueue element if possible */ + if(usbSendQueue.num == 1) + { + error = _usb_prepare_to_send(handle); + } + toSend = FALSE; + } + } + } +#endif /* USB_UNDERRUN_WA */ + + if(toSend == TRUE) + { + /* Get a transfer descriptor */ + USB_XD_QGET(usb_dev_ptr->XD_HEAD, usb_dev_ptr->XD_TAIL, xd_ptr); + + usb_dev_ptr->XD_ENTRIES--; + + if(buff_ptr != NULL) + USB_dcache_flush((pointer)buff_ptr, size); + + /* Initialize the new transfer descriptor */ + xd_ptr->EP_NUM = ep_num; + xd_ptr->BDIRECTION = ARC_USB_SEND; + xd_ptr->WTOTALLENGTH = size; + xd_ptr->WSOFAR = 0; + xd_ptr->WSTARTADDRESS = buff_ptr; + xd_ptr->BSTATUS = ARC_USB_STATUS_TRANSFER_ACCEPTED; + + error = _usb_dci_vusb20_add_dTD(handle, xd_ptr); + } + USB_unlock(lockKey); + + if (error) + { + printk(KERN_DEBUG "_usb_device_send_data, transfer failed\n"); + return USBERR_TX_FAILED; + } /* Endif */ + return error; + +} /* EndBody */ + + diff --git a/drivers/usb/gadget/mv/mvUsbDevUtl.c b/drivers/usb/gadget/mv/mvUsbDevUtl.c new file mode 100644 index 00000000000000..d99d13f2877da1 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbDevUtl.c @@ -0,0 +1,651 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_unstall_endpoint +* Returned Value : USB_OK or error code +* Comments : +* Unstalls the endpoint in specified direction +* +*END*-----------------------------------------------------------------*/ +void _usb_device_unstall_endpoint + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + lockKey = USB_lock(); + + _usb_dci_vusb20_unstall_endpoint(handle, ep_num, direction); + + USB_unlock(lockKey); + +} /* EndBody */ + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_device_get_status +* Returned Value : USB_OK or error code +* Comments : +* Provides API to access the USB internal state. +* +*END*--------------------------------------------------------------------*/ +uint_8 _usb_device_get_status + ( + /* [IN] Handle to the USB device */ + _usb_device_handle handle, + + /* [IN] What to get the status of */ + uint_8 component, + + /* [OUT] The requested status */ + uint_16_ptr status + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + lockKey = USB_lock(); + + switch (component) + { + case ARC_USB_STATUS_DEVICE_STATE: + *status = usb_dev_ptr->USB_STATE; + break; + + case ARC_USB_STATUS_DEVICE: + *status = usb_dev_ptr->USB_DEVICE_STATE; + break; + + case ARC_USB_STATUS_INTERFACE: + *status = 0; + break; + + case ARC_USB_STATUS_ADDRESS: + *status = usb_dev_ptr->DEVICE_ADDRESS; + break; + + case ARC_USB_STATUS_CURRENT_CONFIG: + *status = usb_dev_ptr->USB_CURR_CONFIG; + break; + + case ARC_USB_STATUS_SOF_COUNT: + *status = usb_dev_ptr->USB_SOF_COUNT; + break; + + default: + USB_unlock(lockKey); + USB_printf("_usb_device_get_status, bad status\n"); + return USBERR_BAD_STATUS; + + } /* Endswitch */ + USB_unlock(lockKey); + + return USB_OK; +} /* EndBody */ + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_device_set_status +* Returned Value : USB_OK or error code +* Comments : +* Provides API to set internal state +* +*END*--------------------------------------------------------------------*/ +uint_8 _usb_device_set_status + ( + /* [IN] Handle to the usb device */ + _usb_device_handle handle, + + /* [IN] What to set the status of */ + uint_8 component, + + /* [IN] What to set the status to */ + uint_16 setting + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + int lockKey; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_STATUS, + "set_status: component=0x%x, value=0x%x\n", component, setting); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + lockKey = USB_lock(); + + switch (component) + { + case ARC_USB_STATUS_DEVICE_STATE: + usb_dev_ptr->USB_STATE = setting; + break; + + case ARC_USB_STATUS_DEVICE: + usb_dev_ptr->USB_DEVICE_STATE = setting; + break; + + case ARC_USB_STATUS_INTERFACE: + break; + + case ARC_USB_STATUS_CURRENT_CONFIG: + usb_dev_ptr->USB_CURR_CONFIG = setting; + break; + + case ARC_USB_STATUS_SOF_COUNT: + usb_dev_ptr->USB_SOF_COUNT = setting; + break; + + case ARC_USB_FORCE_FULL_SPEED: + _usb_dci_vusb20_set_speed_full((pointer)usb_dev_ptr, setting); + break; + + case ARC_USB_PHY_LOW_POWER_SUSPEND: + _usb_dci_vusb20_suspend_phy((pointer)usb_dev_ptr, setting); + break; + + case ARC_USB_STATUS_ADDRESS: + usb_dev_ptr->DEVICE_ADDRESS = setting; + + _usb_dci_vusb20_set_address((pointer)usb_dev_ptr, setting); + break; + + case ARC_USB_STATUS_TEST_MODE: + _usb_dci_vusb20_set_test_mode(handle, setting); + break; + + default: + USB_unlock(lockKey); + USB_printf("_usb_device_set_status, bad status\n"); + return USBERR_BAD_STATUS; + + } /* Endswitch */ + + USB_unlock(lockKey); + + return USB_OK; +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_stall_endpoint +* Returned Value : USB_OK or error code +* Comments : +* Stalls the endpoint. +* +*END*-----------------------------------------------------------------*/ +void _usb_device_stall_endpoint + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + lockKey = USB_lock(); + + _usb_dci_vusb20_stall_endpoint(handle, ep_num, direction); + + USB_unlock(lockKey); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_is_endpoint_stalled +* Returned Value : USB_OK or error code +* Comments : +* Stalls the endpoint. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_device_is_endpoint_stalled + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + uint_8 val; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + lockKey = USB_lock(); + + val = _usb_dci_vusb20_is_endpoint_stalled(handle, ep_num, direction); + + USB_unlock(lockKey); + + return val; + +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_device_process_resume +* Returned Value : USB_OK or error code +* Comments : +* Process Resume event +* +*END*-----------------------------------------------------------------*/ +void _usb_device_assert_resume + ( + /* [IN] the USB_USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + _usb_dci_vusb20_assert_resume(handle); + +} /* EndBody */ + + +/***************************/ +/* ARC USB Debug functions */ +/***************************/ +void _usb_status(void* usbHandle) +{ + USB_DEV_STATE_STRUCT* pUsbDev = (USB_DEV_STATE_STRUCT*)usbHandle; + + if(pUsbDev == NULL) + { + USB_printf("USB Device core is not initialized\n"); + return; + } + + USB_printf("\n\tUSB Status\n\n"); + + USB_printf("DEV_NUM=%d, DEV_ADDR=%d, CAP_REGS=0x%x, DEV_REGS=0x%x, MAX_EP=%d\n", + pUsbDev->DEV_NUM, + pUsbDev->DEVICE_ADDRESS, + (unsigned)pUsbDev->CAP_REGS_PTR, + (unsigned)pUsbDev->DEV_PTR, + pUsbDev->MAX_ENDPOINTS); + + USB_printf("BUS_RESET=%s, USB_STATE=0x%02x, USB_DEV_STATE=0x%02x, SPEED=%d, ERRORS=0x%04x\n", + pUsbDev->BUS_RESETTING ? "Yes" : "No", + pUsbDev->USB_STATE, + pUsbDev->USB_DEVICE_STATE, + pUsbDev->SPEED, + pUsbDev->ERRORS); + + USB_printf("EP_QUEUE_HEAD: SIZE=%d, BASE=%p (0x%08x), ALIGNED=%p, SERVICE_HEAD=%p\n", + pUsbDev->EP_QUEUE_HEAD_SIZE, + pUsbDev->EP_QUEUE_HEAD_BASE, + pUsbDev->EP_QUEUE_HEAD_PHYS, + pUsbDev->EP_QUEUE_HEAD_PTR, + pUsbDev->SERVICE_HEAD_PTR); + + USB_printf("XD: BASE=%p, HEAD=%p, TAIL=%p, ENTRIES=%d, SCRATCH=%p, TEMP=%p\n", + pUsbDev->XD_BASE, + pUsbDev->XD_HEAD, + pUsbDev->XD_TAIL, + pUsbDev->XD_ENTRIES, + pUsbDev->XD_SCRATCH_STRUCT_BASE, + pUsbDev->TEMP_XD_PTR); + + USB_printf("DTD: SIZE=%d, BASE=%p (0x%08x), ALIGNED=%p, HEAD=0x%08x, TAIL=0x%08x, ENTRIES=%d, SCRATCH=%p\n", + pUsbDev->DTD_SIZE, + pUsbDev->DTD_BASE_PTR, + pUsbDev->DTD_BASE_PHYS, + pUsbDev->DTD_ALIGNED_BASE_PTR, + pUsbDev->DTD_HEAD, + pUsbDev->DTD_TAIL, + pUsbDev->DTD_ENTRIES, + pUsbDev->SCRATCH_STRUCT_BASE); +} + +void _usb_stats(void* usbHandle) +{ + USB_DEV_STATE_STRUCT* pUsbDev = (USB_DEV_STATE_STRUCT*)usbHandle; + USB_STATS* pUsbStats = &pUsbDev->STATS; + int i; + + USB_printf("\n\tUSB Statistics\n\n"); + + USB_printf("isr=%u, empty_isr=%u, reset=%u, setup=%u, read_setup=%u\n", + pUsbStats->usb_isr_count, pUsbStats->usb_empty_isr_count, + pUsbStats->usb_reset_count, pUsbStats->usb_setup_count, + pUsbStats->usb_read_setup_count); + + USB_printf("recv=%u, send=%u, add=%u (%u), cancel=%u\n", + pUsbStats->usb_recv_count, pUsbStats->usb_send_count, + pUsbStats->usb_add_count, pUsbStats->usb_add_not_empty_count, + pUsbStats->usb_cancel_count); + + USB_printf("free_XD=%u, free_dTD=%u\n", + pUsbStats->free_XD_count, pUsbStats->free_dTD_count); + + USB_printf("complete_isr=%u, complete=%u, empty_complete=%u, max_complete=%u\n", + pUsbStats->usb_complete_isr_count, pUsbStats->usb_complete_count, + pUsbStats->usb_empty_complete_count, pUsbStats->usb_complete_max_count); + + USB_printf("port_change=%u, suspend=%u\n", + pUsbStats->usb_port_change_count, pUsbStats->usb_suspend_count); + for(i=0; i<(pUsbDev->MAX_ENDPOINTS); i++) + { + if( (pUsbStats->usb_complete_ep_count[i*2] == 0) && + (pUsbStats->usb_complete_ep_count[i*2+1] == 0) ) + continue; + + USB_printf("EP #%d: RECV (OUT) = %3u, \tSEND (IN) = %u\n", i, + pUsbStats->usb_complete_ep_count[i*2], + pUsbStats->usb_complete_ep_count[i*2+1]); + } + USB_printf("\n"); +} + +void _usb_clear_stats(void* usbHandle) +{ + USB_DEV_STATE_STRUCT* pUsbDev = (USB_DEV_STATE_STRUCT*)usbHandle; + + USB_memzero(&pUsbDev->STATS, sizeof(pUsbDev->STATS)); +} + +void _usb_regs(void* usbHandle) +{ + USB_DEV_STATE_STRUCT* pUsbDev = (USB_DEV_STATE_STRUCT*)usbHandle; + VUSB20_REG_STRUCT* cap_regs, *dev_regs; + int dev_num; + + if(pUsbDev == NULL) + { + USB_printf("USB Device core is not initialized\n"); + return; + } + USB_printf("\n\tUSB Capability Registers\n\n"); + + cap_regs = pUsbDev->CAP_REGS_PTR; + USB_printf("CAPLENGTH_HCIVER (0x%08x) = 0x%08x\n", + (unsigned)&cap_regs->REGISTERS.CAPABILITY_REGISTERS.CAPLENGTH_HCIVER, + (unsigned)USB_32BIT_LE(cap_regs->REGISTERS.CAPABILITY_REGISTERS.CAPLENGTH_HCIVER)); + + USB_printf("DCI_VERSION (0x%08x) = 0x%08x\n", + (unsigned)&cap_regs->REGISTERS.CAPABILITY_REGISTERS.DCI_VERSION, + (unsigned)USB_32BIT_LE(cap_regs->REGISTERS.CAPABILITY_REGISTERS.DCI_VERSION)); + + USB_printf("DCC_PARAMS (0x%08x) = 0x%08x\n", + (unsigned)&cap_regs->REGISTERS.CAPABILITY_REGISTERS.DCC_PARAMS, + (unsigned)USB_32BIT_LE(cap_regs->REGISTERS.CAPABILITY_REGISTERS.DCC_PARAMS)); + + dev_regs = pUsbDev->DEV_PTR; + dev_num = pUsbDev->DEV_NUM; + USB_printf("\n\tUSB Device Operational Registers\n\n"); + + USB_printf("USB_CMD (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD)); + + USB_printf("USB_STS (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS)); + + USB_printf("USB_INTR (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR)); + + USB_printf("USB_FRINDEX (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX)); + + /* Skip CTRLDSSEGMENT register */ + USB_printf("DEVICE_ADDR (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR)); + + USB_printf("EP_LIST_ADDR (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR)); + + /* Skip CONFIG_FLAG register */ + + /* Skip PORTSCX[0..15] registers*/ + USB_printf("PORTSCX[0] (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0], + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0])); + + /* Skip OTGSC register */ + + USB_printf("USB_MODE (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_MODE, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_MODE)); + + USB_printf("ENDPT_SETUP_STAT (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT)); + + USB_printf("ENDPTPRIME (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME)); + + USB_printf("ENDPTFLUSH (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH)); + + USB_printf("ENDPTSTATUS (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS)); + + USB_printf("ENDPTCOMPLETE (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE)); + + USB_printf("OTGSC (0x%08x) = 0x%08x\n", + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.OTGSC, + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.OTGSC)); +} + + +void _usb_ep_status(void* usbHandle, int ep_num, int direction) +{ + USB_DEV_STATE_STRUCT* pUsbDev = (USB_DEV_STATE_STRUCT*)usbHandle; + int i, ep_idx; + VUSB20_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr; + VUSB20_EP_TR_STRUCT_PTR dTD_ptr, head_dTD_ptr, tail_dTD_ptr, next_dTD_ptr; + XD_STRUCT_PTR xd_ptr, next_xd_ptr; + VUSB20_REG_STRUCT_PTR dev_regs; + + if(pUsbDev == NULL) + { + USB_printf("USB Device core is not initialized\n"); + return; + } + + USB_printf("\n\tUSB Endpoint #%d - %s status\n\n", ep_num, + (direction == ARC_USB_SEND) ? "SEND (IN)" : "RECV (OUT)" ); + + ep_idx = ep_num*2 + direction; + dev_regs = pUsbDev->DEV_PTR; + + USB_printf("ENDPTCTRLX[%d] (0x%08x) = 0x%08x\n", ep_num, + (unsigned)&dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num], + (unsigned)USB_32BIT_LE(dev_regs->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num])); + + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)pUsbDev->EP_QUEUE_HEAD_PTR + ep_idx; + + head_dTD_ptr = pUsbDev->EP_DTD_HEADS[ep_idx]; + tail_dTD_ptr = pUsbDev->EP_DTD_TAILS[ep_idx]; + + USB_printf("EP_QH=0x%08x: MAX_PKT=0x%x, SIZE_IOC_INT_STS=0x%x, CURR_DTD=0x%x, NEXT_DTD=0x%x\n", + (unsigned)ep_queue_head_ptr, (unsigned)USB_32BIT_LE(ep_queue_head_ptr->MAX_PKT_LENGTH), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->CURR_DTD_PTR), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->NEXT_DTD_PTR)); + + USB_printf("\tBUF_0=0x%08x, BUF_1=0x%08x, BUF_2=0x%08x, BUF_3=0x%08x, BUF_4=0x%08x\n", + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->BUFF_PTR0), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->BUFF_PTR1), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->BUFF_PTR2), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->BUFF_PTR3), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->BUFF_PTR4)); + + USB_printf("\tSETUP_BUFFER (%p): ", ep_queue_head_ptr->SETUP_BUFFER); + for(i=0; iSETUP_BUFFER); i++) + USB_printf("%02x", ep_queue_head_ptr->SETUP_BUFFER[i] & 0xFF); + USB_printf("\n"); + + USB_printf("\ndTD_HEAD=0x%08x, dTD_TAIL=0x%08x\n", + (unsigned)head_dTD_ptr, (unsigned)tail_dTD_ptr); + + dTD_ptr = head_dTD_ptr; + i = 0; + while(dTD_ptr != NULL) + { + USB_printf("%d. dTD=0x%08x (0x%08x), SIZE_IOC_STS=0x%08x, BUF_0=0x%08x, NEXT=0x%08x\n", + i, (unsigned)dTD_ptr, USB_DTD_VIRT_TO_PHYS(pUsbDev, dTD_ptr), + (unsigned)USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS), + (unsigned)USB_32BIT_LE(dTD_ptr->BUFF_PTR0), + (unsigned)USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR)); + + xd_ptr = dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD; + + next_dTD_ptr = (VUSB20_EP_TR_STRUCT_PTR)USB_DTD_PHYS_TO_VIRT(pUsbDev, + (uint_32)(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK)); + if(next_dTD_ptr != NULL) + next_xd_ptr = next_dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD; + else + next_xd_ptr = NULL; + + if(next_xd_ptr != xd_ptr) + { + USB_printf("\tXD=0x%08x, ADDR=0x%08x, SIZE=%u, STATUS=0x%02x\n", + (unsigned)xd_ptr, (unsigned)xd_ptr->WSTARTADDRESS, + (unsigned)xd_ptr->WTOTALLENGTH, xd_ptr->BSTATUS); + } + i++; + dTD_ptr = next_dTD_ptr; + } +} + + +/* DEBUG */ +uint_32 usbDebugFlags = ARC_DEBUG_FLAG_STATS + | ARC_DEBUG_FLAG_INIT + | ARC_DEBUG_FLAG_ERROR +#if 1 +// | ARC_DEBUG_FLAG_TRACE // for free dTD check + | ARC_DEBUG_FLAG_ISR //xj add + | ARC_DEBUG_FLAG_CTRL //xj add + | ARC_DEBUG_FLAG_RX //xj add + | ARC_DEBUG_FLAG_TX //xj add + | ARC_DEBUG_FLAG_INIT //xj add + | ARC_DEBUG_FLAG_TRANSFER //xj add + | ARC_DEBUG_FLAG_SETUP //xj add + | ARC_DEBUG_FLAG_EP0 //xj add + | ARC_DEBUG_FLAG_ADDR //xj add +#endif + | ARC_DEBUG_FLAG_STALL + | ARC_DEBUG_FLAG_RESET; + /*| ARC_DEBUG_FLAG_TRANSFER;*/ + +void _usb_debug_set_flags(uint_32 flags) +{ + usbDebugFlags = (flags); +} + +uint_32 _usb_debug_get_flags(void) +{ + return usbDebugFlags; +} + +#if defined(MV_USB_TRACE_LOG) + +uint_16 DEBUG_TRACE_ARRAY_COUNTER = 0; +char DEBUG_TRACE_ARRAY[TRACE_ARRAY_SIZE][MAX_STRING_SIZE]; + +void _usb_debug_init_trace_log(void) +{ + USB_memzero(DEBUG_TRACE_ARRAY, TRACE_ARRAY_SIZE*MAX_STRING_SIZE); + DEBUG_TRACE_ARRAY_COUNTER =0; +} + +void _usb_debug_print_trace_log(void) +{ + int i; + + USB_printf("USB Trace log: start=0x%x, end=0x%x, idx=%d, flags=0x%x\n\n", + &DEBUG_TRACE_ARRAY[0][0], &DEBUG_TRACE_ARRAY[TRACE_ARRAY_SIZE-1][0], + DEBUG_TRACE_ARRAY_COUNTER, usbDebugFlags); + + for(i=DEBUG_TRACE_ARRAY_COUNTER; iDEV_PTR; + + bit_pos = (1 << (16 * direction + ep_num)); + temp = (2*ep_num + direction); + + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp; + + /* Unlink the dTD */ + dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[temp]; + + if (dTD_ptr) + { + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_cancel_count++)); + + check_dTD_ptr = (VUSB20_EP_TR_STRUCT_PTR)USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, + ((uint_32)USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK)); + + if (vbus_check) + goto next; + if (USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSBHS_TD_STATUS_ACTIVE) + { + /* Flushing will halt the pipe */ + /* Write 1 to the Flush register */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH = USB_32BIT_LE(bit_pos); + + /* Wait until flushing completed */ + timeout = 0x10000; + while (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH) & bit_pos) + { + /* ENDPTFLUSH bit should be cleared to indicate this operation is complete */ + timeout--; + if(timeout == 0) + { + USB_printf("USB Cancel: - TIMEOUT for ENDPTFLUSH=0x%x, bit_pos=0x%x \n", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH), + (unsigned)bit_pos); + break; + } + } /* EndWhile */ + status_timeout = 0x10000; + while (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos) + { + status_timeout--; + if(status_timeout == 0) + { + USB_printf("USB Cancel: - TIMEOUT for ENDPTSTATUS=0x%x, bit_pos=0x%x\n", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + (unsigned)bit_pos); + break; + } + + /* Write 1 to the Flush register */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH = USB_32BIT_LE(bit_pos); + + /* Wait until flushing completed */ + timeout = 0x10000; + while (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH) & bit_pos) + { + /* ENDPTFLUSH bit should be cleared to indicate this operation is complete */ + timeout--; + if(timeout == 0) + { + USB_printf("USB Cancel: - TIMEOUT for ENDPTFLUSH=0x%x, ENDPTSTATUS=0x%x, bit_pos=0x%x\n", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + (unsigned)bit_pos); + break; + } + } /* EndWhile */ + } /* EndWhile */ + } /* Endif */ + +next: + /* Retire the current dTD */ + dTD_ptr->SIZE_IOC_STS = 0; + dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(VUSBHS_TD_NEXT_TERMINATE); + + /* The transfer descriptor for this dTD */ + xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD; + dTD_ptr->SCRATCH_PTR->PRIVATE = (pointer)usb_dev_ptr; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER, + "cncl_%d: fri=0x%x, ep=%d%s, buf=%p, size=%d, xd=%p, dTD=%p %p, bit=0x%x\n", + usb_dev_ptr->STATS.usb_cancel_count & 0xFFFF, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), + ep_num, direction ? "in" : "out", + xd_ptr->WSTARTADDRESS, xd_ptr->WTOTALLENGTH, xd_ptr, + dTD_ptr, check_dTD_ptr, bit_pos); + + /* Free the dTD */ + _usb_dci_vusb20_free_dTD((pointer)dTD_ptr); + + /* Update the dTD head and tail for specific endpoint/direction */ + if (!check_dTD_ptr) + { + usb_dev_ptr->EP_DTD_HEADS[temp] = NULL; + usb_dev_ptr->EP_DTD_TAILS[temp] = NULL; + if (xd_ptr) + { + xd_ptr->SCRATCH_PTR->PRIVATE = (pointer)usb_dev_ptr; + /* Free the transfer descriptor */ + _usb_device_free_XD((pointer)xd_ptr); + } /* Endif */ + /* No other transfers on the queue */ + ep_queue_head_ptr->NEXT_DTD_PTR = USB_32BIT_LE(VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE); + ep_queue_head_ptr->SIZE_IOC_INT_STS = 0; + } + else + { + usb_dev_ptr->EP_DTD_HEADS[temp] = check_dTD_ptr; + + if (xd_ptr) + { + if ((uint_32)check_dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD != (uint_32)xd_ptr) + { + xd_ptr->SCRATCH_PTR->PRIVATE = (pointer)usb_dev_ptr; + /* Free the transfer descriptor */ + _usb_device_free_XD((pointer)xd_ptr); + } /* Endif */ + } /* Endif */ + + if (vbus_check) + goto done; + + if (USB_32BIT_LE(check_dTD_ptr->SIZE_IOC_STS) & VUSBHS_TD_STATUS_ACTIVE) + { + /* Start CR 1015 */ + /* Prime the Endpoint */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos); + + if (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos)) + { + timeout = 0x10000; + while (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & bit_pos) + { + /* Wait for the ENDPTPRIME to go to zero */ + timeout--; + if(timeout == 0) + { + USB_printf("USB Cancel: - TIMEOUT for ENDPTPRIME=0x%x, bit_pos=0x%x\n", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), + (unsigned)bit_pos); + break; + } + } /* EndWhile */ + + if (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos) + { + /* The endpoint was not not primed so no other transfers on + ** the queue + */ + goto done; + } /* Endif */ + } + else + { + goto done; + } /* Endif */ + +#if 0 //xj del, otherwise SET EPPRIME twice, USBSTS[SEI] bit would be set, and EP never work again + /* No other transfers on the queue */ + ep_queue_head_ptr->NEXT_DTD_PTR = (uint_32)USB_32BIT_LE((uint_32)check_dTD_ptr); + ep_queue_head_ptr->SIZE_IOC_INT_STS = 0; + + /* Prime the Endpoint */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos); +#endif + } /* Endif */ + } /* Endif */ + } /* Endif */ + +done: + + /* End CR 1015 */ + return USB_OK; +} /* EndBody */ + +/* EOF */ + diff --git a/drivers/usb/gadget/mv/mvUsbHsDevMain.c b/drivers/usb/gadget/mv/mvUsbHsDevMain.c new file mode 100644 index 00000000000000..4a420df853717f --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbHsDevMain.c @@ -0,0 +1,2039 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" + +#include +#include +#include + +extern int rely_on_vbus; +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_init +* Returned Value : USB_OK or error code +* Comments : +* Initializes the USB device controller. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_dci_vusb20_init + ( + /* [IN] the USB device controller to initialize */ + uint_8 devnum, + + /* [OUT] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_TR_STRUCT_PTR dTD_ptr; + uint_32 i; + SCRATCH_STRUCT_PTR temp_scratch_ptr; + uint_32 temp; + uint_8* pBuf; + unsigned long phyAddr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + usb_dev_ptr->CAP_REGS_PTR = + (VUSB20_REG_STRUCT_PTR)USB_get_cap_reg_addr(devnum); + + /* Get the base address of the VUSB_HS registers */ + usb_dev_ptr->DEV_PTR = + (VUSB20_REG_STRUCT_PTR)(((uint_32)usb_dev_ptr->CAP_REGS_PTR) + + (USB_32BIT_LE(usb_dev_ptr->CAP_REGS_PTR->REGISTERS.CAPABILITY_REGISTERS.CAPLENGTH_HCIVER) & + EHCI_CAP_LEN_MASK)); + + /* Get the maximum number of endpoints supported by this USB controller */ + usb_dev_ptr->MAX_ENDPOINTS = + (USB_32BIT_LE(usb_dev_ptr->CAP_REGS_PTR->REGISTERS.CAPABILITY_REGISTERS.DCC_PARAMS) & + VUSB20_DCC_MAX_ENDPTS_SUPPORTED); + + USB_printf("USB init: CAP_REGS=0x%x, DEV_REGS=0x%x, MAX_EP=%d\n", + (unsigned)usb_dev_ptr->CAP_REGS_PTR, (unsigned)usb_dev_ptr->DEV_PTR, + usb_dev_ptr->MAX_ENDPOINTS); + + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + USB_printf(" DEVICE_ADDR(%x)=0x%x, EP_LIST_ADDR(%x)=0x%x, EP_NAK(%x)=%x, OTGSC(%x)=%x, MCR(%x)=%x\n", + &dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR, dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR, + &dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR, dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR, + &dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_NAK, dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_NAK, + &dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.OTGSC, dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.OTGSC, + &dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.MCR, dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.MCR); + + temp = (usb_dev_ptr->MAX_ENDPOINTS * 2); + + pBuf = (uint_8*)USB_uncached_memalloc(temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT), + 2048, &phyAddr); + if (pBuf == NULL) + { + USB_printf("_usb_dci_vusb20_init, malloc of %d bytes in Uncached area failed\n", + temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT)); + return USBERR_ALLOC; + } + + /**************************************************************** + Assign QH base + ****************************************************************/ + usb_dev_ptr->EP_QUEUE_HEAD_BASE = pBuf; + usb_dev_ptr->EP_QUEUE_HEAD_PHYS = (uint_32)phyAddr; + + /* Align the endpoint queue head to 2K boundary */ + usb_dev_ptr->EP_QUEUE_HEAD_PTR = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR) + USB_MEM2048_ALIGN((uint_32)usb_dev_ptr->EP_QUEUE_HEAD_BASE); + + usb_dev_ptr->EP_QUEUE_HEAD_SIZE = temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT) + + ((uint_32)usb_dev_ptr->EP_QUEUE_HEAD_PTR - + (uint_32)usb_dev_ptr->EP_QUEUE_HEAD_BASE); + + /**************************************************************** + Zero out the memory allocated + ****************************************************************/ + USB_memzero( (void*)usb_dev_ptr->EP_QUEUE_HEAD_PTR, + temp*sizeof(VUSB20_EP_QUEUE_HEAD_STRUCT)); + + USB_printf("USB EP_QH: Base=%p (0x%x), Aligned(%d)=%p, Size=%d\n", + usb_dev_ptr->EP_QUEUE_HEAD_BASE, usb_dev_ptr->EP_QUEUE_HEAD_PHYS, + 2048, usb_dev_ptr->EP_QUEUE_HEAD_PTR, usb_dev_ptr->EP_QUEUE_HEAD_SIZE); + + /**************************************************************** + Assign DTD base + ****************************************************************/ + pBuf = (uint_8*)USB_uncached_memalloc(MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT), + 32, &phyAddr); + if (pBuf == NULL) + { + USB_printf("_usb_dci_vusb20_init, malloc of %d bytes in Uncached area failed\n", + MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT)); + return USBERR_ALLOC; + } + + usb_dev_ptr->DTD_BASE_PTR = pBuf; + usb_dev_ptr->DTD_BASE_PHYS = (uint_32)phyAddr; + + /* Align the dTD base to 32 byte boundary */ + usb_dev_ptr->DTD_ALIGNED_BASE_PTR = (VUSB20_EP_TR_STRUCT_PTR) + USB_MEM32_ALIGN((uint_32)usb_dev_ptr->DTD_BASE_PTR); + + usb_dev_ptr->DTD_SIZE = MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT) + + ((uint_32)usb_dev_ptr->EP_QUEUE_HEAD_PTR - + (uint_32)usb_dev_ptr->EP_QUEUE_HEAD_BASE); + + /**************************************************************** + Zero out the memory allocated + ****************************************************************/ + USB_memzero((void*)usb_dev_ptr->DTD_ALIGNED_BASE_PTR, + MAX_EP_TR_DESCRS*sizeof(VUSB20_EP_TR_STRUCT)); + + /**************************************************************** + Assign SCRATCH Structure base + ****************************************************************/ + /* Allocate memory for internal scratch structure */ + pBuf = USB_memalloc(MAX_EP_TR_DESCRS*sizeof(SCRATCH_STRUCT)); + if (pBuf == NULL) + { + USB_printf("_usb_dci_vusb20_init, malloc of %d bytes failed\n", + MAX_EP_TR_DESCRS*sizeof(SCRATCH_STRUCT)); + return USBERR_ALLOC; + } + usb_dev_ptr->SCRATCH_STRUCT_BASE = (SCRATCH_STRUCT_PTR)pBuf; + USB_memzero(usb_dev_ptr->SCRATCH_STRUCT_BASE, + MAX_EP_TR_DESCRS*sizeof(SCRATCH_STRUCT)); + + USB_printf("USB dTD(%d): Base=%p (0x%x), Aligned(%d)=%p, Size=%d, Scratch=%p\n", + MAX_EP_TR_DESCRS, usb_dev_ptr->DTD_BASE_PTR, usb_dev_ptr->DTD_BASE_PHYS, + 32, usb_dev_ptr->DTD_ALIGNED_BASE_PTR, usb_dev_ptr->DTD_SIZE, + usb_dev_ptr->SCRATCH_STRUCT_BASE); + +#ifdef USB_UNDERRUN_WA + usbSramBase = (uint_8*)USB_get_sram_addr(&usbSramSize); + if (usbSramBase == NULL) + { + USB_printf("_usb_dci_vusb20_init, SRAM is not available\n"); + return USBERR_ALLOC; + } + USB_memzero(usbSramBase, usbSramSize); + USB_printf("USB WA_Queue: base=%p, size=%d, parts=%d\n", + usbSramBase, usbSramSize, global_wa_sram_parts); +#endif /* USB_UNDERRUN_WA */ + + usb_dev_ptr->USB_STATE = ARC_USB_STATE_UNKNOWN; + + /*-------- Init dTD's and setup dTD Queue ------------*/ + /* Initialize the internal dTD head and tail to NULL */ + usb_dev_ptr->DTD_HEAD = NULL; + usb_dev_ptr->DTD_TAIL = NULL; + usb_dev_ptr->DTD_ENTRIES = 0; + usb_dev_ptr->ERROR_STATE = 0; + + dTD_ptr = usb_dev_ptr->DTD_ALIGNED_BASE_PTR; + temp_scratch_ptr = usb_dev_ptr->SCRATCH_STRUCT_BASE; + + /* Enqueue all the dTDs */ + for (i = 0; i < MAX_EP_TR_DESCRS; i++) { + dTD_ptr->SCRATCH_PTR = temp_scratch_ptr; + dTD_ptr->SCRATCH_PTR->FREE = _usb_dci_vusb20_free_dTD; + /* Set the dTD to be invalid */ + dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(VUSBHS_TD_NEXT_TERMINATE); + /* Set the Reserved fields to 0 */ + dTD_ptr->SIZE_IOC_STS &= ~(USB_32BIT_LE(VUSBHS_TD_RESERVED_FIELDS)); + dTD_ptr->SCRATCH_PTR->PRIVATE = (pointer)usb_dev_ptr; + _usb_dci_vusb20_free_dTD((pointer)dTD_ptr); + dTD_ptr++; + temp_scratch_ptr++; + } /* Endfor */ + + if (!rely_on_vbus) + /* Initialize the VUSB_HS controller */ + _usb_dci_vusb20_chip_initialize((pointer)usb_dev_ptr); + + return USB_OK; +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_chip_initialize +* Returned Value : USB_OK or error code +* Comments : +* Initializes the USB device controller. +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_chip_initialize + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr; + uint_32 i, port_control; + volatile unsigned long delay; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, "chip_initialize\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Stop the controller */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= ~(USB_32BIT_LE(EHCI_CMD_RUN_STOP)); + + /* Reset the controller to get default values */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD = USB_32BIT_LE(EHCI_CMD_CTRL_RESET); + + USB_printf("USB Init: Wait for RESET completed\n"); + + delay = 0x100000; + while (dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD & + (USB_32BIT_LE(EHCI_CMD_CTRL_RESET))) + { + /* Wait for the controller reset to complete */ + delay--; + if(delay == 0) + break; + } /* EndWhile */ + + if(delay == 0) + { + USB_printf("USB Init: Wait for RESET completed TIMEOUT\n"); + } + else + { + USB_printf("USB Init: RESET completed\n"); + } + /* Call BSP callback to complete reset process */ + USB_reset_complete(usb_dev_ptr->DEV_NUM); + + /* Make sure the 16 MSBs of this register are 0s */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT = USB_32BIT_LE(0); + + ep_queue_head_ptr = usb_dev_ptr->EP_QUEUE_HEAD_PTR; + + /* Initialize all device queue heads */ + for (i=0; i<(usb_dev_ptr->MAX_ENDPOINTS*2); i++) + { + /* Interrupt on Setup packet */ + (ep_queue_head_ptr + i)->MAX_PKT_LENGTH = (USB_32BIT_LE( + ((uint_32)USB_MAX_CTRL_PAYLOAD << VUSB_EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | + VUSB_EP_QUEUE_HEAD_IOS)); + + (ep_queue_head_ptr + i)->NEXT_DTD_PTR = (USB_32BIT_LE(VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE)); + } /* Endfor */ + + /* Configure the Endpoint List Address */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.EP_LIST_ADDR = + USB_32BIT_LE(USB_EP_QH_VIRT_TO_PHYS(usb_dev_ptr, ep_queue_head_ptr)); + + port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]); + if (usb_dev_ptr->CAP_REGS_PTR->REGISTERS.CAPABILITY_REGISTERS.HCS_PARAMS & + USB_32BIT_LE(VUSB20_HCS_PARAMS_PORT_POWER_CONTROL_FLAG)) + { + port_control &= (~EHCI_PORTSCX_W1C_BITS | ~EHCI_PORTSCX_PORT_POWER); + } /* Endif */ + + if(usb_dev_ptr->FORCE_FS == TRUE) + { + port_control |= EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT; + } + else + { + port_control &= (~EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT); +#ifdef XJ_DEBUG + port_control |= EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT; +#endif + } + if (cpu_is_pxa920_z2()) + port_control |= EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT; + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0] = USB_32BIT_LE(port_control); + port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]); + USB_printf("PORTSCX[0]===========%x\n", port_control); + +#if 1 + //xj add + + /* Initialize the endpoint 0 properties */ + // TX_DATA_TOGGLE_RST & RX_DATA_TOGGLE_RST are reserved for Tavor PV - JZhou + //dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0] = USB_32BIT_LE( + // (EHCI_EPCTRL_TX_DATA_TOGGLE_RST | EHCI_EPCTRL_RX_DATA_TOGGLE_RST)); + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0] &= ~(USB_32BIT_LE + (EHCI_EPCTRL_TX_EP_STALL | EHCI_EPCTRL_RX_EP_STALL)); + + /*USB_printf("ENDPOINTCTRL0: 0x%x -> 0x%x\n", + &dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0], + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0]); */ + + // Enable USB2 PHY for operation - moved to Usb2COtgFocus.c + //usb_dev_ptr->U2P_PTR->U2PT0 |= USB_32BIT_LE(USB_REG_ARC_DPDM_MODE); + + // DCB RETURN: TPV A0 app specific, set UTMI+ + // interface and 8 bit width + //------------------------------------------- +// if (gsUsb2CIConfig.gbU2HDevice) +// _usb_dci_vusb20_set_xcvr_interface( handle, EHCI_PORTSCX_SERIAL_XCVR_SELECT, 0 ); +// else +// _usb_dci_vusb20_set_xcvr_interface( handle, EHCI_PORTSCX_UTMI_XCVR_SELECT, 0 ); + +#endif +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_free_dTD +* Returned Value : void +* Comments : +* Enqueues a dTD onto the free DTD ring. +* +*END*-----------------------------------------------------------------*/ + +void _usb_dci_vusb20_free_dTD + ( + /* [IN] the dTD to enqueue */ + pointer dTD_ptr + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + int lockKey; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)(((VUSB20_EP_TR_STRUCT_PTR)dTD_ptr)->SCRATCH_PTR->PRIVATE); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRACE, "free_dTD: dTD_ptr=0x%x\n", (unsigned)dTD_ptr); + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.free_dTD_count++)); + + + /* + ** This function can be called from any context, and it needs mutual + ** exclusion with itself. + */ + lockKey = USB_lock(); + + /* + ** Add the dTD to the free dTD queue (linked via PRIVATE) and + ** increment the tail to the next descriptor + */ + EHCI_DTD_QADD(usb_dev_ptr->DTD_HEAD, usb_dev_ptr->DTD_TAIL, (VUSB20_EP_TR_STRUCT_PTR)dTD_ptr); + usb_dev_ptr->DTD_ENTRIES++; + + USB_unlock(lockKey); + +} /* Endbody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_add_dTD +* Returned Value : USB_OK or error code +* Comments : +* Adds a device transfer desriptor(s) to the queue. +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_dci_vusb20_add_dTD + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] The transfer descriptor address */ + XD_STRUCT_PTR xd_ptr + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_TR_STRUCT_PTR dTD_ptr, temp_dTD_ptr, first_dTD_ptr = NULL; + VUSB20_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr; + uint_32 curr_pkt_len, remaining_len; + uint_32 curr_offset, temp, bit_pos; + volatile unsigned long timeout; + + /********************************************************************* + For a optimal implementation, we need to detect the fact that + we are adding DTD to an empty list. If list is empty, we can + actually skip several programming steps esp. those for ensuring + that there is no race condition.The following boolean will be useful + in skipping some code here. + *********************************************************************/ + boolean list_empty = FALSE; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + remaining_len = xd_ptr->WTOTALLENGTH; + + curr_offset = 0; + temp = (2*xd_ptr->EP_NUM + xd_ptr->BDIRECTION); + bit_pos = (1 << (16 * xd_ptr->BDIRECTION + xd_ptr->EP_NUM)); + + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp; + + /********************************************************************* + This loops iterates through the length of the transfer and divides + the data in to DTDs each handling the a max of 0x4000 bytes of data. + The first DTD in the list is stored in a pointer called first_dTD_ptr. + This pointer is later linked in to QH for processing by the hardware. + *********************************************************************/ + + do + { + /* Check if we need to split the transfer into multiple dTDs */ + if (remaining_len > VUSB_EP_MAX_LENGTH_TRANSFER) + { + curr_pkt_len = VUSB_EP_MAX_LENGTH_TRANSFER; + } + else + { + curr_pkt_len = remaining_len; + } /* Endif */ + + /* Get a dTD from the queue */ + EHCI_DTD_QGET(usb_dev_ptr->DTD_HEAD, usb_dev_ptr->DTD_TAIL, dTD_ptr); + + if (!dTD_ptr) + { + USB_printf("Error: Can't get dTD\n"); + printk(KERN_DEBUG "Error: Can't get dTD\n"); + return USBERR_TR_FAILED; + } /* Endif */ + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_add_count++)); + + USB_printf("add_dTD_%d: ep=%d %s, size=%d, CURR_dTD=0x%x, xd_ptr=0x%x, dTD_ptr=0x%x\n", + usb_dev_ptr->STATS.usb_add_count, xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "SEND" : "RECV", + (int)remaining_len, (unsigned)USB_32BIT_LE(ep_queue_head_ptr->CURR_DTD_PTR), + (unsigned)xd_ptr, (unsigned)dTD_ptr); + + remaining_len -= curr_pkt_len; + + usb_dev_ptr->DTD_ENTRIES--; + + if (curr_offset == 0) + { + first_dTD_ptr = dTD_ptr; + } /* Endif */ + + /* Zero the dTD. Leave the last 4 bytes as that is the scratch pointer */ + USB_memzero((void *) dTD_ptr,(sizeof(VUSB20_EP_TR_STRUCT) - 4)); + + /* Initialize the dTD */ + dTD_ptr->SCRATCH_PTR->PRIVATE = handle; + + /* Set the Terminate bit */ + dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(VUSB_EP_QUEUE_HEAD_NEXT_TERMINATE); + + /************************************************************* + FIX ME: For hig-speed and high-bandwidth ISO IN endpoints, + we must initialize the multiplied field so that Host can issues + multiple IN transactions on the endpoint. See the DTD data + structure for MultiIO field. + + S Garg 11/06/2003 + *************************************************************/ + + /* Fill in the transfer size */ + if (!remaining_len) + { + dTD_ptr->SIZE_IOC_STS = USB_32BIT_LE((curr_pkt_len << + VUSBHS_TD_LENGTH_BIT_POS) | (VUSBHS_TD_IOC) | (VUSBHS_TD_STATUS_ACTIVE)); + } + else + { + dTD_ptr->SIZE_IOC_STS = USB_32BIT_LE((curr_pkt_len << VUSBHS_TD_LENGTH_BIT_POS) + | VUSBHS_TD_STATUS_ACTIVE); + } /* Endif */ + + /* Set the reserved field to 0 */ + dTD_ptr->SIZE_IOC_STS &= ~USB_32BIT_LE(VUSBHS_TD_RESERVED_FIELDS); + + /* 4K apart buffer page pointers */ + if(xd_ptr->WSTARTADDRESS != NULL) + { + uint_32 physAddr = USB_virt_to_phys((uint_8*)xd_ptr->WSTARTADDRESS + curr_offset); + +#if 1 //xj for test + uint_32 virtAddr = (uint_32) ((uint_8*)xd_ptr->WSTARTADDRESS + curr_offset); + + dTD_ptr->BUFF_PTR0 = USB_32BIT_LE(physAddr); + + virtAddr += 4096; + physAddr = USB_virt_to_phys((void *)virtAddr); + dTD_ptr->BUFF_PTR1 = USB_32BIT_LE(physAddr); + + virtAddr += 4096; + physAddr = USB_virt_to_phys((void *)virtAddr); + dTD_ptr->BUFF_PTR2 = USB_32BIT_LE(physAddr); + + virtAddr += 4096; + physAddr = USB_virt_to_phys((void *)virtAddr); + dTD_ptr->BUFF_PTR3 = USB_32BIT_LE(physAddr); + + virtAddr += 4096; + physAddr = USB_virt_to_phys((void *)virtAddr); + dTD_ptr->BUFF_PTR4 = USB_32BIT_LE(physAddr); +#else + dTD_ptr->BUFF_PTR0 = USB_32BIT_LE(physAddr); + + physAddr += 4096; + dTD_ptr->BUFF_PTR1 = USB_32BIT_LE(physAddr); + + physAddr += 4096; + dTD_ptr->BUFF_PTR2 = USB_32BIT_LE(physAddr); + + physAddr += 4096; + dTD_ptr->BUFF_PTR3 = USB_32BIT_LE(physAddr); + + physAddr += 4096; + dTD_ptr->BUFF_PTR4 = USB_32BIT_LE(physAddr); +#endif + } + else + { + dTD_ptr->BUFF_PTR0 = dTD_ptr->BUFF_PTR1 = dTD_ptr->BUFF_PTR2 = 0; + dTD_ptr->BUFF_PTR3 = dTD_ptr->BUFF_PTR4 = 0; + } + curr_offset += curr_pkt_len; + + /* Maintain the first and last device transfer descriptor per + ** endpoint and direction + */ + if (!usb_dev_ptr->EP_DTD_HEADS[temp]) + { + usb_dev_ptr->EP_DTD_HEADS[temp] = dTD_ptr; + + /*********************************************** + If list does not have a head, it means that list + is empty. An empty condition is detected. + ***********************************************/ + list_empty = TRUE; + } /* Endif */ + + /* Check if the transfer is to be queued at the end or beginning */ + temp_dTD_ptr = usb_dev_ptr->EP_DTD_TAILS[temp]; + + /* Remember which XD to use for this dTD */ + dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD = (pointer)xd_ptr; + + /* New tail */ + usb_dev_ptr->EP_DTD_TAILS[temp] = dTD_ptr; + if (temp_dTD_ptr) + { + /* Should not do |=. The Terminate bit should be zero */ + temp_dTD_ptr->NEXT_TR_ELEM_PTR = USB_32BIT_LE(USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, dTD_ptr)); + } /* Endif */ + } while (remaining_len); /* EndWhile */ + + + /************************************************************** + In the loop above DTD has already been added to the list + However endpoint has not been primed yet. If list is not empty + we need safter ways to add DTD to the existing list. + Else we just skip to adding DTD to QH safely. + **************************************************************/ + + if(list_empty == FALSE) + { + volatile boolean read_safe = FALSE; + uint_32 prime, temp_ep_stat=0; + + /********************************************************* + Hardware v3.2+ require the use of semaphore to ensure that + QH is safely updated. + *********************************************************/ + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_add_not_empty_count++)); + + /********************************************************* + Check the prime bit. If set goto done + *********************************************************/ + prime = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME); + + USB_printf("%03d: Add not empty: bit_pos=0x%x, prime=%x, status=0x%x, qh->sts=0x%x\n", + usb_dev_ptr->STATS.usb_add_not_empty_count, bit_pos, prime, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS) ); + + if(prime & bit_pos) + { + timeout = 0x1000; + while( dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME + & USB_32BIT_LE(bit_pos) ) + { + /* Wait for the ENDPTPRIME to go to zero */ + timeout--; + if(timeout <= 0) + { + //USB_printf( + printk(KERN_DEBUG + "timeout: CTRL=%x, PRIME=%x, STAT=%x, INTR=%x, ADDR=%x, PORTSC=%x, dTD=%p, temp_dTD=%p\n", + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0]), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]), + first_dTD_ptr, temp_dTD_ptr); + + _usb_ep_status(handle, xd_ptr->EP_NUM, xd_ptr->BDIRECTION); + + _usb_dci_vusb20_free_dTD((pointer)dTD_ptr); + return USBERR_TR_FAILED; + } + } /* EndWhile */ + + /*ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER,*/ + if(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS & USB_32BIT_LE(bit_pos)) + { + goto done; + } + } + + read_safe = FALSE; + timeout = 1000000; + while(read_safe == FALSE) + { + timeout--; + if(timeout <= 0) + { + printk(KERN_DEBUG "%s: Timeout for ATDTW_TRIPWIRE reg = 0x%x\n", __FUNCTION__, + //USB_printf("%s: Timeout for ATDTW_TRIPWIRE reg = 0x%x\n", __FUNCTION__, + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD)); + _usb_dci_vusb20_free_dTD((pointer)dTD_ptr); + return USBERR_TR_FAILED; + } + + /********************************************************* + start with setting the semaphores + *********************************************************/ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD |= + USB_32BIT_LE(EHCI_CMD_ATDTW_TRIPWIRE_SET); + + /********************************************************* + Read the endpoint status + *********************************************************/ + temp_ep_stat = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) + & bit_pos; + + /********************************************************* + Reread the ATDTW semaphore bit to check if it is cleared. + When hardware see a hazard, it will clear the bit or + else we remain set to 1 and we can proceed with priming + of endpoint if not already primed. + *********************************************************/ + if( dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD & + USB_32BIT_LE(EHCI_CMD_ATDTW_TRIPWIRE_SET)) + { + read_safe = TRUE; + } + + }/*end while loop */ + + /********************************************************* + Clear the semaphore + *********************************************************/ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= + USB_32BIT_LE(EHCI_CMD_ATDTW_TRIPWIRE_CLEAR); + + /********************************************************* + * If endpoint is not active, we activate it now. + *********************************************************/ + if(!temp_ep_stat) + { + if (xd_ptr->BDIRECTION == ARC_USB_SEND) { + /* FIXME add status check after prime the IN ep */ + VUSB20_EP_TR_STRUCT_PTR curr_dtd = (VUSB20_EP_TR_STRUCT_PTR)USB_32BIT_LE( + USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, ep_queue_head_ptr->CURR_DTD_PTR)); + + timeout = 1000; + while ((curr_dtd->SIZE_IOC_STS & VUSBHS_TD_STATUS_ACTIVE) && (timeout-- >= 0)) { + if (timeout <= 500) + printk(KERN_DEBUG "%s line %d\n", __func__, __LINE__); + } + if (timeout <= 0) { + printk(KERN_ALERT "\n===============previous dtd still active???\n"); + printk(KERN_ALERT "ep %d bit_pos %x qh->cur %x nxt %x\n", xd_ptr->EP_NUM, bit_pos, + ep_queue_head_ptr->CURR_DTD_PTR, ep_queue_head_ptr->NEXT_DTD_PTR); + dump_epin(xd_ptr->EP_NUM); + } + } + /* No other transfers on the queue */ + ep_queue_head_ptr->NEXT_DTD_PTR = USB_32BIT_LE( + USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, first_dTD_ptr)); + ep_queue_head_ptr->SIZE_IOC_INT_STS = 0; + + /* Prime the Endpoint */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos); + + } + } + else + { + /* No other transfers on the queue */ + ep_queue_head_ptr->NEXT_DTD_PTR = USB_32BIT_LE( + USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, first_dTD_ptr)); + ep_queue_head_ptr->SIZE_IOC_INT_STS = 0; + + /* Prime the Endpoint */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos); + + if (xd_ptr->BDIRECTION == ARC_USB_SEND) { + /* FIXME add status check after prime the IN ep */ + static int prime_again; + VUSB20_EP_TR_STRUCT_PTR curr_dtd = (VUSB20_EP_TR_STRUCT_PTR)USB_32BIT_LE( + USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, ep_queue_head_ptr->CURR_DTD_PTR)); + + timeout = 1000; + prime_again = 0; + while ((curr_dtd != first_dTD_ptr) && (timeout >= 0)) { + curr_dtd = (VUSB20_EP_TR_STRUCT_PTR)USB_32BIT_LE( + USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, ep_queue_head_ptr->CURR_DTD_PTR)); + timeout--; + if (timeout <= 500) { + if (!prime_again) { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos); + prime_again = 1; + printk(KERN_DEBUG "%s line %d, prime again\n", __func__, __LINE__); + } + } + } + + if (timeout <= 0) { + printk(KERN_ALERT "\n-------------------dtd not updated???\n"); + printk(KERN_ALERT "ep %d bit_pos %x qh->cur %x nxt %x, first_dTD_ptr %p\n", + xd_ptr->EP_NUM, bit_pos, ep_queue_head_ptr->CURR_DTD_PTR, + ep_queue_head_ptr->NEXT_DTD_PTR, first_dTD_ptr); + /* dump_epin(xd_ptr->EP_NUM); */ + } + } + +#if 0 //xj del + /* delay */ + timeout = 0x100; + while(timeout > 0) + timeout--; + + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME = USB_32BIT_LE(bit_pos); +#endif + } + +done: + if(first_dTD_ptr == NULL) + //USB_printf("ERROR !!!! first_dTD_ptr=NULL\n"); + printk(KERN_DEBUG "ERROR !!!! first_dTD_ptr=NULL\n"); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER, + " add_%d: fri=0x%x, ep=%d%s, buf=%p, size=%d, xd=%p, dTD=%p %p, empty=%d\n", + usb_dev_ptr->STATS.usb_add_count & 0xFFFF, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), + xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "in" : "out", + xd_ptr->WSTARTADDRESS, (int)xd_ptr->WTOTALLENGTH, + xd_ptr, (unsigned)first_dTD_ptr, + usb_dev_ptr->EP_DTD_HEADS[temp], list_empty); + return USB_OK; + /* End CR 1015 */ +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_process_tr_complete +* Returned Value : None +* Comments : +* Services transaction complete interrupt +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_process_tr_complete + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + volatile VUSB20_REG_STRUCT_PTR dev_ptr; + volatile VUSB20_EP_TR_STRUCT_PTR dTD_ptr; + VUSB20_EP_TR_STRUCT_PTR temp_dTD_ptr; + VUSB20_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr; + uint_32 temp, i, ep_num = 0, direction = 0, bit_pos; + uint_32 remaining_length = 0; + uint_32 actual_transfer_length = 0; + uint_32 counter, errors = 0; + XD_STRUCT_PTR xd_ptr; + XD_STRUCT_PTR temp_xd_ptr = NULL; + uint_8_ptr buff_start_address = NULL; + boolean endpoint_detected = FALSE; +// boolean setup_dir; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_tr_complete_isr\n"); + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_complete_isr_count++)); + + /* We use separate loops for ENDPTSETUPSTAT and ENDPTCOMPLETE because the + ** setup packets are to be read ASAP + */ + + /* Process all Setup packet received interrupts */ + bit_pos = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT); + + if (bit_pos) + { + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "setup_isr: bit_pos=0x%x\n", (unsigned)bit_pos); + for(i=0; iSTATS.usb_setup_count++)); + _usb_device_call_service(handle, i, TRUE, 0, 0, 8, 0); + } /* Endif */ + } /* Endfor */ + } /* Endif */ + + /* Don't clear the endpoint setup status register here. It is cleared as a + ** setup packet is read out of the buffer + */ + + /* Process non-setup transaction complete interrupts */ + bit_pos = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "tr_complete: bit_pos = 0x%x\n", (unsigned)bit_pos); + + if (bit_pos) + { + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_complete_count++)); + + /* Clear the bits in the register */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE = USB_32BIT_LE(bit_pos); + + /* Get the endpoint number and the direction of transfer */ + counter = 0; + for (i=0; i<(ARC_USB_MAX_ENDPOINTS*2); i++) + { + endpoint_detected = FALSE; + if ((i < ARC_USB_MAX_ENDPOINTS) && (bit_pos & (1 << i))) + { + ep_num = i; + direction = ARC_USB_RECV; + endpoint_detected = TRUE; + } + else + { + if( (i >= ARC_USB_MAX_ENDPOINTS) && + (bit_pos & (1 << (i+16-ARC_USB_MAX_ENDPOINTS)))) + { + ep_num = (i - ARC_USB_MAX_ENDPOINTS); + direction = ARC_USB_SEND; + endpoint_detected = TRUE; + } + } + + if(endpoint_detected) + { + temp = (2*ep_num + direction); + + /* Get the first dTD */ + dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[temp]; + + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, " ep_num %d temp %d tr_complete: dTD_ptr %p ep_queue_head_ptr %p\n", + ep_num, temp, dTD_ptr, ep_queue_head_ptr); + + /* Process all the dTDs for respective transfers */ + while (dTD_ptr) + { + if (USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSBHS_TD_STATUS_ACTIVE) + { + /* No more dTDs to process. Next one is owned by VUSB */ + if(counter == 0) + { + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "tr_complete - break: ep=%d %s, bit_pos=0x%x\n", + (unsigned)ep_num, direction ? "SEND" : "RECV", (unsigned)bit_pos); + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_empty_complete_count++)); + } + break; + } /* Endif */ + + /* Get the correct internal transfer descriptor */ + xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD; + if (xd_ptr) + { + buff_start_address = xd_ptr->WSTARTADDRESS; + actual_transfer_length = xd_ptr->WTOTALLENGTH; + temp_xd_ptr = xd_ptr; + } /* Endif */ + + /* Get the address of the next dTD */ + temp_dTD_ptr = (VUSB20_EP_TR_STRUCT_PTR)USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, + (uint_32)(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK) ); + + /* Read the errors */ + errors = (USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSBHS_TD_ERROR_MASK); + if (!errors) + { + /* No errors */ + /* Get the length of transfer from the current dTD */ + remaining_length += ((USB_32BIT_LE(dTD_ptr->SIZE_IOC_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16); + actual_transfer_length -= remaining_length; + } + else + { + //mv_debug = 1; + USB_printf("complete_tr error: ep=%d %s: error = 0x%x\n", + (unsigned)ep_num, direction ? "SEND" : "RECV", (unsigned)errors); + if (errors & VUSBHS_TD_STATUS_HALTED) + { + /* Clear the errors and Halt condition */ + ep_queue_head_ptr->SIZE_IOC_INT_STS &= USB_32BIT_LE(~errors); + } /* Endif */ + } /* Endif */ + + /* Retire the processed dTD */ + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "tr_complete - cancel: ep=%d %s, bit_pos = 0x%x\n", + (unsigned)ep_num, direction ? "SEND" : "RECV", (unsigned)bit_pos); + + counter++; + _usb_dci_vusb20_cancel_transfer(handle, ep_num, direction); + if( (temp_dTD_ptr == NULL) || + (temp_dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD != temp_xd_ptr) ) + { + /* Transfer complete. Call the register service function for the endpoint */ + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_complete_ep_count[temp]++)); + +#if defined(USB_UNDERRUN_WA) + if( (direction == ARC_USB_SEND) && + (((ep_queue_head_ptr->MAX_PKT_LENGTH >> 16) & 0x7FF) > global_wa_threshold) ) + usbSendComplete(handle, ep_num, FALSE, direction, + buff_start_address, actual_transfer_length, errors); + else +#endif /* USB_UNDERRUN_WA */ + _usb_device_call_service(handle, ep_num, FALSE, direction, + buff_start_address, actual_transfer_length, errors); + remaining_length = 0; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER, + "comp_%d: fri=0x%x, ep=%d%s, buf=%p, size=%d, xd=%p, dTD=%p %p %p, COMP=0x%x\n", + usb_dev_ptr->STATS.usb_complete_count & 0xFFFF, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), + (unsigned)ep_num, direction ? "in" : "out", + buff_start_address, actual_transfer_length, + temp_xd_ptr, dTD_ptr, temp_dTD_ptr, usb_dev_ptr->EP_DTD_HEADS[temp], (unsigned)bit_pos); + + } /* Endif */ + else + { + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRANSFER, "tr_complete not completed: ep=%d %s\n", + (unsigned)ep_num, direction ? "SEND" : "RECV"); + } + if( (temp_dTD_ptr == NULL) && (usb_dev_ptr->EP_DTD_HEADS[temp] != NULL) ) + { +/* + USB_printf("tr_complete: ep=%d, temp_dTD=%p, dTD_ptr=%p (%p), DTD_HEADS=%p, remain=%d\n", + temp, temp_dTD_ptr, dTD_ptr, + USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, (uint_32)(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK) ), + usb_dev_ptr->EP_DTD_HEADS[temp], remaining_length); +*/ + dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[temp]; + } + else + { + dTD_ptr = temp_dTD_ptr; + } + errors = 0; + } /* Endwhile */ + } /* Endif */ + } /* Endfor */ + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, + ( {if(usb_dev_ptr->STATS.usb_complete_max_count < counter) + usb_dev_ptr->STATS.usb_complete_max_count = counter;})); + } /* Endif */ +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_isr +* Returned Value : None +* Comments : +* Services all the VUSB_HS interrupt sources +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_isr + ( + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 status; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_isr_count++)); + + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + status = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS); + + status &= USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR); + + if(status == 0) + { + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_empty_isr_count++)); + return; + } /* Endif */ + + USB_printf("\nUSB_ISR: FRINDEX=0x%x (Frame# %d), status=0x%x, PORTSC=0x%x, EP_SETUP=0x%x, EP_COMPLETE=0x%x, EP_PRIME=0x%x\n", + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), + (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX)>>3), + status, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME)); + + + /* Clear all the interrupts occured */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_STS = USB_32BIT_LE(status); + + if (status & EHCI_STS_ERR) + { + _usb_dci_vusb20_process_error((pointer)usb_dev_ptr); + USB_printf("USB process error: status=0x%x\n", status); + usb_dev_ptr->ERROR_STATE |= (status & 0xFFFF); + } /* Endif */ + + if (status & EHCI_STS_RESET) + { + _usb_dci_vusb20_process_reset((pointer)usb_dev_ptr); + } /* Endif */ + + if (status & EHCI_STS_PORT_CHANGE) + { + _usb_dci_vusb20_process_port_change((pointer)usb_dev_ptr); + } /* Endif */ + + if (status & EHCI_STS_SOF) + { + _usb_dci_vusb20_process_SOF((pointer)usb_dev_ptr); + } /* Endif */ + + if (status & EHCI_STS_INT) + { + _usb_dci_vusb20_process_tr_complete((pointer)usb_dev_ptr); + } /* Endif */ + + if (status & EHCI_STS_SUSPEND) + { + _usb_dci_vusb20_process_suspend((pointer)usb_dev_ptr); + } /* Endif */ + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_process_reset +* Returned Value : None +* Comments : +* Services reset interrupt +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_process_reset + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 temp; + volatile unsigned long timeout; + static int reset_done; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_reset\n"); + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_reset_count++)); + + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Inform the application so that it can cancel all previously queued transfers */ + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_BUS_RESET, 0, 0, 0, 0, 0); + +#if defined(USB_UNDERRUN_WA) + _usb_reset_send_queue(); +#endif /* USB_UNDERRUN_WA */ + + /* The address bits are past bit 25-31. Set the address */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR &= ~USB_32BIT_LE(0xFE000000); + + /* Clear all the setup token semaphores */ + temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT); + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT = USB_32BIT_LE(temp); + + /* Clear all the endpoint complete status bits */ + temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE); + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE = USB_32BIT_LE(temp); + + timeout = 0x10000; + while (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & 0xFFFFFFFF) + { + timeout--; + if(timeout <= 0) + { + USB_printf("%s: Timeout for ENDPTPRIME = 0x%x\n", __FUNCTION__, + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME)); + break; + } + + /* Wait until all ENDPTPRIME bits cleared */ + } /* Endif */ + + /* Write 1s to the Flush register */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTFLUSH = USB_32BIT_LE(0xFFFFFFFF); + + if( (usb_dev_ptr->ERROR_STATE == 0x0) && + (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & + EHCI_PORTSCX_PORT_RESET) ) + { + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, + "USB Bus Reset: fri=0x%x, dev_ptr=%p, STATE=%d, PORTSC=0x%x, CMD=0x%x, ENDPT[0]=0x%x\n", + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), + usb_dev_ptr, usb_dev_ptr->USB_STATE, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0])); + + usb_dev_ptr->BUS_RESETTING = TRUE; + usb_dev_ptr->USB_STATE = ARC_USB_STATE_POWERED; + } + else + { + USB_printf("USB Chip reinit: PORTSC=0x%x, frame# %d ERROR_STATE %x\n", + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]), + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX)>>3, + usb_dev_ptr->ERROR_STATE); + +#if 1 + // xj add + if(reset_done) { + goto reinit; + } else { + reset_done = 1; + } + + //xj del +#endif + /* re-initialize */ + _usb_dci_vusb20_chip_initialize((pointer)usb_dev_ptr); + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, "process_reset, Chip reinit hw\n"); + } /* Endif */ + +reinit: + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_BUS_RESET, 1, 0, 0, 0, 0); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_process_suspend +* Returned Value : None +* Comments : +* Services suspend interrupt +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_process_suspend + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_suspend\n"); + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_suspend_count++)); + + usb_dev_ptr->USB_DEV_STATE_B4_SUSPEND = usb_dev_ptr->USB_STATE; + + usb_dev_ptr->USB_STATE = ARC_USB_STATE_SUSPEND; + + /* Inform the upper layers */ + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SLEEP, 0, 0, 0, 0, 0); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_process_SOF +* Returned Value : None +* Comments : +* Services SOF interrupt +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_process_SOF + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_SOF\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Inform the upper layer */ + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SOF, 0, 0, 0, + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_FRINDEX), 0); + +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_process_port_change +* Returned Value : None +* Comments : +* Services port change detect interrupt +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_process_port_change + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ISR, "process_port_change\n"); + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_port_change_count++)); + + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + USB_printf("port_change: PORTSC=0x%x, DTD_ENTRIES=%d, XD_ENTRIES=%d\n", + USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]), + usb_dev_ptr->DTD_ENTRIES, usb_dev_ptr->XD_ENTRIES); + + if (usb_dev_ptr->BUS_RESETTING) + { + /* Bus reset operation complete */ + usb_dev_ptr->BUS_RESETTING = FALSE; + } /* Endif */ + + if (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & + EHCI_PORTSCX_PORT_RESET)) + { + /* Get the speed */ + if (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & + EHCI_PORTSCX_PORT_HIGH_SPEED) + { + usb_dev_ptr->SPEED = ARC_USB_SPEED_HIGH; + } + else + { + usb_dev_ptr->SPEED = ARC_USB_SPEED_FULL; + } /* Endif */ + + USB_printf("USB %s speed device detected\n", + (usb_dev_ptr->SPEED == ARC_USB_SPEED_HIGH) ? "High" : "Full"); + + /* Inform the upper layers of the speed of operation */ + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SPEED_DETECTION, 0, 0, + 0, usb_dev_ptr->SPEED, 0); + } /* Endif */ + + if (USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & + EHCI_PORTSCX_PORT_SUSPEND) + { + usb_dev_ptr->USB_DEV_STATE_B4_SUSPEND = usb_dev_ptr->USB_STATE; + usb_dev_ptr->USB_STATE = ARC_USB_STATE_SUSPEND; + + /* Inform the upper layers */ + USB_printf("USB suspend\n"); + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_SUSPEND, 0, 0, 0, 0, 0); + } /* Endif */ + + if (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]) & EHCI_PORTSCX_PORT_SUSPEND) + && (usb_dev_ptr->USB_STATE == ARC_USB_STATE_SUSPEND)) + { + USB_printf("USB resume\n"); + usb_dev_ptr->USB_STATE = usb_dev_ptr->USB_DEV_STATE_B4_SUSPEND; + /* Inform the upper layers */ + _usb_device_call_service(usb_dev_ptr, ARC_USB_SERVICE_RESUME, 0, 0, 0, 0, 0); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SUSPEND, "process_port_change, SUCCESSFUL, resumed\n"); + return; + } /* Endif */ + + usb_dev_ptr->USB_STATE = ARC_USB_STATE_DEFAULT; + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_process_error +* Returned Value : None +* Comments : +* Services error interrupt +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_process_error + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ERROR, "process_error\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + /* Increment the error count */ + usb_dev_ptr->ERRORS++; + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_set_speed_full +* Returned Value : None +* Comments : +* Force the controller port in full speed mode. +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_set_speed_full + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* The port number on the device */ + uint_8 port_number + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 port_control; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ANY, "FORCE set_speed_full\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number]); + port_control |= EHCI_PORTSCX_FORCE_FULL_SPEED_CONNECT; + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number] = USB_32BIT_LE(port_control); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_suspend_phy +* Returned Value : None +* Comments : +* Suspends the PHY in low power mode +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_suspend_phy + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* The port number on the device */ + uint_8 port_number + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 port_control; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_SUSPEND, "set_suspend_phy\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + port_control = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number]); + port_control |= EHCI_PORTSCX_PHY_CLOCK_DISABLE; + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port_number] = USB_32BIT_LE(port_control); + +} /* EndBody */ + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_set_address +* Returned Value : None +* Comments : +* Sets the newly assigned device address +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_set_address + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* Address of the device assigned by the host */ + uint_8 address + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ADDR, "set_address: address=%d\n",address); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + +#ifdef SET_ADDRESS_HARDWARE_ASSISTANCE + /*********************************************************** + Hardware Rev 4.0 onwards have special assistance built in + for handling the set_address command. As per the USB specs + a device should be able to receive the response on a new + address, within 2 msecs after status phase of set_address is + completed. Since 2 mili second may be a very small time window + (on high interrupt latency systems) before software could + come to the code below and write the device register, + this routine will be called in advance when status phase of + set_address is still not finished. The following line in the + code will set the bit 24 to '1' and hardware will take + the address and queue it in an internal buffer. From which + it will use it to decode the next USB token. Please look + at hardware rev details for the implementation of this + assistance. + + Also note that writing bit 24 to 0x01 will not break + any old hardware revs because it was an unused bit. + ***********************************************************/ + /* The address bits are past bit 25-31. Set the address + also set the bit 24 to 0x01 to start hardware assitance*/ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR = + USB_32BIT_LE((uint_32)address << VUSBHS_ADDRESS_BIT_SHIFT) | + (0x01 << (VUSBHS_ADDRESS_BIT_SHIFT -1)); +#else + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.DEVICE_ADDR = + USB_32BIT_LE((uint_32)address << VUSBHS_ADDRESS_BIT_SHIFT); +#endif /* SET_ADDRESS_HARDWARE_ASSISTANCE */ + + usb_dev_ptr->USB_STATE = ARC_USB_STATE_ADDRESS; + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_get_setup_data +* Returned Value : None +* Comments : +* Reads the Setup data from the 8-byte setup buffer +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_get_setup_data + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [OUT] address of the buffer to read the setup data into */ + uint_8_ptr buffer_ptr + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + volatile VUSB20_REG_STRUCT_PTR dev_ptr; + volatile VUSB20_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr; + volatile boolean read_safe = FALSE; + volatile unsigned long timeout; + + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Get the endpoint queue head */ + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + + 2*ep_num + ARC_USB_RECV; + + /******************************************************************** + CR 1219. Hardware versions 2.3+ have a implementation of tripwire + semaphore mechanism that requires that we read the contents of + QH safely by using the semaphore. Read the USBHS document to under + stand how the code uses the semaphore mechanism. The following are + the steps in brief + + 1. USBCMD Write ‘1’ to Setup Tripwire in register. + 2. Duplicate contents of dQH.StatusBuffer into local software byte + array. + 3 Read Setup TripWire in register. (if set - continue; if + cleared goto 1.) + 4. Write '0' to clear Setup Tripwire in register. + 5. Process setup packet using local software byte array copy and + execute status/handshake phases. + + + ********************************************************************/ + timeout = 0x100000; + while(!read_safe) + { + /********************************************************* + start with setting the semaphores + *********************************************************/ + + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD |= + USB_32BIT_LE(EHCI_CMD_SETUP_TRIPWIRE_SET); + + /* Copy the setup packet to private buffer */ + USB_memcopy((uint_8_ptr)ep_queue_head_ptr->SETUP_BUFFER, buffer_ptr, 8); + + /********************************************************* + If setup tripwire semaphore is cleared by hardware it means + that we have a danger and we need to restart. + else we can exit out of loop safely. + *********************************************************/ + if(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD) & + EHCI_CMD_SETUP_TRIPWIRE_SET) + { + read_safe = TRUE; /* we can proceed exiting out of loop*/ + } + if(timeout <= 0) + { + USB_printf("%s: Timeout for SETUP_TRIPWIRE = 0x%x\n", __FUNCTION__, + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD)); + break; + } + } + ARC_DEBUG_CODE(ARC_DEBUG_FLAG_STATS, (usb_dev_ptr->STATS.usb_read_setup_count++)); + + /********************************************************* + Clear the semaphore bit now + *********************************************************/ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= + USB_32BIT_LE(EHCI_CMD_SETUP_TRIPWIRE_CLEAR); + + /* Clear the bit in the ENDPTSETUPSTAT */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT = USB_32BIT_LE(1 << ep_num); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_init_endpoint +* Returned Value : None +* Comments : +* Initializes the specified endpoint and the endpoint queue head +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_dci_vusb20_init_endpoint + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the transaction descriptor address */ + XD_STRUCT_PTR xd_ptr + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_QUEUE_HEAD_STRUCT _PTR_ ep_queue_head_ptr; + uint_32 val, bit_pos; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Get the endpoint queue head address */ + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + + 2*xd_ptr->EP_NUM + xd_ptr->BDIRECTION; + + bit_pos = (1 << (16 * xd_ptr->BDIRECTION + xd_ptr->EP_NUM)); + + /* Check if the Endpoint is Primed */ + if ((!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & bit_pos)) && + (!(USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos))) + { + /* Set the max packet length, interrupt on Setup and Mult fields */ + if (xd_ptr->EP_TYPE == ARC_USB_ISOCHRONOUS_ENDPOINT) + { + /* Mult bit should be set for isochronous endpoints */ + ep_queue_head_ptr->MAX_PKT_LENGTH = USB_32BIT_LE((xd_ptr->WMAXPACKETSIZE << 16) | + ((xd_ptr->MAX_PKTS_PER_UFRAME ? xd_ptr->MAX_PKTS_PER_UFRAME : 1) << + VUSB_EP_QUEUE_HEAD_MULT_POS)); + } + else + { + if (xd_ptr->EP_TYPE != ARC_USB_CONTROL_ENDPOINT) + { + /* BULK or INTERRUPT */ + ep_queue_head_ptr->MAX_PKT_LENGTH = USB_32BIT_LE((xd_ptr->WMAXPACKETSIZE << 16) | + (xd_ptr->DONT_ZERO_TERMINATE ? VUSB_EP_QUEUE_HEAD_ZERO_LEN_TER_SEL : 0)); + } + else + { + /* CONTROL */ + ep_queue_head_ptr->MAX_PKT_LENGTH = USB_32BIT_LE((xd_ptr->WMAXPACKETSIZE << 16) | + VUSB_EP_QUEUE_HEAD_IOS); + } /* Endif */ + } /* Endif */ + + /* Enable the endpoint for Rx or Tx and set the endpoint type */ + val = dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM]; + if(xd_ptr->BDIRECTION == ARC_USB_SEND) + { + val &= ~(USB_32BIT_LE(EHCI_EPCTRL_TX_ALL_MASK)); + val |= USB_32BIT_LE((EHCI_EPCTRL_TX_ENABLE | EHCI_EPCTRL_TX_DATA_TOGGLE_RST) | + (xd_ptr->EP_TYPE << EHCI_EPCTRL_TX_EP_TYPE_SHIFT)); + } + else + { + val &= ~(USB_32BIT_LE(EHCI_EPCTRL_RX_ALL_MASK)); + val |= USB_32BIT_LE((EHCI_EPCTRL_RX_ENABLE | EHCI_EPCTRL_RX_DATA_TOGGLE_RST) | + (xd_ptr->EP_TYPE << EHCI_EPCTRL_RX_EP_TYPE_SHIFT)); + } + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] = val; + + /* Implement Guideline (GL# USB-7) The unused endpoint type must */ + /* be programmed to bulk. */ + if( (dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] & + USB_32BIT_LE(EHCI_EPCTRL_RX_ENABLE)) == 0) + { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] |= + USB_32BIT_LE(ARC_USB_BULK_ENDPOINT << EHCI_EPCTRL_RX_EP_TYPE_SHIFT); + } + + if( (dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] & + USB_32BIT_LE(EHCI_EPCTRL_TX_ENABLE)) == 0) + { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM] |= + USB_32BIT_LE(ARC_USB_BULK_ENDPOINT << EHCI_EPCTRL_TX_EP_TYPE_SHIFT); + } + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, + "init ep #%d %s: type=0x%x, EPCTRLX=0x%x, SETUP=0x%x, PRIME=0x%x, STATUS=0x%x, COMPL=0x%x\n", + xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "SEND" : "RECV", xd_ptr->EP_TYPE, + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[xd_ptr->EP_NUM]), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE) ); + } + else + { + USB_printf("ep=%d %s: Init ERROR: ENDPTPRIME=0x%x, ENDPTSTATUS=0x%x, bit_pos=0x%x\n", + (unsigned)xd_ptr->EP_NUM, xd_ptr->BDIRECTION ? "SEND" : "RECV", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + (unsigned)bit_pos); + return USBERR_EP_INIT_FAILED; + } /* Endif */ + + return USB_OK; + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_get_transfer_status +* Returned Value : USB_OK or error code +* Comments : +* Gets the status of a transfer +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_dci_vusb20_get_transfer_status + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_EP_TR_STRUCT_PTR dTD_ptr; + XD_STRUCT_PTR xd_ptr; + uint_8 status; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + + /* Unlink the dTD */ + dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[2*ep_num + direction]; + + if (dTD_ptr) + { + /* Get the transfer descriptor for the dTD */ + xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD; + status = xd_ptr->BSTATUS; + } + else + { + status = ARC_USB_STATUS_IDLE; + } /* Endif */ + + return (status); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_get_transfer_details +* Returned Value : pointer to structure that has details for transfer +* Gets the status of a transfer +* +*END*-----------------------------------------------------------------*/ +XD_STRUCT_PTR _usb_dci_vusb20_get_transfer_details + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_TR_STRUCT_PTR dTD_ptr, temp_dTD_ptr; + XD_STRUCT_PTR xd_ptr; + uint_32 temp, remaining_bytes; + VUSB20_EP_QUEUE_HEAD_STRUCT_PTR ep_queue_head_ptr; + + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + temp = (2*ep_num + direction); + + /* get a pointer to QH for this endpoint */ + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + temp; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRACE, "get_transfer_details\n"); + + /* Unlink the dTD */ + dTD_ptr = usb_dev_ptr->EP_DTD_HEADS[2*ep_num + direction]; + + if (dTD_ptr) + { + /* Get the transfer descriptor for the dTD */ + xd_ptr = (XD_STRUCT_PTR)dTD_ptr->SCRATCH_PTR->XD_FOR_THIS_DTD; + if(!xd_ptr) return NULL; + + /* Initialize the transfer length field */ + xd_ptr->WSOFAR =0; + remaining_bytes =0; + + /*if length of this transfer is greater than 20K + we have multiple DTDs to count */ + if(xd_ptr->WTOTALLENGTH > VUSB_EP_MAX_LENGTH_TRANSFER) + { + /* it is a valid DTD. We should parse all DTDs for this XD + and find the total bytes used so far */ + temp_dTD_ptr = dTD_ptr; + + /*loop through the list of DTDS until an active DTD is found + or list has finished */ + while(!(USB_32BIT_LE(dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_NEXT_TERMINATE)) + { + + /********************************************************** + If this DTD has been overlayed, we take the actual length + from QH. + **********************************************************/ + + if ((uint_32)(USB_32BIT_LE(ep_queue_head_ptr->CURR_DTD_PTR) & VUSBHS_TD_ADDR_MASK) == + USB_DTD_VIRT_TO_PHYS(usb_dev_ptr, temp_dTD_ptr) ) + { + remaining_bytes += + ((USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16); + } + else + { + /* take the length from DTD itself */ + remaining_bytes += + ((USB_32BIT_LE(temp_dTD_ptr->SIZE_IOC_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16); + } + + dTD_ptr = temp_dTD_ptr; + + /* Get the address of the next dTD */ + temp_dTD_ptr = (VUSB20_EP_TR_STRUCT_PTR)USB_DTD_PHYS_TO_VIRT(usb_dev_ptr, + (uint_32)(USB_32BIT_LE(temp_dTD_ptr->NEXT_TR_ELEM_PTR) & VUSBHS_TD_ADDR_MASK) ); + } + xd_ptr->WSOFAR = xd_ptr->WTOTALLENGTH - remaining_bytes; + } + else + { + /*look at actual length from QH*/ + xd_ptr->WSOFAR = xd_ptr->WTOTALLENGTH - + ((USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS) & VUSB_EP_TR_PACKET_SIZE) >> 16); + } + } + else + { + xd_ptr = NULL; + } /* Endif */ + + return (xd_ptr); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_deinit_endpoint +* Returned Value : None +* Comments : +* Disables the specified endpoint and the endpoint queue head +* +*END*-----------------------------------------------------------------*/ +uint_8 _usb_dci_vusb20_deinit_endpoint + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_QUEUE_HEAD_STRUCT* ep_queue_head_ptr; + uint_32 bit_pos; + uint_8 status = USB_OK; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Get the endpoint queue head address */ + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + + (2*ep_num + direction); + + bit_pos = (1 << (16 * direction + ep_num)); + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_INIT, + "deinit ep #%d-%s: bit_pos=0x%x, EPCTRLX=0x%x, SETUP=0x%x, PRIME=0x%x, STATUS=0x%x, COMPL=0x%x\n", + ep_num, direction ? "SEND" : "RECV", bit_pos, + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num]), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPT_SETUP_STAT), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCOMPLETE) ); + + /* Check if the Endpoint is Primed */ + if( ((USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME) & bit_pos)) || + ((USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS) & bit_pos)) ) + { + USB_printf("ep=%d %s: Deinit ERROR: ENDPTPRIME=0x%x, ENDPTSTATUS=0x%x, bit_pos=0x%x\n", + (unsigned)ep_num, direction ? "SEND" : "RECV", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTPRIME), + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTSTATUS), + (unsigned)bit_pos); + status = USBERR_EP_DEINIT_FAILED; + } + + /* Reset the max packet length and the interrupt on Setup */ + ep_queue_head_ptr->MAX_PKT_LENGTH = 0; + + /* Disable the endpoint for Rx or Tx and reset the endpoint type */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] &= + USB_32BIT_LE( ~((direction ? EHCI_EPCTRL_TX_ENABLE : EHCI_EPCTRL_RX_ENABLE) | + (direction ? EHCI_EPCTRL_TX_TYPE : EHCI_EPCTRL_RX_TYPE))); + + return status; +} + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_shutdown +* Returned Value : None +* Comments : +* Shuts down the VUSB_HS Device +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_shutdown + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Disable interrupts */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR &= + ~(USB_32BIT_LE(EHCI_INTR_INT_EN | EHCI_INTR_ERR_INT_EN | + EHCI_INTR_PORT_CHANGE_DETECT_EN | EHCI_INTR_RESET_EN)); + + USB_uncached_memfree(usb_dev_ptr->EP_QUEUE_HEAD_BASE, + usb_dev_ptr->EP_QUEUE_HEAD_SIZE, + usb_dev_ptr->EP_QUEUE_HEAD_PHYS); + + USB_uncached_memfree(usb_dev_ptr->DTD_BASE_PTR, + usb_dev_ptr->DTD_SIZE, + usb_dev_ptr->DTD_BASE_PHYS); + + USB_memfree(usb_dev_ptr->SCRATCH_STRUCT_BASE); + + USB_printf("USB shutdown: usb_dev_ptr=%p\n", usb_dev_ptr); + + /* Reset the Run the bit in the command register to stop VUSB */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= ~USB_32BIT_LE(EHCI_CMD_RUN_STOP); + + /* Reset the controller to get default values */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD = USB_32BIT_LE(EHCI_CMD_CTRL_RESET); + +} /* EndBody */ +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_stop +* Returned Value : None +* Comments : +* Stop USB device controller +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_stop(_usb_device_handle handle) +{ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Disable interrupts */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR &= + ~(USB_32BIT_LE(EHCI_INTR_INT_EN | EHCI_INTR_ERR_INT_EN | + EHCI_INTR_PORT_CHANGE_DETECT_EN | EHCI_INTR_RESET_EN)); + + /* Reset the Run the bit in the command register to stop VUSB */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD &= ~USB_32BIT_LE(EHCI_CMD_RUN_STOP); +} + + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_start +* Returned Value : None +* Comments : +* Start USB device controller +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_start(_usb_device_handle handle) +{ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Enable interrupts */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_INTR = USB_32BIT_LE( + EHCI_INTR_INT_EN + | EHCI_INTR_ERR_INT_EN + | EHCI_INTR_PORT_CHANGE_DETECT_EN + | EHCI_INTR_RESET_EN + | EHCI_INTR_DEVICE_SUSPEND + /* + | EHCI_INTR_SOF_UFRAME_EN + */ + ); + + usb_dev_ptr->USB_STATE = ARC_USB_STATE_UNKNOWN; + + /* Set the Run bit in the command register */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.USB_CMD = USB_32BIT_LE(EHCI_CMD_RUN_STOP); +} + +//---------------------------------------------------------------------------------------- +// _usb_dci_vusb20_set_xcvr_interface() +// +// +//---------------------------------------------------------------------------------------- +void _usb_dci_vusb20_set_xcvr_interface( _usb_device_handle handle, unsigned int setting, unsigned int port ) +{ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + unsigned int temp; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + temp = dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port]; + + temp &= ~EHCI_PORTSCX_PAR_XCVR_SELECT; + temp |= setting; + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[port] = temp; +} + + +/* EOF */ + + diff --git a/drivers/usb/gadget/mv/mvUsbHsDevUtl.c b/drivers/usb/gadget/mv/mvUsbHsDevUtl.c new file mode 100644 index 00000000000000..287c2e4d961044 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbHsDevUtl.c @@ -0,0 +1,272 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#include "mvUsbDevApi.h" +#include "mvUsbDevPrv.h" +#include "mvUsbDefs.h" + +/* Test packet for Test Mode : TEST_PACKET. USB 2.0 Specification section 7.1.20 */ +uint_8 test_packet[USB_TEST_MODE_TEST_PACKET_LENGTH] = +{ + /* Synch */ + /* DATA 0 PID */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, + 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xDF, + 0xEF, 0xF7, 0xFB, 0xFD, 0xFC, 0x7E, 0xBF, 0xDF, + 0xEF, 0xF7, 0xFB, 0xFD, 0x7E +}; + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_assert_resume +* Returned Value : None +* Comments : +* Resume signalling for remote wakeup +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_assert_resume + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 temp; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Assert the Resume signal */ + temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]); + temp &= ~EHCI_PORTSCX_W1C_BITS; + temp |= EHCI_PORTSCX_PORT_FORCE_RESUME; + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0] = USB_32BIT_LE(temp); + + /* Port change interrupt will be asserted at the end of resume + ** operation + */ + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_stall_endpoint +* Returned Value : None +* Comments : +* Stalls the specified endpoint +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_stall_endpoint + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + VUSB20_EP_QUEUE_HEAD_STRUCT _PTR_ ep_queue_head_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Get the endpoint queue head address */ + ep_queue_head_ptr = (VUSB20_EP_QUEUE_HEAD_STRUCT_PTR)usb_dev_ptr->EP_QUEUE_HEAD_PTR + + 2*ep_num + direction; + /* Stall the endpoint for Rx or Tx and set the endpoint type */ + if (ep_queue_head_ptr->MAX_PKT_LENGTH & USB_32BIT_LE(VUSB_EP_QUEUE_HEAD_IOS)) + { + /* This is a control endpoint so STALL both directions */ + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] |= + USB_32BIT_LE((EHCI_EPCTRL_TX_EP_STALL | EHCI_EPCTRL_RX_EP_STALL)); + } + else + { + if(direction) + { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] |= + USB_32BIT_LE(EHCI_EPCTRL_TX_EP_STALL); + } + else { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] |= + USB_32BIT_LE(EHCI_EPCTRL_RX_EP_STALL); + } + } /* Endif */ + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_STALL, + "STALL ep=%d %s: EPCTRLX=0x%x, CURR_dTD=0x%x, NEXT_dTD=0x%x, SIZE=0x%x\n", + ep_num, direction ? "SEND" : "RECV", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num]), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->CURR_DTD_PTR), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->NEXT_DTD_PTR), + (unsigned)USB_32BIT_LE(ep_queue_head_ptr->SIZE_IOC_INT_STS)); + +} /* EndBody */ + +/*FUNCTION*------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_unstall_endpoint +* Returned Value : None +* Comments : +* Unstall the specified endpoint in the specified direction +* +*END*-----------------------------------------------------------------*/ +void _usb_dci_vusb20_unstall_endpoint + ( + /* [IN] the USB_dev_initialize state structure */ + _usb_device_handle handle, + + /* [IN] the Endpoint number */ + uint_8 ep_num, + + /* [IN] direction */ + uint_8 direction + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + /* Enable the endpoint for Rx or Tx and set the endpoint type */ + if(direction) + { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] |= + USB_32BIT_LE(EHCI_EPCTRL_TX_DATA_TOGGLE_RST); + + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] &= + ~(USB_32BIT_LE(EHCI_EPCTRL_TX_EP_STALL)); + } + else + { + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] |= + USB_32BIT_LE(EHCI_EPCTRL_RX_DATA_TOGGLE_RST); + + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num] &= + ~(USB_32BIT_LE(EHCI_EPCTRL_RX_EP_STALL)); + } + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_STALL, + "UNSTALL ep=%d %s: EPCTRLX=0x%x\n", + ep_num, direction ? "SEND" : "RECV", + (unsigned)USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep_num])); + + +} /* EndBody */ + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_is_endpoint_stalled +* Returned Value : None +* Comments : +* Gets the endpoint status +* +*END*--------------------------------------------------------------------*/ +uint_8 _usb_dci_vusb20_is_endpoint_stalled + ( + /* [IN] Handle to the USB device */ + _usb_device_handle handle, + + /* [IN] Endpoint number */ + uint_8 ep, + + /* [IN] Endpoint direction */ + uint_8 dir + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 value; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_TRACE, "is_endpoint_stalled\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + if(dir) + { + value = dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep] & + (USB_32BIT_LE(EHCI_EPCTRL_TX_EP_STALL)); + } + else + { + value = dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[ep] & + (USB_32BIT_LE(EHCI_EPCTRL_RX_EP_STALL)); + } + return (value) ? 1 : 0; +} /* EndBody */ + +/*FUNCTION*---------------------------------------------------------------- +* +* Function Name : _usb_dci_vusb20_set_test_mode +* Returned Value : None +* Comments : +* sets/resets the test mode +* +*END*--------------------------------------------------------------------*/ +void _usb_dci_vusb20_set_test_mode + ( + /* [IN] Handle to the USB device */ + _usb_device_handle handle, + + /* [IN] Test mode */ + uint_16 test_mode + ) +{ /* Body */ + USB_DEV_STATE_STRUCT_PTR usb_dev_ptr; + VUSB20_REG_STRUCT_PTR dev_ptr; + uint_32 temp; + + ARC_DEBUG_TRACE(ARC_DEBUG_FLAG_ANY, "set_test_mode\n"); + + usb_dev_ptr = (USB_DEV_STATE_STRUCT_PTR)handle; + dev_ptr = (VUSB20_REG_STRUCT_PTR)usb_dev_ptr->DEV_PTR; + + temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0]); + + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.ENDPTCTRLX[0] = + USB_32BIT_LE((temp | EHCI_EPCTRL_TX_DATA_TOGGLE_RST)); + + if (test_mode == ARC_USB_TEST_MODE_TEST_PACKET) + { + USB_memcopy(test_packet, usb_dev_ptr->TEST_PKT_PTR, USB_TEST_MODE_TEST_PACKET_LENGTH); + _usb_device_send_data(handle, 0, usb_dev_ptr->TEST_PKT_PTR, USB_TEST_MODE_TEST_PACKET_LENGTH); + + } /* Endif */ + + temp = USB_32BIT_LE(dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0]); + temp &= ~EHCI_PORTSCX_W1C_BITS; + + dev_ptr->REGISTERS.OPERATIONAL_DEVICE_REGISTERS.PORTSCX[0] = + USB_32BIT_LE(temp | ((uint_32)test_mode << 8)); + +} /* EndBody */ + + diff --git a/drivers/usb/gadget/mv/mvUsbTypes.h b/drivers/usb/gadget/mv/mvUsbTypes.h new file mode 100644 index 00000000000000..d260b9ff6f9138 --- /dev/null +++ b/drivers/usb/gadget/mv/mvUsbTypes.h @@ -0,0 +1,240 @@ +/******************************************************************************* + +This software file (the "File") is distributed by Marvell International Ltd. +or its affiliate(s) under the terms of the GNU General Public License Version 2, +June 1991 (the "License"). You may use, redistribute and/or modify this File +in accordance with the terms and conditions of the License, a copy of which +is available along with the File in the license.txt file or by writing to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. + +(C) Copyright 2004 - 2007 Marvell Semiconductor Israel Ltd. All Rights Reserved. +(C) Copyright 1999 - 2004 Chipidea Microelectronica, S.A. All Rights Reserved. + +*******************************************************************************/ + +#ifndef __mvUsbTypes_h__ +#define __mvUsbTypes_h__ + +#define _PTR_ * +#define _CODE_PTR_ * + +typedef char _PTR_ char_ptr; /* signed character */ + +typedef signed char int_8, _PTR_ int_8_ptr; /* 8-bit signed integer */ +typedef unsigned char uint_8, _PTR_ uint_8_ptr; /* 8-bit signed integer */ + +typedef short int_16, _PTR_ int_16_ptr; /* 16-bit signed integer */ +typedef unsigned short uint_16, _PTR_ uint_16_ptr; /* 16-bit unsigned integer*/ + +typedef int int_32, _PTR_ int_32_ptr; /* 32-bit signed integer */ +typedef unsigned int uint_32, _PTR_ uint_32_ptr; /* 32-bit unsigned integer*/ + +typedef unsigned long boolean; /* Machine representation of a boolean */ + +typedef void _PTR_ pointer; /* Machine representation of a pointer */ + +/*--------------------------------------------------------------------------*/ +/* +** STANDARD CONSTANTS +** +** Note that if standard 'C' library files are included after types.h, +** the defines of TRUE, FALSE and NULL may sometimes conflict, as most +** standard library files do not check for previous definitions. +*/ + +#ifndef FALSE +# define FALSE ((boolean)0) +#endif + +#ifndef TRUE +# define TRUE ((boolean)!FALSE) +#endif + +#ifndef NULL +# ifdef __cplusplus +# define NULL (0) +# else +# define NULL ((pointer)0) +# endif +#endif + +#ifndef _ASSERT_ + #define ASSERT(X,Y) +#else + #define ASSERT(X,Y) if(Y) { USB_printf(X); exit(1);} +#endif + +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define USB_MEM_ALIGN(n, align) ((n) + (-(n) & (align-1))) + +/* Macro for aligning the EP queue head to 32 byte boundary */ +#define USB_MEM32_ALIGN(n) USB_MEM_ALIGN(n, 32) + +/* Macro for aligning the EP queue head to 1024 byte boundary */ +#define USB_MEM1024_ALIGN(n) USB_MEM_ALIGN(n, 1024) + +/* Macro for aligning the EP queue head to 1024 byte boundary */ +#define USB_MEM2048_ALIGN(n) USB_MEM_ALIGN(n, 2048) + +#define PSP_CACHE_LINE_SIZE 32 + +#define USB_uint_16_low(x) ((x) & 0xFF) +#define USB_uint_16_high(x) (((x) >> 8) & 0xFF) + +#define USB_CACHE_ALIGN(n) USB_MEM_ALIGN(n, PSP_CACHE_LINE_SIZE) + +#ifndef INLINE +# if defined(MV_VXWORKS) +# define INLINE __inline +# else +# define INLINE inline +# endif /* MV_VXWORKS */ +#endif /* INLINE */ + +/* 16bit byte swap. For example 0x1122 -> 0x2211 */ +static INLINE uint_16 USB_BYTE_SWAP_16BIT(uint_16 value) +{ + return ( ((value & 0x00ff) << 8) | + ((value & 0xff00) >> 8) ); +} + +/* 32bit byte swap. For example 0x11223344 -> 0x44332211 */ +static INLINE uint_32 USB_BYTE_SWAP_32BIT(uint_32 value) +{ + return ( ((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + ((value & 0xff000000) >> 24)); +} + +#define MV_CPU_LE //xj + +/* Endianess macros. */ +#if defined(MV_CPU_LE) +# define USB_16BIT_LE(X) (X) +# define USB_32BIT_LE(X) (X) +# define USB_16BIT_BE(X) USB_BYTE_SWAP_16BIT(X) +# define USB_32BIT_BE(X) USB_BYTE_SWAP_32BIT(X) +#elif defined(MV_CPU_BE) +# define USB_16BIT_LE(X) USB_BYTE_SWAP_16BIT(X) +# define USB_32BIT_LE(X) USB_BYTE_SWAP_32BIT(X) +# define USB_16BIT_BE(X) (X) +# define USB_32BIT_BE(X) (X) +#else + #error "CPU endianess isn't defined!\n" +#endif + +typedef struct +{ + void (*bspPrintf) (const char * fmt, ...); + int (*bspSprintf) (char* buffer, const char * fmt, ...); + void* (*bspUncachedMalloc) (void* pDev, uint_32 size, uint_32 align, + unsigned long* pPhyAddr); + void (*bspUncachedFree) (void* pDev, uint_32 size, unsigned long phyAddr, + void* pVirtAddr); + void* (*bspMalloc) (unsigned int size); + void (*bspFree) (void* ptr); + void* (*bspMemset) (void* ptr, int val, unsigned int size); + void* (*bspMemcpy) (void* dst, const void* src, unsigned int size); + unsigned long (*bspCacheFlush) (void* pDev, void* pVirtAddr, int size); + unsigned long (*bspCacheInv) (void* pDev, void* pVirtAddr, int size); + unsigned long (*bspVirtToPhys) (void* pDev, void* pVirtAddr); + int (*bspLock) (void); + void (*bspUnlock) (int lockKey); + uint_32 (*bspGetCapRegAddr) (int devNo); + void (*bspResetComplete) (int devNo); + +} USB_IMPORT_FUNCS; + +extern USB_IMPORT_FUNCS* global_import_funcs; + +#define USB_sprintf(frmt, x...) if( (global_import_funcs != NULL) && \ + global_import_funcs->bspSprintf != NULL) \ + global_import_funcs->bspSprintf(frmt, ##x) + +#define USB_printf(frmt, x...) if( (global_import_funcs != NULL) && \ + (global_import_funcs->bspPrintf != NULL) ) \ + global_import_funcs->bspPrintf(frmt, ##x) + + +#define USB_virt_to_phys(pVirt) (global_import_funcs->bspVirtToPhys == NULL) ? \ + (uint_32)(pVirt) : global_import_funcs->bspVirtToPhys(NULL, pVirt) + +#define USB_get_cap_reg_addr(dev) global_import_funcs->bspGetCapRegAddr(dev) + +static INLINE void* USB_uncached_memalloc(uint_32 size, uint_32 align, unsigned long* pPhyAddr) +{ + /*USB_printf("**** USB_uncached_memalloc: size=%d\n", (size)); */ + return global_import_funcs->bspUncachedMalloc(NULL, size, align, pPhyAddr); +} + +static INLINE void* USB_memalloc(uint_32 size) +{ + /*USB_printf("**** USB_memalloc: size=%d\n", (size)); */ + return global_import_funcs->bspMalloc(size); +} + +#define USB_uncached_memfree(pVirt, size, physAddr) \ + /*USB_printf("#### USB_uncached_memfree: pVirt=0x%x\n", (pVirt)); */\ + global_import_funcs->bspUncachedFree(NULL, size, physAddr, pVirt); + +#define USB_memfree(ptr) \ + /*USB_printf("#### USB_memfree: ptr=0x%x\n", (ptr));*/ \ + global_import_funcs->bspFree(ptr); + +#define USB_memzero(ptr, n) global_import_funcs->bspMemset(ptr, 0, n) +#define USB_memcopy(src, dst, n) global_import_funcs->bspMemcpy(dst, src, n) + +#define USB_dcache_inv(ptr, size) if(global_import_funcs->bspCacheInv != NULL) \ + global_import_funcs->bspCacheInv(NULL, ptr, size) + +#define USB_dcache_flush(ptr, size) if(global_import_funcs->bspCacheFlush != NULL) \ + global_import_funcs->bspCacheFlush(NULL, ptr, size) + +#define USB_lock() (global_import_funcs->bspLock == NULL) ? \ + 0 : global_import_funcs->bspLock() + +#define USB_unlock(key) if(global_import_funcs->bspUnlock != NULL) \ + global_import_funcs->bspUnlock(key) + +#define USB_reset_complete(dev) if(global_import_funcs->bspResetComplete) \ + global_import_funcs->bspResetComplete(dev) + + +#if defined(USB_UNDERRUN_WA) + +#define USB_SRAM_MAX_PARTS 16 + +typedef struct +{ + uint_32 (*bspGetSramAddr) (uint_32* pSize); + void (*bspIdmaCopy) (void* dst, void* src, unsigned int size); + +} USB_WA_FUNCS; + +extern USB_WA_FUNCS* global_wa_funcs; +extern int global_wa_sram_parts; +extern int global_wa_threshold; + +#define USB_get_sram_addr(pSize) global_wa_funcs->bspGetSramAddr(pSize) + +#define USB_idma_copy(dst, src, size) \ + if(global_wa_funcs->bspIdmaCopy != NULL) \ + global_wa_funcs->bspIdmaCopy(dst, src, size) + +#endif /* USB_UNDERRUN_WA */ + +#endif /* __mvUsbTypes_h__ */ + +/* EOF */ + + diff --git a/drivers/usb/gadget/mvLog.h b/drivers/usb/gadget/mvLog.h new file mode 100644 index 00000000000000..068ed165712713 --- /dev/null +++ b/drivers/usb/gadget/mvLog.h @@ -0,0 +1,125 @@ +/******************************************************************************* +* +* Marvell Serial ATA Linux Driver +* Copyright 2004 +* Marvell International Ltd. +* +* This software program (the "Program") is distributed by Marvell International +* ltd. under the terms of the GNU General Public License Version 2, June 1991 +* (the "License"). You may use, redistribute and/or modify this Program in +* accordance with the terms and conditions of the License, a copy of which is +* available along with the Program in the license.txt file or by writing to the +* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +* MA 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. +* +* THE PROGRAM IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE +* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE +* EXPRESSLY DISCLAIMED. The License provides additional details about this +* warranty disclaimer. +* +* For more information about the Program or the License as it applies to the +* Program, please contact Marvell International Ltd. via its affiliate, Marvell +* Semiconductor, Inc., 700 First Avenue, Sunnyvale, CA 94010 +* +******************************************************************************** +* mvLog.h - Header File for CORE driver logger +* +* DESCRIPTION: +* None. +* +* DEPENDENCIES: +* +*******************************************************************************/ +#ifndef __INCmvLogh +#define __INCmvLogh + +#ifdef __cplusplus +extern "C" /*{*/ +#endif /* __cplusplus */ + +/*-------------H file-----------------------------*/ +#define MV_DEBUG_FATAL_ERROR 0x01 +#define MV_DEBUG_ERROR 0x02 +#define MV_DEBUG_INIT 0x04 +#define MV_DEBUG_INTERRUPTS 0x08 +#define MV_DEBUG_SATA_LINK 0x10 +#define MV_DEBUG_UDMA_COMMAND 0x20 +#define MV_DEBUG_NON_UDMA_COMMAND 0x40 +#define MV_DEBUG_PM 0x80 +#define MV_DEBUG 0x100 + + + +#define MV_MAX_LOG_MODULES 16 +#define MV_MAX_MESSAGE_TYPE 9 +#define MV_RAW_MSG_ID 0xF + +typedef struct +{ + MV_BOOLEAN used; + MV_U32 filterMask; + char *name; + char *filters; +} MV_LOG_FILTER_HEADER; + + +#if defined (MV_LOG_DEBUG) || defined (MV_LOG_ERROR) + #define MV_LOGGER 1 + #if defined (WIN32) +ULONG +_cdecl +DbgPrint( + PCH Format, + ... + ); + #define MV_LOG_PRINT DbgPrint + #elif defined (LINUX) + #define MV_LOG_PRINT printk + #else + #define MV_LOG_PRINT printf + #endif + + +MV_BOOLEAN mvLogRegisterModule(MV_U8 moduleId, MV_U32 filterMask, char* name); +MV_BOOLEAN mvLogSetModuleFilter(MV_U8 moduleId, MV_U32 filterMask); +MV_U32 mvLogGetModuleFilter(MV_U8 moduleId); +void mvLogMsg(MV_U8 moduleId, MV_U32 type, char* format, ...); + +#else /*defined (MV_LOG_DEBUG) || defined (MV_LOG_ERROR)*/ + + #undef MV_LOGGER + + #if defined (WIN32) + #define MV_LOG_PRINT + #define mvLogRegisterModule + #define mvLogGetModuleFilter + #define mvLogRegisterAllModules + #define mvLogMsg + + #elif defined (LINUX) + #define MV_LOG_PRINT(x...) + #define mvLogRegisterModule(x...) + #define mvLogSetModuleFilter(x...) + #define mvLogGetModuleFilter(x...) + #define mvLogRegisterAllModules(x...) + #define mvLogMsg(x...) + + #else + #define MV_LOG_PRINT + #define mvLogRegisterModule + #define mvLogSetModuleFilter + #define mvLogGetModuleFilter + #define mvLogRegisterAllModules + #define mvLogMsg + #endif + +#endif /*!defined (MV_LOG_DEBUG) && !defined (MV_LOG_ERROR)*/ + +#ifdef __cplusplus + +/*}*/ +#endif /* __cplusplus */ + +#endif + + diff --git a/drivers/usb/gadget/mvOs.h b/drivers/usb/gadget/mvOs.h new file mode 100644 index 00000000000000..78f19b23239b17 --- /dev/null +++ b/drivers/usb/gadget/mvOs.h @@ -0,0 +1,149 @@ +/******************************************************************************* +* +* Marvell Serial ATA Linux Driver +* Copyright 2004 +* Marvell International Ltd. +* +* This software program (the "Program") is distributed by Marvell International +* ltd. under the terms of the GNU General Public License Version 2, June 1991 +* (the "License"). You may use, redistribute and/or modify this Program in +* accordance with the terms and conditions of the License, a copy of which is +* available along with the Program in the license.txt file or by writing to the +* Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, +* MA 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. +* +* THE PROGRAM IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE +* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE +* EXPRESSLY DISCLAIMED. The License provides additional details about this +* warranty disclaimer. +* +* For more information about the Program or the License as it applies to the +* Program, please contact Marvell International Ltd. via its affiliate, Marvell +* Semiconductor, Inc., 700 First Avenue, Sunnyvale, CA 94010 +* +******************************************************************************** +* mvOsLinux.h - O.S. interface header file for Linux +* +* DESCRIPTION: +* This header file contains OS dependent definition under Linux +* +* DEPENDENCIES: +* Linux kernel header files. +* +* FILE REVISION NUMBER: +* $Revision: 1.1.2.1 $ +*******************************************************************************/ + +#ifndef __INCmvOsLinuxh +#define __INCmvOsLinuxh + +/* Includes */ +#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 + +#include +#include +#include +#include +#include + + +/* Definitions */ + +#define MV_DEFAULT_QUEUE_DEPTH 2 +/* System dependent macro for flushing CPU write cache */ +#define MV_CPU_WRITE_BUFFER_FLUSH() wmb() + +/* System dependent little endian from / to CPU conversions */ +#define MV_CPU_TO_LE16(x) cpu_to_le16(x) +#define MV_CPU_TO_LE32(x) cpu_to_le32(x) + +#define MV_LE16_TO_CPU(x) le16_to_cpu(x) +#define MV_LE32_TO_CPU(x) le32_to_cpu(x) + +/* System dependent register read / write in byte/word/dword variants */ +#define MV_REG_WRITE_BYTE(base, offset, val) writeb(val, base + offset) +#define MV_REG_WRITE_WORD(base, offset, val) writew(val, base + offset) +#define MV_REG_WRITE_DWORD(base, offset, val) writel(val, base + offset) +#define MV_REG_READ_BYTE(base, offset) readb(base + offset) +#define MV_REG_READ_WORD(base, offset) readw(base + offset) +#define MV_REG_READ_DWORD(base, offset) readl(base + offset) + + +/* Typedefs */ +typedef enum mvBoolean{MV_FALSE, MV_TRUE} MV_BOOLEAN; + +/* System dependant typedefs */ +typedef void MV_VOID; +typedef u32 MV_U32; +typedef u16 MV_U16; +typedef u8 MV_U8; +typedef void *MV_VOID_PTR; +typedef u32 *MV_U32_PTR; +typedef u16 *MV_U16_PTR; +typedef u8 *MV_U8_PTR; +typedef char *MV_CHAR_PTR; +typedef void *MV_BUS_ADDR_T; +typedef unsigned long MV_CPU_FLAGS; + + +/* Structures */ +/* System dependent structure */ +typedef struct mvOsSemaphore +{ + int notUsed; +} MV_OS_SEMAPHORE; + + +/* Functions (User implemented)*/ + +/* Semaphore init, take and release */ +#define mvOsSemInit(x) MV_TRUE +#define mvOsSemTake(x) +#define mvOsSemRelease(x) + +/* Interrupt masking and unmasking functions */ +MV_CPU_FLAGS mvOsSaveFlagsAndMaskCPUInterrupts(MV_VOID); +MV_VOID mvOsRestoreFlags(MV_CPU_FLAGS); + +/* Delay function in micro seconds resolution */ +void mvMicroSecondsDelay(MV_VOID_PTR, MV_U32); + +/* System logging function */ +#include "mvLog.h" +/* Enable READ/WRITE Long SCSI command only when driver is compiled for debugging */ +#ifdef MV_LOGGER +#define MV_SATA_SUPPORT_READ_WRITE_LONG +#endif + +#define MV_IAL_LOG_ID 3 + +#endif /* __INCmvOsLinuxh */ + diff --git a/drivers/usb/gadget/mvUsb.h b/drivers/usb/gadget/mvUsb.h new file mode 100644 index 00000000000000..dfb126cd65ed14 --- /dev/null +++ b/drivers/usb/gadget/mvUsb.h @@ -0,0 +1,159 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#ifndef __INCmvUsbh +#define __INCmvUsbh + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifdef CONFIG_USB_COMPOSITE +#include +#endif +/* includes */ +#include "mvUsbRegs.h" +//#include "mvCtrlEnvLib.h" +//#include "mvCtrlEnvAddrDec.h" + +struct mv_usb_dev; + +struct mv_usb_ep +{ + struct usb_ep ep; + struct mv_usb_dev* usb_dev; + struct list_head req_list; + spinlock_t req_lock; + unsigned num : 8, + processing : 1, +#ifdef CONFIG_USB_COMPOSITE + assigned : 1, +#endif + ack_sent : 1, + ack_recv : 1, + is_enabled : 1, + is_in : 1; +}; + +struct mv_usb_dev +{ + /* each pci device provides one gadget, several endpoints */ + struct usb_gadget gadget; + spinlock_t lock; + struct usb_gadget_driver *driver; + struct mv_usb_ep ep[2*ARC_USB_MAX_ENDPOINTS]; + unsigned enabled : 1, + protocol_stall : 1, + got_irq : 1; + u16 chiprev; + struct device *dev; + void* mv_usb_handle; + int dev_no; + u8 vbus_gpp_no; +#if defined(CONFIG_PLAT_PXA) + struct pxa_usb_plat_info *info; + struct clk *clk; + unsigned int __iomem regbase; + unsigned int __iomem phybase; + struct pxa3xx_comp cdev; + struct work_struct work; + struct timer_list timer; +#ifdef CONFIG_USB_OTG + struct otg_transceiver *transceiver; +#endif + int in_single; + int out_single; +#endif +}; + +/* Functions */ +//MV_STATUS mvUsbInit(int dev, MV_BOOL isHost); +//static MV_U32 mvUsbGetCapRegAddr(int devNo); +void mvUsbDevResetComplete(int devNo); + +#ifdef MV_USB_VOLTAGE_FIX +MV_U8 mvUsbGppInit(int dev); +int mvUsbBackVoltageUpdate(int dev, MV_U8 gppNo); +#endif /* MV_USB_VOLTAGE_FIX */ + +/* Power management routines */ +void mvUsbPowerDown(int port); +void mvUsbPowerUp(int port); + +/* +MV_STATUS mvUsbWinInit(int dev); +MV_STATUS mvUsbWinSet(int dev, MV_U32 winNum, MV_DEC_WIN *pAddrWin); +MV_STATUS mvUsbWinGet(int dev, MV_U32 winNum, MV_DEC_WIN *pAddrWin); +*/ + +void mvUsbAddrDecShow(void); +void mvUsbRegs(int dev); +void mvUsbCoreRegs(int dev, int isHost); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __INCmvUsbh */ + diff --git a/drivers/usb/gadget/mvUsbRegs.h b/drivers/usb/gadget/mvUsbRegs.h new file mode 100644 index 00000000000000..bc26689d994bbc --- /dev/null +++ b/drivers/usb/gadget/mvUsbRegs.h @@ -0,0 +1,498 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +This software file (the "File") is owned and distributed by Marvell +International Ltd. and/or its affiliates ("Marvell") under the following +alternative licensing terms. Once you have made an election to distribute the +File under one of the following license alternatives, please (i) delete this +introductory statement regarding license alternatives, (ii) delete the two +license alternatives that you have not elected to use and (iii) preserve the +Marvell copyright notice above. + +******************************************************************************** +Marvell Commercial License Option + +If you received this File from Marvell and you have entered into a commercial +license agreement (a "Commercial License") with Marvell, the File is licensed +to you under the terms of the applicable Commercial License. + +******************************************************************************** +Marvell GPL License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File in accordance with the terms and conditions of the General +Public License Version 2, June 1991 (the "GPL License"), a copy of which is +available along with the File in the license.txt file or by writing to the Free +Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or +on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + +THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED +WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY +DISCLAIMED. The GPL License provides additional details about this warranty +disclaimer. +******************************************************************************** +Marvell BSD License Option + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#ifndef __INCmvUsbRegsh +#define __INCmvUsbRegsh + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +//#include "mvCtrlEnvSpec.h" + +#define USB_REG_BASE(dev) (&__REG_3(0x55502000)) +#define USB_PHY_REG_BASE(dev) (&__REG_3(0x5550A000)) +#define MV_USB_MAX_ADDR_DECODE_WIN 4 + +/*******************************************/ +/* USB ARC Core Registers */ +/*******************************************/ +#if 1 +#define MV_USB_CORE_ID_REG(dev) (0x00) +#define MV_USB_CORE_GENERAL_REG(dev) (0x04) +#define MV_USB_CORE_HOST_REG(dev) (0x08) +#define MV_USB_CORE_DEVICE_REG(dev) (0x0C) +#define MV_USB_CORE_TX_BUF_REG(dev) (0x10) +#define MV_USB_CORE_RX_BUF_REG(dev) (0x14) +//#define MV_USB_CORE_TTTX_BUF_REG(dev) (USB_REG_BASE(dev) + 0x18) +//#define MV_USB_CORE_TTRX_BUF_REG(dev) (USB_REG_BASE(dev) + 0x1C) + +#define MV_USB_CORE_GPTIMER0_LD_REG(dev) (0x80) +#define MV_USB_CORE_GPTIMER0_CTRL_REG(dev) (0x84) +#define MV_USB_CORE_GPTIMER1_LD_REG(dev) (0x88) +#define MV_USB_CORE_GPTIMER1_CTRL_REG(dev) (0x8C) +#define MV_USB_CORE_SUBS_CFG_REG(dev) (0x90) + +#define MV_USB_CORE_CAP_LENGTH_REG(dev) (0x100) +#define MV_USB_CORE_CAP_HCS_PARAMS_REG(dev) (0x104) +#define MV_USB_CORE_CAP_HCC_PARAMS_REG(dev) (0x108) + +#define MV_USB_CORE_CAP_DCI_VERSION_REG(dev) (0x120) +#define MV_USB_CORE_CAP_DCC_PARAMS_REG(dev) (0x124) + +#define MV_USB_CORE_CMD_REG(dev) (0x140) + +#define MV_USB_CORE_CMD_RUN_BIT 0 +#define MV_USB_CORE_CMD_RUN_MASK (1 << MV_USB_CORE_CMD_RUN_BIT) + +#define MV_USB_CORE_CMD_RESET_BIT 1 +#define MV_USB_CORE_CMD_RESET_MASK (1 << MV_USB_CORE_CMD_RESET_BIT) + +#define MV_USB_CORE_STATUS_REG(dev) (0x144) +#define MV_USB_CORE_INTR_REG(dev) (0x148) +#define MV_USB_CORE_FRAME_INDEX_REG(dev) (0x14C) + +#define MV_USB_CORE_PERIODIC_LIST_BASE_REG(dev) (0x154) +#define MV_USB_CORE_DEV_ADDR_REG(dev) (0x154) + +#define MV_USB_CORE_ASYNC_LIST_ADDR_REG(dev) (0x158) +#define MV_USB_CORE_ENDPOINT_LIST_ADDR_REG(dev) (0x158) + +#define MV_USB_CORE_TT_CTRL_REG(dev) (0x15C) +#define MV_USB_CORE_BURST_SIZE_REG(dev) (0x160) +#define MV_USB_CORE_TX_FILL_TUNING_REG(dev) (0x164) +//#define MV_USB_CORE_TX_TT_FILL_TUNING_REG(dev) (USB_REG_BASE(dev) + 0x168) +#define MV_USB_CORE_EP_NAK_REG(dev) (0x178) +#define MV_USB_CORE_EP_NAKEN_REG(dev) (0x17C) +#define MV_USB_CORE_CONFIG_FLAG_REG(dev) (0x180) +#define MV_USB_CORE_PORTSC_REG(dev) (0x184) +#define MV_USB_CORE_OTGSC_REG(dev) (0x1A4) + +#define MV_USB_CORE_MODE_REG(dev) (0x1A8) + +#define MV_USB_CORE_MODE_OFFSET 0 +#define MV_USB_CORE_MODE_MASK (3 << MV_USB_CORE_MODE_OFFSET) +#define MV_USB_CORE_MODE_HOST (3 << MV_USB_CORE_MODE_OFFSET) +#define MV_USB_CORE_MODE_DEVICE (2 << MV_USB_CORE_MODE_OFFSET) + +/* Bit[2] (ES) - don't care */ + +#define MV_USB_CORE_SETUP_LOCK_DISABLE_BIT 3 +#define MV_USB_CORE_SETUP_LOCK_DISABLE_MASK (1 << MV_USB_CORE_SETUP_LOCK_DISABLE_BIT) + +#define MV_USB_CORE_STREAM_DISABLE_BIT 4 +#define MV_USB_CORE_STREAM_DISABLE_MASK (1 << MV_USB_CORE_STREAM_DISABLE_BIT) + + +#define MV_USB_CORE_ENDPT_SETUP_STAT_REG(dev) (0x1AC) +#define MV_USB_CORE_ENDPT_PRIME_REG(dev) (0x1B0) +#define MV_USB_CORE_ENDPT_FLUSH_REG(dev) (0x1B4) +#define MV_USB_CORE_ENDPT_STATUS_REG(dev) (0x1B8) +#define MV_USB_CORE_ENDPT_COMPLETE_REG(dev) (0x1BC) +#define MV_USB_CORE_ENDPT_CTRL_REG(dev, ep) (0x1C0 + (ep*4)) + +/*******************************************/ +/* USB Bridge Registers */ +/*******************************************/ +/*#define MV_USB_BRIDGE_CTRL_REG(dev) (USB_REG_BASE(dev) + 0x300) +#define MV_USB_BRIDGE_CORE_BYTE_SWAP_OFFSET 4 +#define MV_USB_BRIDGE_CORE_BYTE_SWAP_MASK (1 << MV_USB_BRIDGE_CORE_BYTE_SWAP_OFFSET) +#define MV_USB_BRIDGE_CORE_BYTE_SWAP_EN (0 << MV_USB_BRIDGE_CORE_BYTE_SWAP_OFFSET) + +#define MV_USB_BRIDGE_INTR_CAUSE_REG(dev) (USB_REG_BASE(dev) + 0x310) +#define MV_USB_BRIDGE_INTR_MASK_REG(dev) (USB_REG_BASE(dev) + 0x314) +*/ +#define MV_USB_MUX_CTRL_REG(dev) (0x300) +#define MV_USB_INT_STATUS_REG(dev) (0x304) +#define MV_USB_INT_ENABLE_REG(dev) (0x308) + +/* BITs in Interrupt Enable and Status registers */ +#define MV_USB_INT_WKEN_BIT 0 +#define MV_USB_INT_WKEN_MASK (1< +#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 +#include +#include +#include +#include +#include +#include +#include + +#include "mv/mvUsbDevApi.h" +#include "mv/mvUsbCh9.h" +#include +#include +#include +#include "mvUsb.h" + +#if defined(CONFIG_MV645xx) || defined(CONFIG_MV646xx) +# include "marvell_pic.h" +#endif /* CONFIG_MV645xx */ + +#ifdef CONFIG_SMP +#define MV_SPIN_LOCK_IRQSAVE(spinlock,flags) \ +if(!in_interrupt()) \ +spin_lock_irqsave(spinlock, flags) + +#define MV_SPIN_UNLOCK_IRQRESTORE(spinlock,flags) \ +if(!in_interrupt()) \ +spin_unlock_irqrestore(spinlock, flags) + +#else /* CONFIG_SMP */ + +#define MV_SPIN_LOCK_IRQSAVE(spinlock,flags) spin_lock_irqsave(spinlock, flags) +#define MV_SPIN_UNLOCK_IRQRESTORE(spinlock,flags) spin_unlock_irqrestore(spinlock, flags) + +#endif /* CONFIG_SMP */ + +#define mvOsMalloc(buf) kmalloc(buf, GFP_KERNEL) +#define mvOsFree kfree + +#define MV_USB_VERSION 0 +#define MV_BOARD_MAX_USB_IF 2 + +/* + * Enable/Disable Streaming mode for USB Core + */ +/*#if (defined(CONFIG_MV88F6082) || defined(CONFIG_MV645xx) || defined(USB_UNDERRUN_WA))*/ +#if (MV_USB_VERSION >= 1) || defined(USB_UNDERRUN_WA) +static int streaming = 1; +#else +static int streaming = 0; +#endif +module_param_named(streaming, streaming, int, S_IRUGO); +MODULE_PARM_DESC(streaming, "0 - Streaming Disable, 1 - Streaming Enable"); + +static struct mv_usb_dev* the_controllers[MV_BOARD_MAX_USB_IF] = {NULL, NULL}; + +unsigned long temp_remove_count = 0; +unsigned long send_not_empty = 0; +int IRQ_USB_CTRL[1]; + +/*************************************************************************** + * PMIC related operations + */ + +#ifdef CONFIG_PXA3xx_DVFM +#include +#endif + +#define ENABLE_CABLE_DETECT +#define USB_TIMER_TIMEOUT (3*HZ) +int connected; +static int conn_check = 1; +int rely_on_vbus; + +#if defined(CONFIG_PXA3xx_DVFM) +static struct dvfm_lock dvfm_lock = { + .lock = SPIN_LOCK_UNLOCKED, + .dev_idx = -1, + .count = 0, +}; + +static void set_dvfm_constraint(void) +{ + spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags); + /* Disable D0CS */ + dvfm_disable_op_name("D0CS", dvfm_lock.dev_idx); + /* Disable D1/D2 mode */ + dvfm_disable_op_name("D1", dvfm_lock.dev_idx); + dvfm_disable_op_name("D2", dvfm_lock.dev_idx); + if (cpu_is_pxa935()) + dvfm_disable_op_name("CG", dvfm_lock.dev_idx); + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); +} + +static void unset_dvfm_constraint(void) +{ + spin_lock_irqsave(&dvfm_lock.lock, dvfm_lock.flags); + /* Enable D0CS */ + dvfm_enable_op_name("D0CS", dvfm_lock.dev_idx); + /* Enable D1/D2 mode */ + dvfm_enable_op_name("D1", dvfm_lock.dev_idx); + dvfm_enable_op_name("D2", dvfm_lock.dev_idx); + if (cpu_is_pxa935()) + dvfm_enable_op_name("CG", dvfm_lock.dev_idx); + spin_unlock_irqrestore(&dvfm_lock.lock, dvfm_lock.flags); +} +#else +static void set_dvfm_constraint(void) {} +static void unset_dvfm_constraint(void) {} +#endif + +#ifdef CONFIG_PLAT_PXA +static int mv_usb_vbus_detect(void *func, int enable); +void mv_clock_enable(struct mv_usb_dev *dev, int enable) +{ + static int enabled; + + pr_debug("%s enable %d enabled %d\n", __func__, enable, enabled); + if (enable == enabled) + return; + + if (enable) { + clk_enable(dev->clk); + if (dev->info && dev->info->phy_init) + dev->info->phy_init(dev->info->phybase); + } else { + clk_disable(dev->clk); + if (dev->info && dev->info->phy_deinit) + dev->info->phy_deinit(dev->info->phybase); + } + enabled = enable; +} +#endif + +static int mv_usb_vbus_status(unsigned base) +{ + int status = VBUS_LOW; + + pr_debug("mv_usb_vbus_status: u2o_get(base, U2xOTGSC) %x\n", + u2o_get(base, U2xOTGSC)); + if (U2xOTGSC_ASV & u2o_get(base, U2xOTGSC)) { + status = VBUS_HIGH; + } + + return status; +} + +static irqreturn_t mv_usb_vbus_event(int irq, void *(func)(int)) +{ + struct mv_usb_dev *dev = the_controllers[0]; + unsigned base = dev->regbase, otgsc = u2o_get(base, U2xOTGSC); + + /* clear interrupt status */ + u2o_set(base, U2xOTGSC, otgsc); + + pr_debug("%s otgsc %x\n", __func__, otgsc); + if ((otgsc & (U2xOTGSC_ASVIS|U2xOTGSC_BSEIS)) && func) + func(1); + + return IRQ_HANDLED; +} + +static int mv_usb_vbus_detect(void *func, int enable) +{ + struct mv_usb_dev *dev = the_controllers[0]; + int ret = 0, irq = IRQ_USB_CTRL[0]; + unsigned base = dev->regbase; + + if (enable) { + ret = request_irq(irq, (irq_handler_t)mv_usb_vbus_event, + IRQF_DISABLED | IRQF_SHARED, "vbus", func); + if (ret) + printk(KERN_INFO "request irq vbus %d failed: %d\n", + irq, ret); + + u2o_write(base, U2xOTGSC, U2xOTGSC_ASVIE | U2xOTGSC_BSEIE + | U2xOTGSC_IDPU | U2xOTGSC_OT); + } else { + u2o_clear(base, U2xOTGSC, U2xOTGSC_ASVIE | U2xOTGSC_BSEIE); + free_irq(irq, func); + } + + return ret; +} + +/* detect USB cable attach and detach by PMIC + * 1 -- cable attached; 0 -- cable detached + */ +int is_cable_attached(void) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + if (!dev->driver) + connected = 0; + else + connected = 1; + +#ifdef ENABLE_CABLE_DETECT + if (dev->info->vbus_status) { + if (dev->info->vbus_status(dev->regbase) & VBUS_HIGH) + connected = 1; + else + connected = 0; + } +#endif + return connected; +} + +void vbus_detect_init(void) +{ +#ifdef ENABLE_CABLE_DETECT +#ifndef CONFIG_USB_OTG + struct mv_usb_dev *dev = the_controllers[0]; + unsigned base = dev->regbase; + u32 tmp = u2o_get(base, U2xOTGSC); + + u2o_write(base, U2xOTGSC, tmp & 0xffff); +#endif +#endif +} + +#ifdef ENABLE_CABLE_DETECT +static int is_set_conf(SETUP_STRUCT_PTR ctrl_req) +{ + if ((ctrl_req->REQUESTTYPE & REQ_TYPE_MASK) == REQ_TYPE_STANDARD) { + if (ctrl_req->REQUEST == REQ_SET_CONFIGURATION) { + return 1; + } + } + return 0; +} + +static void uevent_worker(struct work_struct *work) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + if (connected) + kobject_uevent(&dev->dev->kobj, KOBJ_ADD); + else + kobject_uevent(&dev->dev->kobj, KOBJ_REMOVE); +} + +static void mv_usb_clock_disable(unsigned long data) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + if (connected || !dev->info->clk_gating) + return; + + pr_info("USB: no cable connected\n"); + mv_clock_enable(dev, 0); + if (dev->info && dev->info->set_power) + dev->info->set_power(0); +} + +extern void _usb_dci_vusb20_chip_initialize(_usb_device_handle); +static uint_8 mv_usb_start_ep0(struct mv_usb_dev *mv_dev); +static void mv_usb_connect_change(int status) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + if (!dev || !dev->driver) + return; + + pr_debug("%s status %d\n", __func__, status); + if (status & (VBUS_HIGH | VBUS_SRP)) { + connected = 1; + set_dvfm_constraint(); +#ifdef CONFIG_PLAT_PXA + mv_clock_enable(dev, 1); + if (dev->info && dev->info->set_power) + dev->info->set_power(1); +#endif +#ifdef CONFIG_USB_OTG + if (dev->transceiver) { + pxa3xx_otg_require_bus(USBOTG_VBUS_VALID); + /* fix the issue of composite mode, when insert 2nd + * module, it won't init usb in require_bus, init + * usb client mode here instead */ + if (dev->transceiver->state + == OTG_STATE_B_PERIPHERAL) { + _usb_device_start(dev->mv_usb_handle); + mv_usb_start_ep0(dev); + } + } else +#endif + { + udc_stop(&dev->cdev, &dev->gadget, dev->driver, 1); + _usb_dci_vusb20_chip_initialize(dev->mv_usb_handle); + + /* start device here */ + udc_stop(&dev->cdev, &dev->gadget, dev->driver, 0); + _usb_device_start(dev->mv_usb_handle); + mv_usb_start_ep0(dev); + } + } else { /* Cable detached */ + connected = 0; + schedule_work(&dev->work); +#ifdef CONFIG_USB_OTG + if (dev->transceiver) { + pxa3xx_otg_require_bus(0); + } else +#endif + if (!rely_on_vbus) { + udc_stop(&dev->cdev, &dev->gadget, dev->driver, 1); + _usb_device_stop(dev->mv_usb_handle); + } + +#ifdef CONFIG_PLAT_PXA + /* add some delay to avoid some thread that may access + * usb stuff, like hub_thread when OTG enabled */ + mod_timer(&dev->timer, jiffies + USB_TIMER_TIMEOUT); +#endif + unset_dvfm_constraint(); + } +} + +static void mv_usb_vbus_change(unsigned long events) +{ + struct mv_usb_dev *dev = the_controllers[0]; + unsigned long flags; + int status; + + pr_debug("%s is called, events:%d\n", __func__, (int)events); + local_irq_save(flags); + if (events) { + status = dev->info->vbus_status(dev->regbase); + pr_debug("status %x\n", status); + mv_usb_connect_change(status); + } + local_irq_restore(flags); +} +#else +static void mv_usb_vbus_change(unsigned long events) {} +#endif + + + +/***************************************************************************/ + +#ifdef DEBUG +int mv_debug = 1; +#else +int mv_debug = 0; +#endif + +static void mvOsPrintf(const char *fmt, ...) +{ +#if 1 + va_list args; + int r; + + if (mv_debug) { + va_start(args, fmt); + r = vprintk(fmt, args); + va_end(args); + } +#endif +} + +int mvCtrlUsbMaxGet(void) +{ + return 1; +} + +#if defined(USB_UNDERRUN_WA) +#include "mvIdma.h" + +extern int mv_idma_usage_get(int* free_map); +extern unsigned char* mv_sram_usage_get(int* sram_size_ptr); + + +#define USB_IDMA_CTRL_LOW_VALUE ICCLR_DST_BURST_LIM_128BYTE \ + | ICCLR_SRC_BURST_LIM_128BYTE \ + | ICCLR_BLOCK_MODE \ + | ICCLR_DESC_MODE_16M + +/* + * 0..3 - use specifed IDMA engine. If the engine is busy - find free one. + * Other - don't use IDMA (use memcpy instead) + */ +static int idma = 1; +module_param_named(idma, idma, int, S_IRUGO); +MODULE_PARM_DESC(idma, "IDMA engine used for copy from DRAM to SRAM [0..3]"); + +/* + */ +static int wa_sram_parts = 2; +module_param_named(wa_sram_parts, wa_sram_parts, int, S_IRUGO); +MODULE_PARM_DESC(wa_sram_parts, ""); + +static int wa_sram_descr = 1; +module_param_named(wa_sram_descr, wa_sram_descr, int, S_IRUGO); +MODULE_PARM_DESC(wa_sram_descr, ""); + +/* + */ +static int wa_threshold = 64; +module_param_named(wa_threshold, wa_threshold, int, S_IRUGO); +MODULE_PARM_DESC(wa_threshold, ""); + + +static char* sramBase = (char*)NULL; +static int sramSize = 0; + +u32 mvUsbSramGet(u32* pSize) +{ + char* pBuf; + + /* Align address to 64 bytes */ + pBuf = (char*)MV_ALIGN_UP((u32)sramBase, 64); + + if(pSize != NULL) + { + *pSize = sramSize - (pBuf - sramBase); + } + + mvOsPrintf("mvUsbSramGet: Base=%p (%p), Size=%d (%d)\n", + sramBase, pBuf, sramSize, *pSize); + + return (u32)pBuf; +} + +void mvUsbIdmaToSramCopy(void* sram_buf, void* src_buf, unsigned int size) +{ + unsigned long phys_addr; + + + mvOsPrintf("IdmaToSramCopy: idma=%d, sram=%p, src=%p (0x%x)\n", + usbWaIdma, sram_buf, src_buf, phys_addr); + + if( (idma >= 0) && (idma < MV_IDMA_MAX_CHAN) ) + { + phys_addr = pci_map_single(NULL, src_buf, size, PCI_DMA_TODEVICE ); + + /* !!!! SRAM Uncached */ + /*mvOsCacheInvalidate(NULL, sram_buf, size);*/ + + mvDmaTransfer(idma, (MV_U32)phys_addr, + (MV_U32)sram_buf, size, 0); + + /* Wait until copy is finished */ + while( mvDmaStateGet(idma) != MV_IDLE ); + } + else + { + mvOsPrintf("usbWA: copy to SRAM %d bytes: \n", size); + memcpy(sram_buf, src_buf, size); + /* !!!! SRAM Uncached */ + /*mvOsCacheFlush(NULL, sram_buf, size);*/ + } +} + +USB_WA_FUNCS usbWaFuncs = +{ + mvUsbSramGet, + mvUsbIdmaToSramCopy +}; + +int mv_usb_find_idma_engine(int idma_no) +{ + int idma, free = 0; + int free_map[MV_IDMA_MAX_CHAN]; + + if( (idma_no < 0) || (idma_no >= MV_IDMA_MAX_CHAN) ) + { + mvOsPrintf("Wrong IDMA number (%d): Valid range [0..%d]\n", + idma_no, (MV_IDMA_MAX_CHAN-1) ); + return -1; + } + free = mv_idma_usage_get(free_map); + if(free == 0) + { + mvOsPrintf("No free IDMAs for USB Underrun WA: use memcpy\n"); + return -1; + } + /* First of all check user idma_no */ + if(free_map[idma_no] != 0) + return idma_no; + + /* User idma_no is Busy. Look for free IDMA engine */ + for(idma=0; idmaphybase; + printk("UTMI regs\n"); + for (offset=4;offset < 0x40;offset+=4) { + printk("\t%2X: 0x%8X\n", offset, u2o_get(base, offset)); + } + + base = mv_dev->regbase; + printk("\nU2O regs\n"); + for (offset=0;offset < 0x1c4;offset+=4) { + printk("\t %2X: 0x%8X\n", offset, u2o_get(base, offset)); + } +} + +static void MV_REG_WRITE(unsigned offset, unsigned int value) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + unsigned int base = mv_dev->regbase; + unsigned long flags; + unsigned int addr; + + addr = (unsigned int)(base + offset); + local_irq_save(flags); + writel(value, addr); + __raw_readl(addr); + local_irq_restore(flags); +} + +static unsigned int MV_REG_READ(unsigned int offset) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + unsigned int base = mv_dev->regbase; + unsigned int addr; + + addr = (unsigned int)(base + offset); + return __raw_readl(addr); +} + +static int MV_BIT_CHECK(unsigned a, unsigned b) +{ + if (a & (1<= sramBase) && + ((char*)p < (sramBase + sramSize)) ) + return (unsigned long)p; +#endif + +#ifdef CONFIG_PCI + return pci_map_single( pDev, p, size, PCI_DMA_FROMDEVICE ); +#else + return dma_map_single(pDev, p, size, DMA_FROM_DEVICE); +#endif +} + +static MV_ULONG mvUsbCacheFlush( void* pDev, void* p, int size ) +{ +#if defined(USB_UNDERRUN_WA) + if( ((char*)p >= sramBase) && + ((char*)p < (sramBase + sramSize)) ) + return (unsigned long)p; +#endif + +#ifdef CONFIG_PCI + return pci_map_single( pDev, p, size, PCI_DMA_TODEVICE ); +#else + return dma_map_single( pDev, p, size, DMA_TO_DEVICE ); +#endif +} + +static unsigned long mvUsbVirtToPhys(void* pDev, void* pVirtAddr) +{ +#if defined(USB_UNDERRUN_WA) + if( ((char*)pVirtAddr >= sramBase) && + ((char*)pVirtAddr < (sramBase + sramSize)) ) + return (unsigned long)pVirtAddr; +#endif + + return virt_to_phys(pVirtAddr); +} + +static void usbDevResetComplete(int devNo) +{ + MV_U32 regVal; + + regVal = MV_USB_CORE_MODE_DEVICE | MV_USB_CORE_SETUP_LOCK_DISABLE_MASK; + if(streaming == 0) + regVal |= MV_USB_CORE_STREAM_DISABLE_MASK; + + /* Set USB_MODE register */ + MV_REG_WRITE(MV_USB_CORE_MODE_REG(devNo), regVal); +} + +static MV_U32 mvUsbGetCapRegAddr(int devNo) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + return (unsigned int)((unsigned)mv_dev->regbase + 0x100); +// return &(MV_USB_CORE_CAP_LENGTH_REG(devNo)); +} + +static unsigned long flags; +int mv_lock(void) +{ + local_irq_save(flags); + return flags; +} +void mv_unlock(int flags) +{ + local_irq_restore((unsigned long)flags); +} + +USB_IMPORT_FUNCS usbImportFuncs = +{ +#ifdef DEBUG + .bspPrintf = mvOsPrintf, +#else +// .bspPrintf = mvOsPrintf, +#endif + .bspSprintf = sprintf, + .bspUncachedMalloc = mvUsbIoUncachedMalloc, + .bspUncachedFree = mvUsbIoUncachedFree, + .bspMalloc = mvUsbMalloc, + .bspFree = mvUsbFree, + .bspMemset = memset, + .bspMemcpy = memcpy, + .bspCacheFlush = mvUsbCacheFlush, + .bspCacheInv = mvUsbCacheInvalidate, + .bspVirtToPhys = mvUsbVirtToPhys, + .bspLock = mv_lock, + .bspUnlock = mv_unlock, + .bspGetCapRegAddr = mvUsbGetCapRegAddr, + .bspResetComplete = usbDevResetComplete +}; + + + +static const char driver_name [] = "pxa-u2o"; +static const char driver_desc [] = DRIVER_DESC; + +static char ep_name [2*ARC_USB_MAX_ENDPOINTS][10] = +{ + "ep0out", "ep0in", +}; + +#ifdef CONFIG_USB_OTG +struct mv_usb_dev* get_the_controller(void) +{ + return the_controllers[0]; +} +#endif + +static struct usb_ep_ops mv_usb_ep_ops; + +static void mv_usb_ep_cancel_all_req(struct mv_usb_ep *mv_ep) +{ + struct mv_usb_dev* mv_dev = mv_ep->usb_dev; + struct usb_request* usb_req; + int req_cntr, tr_cntr; + + req_cntr = tr_cntr = 0; + + /* Cancel all transfers */ + while(_usb_device_get_transfer_status(mv_dev->mv_usb_handle, mv_ep->num, + mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV) != ARC_USB_STATUS_IDLE) + { + tr_cntr++; + _usb_device_cancel_transfer(mv_dev->mv_usb_handle, mv_ep->num, + mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV); + } + + if(tr_cntr > 0) + { + mvOsPrintf("Cancel ALL transfers: ep=%d-%s, %d transfers\n", + mv_ep->num, mv_ep->is_in ? "in" : "out", tr_cntr); + } + + while (!list_empty (&mv_ep->req_list)) + { + usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list); + + /* Dequeue request and call complete function */ + list_del_init (&usb_req->list); + mv_ep->processing = 0; + + if (usb_req->status == -EINPROGRESS) + usb_req->status = -ESHUTDOWN; + + usb_req->complete (&mv_ep->ep, usb_req); + req_cntr++; + if(req_cntr >= MAX_XDS_FOR_TR_CALLS) + break; + } + + if(req_cntr > 0) + { + mvOsPrintf("Cancel ALL Requests: ep=%d-%s, %d requests\n", + mv_ep->num, mv_ep->is_in ? "in" : "out", req_cntr); + _usb_stats(mv_dev->mv_usb_handle); + } + +} + +static uint_8 mv_usb_start_ep0(struct mv_usb_dev *mv_dev) +{ + DBGMSG("%s: mv_dev=%p, mv_usb_handle=%p, mv_ep=%p, usb_ep=%p\n", + __FUNCTION__, mv_dev, mv_dev->mv_usb_handle, &mv_dev->ep[0], &mv_dev->ep[0].ep); + + /* Init ep0 IN and OUT */ + mv_dev->ep[0].is_enabled = 1; + + _usb_device_init_endpoint(mv_dev->mv_usb_handle, 0, mv_dev->ep[0].ep.maxpacket, + ARC_USB_SEND, ARC_USB_CONTROL_ENDPOINT, 0); + + _usb_device_init_endpoint(mv_dev->mv_usb_handle, 0, mv_dev->ep[0].ep.maxpacket, + ARC_USB_RECV, ARC_USB_CONTROL_ENDPOINT, 0); + + return USB_OK; +} + +static uint_8 mv_usb_reinit (struct mv_usb_dev *usb_dev) +{ + int i, num; + MV_BOOL is_in; + struct mv_usb_ep *ep; + unsigned long flags; + + DBGMSG("%s: mv_dev=%p, mv_usb_handle=%p\n", + __FUNCTION__, usb_dev, usb_dev->mv_usb_handle); + + INIT_LIST_HEAD (&usb_dev->gadget.ep_list); + num = 0; + for(i=0; i<2*ARC_USB_MAX_ENDPOINTS; i++) + { + is_in = i % 2; + ep = &usb_dev->ep[i]; + if (num != 0) + { + INIT_LIST_HEAD(&ep->ep.ep_list); + list_add_tail (&ep->ep.ep_list, &usb_dev->gadget.ep_list); + sprintf(&ep_name[i][0], "ep%d%s", num, is_in ? "in" : "out"); + } + + ep->ep.name = &ep_name[i][0]; + ep->usb_dev = usb_dev; + ep->num = num; + ep->is_in = is_in; + ep->is_enabled = 0; + + spin_lock_init (&ep->req_lock); + spin_lock_irqsave(&ep->req_lock, flags); + INIT_LIST_HEAD (&ep->req_list); + spin_unlock_irqrestore(&ep->req_lock, flags); + + ep->ep.maxpacket = ~0; + ep->ep.ops = &mv_usb_ep_ops; + if(is_in) + num++; + } + usb_dev->ep[0].ep.maxpacket = 64; + usb_dev->gadget.ep0 = &usb_dev->ep[0].ep; + INIT_LIST_HEAD (&usb_dev->gadget.ep0->ep_list); + return USB_OK; +} + +static int mv_usb_start_transfer(struct mv_usb_dev *usb_dev, + struct mv_usb_ep *usb_ep) +{ + struct usb_request *_req; + uint_8 error = 0; + + if (!list_empty(&usb_ep->req_list) && !usb_ep->processing) + _req = list_entry(usb_ep->req_list.next, + struct usb_request, list); + else { + DBGMSG("%s list not empty, go out\n", __func__); + goto out; + } + + DBGMSG("\n%s: %s data (err=%d): ep_num=%d, pBuf=0x%x, send_size=%d\n", + __func__, usb_ep->is_in ? "SEND" : "RCV", error, + usb_ep->num, (unsigned)_req->buf, _req->length); + + usb_ep->processing = 1; + /* Add request to list */ + if (((usb_ep->num == 0) && (_req->length == 0)) || (usb_ep->is_in)) { + error = _usb_device_send_data(usb_dev->mv_usb_handle, + usb_ep->num, _req->buf, _req->length); + } else { + error = _usb_device_recv_data(usb_dev->mv_usb_handle, + usb_ep->num, _req->buf, _req->length); + } + + if (error != USB_OK) { + printk(KERN_INFO "ep_queue: Can't %s data (err=%d): ep_num=%d, " + "pBuf=0x%x, send_size=%d\n", usb_ep->is_in ? "in" : "out", + error, usb_ep->num, (unsigned)_req->buf, _req->length); + list_del_init(&_req->list); + usb_ep->processing = 0; + } + +out: + return (int)error; +} + +void mv_usb_bus_reset_service(void* handle, + uint_8 type, + boolean setup, + uint_8 direction, + uint_8_ptr buffer, + uint_32 length, + uint_8 error) +{ + int i, dev_no = _usb_device_get_dev_num(handle); + struct mv_usb_dev *mv_dev = the_controllers[dev_no]; + struct mv_usb_ep *mv_ep; + + if(setup == 0) + { + /* mv_usb_show(mv_dev, 0x3ff); */ + + /* Stop Hardware and cancel all pending requests */ + for (i=0; i<2*(ARC_USB_MAX_ENDPOINTS); i++) + { + mv_ep = &mv_dev->ep[i]; + + if(mv_ep->is_enabled == 0) + continue; + + mv_usb_ep_cancel_all_req(mv_ep); + } + /* If connected call Function disconnect callback */ + if( (mv_dev->gadget.speed != USB_SPEED_UNKNOWN) && + (mv_dev->driver != NULL) && + (mv_dev->driver->disconnect != NULL) ) + + { + + USB_printf("USB gadget device disconnect or port reset: frindex=0x%x\n", + MV_REG_READ(MV_USB_CORE_FRAME_INDEX_REG(dev_no)) ); + + /* mv_dev->driver->disconnect (&mv_dev->gadget); move to vbus falling irq */ + } + mv_dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* Reinit all endpoints */ + mv_usb_reinit(mv_dev); + } + else + { + USB_printf("device start, ep0 start\n"); + _usb_device_start(mv_dev->mv_usb_handle); + /* Restart Control Endpoint #0 */ + mv_usb_start_ep0(mv_dev); + } +} + +void mv_usb_speed_service(void* handle, + uint_8 type, + boolean setup, + uint_8 direction, + uint_8_ptr buffer, + uint_32 length, + uint_8 error) +{ + int dev_no = _usb_device_get_dev_num(handle); + struct mv_usb_dev *mv_dev = the_controllers[dev_no]; + + DBGMSG("Speed = %s\n", (length == ARC_USB_SPEED_HIGH) ? "High" : "Full"); + + if(length == ARC_USB_SPEED_HIGH) + mv_dev->gadget.speed = USB_SPEED_HIGH; + else + mv_dev->gadget.speed = USB_SPEED_FULL; + + return; +} + +void mv_usb_suspend_service(void* handle, + uint_8 type, + boolean setup, + uint_8 direction, + uint_8_ptr buffer, + uint_32 length, + uint_8 error) +{ + int dev_no = _usb_device_get_dev_num(handle); + struct mv_usb_dev *mv_dev = the_controllers[dev_no]; + + DBGMSG("%s\n", __FUNCTION__); + + if( (mv_dev->driver != NULL) && + (mv_dev->driver->suspend != NULL) ) + comp_driver_suspend(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver); +} + +void mv_usb_resume_service(void* handle, + uint_8 type, + boolean setup, + uint_8 direction, + uint_8_ptr buffer, + uint_32 length, + uint_8 error) +{ + int dev_no = _usb_device_get_dev_num(handle); + struct mv_usb_dev *mv_dev = the_controllers[dev_no]; + + DBGMSG("%s\n", __FUNCTION__); + + if( (mv_dev->driver != NULL) && + (mv_dev->driver->resume != NULL) ) + comp_driver_resume(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver); +} + +void mv_usb_tr_complete_service(void* handle, + uint_8 type, + boolean setup, + uint_8 direction, + uint_8_ptr buffer, + uint_32 length, + uint_8 error) +{ + int dev_no = _usb_device_get_dev_num(handle); + struct mv_usb_dev *mv_dev = the_controllers[dev_no]; + struct mv_usb_ep *mv_ep; + struct usb_request *usb_req; + int ep_num = (type*2) + direction; + unsigned long flags; + + DBGMSG("%s: ep_num=%d, setup=%s, direction=%s, pBuf=0x%x, length=%d, error=0x%x\n", + __FUNCTION__, type, setup ? "YES" : "NO", + (direction == ARC_USB_RECV) ? "RECV" : "SEND", + (unsigned)buffer, (int)length, error); + + mv_ep = &mv_dev->ep[ep_num]; + if( !list_empty(&mv_ep->req_list) ) + { + usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list); + if(usb_req->buf != buffer) + { + printk("ep=%d-%s: req=%p, Unexpected buffer pointer: %p, len=%d, expected=%p\n", + ep_num, (direction == ARC_USB_RECV) ? "out" : "in", + usb_req, buffer, length, usb_req->buf); + return; + } + + /* Dequeue request and call complete function */ + spin_lock_irqsave(&mv_ep->req_lock, flags); + list_del_init (&usb_req->list); + spin_unlock_irqrestore(&mv_ep->req_lock, flags); + mv_ep->processing = 0; + + usb_req->actual += length; + usb_req->status = error; + + usb_req->complete (&mv_ep->ep, usb_req); + } + else + mvOsPrintf("ep=%p, epName=%s, epNum=%d - reqList EMPTY\n", + mv_ep, mv_ep->ep.name, mv_ep->num); + + if ((mv_dev->in_single && mv_ep->is_in) || + (mv_dev->out_single && !mv_ep->is_in)) + mv_usb_start_transfer(mv_dev, mv_ep); +} + +unsigned set_config_flag = 0; + +void mv_usb_ep0_complete_service(void* handle, + uint_8 type, + boolean setup, + uint_8 direction, + uint_8_ptr buffer, + uint_32 length, + uint_8 error) +{ /* Body */ + int dev_no = _usb_device_get_dev_num(handle); + struct mv_usb_dev *mv_dev = the_controllers[dev_no]; + struct mv_usb_ep *mv_ep; + struct usb_request* usb_req; + int rc; + boolean is_delegate = FALSE; + static SETUP_STRUCT mv_ctrl_req; + + DBGMSG("%s: EP0(%d), setup=%s, direction=%s, pBuf=0x%x, length=%d, error=0x%x\n", + __FUNCTION__, type, setup ? "YES" : "NO", + (direction == ARC_USB_RECV) ? "RECV" : "SEND", + (unsigned)buffer, (int)length, error); + + mv_ep = &mv_dev->ep[type]; + + if (setup) + { + _usb_device_read_setup_data(handle, type, (u8 *)&mv_ctrl_req); + le16_to_cpus (&mv_ctrl_req.VALUE); + le16_to_cpus (&mv_ctrl_req.INDEX); + le16_to_cpus (&mv_ctrl_req.LENGTH); + + while(_usb_device_get_transfer_status(handle, mv_ep->num, + ARC_USB_SEND) != ARC_USB_STATUS_IDLE) + { + _usb_device_cancel_transfer(mv_dev->mv_usb_handle, mv_ep->num, + ARC_USB_SEND); + } + while(_usb_device_get_transfer_status(handle, mv_ep->num, + ARC_USB_RECV) != ARC_USB_STATUS_IDLE) + { + _usb_device_cancel_transfer(mv_dev->mv_usb_handle, mv_ep->num, + ARC_USB_RECV); + } + /* make sure any leftover request state is cleared */ + while (!list_empty (&mv_ep->req_list)) + { + usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list); + + /* Dequeue request and call complete function */ + list_del_init (&usb_req->list); + mv_ep->processing = 0; + + if (usb_req->status == -EINPROGRESS) + usb_req->status = -EPROTO; + + usb_req->complete (&mv_ep->ep, usb_req); + } + } + /* Setup request direction */ + mv_ep->is_in = (mv_ctrl_req.REQUESTTYPE & REQ_DIR_IN) != 0; + + if(setup) + DBGMSG("*** Setup ***: dir=%s, reqType=0x%x, req=0x%x, value=0x%02x, index=0x%02x, length=0x%02x\n", + (direction == ARC_USB_SEND) ? "In" : "Out", + mv_ctrl_req.REQUESTTYPE, mv_ctrl_req.REQUEST, mv_ctrl_req.VALUE, + mv_ctrl_req.INDEX, mv_ctrl_req.LENGTH); + + /* Handle most lowlevel requests; + * everything else goes uplevel to the gadget code. + */ + if( (mv_ctrl_req.REQUESTTYPE & REQ_TYPE_MASK) == REQ_TYPE_STANDARD) + { + switch (mv_ctrl_req.REQUEST) + { + case REQ_GET_STATUS: + mvUsbCh9GetStatus(handle, setup, &mv_ctrl_req); + break; + + case REQ_CLEAR_FEATURE: + mvUsbCh9ClearFeature(handle, setup, &mv_ctrl_req); + break; + + case REQ_SET_FEATURE: + mvUsbCh9SetFeature(handle, setup, &mv_ctrl_req); + break; + + case REQ_SET_ADDRESS: + mvUsbCh9SetAddress(handle, setup, &mv_ctrl_req); + break; + + default: + /* All others delegate call up-layer gadget code */ + is_delegate = TRUE; + } + } + else + is_delegate = TRUE; + + /* delegate call up-layer gadget code */ + if(is_delegate) + { + if(setup) + { + mv_ep->ack_recv = 0; + mv_ep->ack_sent = 0; + + /* when only one driver is registered, do as original */ + if (mv_dev->cdev.driver_count == 1) + rc = mv_dev->driver->setup (&mv_dev->gadget, + (struct usb_ctrlrequest*)&mv_ctrl_req); + else if (mv_dev->cdev.driver_count <= 0) { + pr_err("%s, error: dev->cdev.driver_count = %d\n", + __FUNCTION__, mv_dev->cdev.driver_count); + return; + } else { + + if ((mv_ctrl_req.REQUESTTYPE & REQ_TYPE_MASK) == REQ_TYPE_STANDARD) { + switch (mv_ctrl_req.REQUEST) + { + case REQ_SET_CONFIGURATION: + mv_dev->cdev.configuration = mv_ctrl_req.VALUE; + comp_print("----------------set_configuration %d\n", + mv_ctrl_req.VALUE); + rc = comp_change_config(&mv_dev->cdev, &mv_dev->gadget, + mv_dev->driver, (struct usb_ctrlrequest *) &mv_ctrl_req); + is_delegate = FALSE; + + break; + + case REQ_SET_INTERFACE: + mv_dev->cdev.interface = mv_ctrl_req.INDEX; + mv_dev->cdev.alternate = mv_ctrl_req.VALUE; + comp_print("----------------set_intf %d alt %d\n", + mv_ctrl_req.INDEX, mv_ctrl_req.VALUE); + comp_change_interface(&mv_dev->cdev, mv_ctrl_req.INDEX, + (struct usb_ctrlrequest *) &mv_ctrl_req, &mv_dev->gadget, + mv_dev->driver, &rc); + printk("rc = %d\n", rc); + is_delegate = FALSE; + break; + } + } + if (is_delegate) + rc = comp_ep0_req(&mv_dev->cdev, &mv_dev->gadget, + &mv_ep->ep, (struct usb_ctrlrequest *) &mv_ctrl_req); + } + + if (is_set_conf(&mv_ctrl_req)) { + schedule_work(&mv_dev->work); + } + + if(rc < 0) + { + mvOsPrintf("Setup is failed: rc=%d, req=0x%02x, reqType=0x%x, value=0x%04x, index=0x%04x\n", + rc, mv_ctrl_req.REQUEST, mv_ctrl_req.REQUESTTYPE, + mv_ctrl_req.VALUE, mv_ctrl_req.INDEX); + _usb_device_stall_endpoint(handle, 0, ARC_USB_RECV); + return; + } + /* Acknowledge */ + if( mv_ep->is_in ) { + mv_ep->ack_recv = 1; + _usb_device_recv_data(handle, 0, NULL, 0); + } + else if( mv_ctrl_req.LENGTH ) { + mv_ep->ack_sent = 1; + _usb_device_send_data(handle, 0, NULL, 0); + } + } + } + + if(!setup) + { + int is_ack = 0; + if( mv_ep->ack_sent && (direction == ARC_USB_SEND) ) { + mv_ep->ack_sent = 0; + is_ack = 1; + } + if( mv_ep->ack_recv && (direction != ARC_USB_SEND) ) { + mv_ep->ack_recv = 0; + is_ack = 1; + } + + if( !list_empty(&mv_ep->req_list) && !is_ack) + { + usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list); + + /* Dequeue request and call complete function */ + list_del_init (&usb_req->list); + mv_ep->processing = 0; + + usb_req->actual = length; + usb_req->status = error; + usb_req->complete (&mv_ep->ep, usb_req); + } + DBGMSG("Setup complete: dir=%s, is_in=%d, length=%d, is_ack=%d\n", + (direction == ARC_USB_SEND) ? "In" : "Out", + mv_ep->is_in, length, is_ack); + } +} + +#ifdef MV_USB_VOLTAGE_FIX + +static irqreturn_t mv_usb_vbus_irq (int irq, void *_dev) +{ + struct mv_usb_dev *mv_dev = _dev; + int vbus_change; + + vbus_change = mvUsbBackVoltageUpdate(mv_dev->dev_no, (int)mv_dev->vbus_gpp_no); + if(vbus_change == 2) + { + if( (mv_dev->gadget.speed != USB_SPEED_UNKNOWN) && + (mv_dev->driver != NULL) && + (mv_dev->driver->disconnect != NULL) ) + mv_dev->driver->disconnect (&mv_dev->gadget); + } + + return IRQ_HANDLED; +} +#endif /* MV_USB_VOLTAGE_FIX */ + +static irqreturn_t mv_usb_dev_irq (int irq, void *_dev) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + +#ifdef CONFIG_USB_OTG + unsigned base = mv_dev->regbase; + u32 status = (u2o_get(base, U2xOTGSC) & U2xOTGSC_IS_MASK) & \ + ((u2o_get(base, U2xOTGSC) & U2xOTGSC_IE_MASK)>>8); + if (!mv_dev->transceiver) + goto client_handle; + + if (status) { + pr_debug("OTGSC %x\n", u2o_get(base, U2xOTGSC)); + otg_interrupt(mv_dev->transceiver); + } + + if (!otg_is_client()) + { + return IRQ_HANDLED; + } + +client_handle: +#endif + + spin_lock (&mv_dev->lock); + + /* handle ARC USB Device interrupts */ + _usb_dci_vusb20_isr(mv_dev->mv_usb_handle); + + spin_unlock (&mv_dev->lock); + + return IRQ_HANDLED; +} + +static int mv_usb_ioctl(struct usb_gadget *g, + unsigned code, unsigned long param) +{ + struct mv_usb_dev *dev = the_controllers[0]; + int irq = IRQ_USB_CTRL[0]; + + switch (code) { + + case USB_GADGET_POLLING: + /* poll controller irq, used by net console, etc. */ + disable_irq(irq); + mv_usb_dev_irq(irq, dev); + enable_irq(irq); + break; + default: + printk("%s unknown ioctl %d\n", __func__, code); + break; + } + + return 0; +} + +/* when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests. then usb traffic follows until a + * disconnect is reported. then a host may connect again, or + * the driver might get unbound. + */ +void ep_list_update(struct mv_usb_dev *mv_dev, int restore); +int usb_gadget_register_driver (struct usb_gadget_driver *driver) +{ + int retval = 0, dev_no; + struct mv_usb_dev *mv_dev = NULL; + uint_8 error = 0; + + mvOsPrintf("ENTER usb_gadget_register_driver: \n"); + + /* Find USB Gadget device controller */ + for(dev_no=0; dev_nospeed != USB_SPEED_HIGH) + || !driver->bind +// || !driver->unbind + || !driver->setup) + { + mvOsPrintf("ERROR: speed=%d, bind=%p, unbind=%p, setup=%p\n", + driver->speed, driver->bind, driver->unbind, driver->setup); + return -EINVAL; + } + + if (!mv_dev) + { + mvOsPrintf("ERROR: max_dev=%d, mv_dev=%p\n", mvCtrlUsbMaxGet(), mv_dev); + return -ENODEV; + } + + if (comp_is_dev_busy(&mv_dev->cdev, mv_dev->driver)) + { + mvOsPrintf("ERROR: driver=%p is busy\n", mv_dev->driver); + return -EBUSY; + } + + mvOsPrintf("usb_gadget_register_driver: dev=%d, mv_dev=%p, pDriver=%p\n", + dev_no, mv_dev, driver); + +#ifdef CONFIG_PLAT_PXA + mv_clock_enable(mv_dev, 1); +#endif + + /* composite preparation */ + ep_list_update(mv_dev, 0); + local_irq_disable(); + _usb_device_stop(mv_dev->mv_usb_handle); + stop_gadget(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver); + local_irq_enable(); + + /* first hook up the driver ... */ + mv_dev->driver = driver; + mv_dev->gadget.dev.driver = &driver->driver; + + if (mv_dev->cdev.driver_count == 0) { + retval = device_add (&mv_dev->gadget.dev); + if (retval) { + mv_dev->driver = NULL; + mv_dev->gadget.dev.driver = NULL; + return retval; + } + } + + retval = driver->bind (&mv_dev->gadget); + if (retval) { + mvOsPrintf("bind to driver %s --> %d\n", + driver->driver.name, retval); + mv_dev->driver = 0; + mv_dev->gadget.dev.driver = 0; + return retval; + } + + /* get information for composite device */ + comp_register_driver(&mv_dev->cdev, &mv_dev->gadget, driver); + mv_dev->cdev.driver_count++; + +#ifdef CONFIG_USB_OTG + if (mv_dev->transceiver) + otg_set_peripheral(mv_dev->transceiver, &mv_dev->gadget); +#endif + + if (mv_dev->cdev.driver_count == 1) { + /* request_irq */ + if (request_irq (IRQ_USB_CTRL[dev_no], mv_usb_dev_irq, IRQF_DISABLED | IRQF_SHARED, + driver_name, mv_dev) != 0) + { + mvOsPrintf("%s register: request interrupt %d failed\n", + driver->driver.name, IRQ_USB_CTRL[dev_no]); + return -EBUSY; + } + +#ifdef CONFIG_PLAT_PXA +#if defined(ENABLE_CABLE_DETECT) + mv_dev->info->vbus_detect(mv_usb_vbus_change, 1); +#else + _usb_device_start(mv_dev->mv_usb_handle); + mv_usb_start_ep0(mv_dev); +#endif +#endif + } + +#if defined(ENABLE_CABLE_DETECT) || defined(CONFIG_USB_OTG) + if (mv_dev->info->clk_gating) + mv_clock_enable(mv_dev, 0); + if (is_cable_attached() || !mv_dev->info->clk_gating) { + mv_usb_connect_change(VBUS_HIGH); + } +#endif + + mvOsPrintf("registered Marvell USB-%d gadget driver %s\n", dev_no, driver->driver.name); + return error; +} +EXPORT_SYMBOL (usb_gadget_register_driver); + +int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) +{ + int i, dev_no; + struct mv_usb_ep *mv_ep; + struct mv_usb_dev *mv_dev = NULL; + unsigned long flags = 0; + + /* Find USB Gadget device controller */ + for(dev_no=0; dev_nodriver == driver) ) +#else + if (the_controllers[dev_no] != NULL) +#endif + { + mv_dev = the_controllers[dev_no]; + break; + } + } + + if (!mv_dev) + { + mvOsPrintf("usb_gadget_unregister_driver FAILED: no such device\n"); + return -ENODEV; + } + + if (!comp_check_driver(&mv_dev->cdev, mv_dev->driver, driver)) + { + mvOsPrintf("usb_gadget_unregister_driver FAILED: no such driver, dev_no=%d\n", dev_no); + return -EINVAL; + } + +#if defined(ENABLE_CABLE_DETECT) || defined(CONFIG_USB_OTG) + if (is_cable_attached()) { + mv_usb_connect_change(0); + } +#endif +#ifdef CONFIG_PLAT_PXA + del_timer(&mv_dev->timer); + mv_clock_enable(mv_dev, 1); +#endif + /* Stop and Disable ARC USB device */ + MV_SPIN_LOCK_IRQSAVE(&mv_dev->lock, flags); + +#ifdef CONFIG_USB_OTG + if (mv_dev->transceiver && (mv_dev->cdev.driver_count == 1)) { + otg_set_peripheral(mv_dev->transceiver, NULL); + } +#endif + + /* Stop Endpoints */ + for (i=0; i<2*(ARC_USB_MAX_ENDPOINTS); i++) + { + mv_ep = &mv_dev->ep[i]; +#ifdef CONFIG_USB_COMPOSITE + mv_ep->assigned = 0; +#endif + if(mv_ep->is_enabled == 0) + continue; + + mv_ep->is_enabled = 0; + mv_usb_ep_cancel_all_req(mv_ep); + + if (!rely_on_vbus || connected) + _usb_device_deinit_endpoint(mv_dev->mv_usb_handle, + mv_ep->num, mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV); + } + MV_SPIN_UNLOCK_IRQRESTORE(&mv_dev->lock, flags); + + local_irq_disable(); + if (!rely_on_vbus || connected) + _usb_device_stop(mv_dev->mv_usb_handle); + stop_cur_gadget(&mv_dev->cdev, &mv_dev->gadget, driver); + local_irq_enable(); + + driver->unbind (&mv_dev->gadget); + + mv_dev->cdev.driver_count--; + comp_unregister_driver(&mv_dev->cdev, &mv_dev->gadget, + &mv_dev->driver, driver); + + if (mv_dev->cdev.driver_count != 0) + goto out; + + /* free_irq */ + free_irq (IRQ_USB_CTRL[dev_no], mv_dev); + + mv_dev->gadget.dev.driver = 0; + mv_dev->driver = 0; + device_del (&mv_dev->gadget.dev); + + mv_dev->gadget.speed = USB_SPEED_UNKNOWN; + + /* Reinit all endpoints */ + mv_usb_reinit(mv_dev); +#ifdef CONFIG_PLAT_PXA +#if defined(ENABLE_CABLE_DETECT) + mv_dev->info->vbus_detect(mv_usb_vbus_change, 0); + mv_clock_enable(mv_dev, 0); +#endif +#endif + +out: + /*device_remove_file(dev->dev, &dev_attr_function); ?????*/ + mvOsPrintf("unregistered Marvell USB %d gadget driver %s\n", dev_no, driver->driver.name); + + return 0; +} +EXPORT_SYMBOL (usb_gadget_unregister_driver); + +static void _usb_ep_req(struct mv_usb_dev* mv_dev, int num) +{ + struct mv_usb_ep *ep = &mv_dev->ep[num]; + struct usb_request *usb_req; + + printk("ep %d requests:\n", num); + list_for_each_entry(usb_req, &ep->req_list, list) { + printk("req (0x%p): buf %p len %d\n", usb_req, usb_req->buf, usb_req->length); + } + printk("\n"); +} + +void mv_usb_show(struct mv_usb_dev* mv_dev, unsigned int mode) +{ + int i; + + if( MV_BIT_CHECK(mode, 0) ) + _usb_regs(mv_dev->mv_usb_handle); + + if( MV_BIT_CHECK(mode, 1) ) + _usb_status(mv_dev->mv_usb_handle); + + if( MV_BIT_CHECK(mode, 2) ) + _usb_stats(mv_dev->mv_usb_handle); + + if( MV_BIT_CHECK(mode, 3) ) + _usb_debug_print_trace_log(); + + for(i=0; iep[i*2].assigned) { + recv = 1; + } + if (mv_dev->ep[i*2+1].assigned) { + send = 1; + } + } +#endif + if( MV_BIT_CHECK(mode, (8+i)) ) + { + if(send) { + _usb_ep_status(mv_dev->mv_usb_handle, i, ARC_USB_SEND); + _usb_ep_req(mv_dev, i); + } + if(recv) { + _usb_ep_status(mv_dev->mv_usb_handle, i, ARC_USB_RECV); + _usb_ep_req(mv_dev, i); + } + } + } +} +EXPORT_SYMBOL (mv_usb_show); + +static int mv_usb_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct mv_usb_dev* usb_dev; + struct mv_usb_ep* usb_ep; + uint_16 maxSize; + uint_8 epType; + unsigned long flags = 0; + + usb_ep = container_of (_ep, struct mv_usb_ep, ep); + if( (_ep == NULL) || (desc == NULL) ) + return -EINVAL; + + usb_dev = usb_ep->usb_dev; + + mvOsPrintf("USB Enable %s: type=%d, maxPktSize=%d\n", + _ep->name, desc->bmAttributes & 0x3, desc->wMaxPacketSize); + + if(usb_ep->num == 0) + { + mvOsPrintf("mv_usb: ep0 is reserved\n"); + return -EINVAL; + } + + if(desc->bDescriptorType != USB_DT_ENDPOINT) + { + mvOsPrintf("mv_usb: wrong descriptor type %d\n", desc->bDescriptorType); + return -EINVAL; + } + + if(usb_ep->is_enabled) + { + mvOsPrintf("mv_usb: %d%s Endpoint (%s) is already in use\n", + usb_ep->num, usb_ep->is_in ? "In" : "Out", usb_ep->ep.name); + return 0; + } + + MV_SPIN_LOCK_IRQSAVE(&usb_dev->lock, flags); + + usb_dev = usb_ep->usb_dev; + if( (usb_dev->driver == NULL) || + (usb_dev->gadget.speed == USB_SPEED_UNKNOWN) ) + { + MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags); + return -ESHUTDOWN; + } + /* Max packet size */ + maxSize = le16_to_cpu (desc->wMaxPacketSize); + + /* Endpoint type */ + if( (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL) + epType = ARC_USB_CONTROL_ENDPOINT; + else if( (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC) + epType = ARC_USB_ISOCHRONOUS_ENDPOINT; + else if( (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) + epType = ARC_USB_BULK_ENDPOINT; + else + epType = ARC_USB_INTERRUPT_ENDPOINT; + + _ep->maxpacket = maxSize & 0x7ff; + usb_ep->is_enabled = 1; + + _usb_device_init_endpoint(usb_dev->mv_usb_handle, usb_ep->num, maxSize, + usb_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV, epType, + (epType == ARC_USB_BULK_ENDPOINT) ? ARC_USB_DEVICE_DONT_ZERO_TERMINATE : 0); + + MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags); + return 0; +} + +static int mv_usb_ep_disable (struct usb_ep *_ep) +{ + struct mv_usb_dev* mv_dev; + struct mv_usb_ep* mv_ep; + unsigned long flags = 0; + uint_8 direction; + + mv_ep = container_of (_ep, struct mv_usb_ep, ep); + if( (_ep == NULL) || (mv_ep->is_enabled == 0) || (mv_ep->num == 0)) + return -EINVAL; + + mv_dev = mv_ep->usb_dev; + + mvOsPrintf("mv_usb_ep_disable: mv_dev=%p, ep=0x%x (%d-%s), name=%s\n", + mv_dev, (unsigned)_ep, mv_ep->num, mv_ep->is_in ? "in" : "out", + _ep->name); + + MV_SPIN_LOCK_IRQSAVE(&mv_dev->lock, flags); + + direction = mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV; + + mv_ep->is_enabled = 0; + + /* Cancell all requests */ + mv_usb_ep_cancel_all_req(mv_ep); + + if (!rely_on_vbus || connected) + /* Disable endpoint */ + _usb_device_deinit_endpoint(mv_dev->mv_usb_handle, + mv_ep->num, direction); + + MV_SPIN_UNLOCK_IRQRESTORE(&mv_dev->lock, flags); + return 0; +} + + +static struct usb_request* mv_usb_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct usb_request* req; + + if (!_ep) + return NULL; + + req = kmalloc (sizeof *req, gfp_flags); + if (!req) + return NULL; + + memset (req, 0, sizeof *req); + INIT_LIST_HEAD (&req->list); + + return req; +} + +static void mv_usb_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + + if (!_ep || !_req) + { + mvOsPrintf("ep_free_request Error: _ep=%p, _req=%p\n", _ep, _req); + return; + } + + kfree (_req); + mvOsPrintf("ep_kfree_request\n"); +} + +/*************************** composite **************************************/ + +#ifdef CONFIG_USB_COMPOSITE + +void ep_list_update(struct mv_usb_dev *mv_dev, int restore) +{ + int i; + + if (restore) { + mv_usb_reinit(mv_dev); + return; + } + + for (i=1;i<2*ARC_USB_MAX_ENDPOINTS;i++) { + if (mv_dev->ep[i].assigned) { + comp_print("\t\tep %d is_in %d, list del\n", + i, mv_dev->ep[i].is_in); + list_del_init(&mv_dev->ep[i].ep.ep_list); + } + } +} + +int stop_udc(struct usb_gadget_driver *driver) +{ + struct mv_usb_dev *dev = the_controllers[0]; + +#ifndef CONFIG_USB_COMPOSITE + /* don't disconnect drivers more than once */ + if (dev->gadget.speed == USB_SPEED_UNKNOWN) + driver = 0; +#endif + dev->gadget.speed = USB_SPEED_UNKNOWN; + return 0; +} + +/* + * udc_reinit - initialize software state + */ +void udc_reinit(void) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + /* device/ep0 records init */ + dev->cdev.ep0state = EP0_IDLE; + + dev->cdev.configuration = 0; + dev->cdev.interface = 0; + dev->cdev.alternate = 0; +} + + +void comp_do_fake_req(struct mv_usb_dev *dev, + struct mv_usb_ep *mv_ep, struct usb_request *req) +{ + comp_print("%s: in EP0_IN_FAKE\n", __func__); + dev->cdev.config_length = req->length; + get_fake_config(&dev->cdev, req, dev->gadget.speed); + dev->cdev.ep0state = EP0_IDLE; + req->actual = req->length; + //done(ep, req, 0); + + + /* from mv_usb_ep0_complete_service */ + /* make sure any leftover request state is cleared */ + while (!list_empty (&mv_ep->req_list)) + { + struct usb_request *usb_req; + + usb_req = list_entry (mv_ep->req_list.next, struct usb_request, list); + + /* Dequeue request and call complete function */ + list_del_init (&req->list); + mv_ep->processing = 0; + + if (req->status == -EINPROGRESS) + req->status = -EPROTO; + + req->complete (&mv_ep->ep, req); + } + +} + +static void set_ep(struct usb_endpoint_descriptor *ep_desc, int ep_num, + int config, int interface, int alt) +{ + struct mv_usb_dev *dev = the_controllers[0]; + int k = ep_num; + + +/* dev->ep[k].desc = ep_desc; + dev->ep[k].assigned = 1; + if (!(ep_desc->wMaxPacketSize)) + ep_desc->wMaxPacketSize = dev->ep[k].ep.maxpacket; + else + dev->ep[k].ep.maxpacket = ep_desc->wMaxPacketSize; + dev->ep[k].dir_in = (ep_desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; + dev->ep[k].ep_type = ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + dev->ep[k].stopped = 1; + dev->ep[k].aisn = alt;*/ + dev->ep[k].assigned = 1; + comp_set_ep(&dev->cdev, k, config, interface); +} + + +/* set_eps + * assign a physical ep to logic one, and + * fill pxa_ep structure with their configuration, interface, alternate + * settings, assigned interface number + */ +int set_eps(__u8 num_eps, struct usb_endpoint_descriptor *p_ep_desc, int len, + int config, int interface, int alt) +{ + struct usb_endpoint_descriptor *ep_desc = p_ep_desc; + struct mv_usb_dev *dev = the_controllers[0]; + int ep_desc_length = len; + int j, k, ret; + + comp_print(" ----%s----\n", __FUNCTION__); + for (j = 0; j < num_eps; j++) { + /* find the ep */ + ret = get_extra_descriptor((char *)p_ep_desc, ep_desc_length, + USB_DT_ENDPOINT, (void **) &ep_desc); + if (ret >= 0) { + /* compare with the ep in controller, if match, + * fill the config, interface and asin number fields */ + for (k = 2; k < 2*ARC_USB_MAX_ENDPOINTS; k++) { + comp_print("search ep 0x%x addr 0x%x is_in %d\n", k/2, + ep_desc->bEndpointAddress, dev->ep[k].is_in); + if (((ep_desc->bEndpointAddress>>7) == dev->ep[k].is_in) \ + && ((k/2) == (ep_desc->bEndpointAddress & 0x0f))) { + comp_print("found match at %d\n", k); + list_del_init (&dev->ep[k].ep.ep_list); + set_ep(ep_desc, k, config, interface, + alt); + break; + } + } + } else { + comp_print(" ep desc not find, ep_desc_length=0x%x," + " p_ep_desc=0x%x\n", + ep_desc_length, (int)p_ep_desc); + return -EFAULT; + } + + ep_desc_length -= (int)ep_desc - (int)p_ep_desc + + ep_desc->bLength; + p_ep_desc = (struct usb_endpoint_descriptor *) + ((unsigned)ep_desc + ep_desc->bLength); + }/* for(j=0;jusb_dev; + +#ifdef CONFIG_USB_COMPOSITE + if (usb_dev->cdev.ep0state == EP0_IN_FAKE) { + comp_print("USB FAKE request for composite\n"); + comp_do_fake_req(usb_dev, usb_ep, _req); + return 0; + } +#endif + + if ( (usb_dev->driver == NULL) || (usb_dev->gadget.speed == USB_SPEED_UNKNOWN) ) + return -ESHUTDOWN; + + if(usb_ep->is_enabled == 0) + { + printk("ep_queue Failed - %s is disabled: usb_ep=%p\n", _ep->name, usb_ep); + return -EINVAL; + } + + DBGMSG("%s: num=%d-%s, name=%s, _req=%p, buf=%p, length=%d\n", + __FUNCTION__, usb_ep->num, usb_ep->is_in ? "in" : "out", + _ep->name, _req, _req->buf, _req->length); + + MV_SPIN_LOCK_IRQSAVE(&usb_dev->lock, flags); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if ((usb_dev->in_single && usb_ep->is_in) || + (usb_dev->out_single && !usb_ep->is_in)) { + is_first_req = list_empty(&usb_ep->req_list); + DBGMSG("queue req %p(first=%s), len %d buf %p\n", _req, + is_first_req ? "yes" : "no", _req->length, _req->buf); + + spin_lock_irqsave(&usb_ep->req_lock, req_flags); + list_add_tail(&_req->list, &usb_ep->req_list); + spin_unlock_irqrestore(&usb_ep->req_lock, req_flags); + if (is_first_req) + error = mv_usb_start_transfer(usb_dev, usb_ep); + + goto out; + } + + /* Add request to list */ + if( ((usb_ep->num == 0) && (_req->length == 0)) || (usb_ep->is_in) ) + { + spin_lock_irqsave(&usb_ep->req_lock, req_flags); + list_add_tail(&_req->list, &usb_ep->req_list); + spin_unlock_irqrestore(&usb_ep->req_lock, req_flags); + + error = _usb_device_send_data(usb_dev->mv_usb_handle, usb_ep->num, _req->buf, _req->length); + if(error != USB_OK) + { + printk("ep_queue: Can't SEND data (err=%d): ep_num=%d, pBuf=0x%x, send_size=%d\n", + error, usb_ep->num, (unsigned)_req->buf, _req->length); + list_del_init (&_req->list); + } + } + else + { + error = _usb_device_recv_data(usb_dev->mv_usb_handle, usb_ep->num, _req->buf, _req->length); + if(error != USB_OK) + { + printk("mv_usb_ep_queue: Can't RCV data (err=%d): ep_num=%d, pBuf=0x%x, size=%d\n", + error, usb_ep->num, (unsigned)_req->buf, _req->length); + } + else { + spin_lock_irqsave(&usb_ep->req_lock, req_flags); + list_add_tail(&_req->list, &usb_ep->req_list); + spin_unlock_irqrestore(&usb_ep->req_lock, req_flags); + } + } + +out: + DBGMSG("%s end\n\n", __func__); + MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags); + + return (int)error; +} + +/* Cancell request */ +static int mv_usb_ep_dequeue (struct usb_ep *_ep, struct usb_request *_req) +{ + struct mv_usb_dev* usb_dev; + struct usb_request *usb_req; + struct mv_usb_ep* usb_ep; + unsigned long flags = 0; + int status = 0; + + usb_ep = container_of (_ep, struct mv_usb_ep, ep); + /* check parameters */ + if( (_ep == NULL) || (_req == NULL) || (usb_ep->is_enabled == 0) ) + return -EINVAL; + + usb_dev = usb_ep->usb_dev; + + if ( (usb_dev->driver == NULL) || (usb_dev->gadget.speed == USB_SPEED_UNKNOWN) ) + { + mvOsPrintf("mv_usb_ep_dequeue: ep=0x%x, num=%d-%s, name=%s, driver=0x%x, speed=%d\n", + (unsigned)_ep, usb_ep->num, usb_ep->is_in ? "in" : "out", + _ep->name, (unsigned)usb_dev->driver, usb_dev->gadget.speed); + + return -ESHUTDOWN; + } + + MV_SPIN_LOCK_IRQSAVE(&usb_dev->lock, flags); + + /* ????? Currently supported only dequeue request from the HEAD of List */ + if( !list_empty(&usb_ep->req_list) ) + { + usb_req = list_entry (usb_ep->req_list.next, struct usb_request, list); + + if(usb_req == _req) + { + /* Cancel transfer */ + _usb_device_cancel_transfer(usb_dev->mv_usb_handle, usb_ep->num, + usb_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV); + /* Dequeue request and call complete function */ + list_del_init (&_req->list); + usb_ep->processing = 0; + + if (_req->status == -EINPROGRESS) + _req->status = -ECONNRESET; + + /* ????? what about enable interrupts */ + _req->complete (&usb_ep->ep, _req); + } + else + { + mvOsPrintf("Cancell request failed: ep=%p, usb_req=%p, req=%p\n", + _ep, usb_req, _req); + status = EINVAL; + } + } + else + mvOsPrintf("%s: ep=%p, epName=%s, epNum=%d - reqList EMPTY\n", + __FUNCTION__, usb_ep, usb_ep->ep.name, usb_ep->num); + + MV_SPIN_UNLOCK_IRQRESTORE(&usb_dev->lock, flags); + + return status; +} + +static int mv_usb_ep_set_halt (struct usb_ep *_ep, int value) +{ + struct mv_usb_ep* mv_ep; + unsigned long flags = 0; + int retval = 0; + + mv_ep = container_of (_ep, struct mv_usb_ep, ep); + if (_ep == NULL) + return -EINVAL; + if( (mv_ep->usb_dev->driver == NULL) || + (mv_ep->usb_dev->gadget.speed == USB_SPEED_UNKNOWN) ) + return -ESHUTDOWN; + + mvOsPrintf("%s - %s \n", + _ep->name, value ? "Stalled" : "Unstalled"); + + MV_SPIN_LOCK_IRQSAVE(&mv_ep->usb_dev->lock, flags); + if (!list_empty (&mv_ep->req_list)) + retval = -EAGAIN; + else + { + /* set/clear, then synch memory views with the device */ + if (value) + { + if (mv_ep->num == 0) + mv_ep->usb_dev->protocol_stall = 1; + else + _usb_device_stall_endpoint(mv_ep->usb_dev->mv_usb_handle, mv_ep->num, + mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV); + } + else + { + _usb_device_unstall_endpoint(mv_ep->usb_dev->mv_usb_handle, mv_ep->num, + mv_ep->is_in ? ARC_USB_SEND : ARC_USB_RECV); + } + } + MV_SPIN_UNLOCK_IRQRESTORE(&mv_ep->usb_dev->lock, flags); + + return retval; +} + + +static void mv_usb_ep_fifo_flush (struct usb_ep *_ep) +{ + DBGMSG("%s: ep=%p, ep_name=%s - NOT supported\n", __FUNCTION__, _ep, _ep->name); +} + +static struct usb_ep_ops mv_usb_ep_ops = +{ + .enable = mv_usb_ep_enable, + .disable = mv_usb_ep_disable, + + .alloc_request = mv_usb_ep_alloc_request, + .free_request = mv_usb_ep_free_request, + + .queue = mv_usb_ep_queue, + .dequeue = mv_usb_ep_dequeue, + + .set_halt = mv_usb_ep_set_halt, + .fifo_flush = mv_usb_ep_fifo_flush, + /*.fifo_status = Not supported */ +}; + +static int mv_usb_get_frame (struct usb_gadget *_gadget) +{ + DBGMSG("Call mv_usb_get_frame - NOT supported\n"); + return 0; +} + +static int mv_usb_wakeup(struct usb_gadget *_gadget) +{ + DBGMSG("Call mv_usb_wakeup - NOT supported\n"); + return 0; +} + +static int mv_usb_set_selfpowered (struct usb_gadget *_gadget, int value) +{ + DBGMSG("Call mv_usb_set_selfpowered - NOT supported\n"); + return 0; +} + +static int mv_usb_vbus_session (struct usb_gadget *_gadget, int is_active) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + + if (rely_on_vbus && !connected) + return 0; + + if (is_active) { + udc_stop(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver, 0); + } else { + udc_stop(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver, 1); + } + return 0; +} + +static int mv_usb_pullup (struct usb_gadget *_gadget, int is_on) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + + if (rely_on_vbus && !connected) + return 0; + + pr_debug("%s is_on %d\n", __func__, is_on); + if (is_on) { + _usb_device_start(mv_dev->mv_usb_handle); + udc_stop(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver, 0); + } else { + udc_stop(&mv_dev->cdev, &mv_dev->gadget, mv_dev->driver, 1); + _usb_device_stop(mv_dev->mv_usb_handle); + } + + return 0; +} +static const struct usb_gadget_ops mv_usb_ops = +{ + .get_frame = mv_usb_get_frame, + .wakeup = mv_usb_wakeup, + .set_selfpowered = mv_usb_set_selfpowered, + .ioctl = mv_usb_ioctl, + .vbus_session = mv_usb_vbus_session, + .pullup = mv_usb_pullup, +}; + +static void mv_usb_gadget_release (struct device *_dev) +{ + struct mv_usb_dev *usb_dev = dev_get_drvdata (_dev); + + mvOsPrintf("Call mv_usb_gadget_release \n"); + mvOsFree(usb_dev); +} + +#if 0 +static void enable_phy(void) +{ + volatile unsigned tmp; +// PLL VCO and TX Impedance Calibration Timing: + +// PU __________|----------------------------------| +// VOCAL STAR ___________________|--|______________________| +// REG_RCAL_START ________________________________|--|_________| +// | 200us |40| 200us |40| 200us | USB PHY READY + +// ACCR1 = ACCR1 | ACCR1_PU_OTG | ACCR1_PU_PLL | ACCR1_PU; + + /* U2PPLL */ + tmp = MV_PHYREG_READ(MV_USB_PHY_PLL_CTRL_REG); + tmp &= ~(MV_USB_PHY_PLL_ICP_MASK | MV_USB_PHY_PLL_KVCO_MASK); + MV_PHYREG_WRITE(MV_USB_PHY_PLL_CTRL_REG, tmp); + + tmp = MV_PHYREG_READ(MV_USB_PHY_PLL_CTRL_REG); + tmp |= (0x6<= mvCtrlUsbMaxGet()) + { + mvOsPrintf("mv_gadget_probe: device is not found\n"); + return -EINVAL; + } + mvOsPrintf("USB-%d Gadget driver probed\n", dev_no); + + if (the_controllers[dev_no]) + { + mvOsPrintf("mv_dev_load: USB-%d controller is BUSY\n", dev_no); + return -EBUSY; + } + + /* alloc, and start init */ + mv_dev = mvOsMalloc (sizeof(struct mv_usb_dev)); + if (mv_dev == NULL) + { + mvOsPrintf("mv_dev_load: malloc failed\n"); + return -ENOMEM; + } + + memset (mv_dev, 0, sizeof *mv_dev); + +#if defined(CONFIG_PXA3xx_DVFM) + dvfm_register("U2O", &dvfm_lock.dev_idx); +#endif + +#if defined(CONFIG_PLAT_PXA) + mv_dev->clk = clk_get(NULL, "U2OCLK"); + if (IS_ERR(mv_dev->clk)) { + retval = PTR_ERR(mv_dev->clk); + mvOsFree (mv_dev); + return -ENODEV; + } +#endif + + spin_lock_init (&mv_dev->lock); + mv_dev->dev = &_dev->dev; + mv_dev->gadget.ops = &mv_usb_ops; + mv_dev->gadget.is_dualspeed = 1; + + /* the "gadget" abstracts/virtualizes the controller */ + mv_dev->gadget.dev.init_name = "gadget"; + + device_initialize(&mv_dev->gadget.dev); + mv_dev->gadget.dev.parent = &_dev->dev; + mv_dev->gadget.dev.dma_mask = _dev->dev.dma_mask; + + mv_dev->gadget.dev.release = mv_usb_gadget_release ; + mv_dev->gadget.name = driver_name; + mv_dev->mv_usb_handle = NULL; + mv_dev->dev_no = dev_no; + + dev_set_drvdata(&_dev->dev, mv_dev); + the_controllers[dev_no] = mv_dev; + +#ifdef MV_USB_VOLTAGE_FIX + + mv_dev->vbus_gpp_no = mvUsbGppInit(dev_no); + mvOsPrintf("USB gadget device #%d: vbus_gpp_no = %d\n", + dev_no, mv_dev->vbus_gpp_no); + + if(mv_dev->vbus_gpp_no != (MV_U8)N_A) + { + if (request_irq (IRQ_GPP_START + mv_dev->vbus_gpp_no, mv_usb_vbus_irq, IRQF_DISABLED, + driver_name, mv_dev) != 0) + { + mvOsPrintf("%s probe: request interrupt %d failed\n", + driver_name, IRQ_GPP_START + mv_dev->vbus_gpp_no); + return -EBUSY; + } + } + +#endif /* MV_USB_VOLTAGE_FIX */ + + /* Reset ARC USB device ????? */ + /* Reinit ARC USB device ????? */ + + /* First of all. */ + _usb_device_set_bsp_funcs(&usbImportFuncs); + +#if defined(USB_UNDERRUN_WA) + + if(wa_sram_parts > USB_SRAM_MAX_PARTS) + { + mvOsPrintf("Wrong param (%d): Valid range [1 .. %d]\n", + wa_sram_parts, USB_SRAM_MAX_PARTS); + return -EINVAL; + } + + global_wa_funcs = &usbWaFuncs; + global_wa_threshold = wa_threshold; + global_wa_sram_parts = wa_sram_parts; + + sramBase = mv_sram_usage_get(&sramSize); + if(sramBase == NULL) + { + mvOsPrintf("USB Underrun WA: No free SRAM space\n"); + return -ENOMEM; + } + memset(sramBase, 0, sramSize); + + idma = mv_usb_find_idma_engine(idma); + if(idma != -1) + { + MV_REG_WRITE(IDMA_BYTE_COUNT_REG(idma), 0); + MV_REG_WRITE(IDMA_CURR_DESC_PTR_REG(idma), 0); + MV_REG_WRITE(IDMA_CTRL_HIGH_REG(idma), ICCHR_ENDIAN_LITTLE | ICCHR_DESC_BYTE_SWAP_EN); + MV_REG_WRITE(IDMA_CTRL_LOW_REG(idma), USB_IDMA_CTRL_LOW_VALUE); + } + mvOsPrintf("USB underrun WA: idma=%d, streaming=%d, threshold=%d, SRAM: base=%p, size=%d, parts=%d\n", + idma, streaming, global_wa_threshold, sramBase, sramSize, global_wa_sram_parts); +#endif /* USB_UNDERRUN_WA */ + + /*_usb_debug_set_flags(MV_USB_GADGET_DEBUG_FLAGS);*/ + +#if defined(CONFIG_PLAT_PXA) + + res = platform_get_resource_byname(_dev, IORESOURCE_MEM, "u2o"); + mv_dev->regbase = (unsigned int) ioremap_nocache(res->start, res_size(res)); + if (!mv_dev->regbase) { + printk(KERN_ERR "Cannot get regbase 0x%x\n", + mv_dev->regbase); + return -ENOMEM; + } + + res = platform_get_resource_byname(_dev, IORESOURCE_MEM, "u2ophy"); + mv_dev->phybase = (unsigned int) ioremap_nocache(res->start, res_size(res)); + if (!mv_dev->phybase) { + printk(KERN_ERR "Cannot get phybase 0x%x\n", + mv_dev->phybase); + return -ENODEV; + } + + IRQ_USB_CTRL[dev_no] = platform_get_irq(_dev, 0); + if (!IRQ_USB_CTRL[dev_no]) { + printk( KERN_ERR "Cannot get irq %x\n", + IRQ_USB_CTRL[dev_no]); + return -ENODEV; + } + printk("u2o regbase 0x%x phybase 0x%x irq %d\n", mv_dev->regbase, + mv_dev->phybase, IRQ_USB_CTRL[dev_no]); + + mv_dev->info = _dev->dev.platform_data; + mv_dev->info->regbase = mv_dev->regbase; + mv_dev->info->phybase = mv_dev->phybase; + /* FIXME some board has the limitation that can't access + * U2O if VBUS not valid, e.g. TTC DKB rev1.0 board */ + rely_on_vbus = mv_dev->info->rely_on_vbus; + if (rely_on_vbus) + mv_dev->info->clk_gating = 0; + + if (!mv_dev->info->vbus_detect) { + mv_dev->info->vbus_detect = mv_usb_vbus_detect; + mv_dev->info->clk_gating = 0; + } + if (!mv_dev->info->vbus_status) { + mv_dev->info->vbus_status = mv_usb_vbus_status; + mv_dev->info->clk_gating = 0; + } + mv_dev->in_single = mv_dev->info->in_single; + mv_dev->out_single = mv_dev->info->out_single; + mv_clock_enable(mv_dev, 1); +#endif + + /* Enable ARC USB device */ + retval = (int)_usb_device_init(dev_no, &mv_dev->mv_usb_handle); + if (retval != USB_OK) + { + mvOsPrintf("\nUSB Initialization failed. Error: %x", retval); + return -EINVAL; + } /* Endif */ + + /* Self Power, Remote wakeup disable */ + _usb_device_set_status(mv_dev->mv_usb_handle, ARC_USB_STATUS_DEVICE, (1 << DEVICE_SELF_POWERED)); + + /* Register all ARC Services */ + error = _usb_device_register_service(mv_dev->mv_usb_handle, + ARC_USB_SERVICE_BUS_RESET, mv_usb_bus_reset_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB BUS_RESET Service Registration failed. Error: 0x%x", error); + return -EINVAL; + } /* Endif */ + + error = _usb_device_register_service(mv_dev->mv_usb_handle, + ARC_USB_SERVICE_SPEED_DETECTION, mv_usb_speed_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB SPEED_DETECTION Service Registration failed. Error: 0x%x", + error); + return -EINVAL; + } /* Endif */ + + error = _usb_device_register_service(mv_dev->mv_usb_handle, + ARC_USB_SERVICE_SUSPEND, mv_usb_suspend_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB SUSPEND Service Registration failed. Error: 0x%x", error); + return -EINVAL; + } /* Endif */ + + error = _usb_device_register_service(mv_dev->mv_usb_handle, + ARC_USB_SERVICE_SLEEP, mv_usb_suspend_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB SUSPEND Service Registration failed. Error: 0x%x", error); + return -EINVAL; + } /* Endif */ + + error = _usb_device_register_service(mv_dev->mv_usb_handle, + ARC_USB_SERVICE_RESUME, mv_usb_resume_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB RESUME Service Registration failed. Error: 0x%x", error); + return -EINVAL; + } /* Endif */ + + error = _usb_device_register_service(mv_dev->mv_usb_handle, 0, + mv_usb_ep0_complete_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB ep0 TR_COMPLETE Service Registration failed. Error: 0x%x", error); + return error; + } /* Endif */ + + for (i=1; imv_usb_handle, i, + mv_usb_tr_complete_service); + if (error != USB_OK) + { + mvOsPrintf("\nUSB ep0 TR_COMPLETE Service Registration failed. Error: 0x%x", error); + return error; + } /* Endif */ + } + + mv_dev->gadget.speed = USB_SPEED_UNKNOWN; + + if( mv_usb_reinit (mv_dev) != USB_OK) + return -EINVAL; + +#if defined(CONFIG_PLAT_PXA) +#ifdef CONFIG_USB_OTG + if (mv_dev->info->is_otg) { + mv_dev->transceiver = otg_get_transceiver(); + mv_dev->cdev.transceiver = otg_get_transceiver(); + mv_dev->gadget.is_otg = 1; + if (!mv_dev->transceiver) + printk("failed to get otg transceiver\n"); + } else { + printk("no otg support yet\n"); + } +#else + vbus_detect_init(); +#endif + mv_clock_enable(mv_dev, 0); + INIT_WORK(&mv_dev->work, uevent_worker); + init_timer(&mv_dev->timer); + mv_dev->timer.function = mv_usb_clock_disable; +#endif + + return retval; +} + +static int __exit mv_usb_gadget_remove(struct device *_dev) +{ + int i; + struct mv_usb_dev *mv_dev = dev_get_drvdata(_dev); + + mvOsPrintf("mv_usb_gadget_remove: mv_dev=%p, driver=%p\n", + mv_dev, mv_dev->driver); + /* start with the driver above us */ + if (mv_dev->driver) + { + /* should have been done already by driver model core */ + mvOsPrintf("pci remove, driver '%s' is still registered\n", + mv_dev->driver->driver.name); + usb_gadget_unregister_driver (mv_dev->driver); + } + + spin_lock (&mv_dev->lock); + + for (i=0; imv_usb_handle, i); + + /* Deregister all other services */ + _usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_BUS_RESET); + _usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_SPEED_DETECTION); + + _usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_SUSPEND); + + _usb_device_unregister_service(mv_dev->mv_usb_handle, ARC_USB_SERVICE_RESUME); + + _usb_device_shutdown(mv_dev->mv_usb_handle); + + spin_unlock (&mv_dev->lock); + +#ifdef MV_USB_VOLTAGE_FIX + + if(mv_dev->vbus_gpp_no != (MV_U8)N_A) + { + free_irq (IRQ_GPP_START + mv_dev->vbus_gpp_no, mv_dev); + } + +#endif /* MV_USB_VOLTAGE_FIX */ + + the_controllers[mv_dev->dev_no] = 0; + device_unregister (&mv_dev->gadget.dev); + + kfree(mv_dev); + + dev_set_drvdata(_dev, 0); + +#ifdef CONFIG_PXA3xx_DVFM + dvfm_unregister("U2O", &dvfm_lock.dev_idx); +#endif + return 0; +} + +#ifdef CONFIG_USB_OTG + +void pxa9xx_gadget_init(void) +{ + struct mv_usb_dev* mv_dev = the_controllers[0]; + + if (!mv_dev->transceiver) { + pr_err("no otg transceiver!\n"); + return; + } + pr_debug("%s otg state %d\n", __func__, mv_dev->transceiver->state); + if (rely_on_vbus) + mv_usb_vbus_session(&mv_dev->gadget, 0); + + if (mv_dev->transceiver->state == OTG_STATE_B_PERIPHERAL) { + _usb_dci_vusb20_chip_initialize(mv_dev->mv_usb_handle); + + _usb_device_start(mv_dev->mv_usb_handle); + mv_usb_start_ep0(mv_dev); + } +} +#endif + +/* dump IN endpoint status */ +static int dump_ep = 3; +void dump_epin(int ep_num) +{ + struct mv_usb_dev *mv_dev = the_controllers[0]; + int tmp = mv_debug; + void *print = usbImportFuncs.bspPrintf; + usbImportFuncs.bspPrintf = mvOsPrintf; + mv_debug = 1; + _usb_ep_status(mv_dev->mv_usb_handle, ep_num, ARC_USB_SEND); + mv_debug = tmp; + usbImportFuncs.bspPrintf = print; +} + +/* global variables from 'regdump' */ +static struct proc_dir_entry *usb_resource_dump; +static u32 usb_resource_dump_result; + +int usb_resource_dump_read (char *buffer, char **buffer_location, off_t offset, + int buffer_length, int *zero, void *ptr); +int usb_resource_dump_write (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char kbuf[8], vol[8]; + int index; + struct mv_usb_dev *mv_dev = the_controllers[0]; + + if (count >= 8) + return -EINVAL; + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + index = (int)simple_strtoul(kbuf, NULL, 10); + + + if ('-' == kbuf[0]) { + /* set the ep num */ + memcpy(vol, kbuf+1, count-1); + dump_ep = (int) simple_strtoul(vol, NULL, 16); + } + + switch (index) { + case 1: + usbImportFuncs.bspPrintf = mvOsPrintf; + mv_debug = 1; + break; + case 2: + usbImportFuncs.bspPrintf = NULL; + mv_debug = 0; + break; + case 3: + usb_dump(); + break; + case 4: + mv_dev->info->vbus_status(mv_dev->regbase); + break; + case 5: + dump_epin(dump_ep); + usb_resource_dump_read(&kbuf, NULL, 0, 0, 0, NULL); + break; + case 6: + conn_check = 0; + break; + + default: + return -EINVAL; + } + + return count; +} + +int usb_resource_dump_read (char *buffer, char **buffer_location, off_t offset, + int buffer_length, int *zero, void *ptr) +{ + int i; + static int count = 0; + int tmp = mv_debug; + void *print = usbImportFuncs.bspPrintf; + + if(offset > 0) + return 0; + + usbImportFuncs.bspPrintf = mvOsPrintf; + mv_debug = 1; + + count++; + usb_resource_dump_result = count; + + for(i=0; iread_proc = usb_resource_dump_read; + usb_resource_dump->write_proc = usb_resource_dump_write; + usb_resource_dump->nlink = 1; + return 0; +} + +static void mv_usb_gadget_shutdown(struct platform_device *_dev) +{ + printk("%s\n\n", __func__); +} + +static int mv_usb_gadget_suspend(struct platform_device *_dev, pm_message_t state) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + if (conn_check && is_cable_attached()) { + printk(KERN_ERR "USB cable connected, please try again!\n"); + return -EAGAIN; + } +#ifdef CONFIG_USB_OTG + if (dev->transceiver) { + pxa3xx_otg_require_bus(0); + otg_set_peripheral(dev->transceiver, NULL); + } +#endif + udc_stop(&dev->cdev, &dev->gadget, dev->driver, 1); + _usb_device_stop(dev->mv_usb_handle); + + return 0; +} + +static int mv_usb_gadget_resume(struct platform_device *_dev) +{ + struct mv_usb_dev *dev = the_controllers[0]; + + mv_clock_enable(dev, 1); + + dev->info->phy_init(dev->phybase); + _usb_dci_vusb20_chip_initialize(dev->mv_usb_handle); +#ifdef CONFIG_USB_OTG + if (dev->transceiver) + otg_set_peripheral(dev->transceiver, &dev->gadget); +#endif + _usb_device_start(dev->mv_usb_handle); + mv_usb_start_ep0(dev); + udc_stop(&dev->cdev, &dev->gadget, dev->driver, 0); + + if (is_cable_attached() || !dev->info->clk_gating) { + mv_usb_connect_change(VBUS_HIGH); + } + return 0; +} + +static struct platform_driver udc_driver = { + .shutdown = mv_usb_gadget_shutdown, + .remove = __exit_p(mv_usb_gadget_remove), + .suspend = mv_usb_gadget_suspend, + .resume = mv_usb_gadget_resume, + .driver = { + .owner = THIS_MODULE, + .name = "pxa-u2o", + }, +}; + +MODULE_VERSION (DRIVER_VERSION); +MODULE_DESCRIPTION (DRIVER_DESC); +MODULE_AUTHOR ("Dima Epshtein"); +MODULE_LICENSE ("GPL"); + +static int __init init (void) +{ + mvOsPrintf("%s: version %s loaded\n", driver_name, DRIVER_VERSION); + usb_start_resource_dump(); + return platform_driver_probe(&udc_driver, mv_usb_gadget_probe); +} +module_init (init); + +static void __exit cleanup (void) +{ + mvOsPrintf("%s: version %s unloaded\n", driver_name, DRIVER_VERSION); + platform_driver_unregister(&udc_driver); +} +module_exit (cleanup); + diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 85b0d8921eae4f..e8aa8ada15220f 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -43,6 +43,14 @@ #include "pxa27x_udc.h" +static char *ep0_state_name[] = { + "WAIT_FOR_SETUP", "SETUP_STAGE", "IN_DATA_STAGE", "OUT_DATA_STAGE", + "IN_STATUS_STAGE", "OUT_STATUS_STAGE", "STALL", + "WAIT_ACK_SET_CONF_INTERF" +}; + +#define EP0_STNAME(udc) ep0_state_name[(udc)->ep0state] + /* * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x * series processors. diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index ff61e4866e8afa..7d653fb18d3d36 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -28,6 +28,8 @@ #include #include +#include + /* * Register definitions */ @@ -388,24 +390,6 @@ struct pxa27x_request { struct list_head queue; }; -enum ep0_state { - WAIT_FOR_SETUP, - SETUP_STAGE, - IN_DATA_STAGE, - OUT_DATA_STAGE, - IN_STATUS_STAGE, - OUT_STATUS_STAGE, - STALL, - WAIT_ACK_SET_CONF_INTERF -}; - -static char *ep0_state_name[] = { - "WAIT_FOR_SETUP", "SETUP_STAGE", "IN_DATA_STAGE", "OUT_DATA_STAGE", - "IN_STATUS_STAGE", "OUT_STATUS_STAGE", "STALL", - "WAIT_ACK_SET_CONF_INTERF" -}; -#define EP0_STNAME(udc) ep0_state_name[(udc)->ep0state] - #define EP0_FIFO_SIZE 16U #define BULK_FIFO_SIZE 64U #define ISO_FIFO_SIZE 256U diff --git a/drivers/usb/gadget/pxa_comp.c b/drivers/usb/gadget/pxa_comp.c new file mode 100644 index 00000000000000..0fbc32f37c2acb --- /dev/null +++ b/drivers/usb/gadget/pxa_comp.c @@ -0,0 +1,1338 @@ +//#define DEBUG +//#define VERBOSE + +#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 +#include +#include +#include +#include + +#include +#include "pxa27x_udc.h" + +#ifdef CONFIG_USB_COMPOSITE +static int gadget_info_init(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +static int gadget_get_device_desc(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +static void set_cdc_desc(struct pxa3xx_comp *dev); +static int gadget_info_uninit(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver **slf_drver, + struct usb_gadget_driver *driver); +static int gadget_get_config_desc_hs(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver *driver); +#endif + +/* + * get_extra_descriptor() finds a descriptor of specific type in the + * extra field of the interface and endpoint descriptor structs. + */ +int get_extra_descriptor(char *buffer, unsigned size, + unsigned char type, void **ptr) +{ + struct usb_descriptor_header *header; + + *ptr = buffer; + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; + + if (header->bLength < 2) { + DMSG("%s: descriptor, type %d length %d not found\n", + __FUNCTION__, + header->bDescriptorType, + header->bLength); + return -ENODATA; + } + + if (header->bDescriptorType == type) { + *ptr = header; + return size; + } + + buffer += header->bLength; + size -= header->bLength; + } + return -ENODATA; +} + +void get_fake_config(struct pxa3xx_comp *dev, struct usb_request *req, int spd) +{ +#ifndef CONFIG_USB_COMPOSITE + memcpy(dev->configs, req->buf, req->length); +#else + if (((__u8 *)(req->buf))[1] == USB_DT_CONFIG) { + if (spd == USB_SPEED_HIGH) + memcpy(dev->active_gadget->config_desc_hs, req->buf, + req->length); + else + memcpy(dev->active_gadget->config_desc, req->buf, + req->length); + } else if (((__u8 *)(req->buf))[1] == USB_DT_DEVICE) + memcpy(dev->active_gadget->device_desc, req->buf, req->length); +#endif +} + +void comp_val_init(struct pxa3xx_comp *dev) +{ +#ifdef CONFIG_USB_COMPOSITE + dev->configuration = 0; + dev->interface = 0; + dev->alternate = 0; +#endif +} + +static struct pxa3xx_comp_ep *find_ep_intf(struct pxa3xx_comp_ep *head, + int assigned_interface) +{ + struct pxa3xx_comp_ep *p = head; + while (p) { + if (p->assigned_interface == assigned_interface) + break; + p = p->next; + } + return p; +} + +struct pxa3xx_comp_ep *find_ep_num(struct pxa3xx_comp_ep *head, int num) +{ + struct pxa3xx_comp_ep *p = head; + while (p) { + if (p->log_ep_num == num) + break; + p = p->next; + } + return p; +} + +static void delete_comp_ep_list(struct pxa3xx_comp_ep **head) +{ + struct pxa3xx_comp_ep *p = *head; + struct pxa3xx_comp_ep *p_cur; + while (p) { + p_cur = p; + p = p->next; + kfree(p_cur); + p_cur = NULL; + } + *head = NULL; +} + +void comp_set_ep(struct pxa3xx_comp *dev, int ep_num, int config, int interface) +{ + struct pxa3xx_comp_ep *ep = kzalloc(sizeof(struct pxa3xx_comp_ep), + GFP_KERNEL); + struct pxa3xx_comp_ep *p; + if (!ep) { + pr_err("%s: cannot alloc mem for ep!\n", __func__); + return; + } + + ep->config = config; + ep->interface = interface; + ep->log_ep_num = ep_num; +#ifdef CONFIG_USB_COMPOSITE + ep->assigned_interface = dev->interface_count; + ep->driver_info = dev->active_gadget; +#endif + if (!dev->first_ep) + dev->first_ep = ep; + else { + p = dev->first_ep; + while (p->next) p = p->next; + p->next = ep; + } +} + +/* After driver is bound, send a fake get configuration command to + * gadget driver to get the configuration information */ +static int gadget_get_config_desc(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct usb_ctrlrequest req; + struct usb_config_descriptor *config_desc; + struct usb_interface_descriptor *interface_desc; + struct usb_endpoint_descriptor *ep_desc; + unsigned config; + int i; + struct usb_config_descriptor *p_config_desc; + int config_desc_length, ret; +#ifdef CONFIG_USB_COMPOSITE + __u8 num_itfs; + __u8 cur_intf = 0, last_intf = 0; +#endif + + DMSG("----------%s------------\n", __FUNCTION__); + req.bRequestType = USB_RECIP_DEVICE | USB_DIR_IN; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = (USB_DT_CONFIG<<8); + req.wIndex = 0; + req.wLength = MAX_CONFIG_LENGTH; + + gadget->speed = USB_SPEED_FULL; + dev->ep0state = EP0_IN_FAKE; + i = driver->setup(gadget, &req); +#ifdef CONFIG_USB_PXA3XX_U2D + gadget->speed = USB_SPEED_UNKNOWN; +#endif + +#ifndef CONFIG_USB_COMPOSITE + config_desc = (struct usb_config_descriptor *)dev->configs; +#else + config_desc = (struct usb_config_descriptor *) + dev->active_gadget->config_desc; +#endif + p_config_desc = config_desc; + config_desc_length = config_desc->wTotalLength; + + if (config_desc->bDescriptorType == USB_DT_CONFIG) { + config = config_desc->bConfigurationValue; + } else { + DMSG("wrong configuration\n"); + return -EFAULT; + } + +#ifndef CONFIG_USB_COMPOSITE + while (config_desc_length >= 0) { + ret = get_extra_descriptor((char *)p_config_desc, + config_desc_length, + USB_DT_INTERFACE, + (void **) &interface_desc); + if (ret >= 0) { + /* search eps and fill the pxa27x_ep_config struct */ + if (interface_desc->bNumEndpoints) { + set_eps(interface_desc->bNumEndpoints, + (struct usb_endpoint_descriptor *) + interface_desc, + config_desc_length, config, + interface_desc->bInterfaceNumber, + interface_desc->bAlternateSetting); + + DMSG("config=%d, intf assigned=%d," + " alt=%d\n", config, + interface_desc->bInterfaceNumber, + interface_desc->bAlternateSetting); + } + } else { + DMSG("interface config not find\n"); + return -EFAULT; + } + + p_config_desc = (struct usb_config_descriptor *) + ((struct usb_interface_descriptor *) + interface_desc + 1); + + config_desc_length -= interface_desc->bLength; /* yfw */ + } +#else + num_itfs = config_desc->bNumInterfaces; + + DMSG("parse the config desc, assigned_intf_start=%d, num of intfs=%d\n", + dev->interface_count, num_itfs); + + dev->active_gadget->assigned_intf_start = dev->interface_count; + dev->active_gadget->config = config; + dev->active_gadget->num_intfs = num_itfs; + + /* get every interface desc, fill the gadget_driver_info structure */ + for (i = 0; i < num_itfs; i++) { + DMSG("\nparse interface %d, p_config_desc=%p," + " config_desc_length=%d\n", i, p_config_desc, + config_desc_length); + + while (config_desc_length >= 0) { + ret = get_extra_descriptor((char *)p_config_desc, + config_desc_length, + USB_DT_INTERFACE, + (void **)&interface_desc); + if (ret >= 0) { + cur_intf = interface_desc->bInterfaceNumber; + + config_desc_length -= (u32)interface_desc - + (u32)p_config_desc; + + if (cur_intf != last_intf) { + p_config_desc = + (struct usb_config_descriptor *) + interface_desc; + goto next_intf; + } + + /* set interface number to assigned one */ + interface_desc->bInterfaceNumber = i + + dev->active_gadget->assigned_intf_start; + + interface_desc->iInterface = 0; + + /* search eps and fill the pxa27x_ep_config*/ + ep_desc = (struct usb_endpoint_descriptor *) + interface_desc; + if (interface_desc->bNumEndpoints) { + set_eps(interface_desc->bNumEndpoints, + ep_desc, config_desc_length, + config, cur_intf, + interface_desc->bAlternateSetting); + } + + DMSG("config=%d, intf=%d(assigned=%d)," + " alt=%d\n", config, cur_intf, + interface_desc->bInterfaceNumber, + interface_desc->bAlternateSetting); + } else { + DMSG("no more alt interfaces," + " config_desc_length=%d, goto next_intf\n", + config_desc_length); + goto next_intf; + } /* if */ + + p_config_desc = (struct usb_config_descriptor *) + ((struct usb_interface_descriptor *) + interface_desc + 1); + + config_desc_length -= interface_desc->bLength;/* yfw */ + DMSG(" p_config_desc=%p, interface_desc=%p, " + "config_desc_length=%d\n", + p_config_desc, interface_desc, config_desc_length); + } /* while */ + +next_intf: + last_intf = cur_intf; + dev->interface_count++; + + DMSG("parse interface %d finished, dev->interface_count=%d\n", + i, dev->interface_count); + } /* for */ + + /* set CDC union descriptors */ + set_cdc_desc(dev); +#endif + return 0; +} + +int comp_calc_config(struct pxa3xx_comp *dev, int ep_num) +{ +#ifndef CONFIG_USB_COMPOSITE + struct pxa3xx_comp_ep *ep = find_ep_num(dev->first_ep, ep_num); + if (!ep) { + printk(KERN_ERR "config can't find ep %d" + " first_ep %p ???\n\n", ep_num, dev->first_ep); + return 0; + } + return ep->config; +#else + return ((struct usb_config_descriptor *) + dev->first_gadget->config_desc)->bConfigurationValue; +#endif +} + +int comp_calc_interface(struct pxa3xx_comp *dev, int ep_num) +{ + struct pxa3xx_comp_ep *ep = find_ep_num(dev->first_ep, ep_num); + if (!ep) { + printk(KERN_ERR "intf can't find ep %d???\n\n", ep_num); + return 0; + } +#ifndef CONFIG_USB_COMPOSITE + return ep->interface; +#else + return ep->assigned_interface; +#endif +} + +static void +#ifndef CONFIG_USB_COMPOSITE +stop_activity(struct usb_gadget *gadget, struct usb_gadget_driver *driver) +{ +#else +stop_activity(struct usb_gadget *gadget, struct gadget_driver_info *p_info) +{ + struct usb_gadget_driver *driver=NULL; +#endif + + DMSG("Trace path 1\n"); + driver = (struct usb_gadget_driver *)stop_udc(driver); + + /* report disconnect; the driver is already quiesced */ +#ifndef CONFIG_USB_COMPOSITE + if (driver) + driver->disconnect(gadget); +#else + if (!p_info->stopped) + p_info->driver->disconnect(gadget); + p_info->stopped = 1; +#endif + + /* re-init driver-visible data structures */ + udc_reinit(); +} + +#ifdef CONFIG_USB_COMPOSITE +struct gadget_driver_info *get_driver_info(struct pxa3xx_comp *dev, + struct usb_gadget_driver *driver) +{ + struct gadget_driver_info *p_info = dev->first_gadget; + + while (p_info && (p_info->driver != driver)) p_info = p_info->next; + + return p_info; +} +#endif + +int comp_check_driver(struct pxa3xx_comp *dev, + struct usb_gadget_driver *slf_drver, + struct usb_gadget_driver *driver) +{ +#ifdef CONFIG_USB_COMPOSITE + struct gadget_driver_info *p_info = get_driver_info(dev, driver); +#endif +#ifdef CONFIG_USB_OTG + if (dev->transceiver && dev->transceiver->default_a) { + printk(KERN_ERR "Mini-A connected! " + "This operation may cause unexpected error!!!\n"); + } +#endif + +#ifdef CONFIG_USB_COMPOSITE + + if (!driver || NULL == p_info) { + printk(KERN_ERR "%s, can't find driver!\n", __FUNCTION__); + return 0; + } + return 1; +#else + if (!driver || driver != slf_drver) { + printk(KERN_ERR "%s, can't find driver!\n", __FUNCTION__); + return 0; + } + return 1; +#endif +} + +int comp_is_dev_busy(struct pxa3xx_comp *dev, struct usb_gadget_driver *driver) +{ +#ifndef CONFIG_USB_COMPOSITE + if (driver) + return 1; +#else + /* FIXME remove all modules before insert again */ + if ((dev->rm_flag) && dev->first_gadget) { + printk(KERN_ERR "left modules may not work! " + "please remove all and insert again!!!\n"); + return 1; + } +#ifdef CONFIG_USB_OTG + if(dev->transceiver && dev->transceiver->default_a) { + printk(KERN_ERR "Mini-A connected! " + "please unplug it and insert module again!!!\n"); + return 1; + } +#endif +#endif + return 0; +} + +int stop_cur_gadget(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ +#ifdef CONFIG_USB_COMPOSITE + struct gadget_driver_info *p_info = get_driver_info(dev, driver); + + set_gadget_data(gadget, p_info->driver_data); + stop_activity(gadget, p_info); + return 0; +#else + stop_activity(gadget, driver); + return 0; +#endif +} +void comp_register_driver(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ +#ifdef CONFIG_USB_COMPOSITE + /* allocate gadget_driver_info and attach it to controller */ + gadget_info_init(dev, gadget, driver); + dev->active_gadget->driver_data = get_gadget_data(gadget); +#ifdef MULTI_P3 + gadget_get_device_desc(dev, gadget, driver); +#endif /* MULTI_P3 */ +#endif + /* After driver is bound, send a fake get configuration command to + * gadget driver to get the configuration information */ + gadget_get_config_desc(dev, gadget, driver); +#if defined(CONFIG_USB_COMPOSITE) && (defined(CONFIG_USB_PXA3XX_U2D) \ + || defined(CONFIG_USB_PXA_U2O)) + gadget_get_config_desc_hs(dev, gadget, driver); +#endif +} + +void comp_unregister_driver(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver **slf_drver, + struct usb_gadget_driver *driver) +{ +#ifndef CONFIG_USB_COMPOSITE + delete_comp_ep_list(&dev->first_ep); +#else + gadget_info_uninit(dev, gadget, slf_drver, driver); + + memset(dev->configs, 0, MAX_CONFIG_LENGTH); + if (dev->driver_count != 0) { + dev->rm_flag = 1; + printk(KERN_WARNING "left modules may not work! " + "please remove all and insmod again!!!\n"); + } else { + delete_comp_ep_list(&dev->first_ep); + dev->rm_flag = 0; + } +#endif +} + +void comp_driver_suspend(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ +#ifndef CONFIG_USB_COMPOSITE + if (driver->suspend) + driver->suspend(gadget); +#else + struct gadget_driver_info *p_info = dev->first_gadget; + + do { + set_gadget_data(gadget, p_info->driver_data); + if (p_info->driver->suspend) + p_info->driver->suspend(gadget); + p_info = p_info->next; + } while (p_info); +#endif +} + +void comp_driver_resume(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ +#ifndef CONFIG_USB_COMPOSITE + if (driver->resume) + driver->resume(gadget); +#else + struct gadget_driver_info *p_info = dev->first_gadget; + + do { + set_gadget_data(gadget, p_info->driver_data); + if (p_info->driver->resume) + p_info->driver->resume(gadget); + p_info = p_info->next; + } while (p_info); +#endif +} + +int comp_change_config(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver, + struct usb_ctrlrequest *req) +{ + int ret; +#ifndef CONFIG_USB_COMPOSITE + ret = driver->setup(gadget, req); +#else + struct gadget_driver_info *p_info = dev->first_gadget; + + do { + set_gadget_data(gadget, p_info->driver_data); +#ifndef MULTIPLE_CONFIGURATION + /* switch to gadget driver's configuration */ + comp_print("set config %d orig %d ===============\n", + req->wValue, p_info->config); + req->wValue = p_info->config; +#endif + if ((ret = p_info->driver->setup(gadget, req)) != 0) { + printk(KERN_DEBUG "set %s config %d fail %d ?\n", + p_info->driver->function, p_info->config, ret); + } + p_info->stopped = 0; + p_info = p_info->next; + } while (p_info); +#endif + return ret; +} + +struct usb_gadget_driver +*comp_change_interface(struct pxa3xx_comp *dev, int active_interface, + struct usb_ctrlrequest *req, struct usb_gadget *gadget, + struct usb_gadget_driver *driver, int *ret) +{ +#ifndef CONFIG_USB_COMPOSITE + dev->ep0state = EP0_IN_DATA_PHASE; + driver->setup(gadget, req); + return driver; +#else + struct gadget_driver_info *p_info = NULL; + struct pxa3xx_comp_ep *ep; + + /* change the assigned interface to gadget interface */ + ep = find_ep_intf(dev->first_ep, active_interface); + if (ep) { + DMSG("dev->ep[%d]:assigned_interface = %d, driver_info=0x%x\n", + ep->log_ep_num, ep->assigned_interface, + (unsigned)(ep->driver_info)); + p_info = ep->driver_info; + req->wIndex = ep->interface; + DMSG(" req.wValue = %d, req.wIndex = %d\n", + req->wValue, req->wIndex); + } + + if (p_info == NULL) { + printk(KERN_ERR "active interface not found, error\n"); + return NULL; + } else { + dev->active_gadget = p_info; + + set_gadget_data(gadget, dev->active_gadget->driver_data); + + dev->interface = active_interface; + + dev->ep0state = EP0_IN_DATA_PHASE; + + *ret = p_info->driver->setup(gadget, req); + if (*ret == -EOPNOTSUPP) + DMSG(" ret EOPNOTSUPP\n"); + + return p_info->driver; + } +#endif +} + +void stop_gadget(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ +#ifndef CONFIG_USB_COMPOSITE + stop_activity(gadget, driver); +#else + struct gadget_driver_info *pInfo = dev->first_gadget; + + while (pInfo) { + set_gadget_data(gadget, pInfo->driver_data); + stop_activity(gadget, pInfo); + pInfo = pInfo->next; + } +#endif +} + +void udc_stop(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver, int state) +{ +#ifdef CONFIG_USB_COMPOSITE + struct gadget_driver_info *pInfo = dev->first_gadget; + + if (driver) { + if (state == 0) { + do { + pInfo->stopped = 0; + pInfo = pInfo->next; + } while (pInfo); + } else if (state == 1) { + stop_gadget(dev, gadget, driver); + } else + printk(KERN_ERR "stop state %d error\n", state); + + } +#else + stop_gadget(dev, gadget, driver); +#endif +} + +#ifdef CONFIG_USB_COMPOSITE +#ifdef DEBUG +/* dump the gadget_driver_info structure + */ +static void gadget_info_dump(struct pxa3xx_comp *dev) +{ + struct gadget_driver_info *p_info = dev->first_gadget; + int i = 1; + + DMSG("%s, dev->interface_count= 0x%x\n", __FUNCTION__, + dev->interface_count); + while (p_info) { + + DMSG("i=%d, p_info=%p\n", i, p_info); + DMSG(" next = 0x%x\n", (unsigned)p_info->next); + DMSG(" config = 0x%x\n", p_info->config); + DMSG(" assigned_intf_start = 0x%x\n", + p_info->assigned_intf_start); + DMSG(" num_intfs = 0x%x\n", p_info->num_intfs); + DMSG(" config_desc = 0x%x\n", (unsigned)p_info->config_desc); + DMSG(" driver = 0x%x\n", (unsigned)p_info->driver); + DMSG(" driver_data = 0x%x\n", (unsigned)p_info->driver_data); + + p_info = p_info->next; + i++; + } + DMSG("dev->first_gadget = %p\n", dev->first_gadget); + DMSG("dev->active_gadget = %p\n", dev->active_gadget); +} +#endif + +static void udc_setup_complete(struct usb_ep *ep, struct usb_request *req); +/* gadget_info_init + * init the gadget_driver_info structure when the driver is registered + * combined from several gadget driver, should be lager ?? + */ +#define REQ_BUFSIZ 256 +static int gadget_info_init(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct gadget_driver_info *info; + struct gadget_driver_info *p_info; + + /* set up the new gadget driver info */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + printk(KERN_ERR "kmalloc gadget_driver_info error\n"); + return -EBUSY; + } + + info->driver = driver; + info->next = NULL; + + info->num_intfs = 0; + info->stopped = 1; + + if (dev->first_gadget) { + /* find the last element */ + p_info = dev->first_gadget; + while (p_info->next) + p_info = p_info->next; + /* set up the struct. + * the last registered driver is always the active one + * before receive the set_interface request + */ + p_info->next = info; + dev->active_gadget = info; + } else { + dev->first_gadget = dev->active_gadget = info; + dev->interface_count = 0; + dev->driver_count = 0; + + /* init ep0 control request queueand buffer */ + memset((void *)&dev->ep0_req, 0, sizeof(dev->ep0_req)); + INIT_LIST_HEAD(&dev->ep0_req.queue); + + dev->ep0_req.req.complete = udc_setup_complete; + dev->ep0_req.req.buf = kzalloc(REQ_BUFSIZ, GFP_KERNEL); + if (!dev->ep0_req.req.buf) { + usb_ep_free_request(gadget->ep0, &dev->ep0_req.req); + DMSG("%s, dev->ep0_req.req.buf malloc error\n", + __FUNCTION__); + } + } + + return 0; +} + +static int gadget_info_uninit(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver **slf_drver, + struct usb_gadget_driver *driver) +{ + struct gadget_driver_info *p_info = dev->first_gadget; + struct gadget_driver_info *info = NULL; + + do { + /* find the gadget driver info to p_info */ + if (p_info->driver == driver) { + /* for the first driver is being removed*/ + if (!info) + info = p_info->next; + break; + } + /* save the previous one */ + info = p_info; + p_info = p_info->next; + } while (p_info); + + if (NULL == p_info) { + printk(KERN_ERR "%s, can't find driver!\n", __FUNCTION__); + return -EINVAL; + } + + /* put the active one to the previous one */ + if (dev->first_gadget == p_info) { + if (info) + dev->first_gadget = info; + else + dev->first_gadget = p_info->next; + } + if (dev->active_gadget == p_info) { + if (info) + dev->active_gadget = info; + else + dev->active_gadget = p_info->next; + } + if ((info) && (info != p_info->next)) + info->next = p_info->next; + + if (dev->active_gadget) { + *slf_drver = dev->active_gadget->driver; + gadget->dev.driver = &dev->active_gadget->driver->driver; + } else + *slf_drver = 0;/* no drivers left */ + + dev->interface_count -= p_info->num_intfs; + + kfree(p_info); + return 0; +} + +#ifdef MULTI_P3 +struct usb_interface_assoc_descriptor +iad_desc = { + .bLength = sizeof iad_desc, + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, + + .bFirstInterface = 0, + .bInterfaceCount = 0, + .bFunctionClass = 0, + .bFunctionSubClass = 0, + .bFunctionProtocol = 0, + .iFunction = 0, +}; + +static void set_iad_desc(struct usb_device_descriptor *device_desc, + __u8 first_intf, __u8 num_intfs) +{ + iad_desc.bFirstInterface = first_intf; + iad_desc.bInterfaceCount = num_intfs; + + iad_desc.bFunctionClass = device_desc->bDeviceClass; + iad_desc.bFunctionSubClass = device_desc->bDeviceSubClass; + iad_desc.bFunctionProtocol = device_desc->bDeviceProtocol; +} + +static int gadget_get_device_desc(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct usb_ctrlrequest req; + int i; + + DMSG(KERN_DEBUG "%s\n", __FUNCTION__); + req.bRequestType = USB_RECIP_DEVICE | USB_DIR_IN; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = (USB_DT_DEVICE << 8); + req.wIndex = 0; + req.wLength = sizeof(struct usb_device_descriptor); + + dev->ep0state = EP0_IN_FAKE; + i = driver->setup(gadget, &req); + + return 0; +} +#endif + +/* combine_configuration + * Combine the configuration descriptors for all gadget drivers registered. + * Add the IAD descriptor if there are more than one interfaces within one + * function. + */ +static int combine_configuration(struct pxa3xx_comp *dev, int speed) +{ + struct gadget_driver_info *p_info = dev->first_gadget; + struct usb_config_descriptor *config_desc; + struct usb_device_descriptor *device_desc; + +#ifdef MULTI_P3 + struct usb_interface_assoc_descriptor *p_iad_desc; +#endif + struct usb_config_descriptor *configs = + (struct usb_config_descriptor *)dev->configs; + int desc_length; + + DMSG("%s\n", __FUNCTION__); + + /* config desc, may diff between gadget drivers */ + configs->bLength = USB_DT_CONFIG_SIZE; + configs->bDescriptorType = USB_DT_CONFIG; + + configs->wTotalLength = USB_DT_CONFIG_SIZE; + configs->bNumInterfaces = 0; + config_desc = (struct usb_config_descriptor *)(p_info->config_desc); + configs->bConfigurationValue = config_desc->bConfigurationValue; + configs->iConfiguration = 0; + configs->bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER; + configs->bMaxPower = 1; + + do { + if (speed == USB_SPEED_HIGH) + config_desc = (struct usb_config_descriptor *) + (p_info->config_desc_hs); + else + config_desc = (struct usb_config_descriptor *) + (p_info->config_desc); + device_desc = (struct usb_device_descriptor *) + (p_info->device_desc); + configs->bNumInterfaces += (u8)(config_desc->bNumInterfaces); + +#ifdef MULTI_P3 + /* add the IAD descriptor if there are multiple interface in one + gadget driver */ + if (config_desc->bNumInterfaces > 1) { + + if ((get_extra_descriptor((char *)config_desc, + config_desc->wTotalLength, + USB_DT_INTERFACE_ASSOCIATION, + (void **)&p_iad_desc)) >= 0) + p_iad_desc->bFirstInterface = + p_info->assigned_intf_start; + else { + /* fill the iad_desc + * functionClass/subclass/protocol fields */ + set_iad_desc((struct usb_device_descriptor *) + p_info->device_desc, + p_info->assigned_intf_start, + config_desc->bNumInterfaces); + memcpy((u8 *)configs + configs->wTotalLength, + (u8 *)&iad_desc, sizeof iad_desc); + configs->wTotalLength += sizeof iad_desc; + } + } +#endif + + /* copy all descriptors except config desc */ + memcpy((u8 *)configs + configs->wTotalLength, + (u8 *)config_desc + USB_DT_CONFIG_SIZE, + config_desc->wTotalLength - USB_DT_CONFIG_SIZE); + + /* modify the interface number to assigned interface number */ + desc_length = config_desc->wTotalLength - USB_DT_CONFIG_SIZE; + + configs->wTotalLength += (config_desc->wTotalLength - + USB_DT_CONFIG_SIZE); + p_info = p_info->next; + DMSG("configs->wTotalLength = 0x%x\n", configs->wTotalLength); + } while (p_info != NULL); + +#ifdef DEBUG + gadget_info_dump(dev); +#endif + + return configs->wTotalLength; +} + +/* set_cdc_desc + * modify the cdc union descriptor which include the master/slave interface + * number + */ +static void set_cdc_desc(struct pxa3xx_comp *dev) +{ + struct usb_device_descriptor *device_desc = + (struct usb_device_descriptor *)dev->active_gadget->device_desc; + struct usb_config_descriptor *config_desc = + (struct usb_config_descriptor *)dev->active_gadget->config_desc; + struct usb_cdc_union_desc *union_desc; + int config_desc_len = config_desc->wTotalLength, ret = 0; + + if (device_desc->bDeviceClass != USB_CLASS_COMM) + return; + + while ((config_desc_len > 0) && (ret >= 0)) { + ret = get_extra_descriptor((char *)config_desc, config_desc_len, + USB_DT_CS_INTERFACE, + (void **)&union_desc); + if (ret >= 0) { + if (union_desc->bDescriptorSubType == + USB_CDC_UNION_TYPE) { + DMSG("found cdc union desc, change to %d\n", + dev->active_gadget->assigned_intf_start); + union_desc->bMasterInterface0 = + dev->active_gadget->assigned_intf_start; + union_desc->bSlaveInterface0 = 1 + + dev->active_gadget->assigned_intf_start; + } + + if (union_desc->bDescriptorSubType + == USB_CDC_CALL_MANAGEMENT_TYPE) { + DMSG("found cdc call mgt desc, change to %d\n", + dev->active_gadget->assigned_intf_start + + 1); + ((struct usb_cdc_call_mgmt_descriptor *) + union_desc)->bDataInterface = 1 + + dev->active_gadget->assigned_intf_start; + } + config_desc_len -= ((unsigned)union_desc - + (unsigned)config_desc + + union_desc->bLength); + config_desc = (struct usb_config_descriptor *) + ((unsigned)union_desc + + union_desc->bLength); + } + } +} + +/* get the hs configuration desc, change the interface number */ +static int gadget_get_config_desc_hs(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct usb_ctrlrequest req; + struct usb_config_descriptor *config_desc; + struct usb_interface_descriptor *interface_desc; + unsigned config; + int i; + __u8 num_itfs; + __u8 cur_intf = 0, last_intf = 0; + struct usb_config_descriptor *p_config_desc; + int config_desc_length, ret; + + DMSG("----------%s------------\n", __FUNCTION__); + req.bRequestType = USB_RECIP_DEVICE | USB_DIR_IN; + req.bRequest = USB_REQ_GET_DESCRIPTOR; + req.wValue = (USB_DT_CONFIG << 8); + req.wIndex = 0; + req.wLength = MAX_CONFIG_LENGTH; + + dev->ep0state = EP0_IN_FAKE; + gadget->speed = USB_SPEED_HIGH; + i = driver->setup(gadget, &req); + gadget->speed = USB_SPEED_UNKNOWN; + + config_desc = (struct usb_config_descriptor *) + dev->active_gadget->config_desc_hs; + + if (config_desc->bDescriptorType == USB_DT_CONFIG) + config = config_desc->bConfigurationValue; + else { + DMSG("wrong configuration\n"); + return -EFAULT; + } + + num_itfs = config_desc->bNumInterfaces; + p_config_desc = config_desc; + config_desc_length = config_desc->wTotalLength; + + DMSG("parse the config desc, assigned_intf_start=%d, num of intfs=%d\n", + dev->interface_count, num_itfs); + + /* get every interface desc, fill the gadget_driver_info structure */ + for (i = 0; i < num_itfs; i++) { + DMSG("\nparse interface %d, config_desc_length=%d\n", + i, config_desc_length); + + while (config_desc_length >= 0) { + ret = get_extra_descriptor((char *)p_config_desc, + config_desc_length, + USB_DT_INTERFACE, + (void **)&interface_desc); + if (ret >= 0) { + cur_intf = interface_desc->bInterfaceNumber; + DMSG(" cur_intf=%d, last_intf=%d," + " config_desc_length=%d\n", cur_intf, + last_intf, config_desc_length); + + config_desc_length -= (u32)interface_desc - + (u32)p_config_desc; + + if (cur_intf != last_intf) { + p_config_desc = + (struct usb_config_descriptor *) + interface_desc; + goto next_intf; + } + + /* set interface number to assigned one */ + interface_desc->bInterfaceNumber = i + + dev->active_gadget->assigned_intf_start; + + interface_desc->iInterface = 0; + } else { + DMSG("no alt interfaces, config_desc_length=%d," + " goto next_intf\n", config_desc_length); + goto next_intf; + } /* if */ + + p_config_desc = (struct usb_config_descriptor *) + ((struct usb_interface_descriptor *) + interface_desc + 1); + + config_desc_length -= interface_desc->bLength; /* yfw */ + } /* while */ + +next_intf: + last_intf = cur_intf; + + DMSG("parse interface %d finished, dev->interface_count=%d\n", + i, dev->interface_count); + } + + /* set CDC union descriptors */ + set_cdc_desc(dev); + + return 0; +} + +/* string desc not supported yet */ +#define UDC_STRING_MANUFACTURER 0 +#define UDC_STRING_PRODUCT 0 + +static struct usb_device_descriptor +udc_device_desc = { + .bLength = sizeof udc_device_desc, + .bDescriptorType = USB_DT_DEVICE, + +#if defined(CONFIG_USB_PXA27X_UDC) + .bcdUSB = __constant_cpu_to_le16(0x0110), +#elif defined(CONFIG_USB_PXA3XX_U2D) + .bcdUSB = __constant_cpu_to_le16(0x0200), +#endif + /* USB_CLASS_COMM, for rndis */ + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, +#ifndef CONFIG_USB_PXA_U2O + .bMaxPacketSize0 = EP0_FIFO_SIZE, /* for pxa3xx */ +#else + .bMaxPacketSize0 = 64, /* for pxa9xx u2o, xj check */ +#endif + .idVendor = __constant_cpu_to_le16(UDC_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(UDC_PRODUCT_NUM), + .iManufacturer = UDC_STRING_MANUFACTURER, + .iProduct = UDC_STRING_PRODUCT, + .bNumConfigurations = 1, +}; + +static struct usb_qualifier_descriptor +usb_qualifier_desc = { + .bLength = sizeof usb_qualifier_desc, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + /* USB_CLASS_COMM, for rndis */ + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, +#ifndef CONFIG_USB_PXA_U2O + .bMaxPacketSize0 = EP0_FIFO_SIZE, /* for pxa3xx */ +#else + .bMaxPacketSize0 = 64, /* for pxa9xx u2o, xj check */ +#endif + .bNumConfigurations = 1, +}; + +static void udc_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DMSG("pseudo setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +static int udc_do_specific_requests(struct pxa3xx_comp *dev, + struct usb_gadget *gadget, + struct usb_ctrlrequest *ctrl, + struct gadget_driver_info **gadget_info) +{ + struct gadget_driver_info *p_info = dev->first_gadget; + + if (((ctrl->bRequestType == 0x21) && (ctrl->bRequest == 0x00)) || + ((ctrl->bRequestType == 0xa1) && (ctrl->bRequest == 0x01))) { + while (p_info && (strcmp(p_info->driver->driver.name, + "g_ether"))) + p_info = p_info->next; + if (p_info == NULL) { + printk(KERN_ERR "%s,eth not found????\n", __func__); + return -1; + } else + set_gadget_data(gadget, p_info->driver_data); + } + + if (((ctrl->bRequestType == 0xa1) && (ctrl->bRequest == 0xfe)) || + ((ctrl->bRequestType == 0x21) && (ctrl->bRequest == 0xff))) { + p_info = dev->first_gadget; + while (p_info && (strcmp(p_info->driver->driver.name, + "g_file_storage"))) + p_info = p_info->next; + if (p_info == NULL) { + printk(KERN_ERR "%s, mass not found????\n", __func__); + return -1; + } else + set_gadget_data(gadget, p_info->driver_data); + } + + if (((ctrl->bRequestType == 0xa1) && (ctrl->bRequest == 0x21)) || + ((ctrl->bRequestType == 0x21) && (ctrl->bRequest == 0x20)) || + ((ctrl->bRequestType == 0x21) && (ctrl->bRequest == 0x22))) { + p_info = dev->first_gadget; + while (p_info && (strcmp(p_info->driver->driver.name, + "gs_modem"))) + p_info = p_info->next; + if (p_info == NULL) { + printk(KERN_DEBUG "%s, gs_modem not found????\n\n", __func__); + return -1; + } else + set_gadget_data(gadget, p_info->driver_data); + } + + *gadget_info = p_info; + return 0; +} + +static int udc_do_request(struct pxa3xx_comp *dev, struct usb_ctrlrequest *ctrl, + struct usb_ep *ep, int speed) +{ + struct usb_request *usb_req = &dev->ep0_req.req; + int value = -EOPNOTSUPP; + int pseudo = 0, ret = 0; + + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + break; + switch (ctrl->wValue >> 8) { + + case USB_DT_DEVICE: + DMSG("%s, get device desc\n", __FUNCTION__); + pseudo = 1; + /* send the pseudo device desc */ + value = min(ctrl->wLength, (u16)sizeof udc_device_desc); + memcpy(usb_req->buf, &udc_device_desc, value); + break; + + case USB_DT_CONFIG: + DMSG("%s, get conf desc\n", __FUNCTION__); + pseudo = 1; + /* send the pseudo configuration desc */ + value = combine_configuration(dev, speed); + value = min((int)ctrl->wLength, value); + memcpy(usb_req->buf, &dev->configs, value); + break; + + case USB_DT_OTHER_SPEED_CONFIG: + DMSG("%s, get other speed conf desc\n", __FUNCTION__); + /* send the pseudo configuration desc */ + if (speed != USB_SPEED_HIGH && + speed != USB_SPEED_FULL) { + pr_err("%s, unknown speed, error\n", __func__); + break; + } + value = combine_configuration(dev, speed); + pseudo = 1; + value = min((int)ctrl->wLength, value); + memcpy(usb_req->buf, &dev->configs, value); + break; + + case USB_DT_DEVICE_QUALIFIER: + DMSG("%s, get device qualifier desc\n", __FUNCTION__); + pseudo = 1; + /* send the pseudo device desc */ + value = min(ctrl->wLength, + (u16)sizeof(usb_qualifier_desc)); + memcpy(usb_req->buf, &usb_qualifier_desc, value); + break; + + default: + break; + } + default: + break; + } + + if (pseudo) { + usb_req->length = value; + usb_req->no_interrupt = 0; + usb_req->zero = value < ctrl->wLength + && (value % ep->maxpacket) == 0; + usb_req->complete = udc_setup_complete; + + ret = ep->ops->queue(ep, usb_req, GFP_KERNEL); + if (!(ret == 0)) + DMSG("%s, ep_queue error = 0x%x", __FUNCTION__, ret); + return value; + } else { + return -1; + } +} +#endif + +int comp_ep0_req(struct pxa3xx_comp *dev, struct usb_gadget *gadget, + struct usb_ep *ep0, struct usb_ctrlrequest *usb_req) +{ +#ifdef CONFIG_USB_COMPOSITE + int i; + struct pxa3xx_comp_ep *ep; + struct gadget_driver_info *p_info = dev->active_gadget; + struct gadget_driver_info *p_cur_info = dev->active_gadget; + + i = udc_do_request(dev, usb_req, ep0, gadget->speed); + + /* class specfic requests needed to be set up */ + if (i < 0) { + if ((usb_req->bRequestType & USB_RECIP_MASK) == + USB_RECIP_INTERFACE) { + ep = find_ep_intf(dev->first_ep, usb_req->wIndex); + if (ep) { + usb_req->wIndex = ep->interface; + goto set_gd_data; + } + } + if ((usb_req->bRequestType & USB_RECIP_MASK) == + USB_RECIP_ENDPOINT) { + i = usb_req->wIndex & 0xf; + ep = find_ep_num(dev->first_ep, i); + if (!ep) + return 0; +set_gd_data: + p_info = ep->driver_info; + if (p_info == NULL) { + pr_err("wrong req!\n"); + p_info = p_cur_info; + } + set_gadget_data(gadget, p_info->driver_data); + } + + udc_do_specific_requests(dev, gadget, usb_req, &p_info); + + i = p_info->driver->setup(gadget, usb_req); + + if (i < 0) { + p_info = dev->first_gadget; + do { + set_gadget_data(gadget, p_info->driver_data); + i = p_info->driver->setup(gadget, usb_req); + p_info = p_info->next; + } while ((i == -EOPNOTSUPP) && (p_info)); + if (i == -EOPNOTSUPP) + DMSG("%s, no correct driver found!\n", + __FUNCTION__); + set_gadget_data(gadget, p_cur_info->driver_data); + } /* if(i) */ + } /* if(!i) */ + return i; +#endif + return 0; +} diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index 16bdf77f582a02..5fe654e6d51013 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -27,7 +27,6 @@ #include "u_serial.h" - /* * This component encapsulates the TTY layer glue needed to provide basic * "serial port" functionality through the USB gadget stack. Each such @@ -136,6 +135,18 @@ static unsigned n_ports; ({ if (0) pr_debug(fmt, ##arg); }) #endif +#define CONFIG_USBSER_CONSOLE +#ifdef CONFIG_USBSER_CONSOLE +#include +void usbcons_init(struct usb_gadget *g, struct portmaster *ports, + unsigned n_ports); +void usbcons_exit(void); +#else +void usbcons_init(struct usb_gadget*, struct portmaster*, unsigned) {} +void usbcons_exit(void) {} +#endif +static int gs_write_polling; + /*-------------------------------------------------------------------------*/ /* Circular Buffer */ @@ -610,6 +621,7 @@ static void gs_write_complete(struct usb_ep *ep, struct usb_request *req) break; } + gs_write_polling = 0; spin_unlock(&port->port_lock); } @@ -892,6 +904,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) port->port_num, tty, count); spin_lock_irqsave(&port->port_lock, flags); + gs_write_polling = 1; if (count) count = gs_buf_put(&port->port_write_buf, buf, count); /* treat count == 0 as flush_chars() */ @@ -1133,6 +1146,8 @@ int __init gserial_setup(struct usb_gadget *g, unsigned count) pr_debug("%s: registered %d ttyGS* device%s\n", __func__, count, (count == 1) ? "" : "s"); + usbcons_init(g, &ports[0], n_ports); + return status; fail: while (count--) @@ -1197,6 +1212,8 @@ void gserial_cleanup(void) tty_unregister_driver(gs_tty_driver); gs_tty_driver = NULL; + usbcons_exit(); + pr_debug("%s: cleaned up ttyGS* support\n", __func__); } @@ -1329,3 +1346,7 @@ void gserial_disconnect(struct gserial *gser) gs_free_requests(gser->in, &port->write_pool); spin_unlock_irqrestore(&port->port_lock, flags); } + +#ifdef CONFIG_USBSER_CONSOLE +#include "usbcons.c" +#endif diff --git a/drivers/usb/gadget/usbcons.c b/drivers/usb/gadget/usbcons.c new file mode 100644 index 00000000000000..ac7306db5d8886 --- /dev/null +++ b/drivers/usb/gadget/usbcons.c @@ -0,0 +1,203 @@ +/* + * USB Serial Console driver + * + * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com) + * + * 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. + * + * Thanks to Randy Dunlap for the original version of this code. + * + */ + +#include +#include +#include +#include +#include +#include + +static int debug; + +struct usbcons_info { + int magic; + int n_ports; + struct portmaster *ports; + struct usb_gadget *gadget; +}; + +static struct usbcons_info usbcons_info; +static struct console usbcons; + +/* + * ------------------------------------------------------------ + * USB Serial console driver + * + * Much of the code here is copied from drivers/char/serial.c + * and implements a phony serial console in the same way that + * serial.c does so that in case some software queries it, + * it will get the same results. + * + * Things that are different from the way the serial port code + * does things, is that we call the lower level usb-serial + * driver code to initialize the device, and we set the initial + * console speeds based on the command line arguments. + * ------------------------------------------------------------ + */ + +static int stop_console = 1; +static int __init usbcons_setup(char *__unused) +{ + stop_console = 0; + return 1; +} +__setup("usbcons", usbcons_setup); + +#include "linux/netdevice.h" +static void usbcons_write_gs(struct tty_struct *tty, + const char *buf, unsigned count) +{ + gs_write(tty, buf, count); + while (gs_write_polling) + usb_gadget_ioctl(usbcons_info.gadget, USB_GADGET_POLLING, 0); +} + +static void usbcons_write_msg(struct tty_struct *tty, + const char *buf, unsigned count) +{ + while (count) { + unsigned int i; + unsigned int lf; + /* search for LF so we can insert CR if necessary */ + for (i = 0, lf = 0 ; i < count ; i++) { + if (*(buf + i) == 10) { + lf = 1; + i++; + break; + } + } + /* pass on to the driver specific version of this function if + it is available */ + usbcons_write_gs(tty, buf, i); + if (lf) { + /* append CR after LF */ + unsigned char cr = 13; + usbcons_write_gs(tty, &cr, 1); + } + buf += i; + count -= i; + } +} + +void usbcons_write(struct console *co, const char *buf, unsigned count) +{ + struct portmaster *ports = usbcons_info.ports; + struct gs_port *port; + unsigned long flags; + int i; + + if (stop_console) + return; + local_irq_save(flags); + for (i = 0; i < usbcons_info.n_ports; i++) { + port = ports->port; + if (port && port->port_tty && port->open_count) { + usbcons_write_msg(port->port_tty, buf, count); + + } + ports++; + } + local_irq_restore(flags); +} + +static struct console usbcons = { + .name = "ttyGS0", + .write = usbcons_write, + .flags = CON_ENABLED, + .index = -1, +}; + +static int usbcons_proc_read(char *buffer, char **buffer_location, + off_t offset, int buffer_length, int *zero, void *ptr) +{ + return 0; +} +static int usbcons_proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char kbuf[8]; + int index; + + if (count >= 8) + return -EINVAL; + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + index = (int)simple_strtoul(kbuf, NULL, 10); + + switch (index) { + case 1: + stop_console = 0; + break; + case 2: + stop_console = 1; + break; + case 3: + printk(KERN_INFO "usb serial console %s\n", + stop_console ? "disabled" : "enabled"); + break; + default: + return -EINVAL; + } + + return count; +} + + +static struct proc_dir_entry *usbcons_proc_file; +int usbcons_proc_init(void) +{ + usbcons_proc_file = create_proc_entry("usbcons" , 0666 , NULL); + usbcons_proc_file->read_proc = usbcons_proc_read; + usbcons_proc_file->write_proc = usbcons_proc_write; + return 0; +} + +void usbcons_deinit(void) +{ + unregister_console(&usbcons); +} + +void usbcons_init(struct usb_gadget *g, struct portmaster *ports, + unsigned n_ports) +{ + debug = 1; + usbcons_info.gadget = g; + usbcons_info.ports = ports; + usbcons_info.n_ports = n_ports; + + /* + * Call register_console() if this is the first device plugged + * in. If we call it earlier, then the callback to + * console_setup() will fail, as there is not a device seen by + * the USB subsystem yet. + */ + /* + * Register console. + * NOTES: + * console_setup() is called (back) immediately (from + * register_console). console_write() is called immediately + * from register_console iff CON_PRINTBUFFER is set in flags. + */ + dbg("registering the USB serial console."); + printk(KERN_INFO "USB serial console: n_ports %d ports %p\n", + n_ports, ports); + register_console(&usbcons); + usbcons_proc_init(); +} + +void usbcons_exit(void) +{ + unregister_console(&usbcons); +} + diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 8d3df0397de392..07281cc67a3712 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -131,6 +131,20 @@ config USB_EHCI_HCD_PPC_OF Enables support for the USB controller present on the PowerPC OpenFirmware platform bus. +config USB_EHCI_PXA_U2H + bool "Support for PXA on-chip EHCI USB controller (U2H)" + depends on USB_EHCI_HCD && CPU_PXA168 + default n + ---help--- + Variation of USB block used in some Marvell chips. + +config USB_EHCI_PXA_U2O + bool "Support for PXA on-chip EHCI USB controller (U2O)" + depends on USB_EHCI_HCD && USB_GADGET_PXA_U2O && USB_OTG + default n + ---help--- + Variation of USB block used in some Marvell chips. + config USB_W90X900_EHCI bool "W90X900(W90P910) EHCI support" depends on USB_EHCI_HCD && ARCH_W90X900 @@ -140,7 +154,7 @@ config USB_W90X900_EHCI config USB_OXU210HP_HCD tristate "OXU210HP HCD support" depends on USB - ---help--- + ---help--- The OXU210HP is an USB host/OTG/device controller. Enable this option if your board has this chip. If unsure, say N. diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 13ead00aecd503..8cfb61025ca49b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1099,6 +1099,20 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_LICENSE ("GPL"); +#ifdef CONFIG_USB_EHCI_PXA_U2H +#include "ehci-pxau2h.c" +#define PLATFORM_DRIVER pxau2h_ehci_driver +#endif + +#ifdef CONFIG_USB_EHCI_PXA_U2O +#include "ehci-pxau2o.c" +#ifndef PLATFORM_DRIVER +#define PLATFORM_DRIVER pxa9xx_ehci_driver +#else +#define PLATFORM_DRIVER2 pxa9xx_ehci_driver +#endif +#endif + #ifdef CONFIG_PCI #include "ehci-pci.c" #define PCI_DRIVER ehci_pci_driver @@ -1197,6 +1211,12 @@ static int __init ehci_hcd_init(void) goto clean0; #endif +#ifdef PLATFORM_DRIVER2 + retval = platform_driver_register(&PLATFORM_DRIVER2); + if (retval < 0) + goto clean0; +#endif + #ifdef PCI_DRIVER retval = pci_register_driver(&PCI_DRIVER); if (retval < 0) diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index c7178bcde67a99..e086aea26b1f46 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -728,8 +728,17 @@ static int ehci_hub_control ( temp = ehci_readl(ehci, status_reg); // wPortChange bits - if (temp & PORT_CSC) + if (temp & PORT_CSC) { status |= 1 << USB_PORT_FEAT_C_CONNECTION; +#ifdef CONFIG_USB_OTG + if (hcd->driver->connect && hcd->driver->disconnect) { + if (temp & PORT_CONNECT) + hcd->driver->connect(hcd, NULL); + else + hcd->driver->disconnect(hcd); + } +#endif + } if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; diff --git a/drivers/usb/host/ehci-pxau2h.c b/drivers/usb/host/ehci-pxau2h.c new file mode 100644 index 00000000000000..d4a98c71754ad2 --- /dev/null +++ b/drivers/usb/host/ehci-pxau2h.c @@ -0,0 +1,348 @@ +/* + * (C) Marvell Inc. 2008 (kvedere@marvell.com) + * Code Based on ehci-fsl.c & ehci-pxa9xx.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include + +static struct pxa_usb_plat_info *info; +static const char *pxau2h_driver_name = "pxau2h-ehci"; + +static int pxau2h_ehci_clk_set(int en) +{ + struct clk *clk = NULL; + + clk = clk_get(NULL, "U2HCLK"); + if (IS_ERR(clk)) { + printk(KERN_ERR "Cannot get USB clk\n"); + return PTR_ERR(clk); + } + if (en) + clk_enable(clk); + else + clk_disable(clk); + + return 0; +} + +/* called during probe() after chip reset completes */ +static int pxau2h_ehci_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + U2x_CAPREGS_OFFSET; + ehci->regs = hcd->regs + U2x_CAPREGS_OFFSET + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + ehci->sbrn = 0x20; + ehci_reset(ehci); + + return retval; +} + +static const struct hc_driver pxau2h_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "Marvell PXA SOC EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = pxau2h_ehci_setup, + .start = ehci_run, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int pxau2h_ehci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct usb_hcd *hcd; + int irq, retval, tmp; + + dev_dbg(&pdev->dev,"Initializing PXA EHCI-SOC USB Controller(U2H)\n"); + info = dev->platform_data; + + pxau2h_ehci_clk_set(1); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "u2h"); + info->regbase = (unsigned)ioremap_nocache(res->start, res_size(res)); + if (!info->regbase) { + printk(KERN_ERR "Cannot get regbase 0x%x\n", info->regbase); + return -ENOMEM; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "u2hphy"); + info->phybase = (unsigned)ioremap_nocache(res->start, res_size(res)); + if (!info->phybase) { + printk(KERN_ERR "Cannot get phybase 0x%x\n", info->phybase); + retval = -ENODEV; + goto err1; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + printk( KERN_ERR "Cannot get irq %x\n", irq); + retval = -ENODEV; + goto err1; + } + + printk("u2h regbase 0x%x phybase 0x%x irq %d\n", info->regbase, + info->phybase, irq); + + if (!info->phy_init || info->phy_init(info->phybase)) { + printk(KERN_ERR "unable to init pxa usb 2.0 host controller" + " phy_init 0x%p\n", info->phy_init); + retval = -EBUSY; + goto err1; + } + + if (info->plat_init) { + info->plat_init(dev); + } + + if (!info->vbus_set || info->vbus_set(1)) { + printk(KERN_ERR "Unable to power USB Host Controller.\n"); + retval = -EBUSY; + goto err1; + } + + hcd = usb_create_hcd(&pxau2h_ehci_hc_driver, &pdev->dev, + pxau2h_driver_name); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + hcd->rsrc_start = virt_to_phys((void *)info->regbase); + hcd->rsrc_len = 0x1ff; + hcd->regs = (void __iomem*)info->regbase; + hcd->irq = (unsigned int)irq; + + /* @USBCMD, Reset USB core */ + tmp = readl(hcd->regs + U2xUSBCMD); + tmp |= U2xUSBCMD_RST; + writel(tmp,hcd->regs + U2xUSBCMD); + udelay(1000); + + retval = usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) { + goto err2; + } + platform_set_drvdata(pdev, hcd); + return retval; + +err2: + usb_put_hcd(hcd); +err1: + dev_err(&pdev->dev, "init pxau2h_ehci_driver failed %d\n", retval); + return retval; +} + +static int pxau2h_ehci_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + if (HC_IS_RUNNING(hcd->state)) + hcd->state = HC_STATE_QUIESCING; + + usb_disconnect(&hcd->self.root_hub); + hcd->driver->stop(hcd); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + iounmap((void *)info->phybase); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + pxau2h_ehci_clk_set(0); + return 0; +} + +#ifdef CONFIG_PM +static int pxau2h_driver_suspend (struct platform_device *pdev, pm_message_t message) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + unsigned long flags; + int rc = 0; + + if (time_before(jiffies, ehci->next_statechange)) + msleep(10); + + /* Root hub was already suspended. Disable irq emission and + * mark HW unaccessible, bail out if RH has been resumed. Use + * the spinlock to properly synchronize with possible pending + * RH suspend or resume activity. + * + * This is still racy as hcd->state is manipulated outside of + * any locks =P But that will be a different fix. + */ + spin_lock_irqsave(&ehci->lock, flags); + if (hcd->state != HC_STATE_SUSPENDED) { + rc = -EINVAL; + goto bail; + } + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + (void)ehci_readl(ehci, &ehci->regs->intr_enable); + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) { + ehci_halt(ehci); + ehci_reset(ehci); + } + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + pxau2h_ehci_clk_set(0); +bail: + spin_unlock_irqrestore(&ehci->lock, flags); + + // could save FLADJ in case of Vaux power loss + // ... we'd only use it to handle clock skew + + return rc; +} + +static int pxau2h_driver_resume (struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + pxau2h_ehci_clk_set(1); + + if (!info->phy_init || info->phy_init(info->phybase)) { + printk("%s phy_init failed\n", __func__); + return 0; + } + + if (time_before(jiffies, ehci->next_statechange)) + udelay(100); + + /* Mark hardware accessible again */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + +#if 0 + /* If CF is still set, we maintained PCI Vaux power. + * Just undo the effect of ehci_pci_suspend(). + */ + if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) { + int mask = INTR_MASK; + + if (!hcd->self.root_hub->do_remote_wakeup) + mask &= ~STS_PCD; + ehci_writel(ehci, mask, &ehci->regs->intr_enable); + ehci_readl(ehci, &ehci->regs->intr_enable); + printk("%s end--------------------------------\n", __func__); + return 0; + } +#endif + + usb_root_hub_lost_power(hcd->self.root_hub); + ehci_halt(ehci); + ehci_reset(ehci); + + /* emptying the schedule aborts any urbs */ + spin_lock_irq(&ehci->lock); + if (ehci->reclaim) + end_unlink_async(ehci); + ehci_work(ehci); + spin_unlock_irq(&ehci->lock); + + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + + /* here we "know" root ports should always stay powered */ + ehci_port_power(ehci, 1); + + hcd->state = HC_STATE_SUSPENDED; + + return 0; +} +#endif + +MODULE_ALIAS("pxau2h-ehci"); + +static struct platform_driver pxau2h_ehci_driver = { + .probe = pxau2h_ehci_probe, + .remove = pxau2h_ehci_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "pxau2h-ehci", + .bus = &platform_bus_type + }, +#ifdef CONFIG_PM + .suspend = pxau2h_driver_suspend, + .resume = pxau2h_driver_resume, +#endif +}; diff --git a/drivers/usb/host/ehci-pxau2o.c b/drivers/usb/host/ehci-pxau2o.c new file mode 100644 index 00000000000000..ad598d3bd85c0c --- /dev/null +++ b/drivers/usb/host/ehci-pxau2o.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2000-2002 by Dima Epshtein + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* #define DRIVER_AUTHOR "Dima Epshtein" */ + +#include +#include +#include + +#include +#include + +#ifdef CONFIG_USB_OTG +#include +#endif + +static struct ehci_hcd *g_ehci; + +void pxa9xx_u2o_host_enable (void) +{ + unsigned base = (unsigned)(&g_ehci->regs->command) & 0xfffff000; +#ifndef CONFIG_USB_OTG + /* Turn off IDPU bit of U2xOTGSC - we are not in OTG mode */ + u2o_set(base, U2xOTGSC, U2xOTGSC_IDPU); +#endif + + /* Set hardware to Host Mode */ + u2o_set(base, U2xUSBMODE, U2xUSBMODE_CM_MASK); + + pr_debug("%s: PORTSC 0x%x USBMODE 0x%x USBSTS 0x%x USBCMD 0x%x\n", + __func__, u2o_get(base, U2xPORTSC), u2o_get(base, U2xUSBMODE), + u2o_get(base, U2xUSBSTS), u2o_get(base, U2xUSBCMD)); +} + +void pxa9xx_ehci_set(int enable) +{ + struct usb_hcd *hcd = ehci_to_hcd(g_ehci); + static int enabled; + int retval = 0; + + pr_debug("%s %s enabled %d\n", __func__, + enable?"enable":"disable", enabled); + + if (enable) { + if (hcd->rh_registered) { + pr_debug("%s not removed???\n", __func__); + usb_remove_hcd(hcd); + } + + retval = usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED); + if (retval < 0) { + printk(KERN_ERR "pxa9xx ehci: hc_reset failed\n"); + goto err; + } + enabled = 1; + } else { + /* + if (hcd->rh_registered) + usb_remove_hcd(hcd); + */ + enabled = 0; + } +err: + return; +} + +#ifndef CONFIG_USB_OTG +static void clock_init(void) +{ + unsigned long flags; + /* Actually, there is no need to disable interrupt, just to be safe */ + local_irq_save(flags); + ACCR1 = ACCR1 | ACCR1_PU_OTG | ACCR1_PU_PLL | ACCR1_PU; + local_irq_restore(flags); +} +#endif + +static int pxa9xx_ehci_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + pr_debug("\n\n%s\n\n", __func__); + pxa9xx_u2o_host_enable(); + + retval = ehci_halt(ehci); + if (retval) { + printk("ehci_halt failed %d\n", retval); + return retval; + } + + /* + * data structure init + */ + retval = ehci_init(hcd); + if (retval) { + printk("ehci_init failed %d\n", retval); + return retval; + } + + hcd->has_tt = 1; + ehci->sbrn = 0x20; + + retval = ehci_reset(ehci); + if (retval) { + printk("ehci_reset failed %d\n", retval); + return retval; + } + + return retval; +} + +static irqreturn_t pxa9xx_ehci_irq (struct usb_hcd *hcd) +{ +#ifdef CONFIG_USB_OTG + if (!otg_is_host()) { + int start = hcd->state; + pr_debug("\n%s start %x\n", __func__, start); + return IRQ_NONE; + } +#endif + + return ehci_irq(hcd); +} + +#ifdef CONFIG_USB_OTG +static void start_hnp(struct ehci_hcd *ehci) +{ + unsigned long flags; + + otg_start_hnp(ehci->transceiver); + + local_irq_save(flags); + ehci->transceiver->state = OTG_STATE_A_SUSPEND; +#if 0 + struct usb_hcd *hcd = ehci_to_hcd(ehci); + const unsigned port = hcd->self.otg_port - 1; + writel(RH_PS_PSS, &ehci->regs->roothub.portstatus [port]); +#endif + local_irq_restore(flags); +} + +static int pxa9xx_ehci_connect(struct usb_hcd *hcd, struct usb_device *udev) +{ + return otg_connect((hcd_to_ehci(hcd))->transceiver, udev); +} + +static int pxa9xx_ehci_disconnect(struct usb_hcd *hcd) +{ + return otg_disconnect((hcd_to_ehci(hcd))->transceiver); +} + +#endif + + +static const struct hc_driver pxa9xx_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "pxa9xx ehci", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = pxa9xx_ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = pxa9xx_ehci_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + +#ifdef CONFIG_USB_OTG + .disconnect = pxa9xx_ehci_disconnect, + .connect = pxa9xx_ehci_connect, +#endif +}; + +static int pxa9xx_ehci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + int irq, retval; + void __iomem *regbase; + struct resource *res; + + if (usb_disabled()) + return -ENODEV; + + hcd = usb_create_hcd(&pxa9xx_ehci_hc_driver, dev, "pxa9xx ehci"); + if (!hcd) { + printk(KERN_ERR "pxa9xx ehci: create_hcd failed\n"); + return -ENOMEM; + } + + ehci = hcd_to_ehci(hcd); + g_ehci = ehci; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "u2o"); + regbase = ioremap_nocache(res->start, res_size(res)); + if (!regbase) { + printk(KERN_ERR "Cannot get regbase 0x%p\n", regbase); + return -ENOMEM; + } + + irq = platform_get_irq(pdev, 0); + if (!irq) { + printk( KERN_ERR "Cannot get irq %x\n", irq); + retval = -ENODEV; + goto err1; + } + + printk("u2oehci regbase 0x%p irq %d\n", regbase, irq); + + hcd->rsrc_start = virt_to_phys(regbase); + hcd->rsrc_len = 0x1ff; + hcd->regs = (void __iomem*)(((unsigned)regbase | 0x100)); + hcd->irq = (unsigned int)irq; + + if (!hcd->regs) { + pr_debug("ioremap failed"); + retval = -ENOMEM; + goto err2; + } + + hcd->self.controller = dev; + hcd->self.bus_name = dev->bus_id; + hcd->product_desc ="pxa9xx ehci"; + + clk_enable(clk_get(NULL, "U2OCLK")); + +#ifndef CONFIG_USB_OTG + /* enable clock & transceiver */ + clock_init(); + /* xcvr_init(); */ + retval = usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED); + if (retval < 0) { + printk(KERN_ERR "pxa9xx_ehci: usb_add_hcd failed.\n"); + goto err2; + } +#else + hcd->self.otg_port = 2; + ehci->transceiver = otg_get_transceiver(); + if (ehci->transceiver) { + otg_set_host(ehci->transceiver, &hcd->self); + } else { + dev_err(hcd->self.controller, "can't find otg transceiver\n"); + return -ENODEV; + } +#endif + + /* + * registers start at offset + */ + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* + * cache this readonly data; minimize chip reads + */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + clk_disable(clk_get(NULL, "U2OCLK")); + return 0; + +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + printk(KERN_ERR "pxa9xx ehci: init error, %d\n", retval); + return retval; +} + +static int pxa9xx_ehci_remove(struct platform_device *dev) +{ + struct ehci_hcd *ehci = dev_get_drvdata(&dev->dev); + struct usb_hcd *hcd = ehci_to_hcd(ehci); + + if (HC_IS_RUNNING(hcd->state)) + hcd->state = HC_STATE_QUIESCING; + + usb_disconnect(&hcd->self.root_hub); + hcd->driver->stop (hcd); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +MODULE_ALIAS("pxau2o-ehci"); + +static struct platform_driver pxa9xx_ehci_driver = { + .probe = pxa9xx_ehci_probe, + .remove = pxa9xx_ehci_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "pxau2o-ehci", + .bus = &platform_bus_type + } +}; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 556c0b48f3abd7..bef4d49aaf074d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -151,6 +151,10 @@ struct ehci_hcd { /* one per controller */ # define COUNT(x) do {} while (0) #endif +#ifdef CONFIG_USB_OTG + struct otg_transceiver *transceiver; +#endif + /* debug files */ #ifdef DEBUG struct dentry *debug_dir; diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index aeb49a8ec41271..0e0bd146db8dfc 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -1,7 +1,15 @@ # -# OTG infrastructure and transceiver drivers +# USB OTG controller driver # +obj-$(CONFIG_USB_PXA3XX_U2D) += pxa310_otg.o +obj-$(CONFIG_USB_PXA3XX_U2D) += pxa930_otg.o +obj-$(CONFIG_USB_OTG_PXA3XX) += pxa3xx_otg_pmic.o +obj-$(CONFIG_USB_OTG_PXA3XX) += pxa3xx_otg.o +obj-$(CONFIG_USB_PXA_U2O) += pxa_u2o.o +ifeq ($(CONFIG_USB_PXA3XX_UDC),y) +obj-$(CONFIG_USB_OTG_PXA3XX) += pxa300_otg.o +endif # infrastructure obj-$(CONFIG_USB_OTG_UTILS) += otg.o diff --git a/drivers/usb/otg/pxa300_otg.c b/drivers/usb/otg/pxa300_otg.c new file mode 100644 index 00000000000000..da97c875852b4b --- /dev/null +++ b/drivers/usb/otg/pxa300_otg.c @@ -0,0 +1,320 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pxa300_otg.h" +#include "../gadget/pxa_comp.h" +#include "../gadget/pxa27x_udc.h" +extern struct pxa27x_udc *get_the_controller(void); +extern int is_cable_attached(void); +/****************************************************************** + * Pxa3xx OTG transceiver functions + ****************************************************************** + */ +static int xceiv_mode = USB_OTG_OFF; +static int pxa300_otgx_get_mode(void) +{ + return xceiv_mode; +} +/* Configures USB host port2 to the desired mode */ +static int pxa300_otgx_set_mode(int mode) +{ + int status = 0; + int up2ocr = UP2OCR; + + if (mode == USB_OTG_CARKIT) + return 0; + up2ocr &= ~(7 << OTG_UP2OCR_SEOS_SHIFT); + up2ocr &= ~(OTG_UP2OCR_HXS | OTG_UP2OCR_HXOE); + up2ocr &= ~(OTG_UP2OCR_DPPUE | OTG_UP2OCR_DMPUE); + up2ocr &= ~(OTG_UP2OCR_DMPDE | OTG_UP2OCR_DPPDE); + switch (mode) { + /* Both differential port and single-ended port + * are turned off */ + case USB_OTG_OFF: + break; + /* Differential Port is off, + * Single-Ended Port2 is Non-OTG USB Client */ + case USB_NON_OTG_CLIENT_SEP: + up2ocr |= (2 << OTG_UP2OCR_SEOS_SHIFT); + break; + + /* Differential Port is off, + * Single-Ended Port2 is Non-OTG USB Host */ + case USB_NON_OTG_HOST_SEP: + up2ocr |= (3 << OTG_UP2OCR_SEOS_SHIFT); + break; + + /* Differential Port is off, Single-Ended Port2 is interfaced to + * external OTG transceiver and is directed into USB client + * controller + */ + case USB_EXT_OTG_CLIENT_SEP: + up2ocr |= (4 << OTG_UP2OCR_SEOS_SHIFT); + break; + + /* Differential Port is off, Single-Ended Port2 is interfaced to + * external OTG transceiver and is directed into USB host + * controller + */ + case USB_EXT_OTG_HOST_SEP: + up2ocr |= (5 << OTG_UP2OCR_SEOS_SHIFT); + break; + /* Differential Port is Non-OTG USB client, + * Single-Ended Port 2 is Non-OTG USB host + */ + case USB_NON_OTG_HOST_SEP_CLIENT_DP: + up2ocr |= OTG_UP2OCR_HXOE; + up2ocr |= (3 << OTG_UP2OCR_SEOS_SHIFT); + break; + + /* Differential Port is Non-OTG USB host, Single-Ended Port 2 is + * Non-OTG USB client + */ + case USB_NON_OTG_CLIENT_SEP_HOST_DP: + up2ocr |= OTG_UP2OCR_HXOE; + up2ocr |= OTG_UP2OCR_HXS; + up2ocr |= (2 << OTG_UP2OCR_SEOS_SHIFT); + break; + + /* Differential Port is USB OTG host, Single-Ended Port 2 is + * interfaced to external charge pump + */ + case USB_INT_OTG_HOST_DP: + up2ocr |= OTG_UP2OCR_HXOE; + up2ocr |= OTG_UP2OCR_HXS; + up2ocr |= (7 << OTG_UP2OCR_SEOS_SHIFT); + up2ocr |= (OTG_UP2OCR_DPPDE | OTG_UP2OCR_DMPDE); + break; + + /* Differential Port is USB OTG client, Single-Ended Port 2 is + * interfaced to external charge pump + */ + case USB_INT_OTG_CLIENT_DP: + case USB_OTG_LP: + up2ocr |= OTG_UP2OCR_HXOE; + up2ocr |= (6 << OTG_UP2OCR_SEOS_SHIFT); + up2ocr |= OTG_UP2OCR_DPPUE; + break; + + /* Differential Port is Non-OTG USB Client, + * Single-Ended Port2 is off */ + case USB_NON_OTG_CLIENT_DP: + up2ocr |= OTG_UP2OCR_HXOE; + break; + + /* Differential Port is Non-OTG USB Host, + * Single-Ended Port2 is off */ + case USB_NON_OTG_HOST_DP: + up2ocr |= (OTG_UP2OCR_HXOE | OTG_UP2OCR_HXS); + break; + + default: + status = OTG_INVALID_PARAMETER; + break; + } + UP2OCR = up2ocr; + + pr_debug("defore transceiver mode %d!\n", xceiv_mode); + xceiv_mode = mode; + pr_debug("set transceiver mode %d!\n", xceiv_mode); + return status; +} + +static void pxa300_otgx_init(void) +{ + pxa300_otgx_set_mode(USB_INT_OTG_CLIENT_DP); +} + +static enum otg_function pxa300_otgx_detect_default_func(void) +{ + int value = OTG_B_DEVICE; + struct pxa27x_udc *dev = get_the_controller(); + + if (dev->mach->udc_is_miniA && dev->mach->udc_is_miniA()) + value = OTG_A_DEVICE; + + return value; +} + +/* Called to start data-line SRP + */ +static int pxa300_otgx_dp_session(void) +{ + int up2ocr; + + /* Enable D+ pull-up resister to enable Data line SRP */ + up2ocr = UP2OCR; + up2ocr &= ~OTG_UP2OCR_DPPDE; + up2ocr |= OTG_UP2OCR_DPPUE; + UP2OCR = up2ocr; + mdelay(T_B_DATA_PLS); + + /* Remove D+ pull-up resister to stop data line SRP */ + up2ocr = UP2OCR; + up2ocr &= ~OTG_UP2OCR_DPPUE; + up2ocr |= OTG_UP2OCR_DPPDE; + UP2OCR = up2ocr; + return 0; +} + +/* Called to start SRP + */ +static int pxa300_otgx_vbus_session(struct pxa_otg *pOtgHandle) +{ +#ifdef PXA3xx_OTG_VBUS_PMIC + pOtgHandle->pmic_ops->otg_set_vbus(VBUS_PULSE); + mdelay(T_B_SRP_INIT - T_B_DATA_PLS); + pOtgHandle->pmic_ops->otg_set_vbus(VBUS_LOW); +#else + ulpi_rtsm(); + + /* start VBUS pulse SRP */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DISCHRGVBUS); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_CHRGVBUS); + + /* stop VBUS pulse SRP */ + mdelay(T_B_SRP_INIT - T_B_DATA_PLS); + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_CHRGVBUS); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DISCHRGVBUS); + +#endif + return 0; +} + +static int pxa300_otgx_check_b_hnp(void) +{ + int ret = 0; + + if (UDCCR_BHNP & UDCCR) + ret = 1; + return ret; +} + +static int pxa300_otgx_check_vbus(void) +{ + return is_cable_attached(); +} + +static int pxa300_otgx_start_autoresume(void) +{ + return 0; +} + +static int pxa300_otgx_drive_resume(void) +{ + return 0; +} + +static struct otg_xceiv_ops pxa300_otg_xceiv_ops = { + .otgx_get_mode = pxa300_otgx_get_mode, + .otgx_set_mode = pxa300_otgx_set_mode, + .otgx_init = pxa300_otgx_init, + .otgx_detect_default_func = pxa300_otgx_detect_default_func, + .otgx_dp_session = pxa300_otgx_dp_session, + .otgx_vbus_session = pxa300_otgx_vbus_session, + .otgx_check_b_hnp = pxa300_otgx_check_b_hnp, + .otgx_check_vbus = pxa300_otgx_check_vbus, + .otgx_start_autoresume = pxa300_otgx_start_autoresume, + .otgx_drive_resume = pxa300_otgx_drive_resume, +}; + +struct otg_xceiv_ops *init_pxa300_otg_xceiv_ops(void) +{ + return &pxa300_otg_xceiv_ops; +} +/****************************************************************** + * Pxa3xx OTG controller functions + ****************************************************************** + */ + +int pxa300_otgc_init(void) +{ + if (UDCCR & UDCCR_UDE) + UDCCR = UDCCR_UDE | UDCCR_OEN; + else + return OTG_UDC_DISABLED; + return 0; +} + +void pxa300_otgc_interrupt_init(void) +{ + pxa300_otgx_init(); + UP2OCR |= OTG_UP2OCR_IDON; + UDCOTGICR |= (OTG_SETFEATURE | UDCOTGISR_IRIDR | UDCOTGISR_IRIDF); +} + +/* This function is called when USB OTG interrupt happened + */ +int pxa300_otgc_interrupt_handle(struct pxa_otg *pOtgHandle) +{ + u32 otgisr; + u8 interrupt_state = OTG_INT_INIT; + struct pxa27x_udc *dev = get_the_controller(); + + pr_debug("otgisr 0x%x, udccr 0x%x\n", + UDCOTGISR, UDCCR); + otgisr = UDCOTGISR; + + /* OTG SET FEATURE received */ + if (otgisr & OTG_SETFEATURE) { + if (UDCCR & UDCCR_BHNP) + pOtgHandle->otg.gadget->b_hnp_enable = 1; + + if (UDCCR & UDCCR_AHNP) + pOtgHandle->otg.gadget->a_hnp_support = 1; + + if (UDCCR & UDCCR_AALTHNP) + pOtgHandle->otg.gadget->a_alt_hnp_support = 1; + + UDCOTGISR = OTG_SETFEATURE; + } + + if (otgisr & (UDCOTGISR_IRIDF | UDCOTGISR_IRIDR)) { + if (dev->mach->udc_is_miniA && dev->mach->udc_is_miniA()) + /* mini-A is plugged in */ + interrupt_state |= OTG_INT_IDF; + else + /* mini-A is plugged out */ + interrupt_state |= OTG_INT_IDR; + + UDCOTGISR = (UDCOTGISR_IRIDR | UDCOTGISR_IRIDF); + } + + pr_debug("interrupt type %x\n", interrupt_state); + return interrupt_state; +} + +void pxa300_otgc_init_gadget(void) +{ + UDCOTGICR &= ~(OTG_SETFEATURE | UDCOTGISR_IRIDR | UDCOTGISR_IRIDF); + UP2OCR &= ~(OTG_UP2OCR_IDON | (0x7 << OTG_UP2OCR_SEOS_SHIFT)); +} + +void pxa300_otgc_deinit(void) +{ +} + +static struct otg_ctrl_ops pxa300_otg_ctrl_ops = { + .otgc_init = pxa300_otgc_init, + .otgc_interrupt_init = pxa300_otgc_interrupt_init, + .otgc_interrupt_handle = pxa300_otgc_interrupt_handle, + .otgc_init_gadget = pxa300_otgc_init_gadget, + .otgc_deinit = pxa300_otgc_deinit, +}; + +struct otg_ctrl_ops *init_pxa300_otg_ctrl_ops(void) +{ + return &pxa300_otg_ctrl_ops; +} + diff --git a/drivers/usb/otg/pxa300_otg.h b/drivers/usb/otg/pxa300_otg.h new file mode 100644 index 00000000000000..3acf8cee63a031 --- /dev/null +++ b/drivers/usb/otg/pxa300_otg.h @@ -0,0 +1,29 @@ +#ifndef __PXA300_USB_OTG_CONTROLLER__ +#define __PXA300_USB_OTG_CONTROLLER__ + +/* fields and bits for UP2OCR */ +#define OTG_UP2OCR_CPVEN (1u << 0) +#define OTG_UP2OCR_CPVEP (1u << 1) +#define OTG_UP2OCR_DPPDE (1u << 2) +#define OTG_UP2OCR_DMPDE (1u << 3) +#define OTG_UP2OCR_DPPUE (1u << 4) +#define OTG_UP2OCR_DMPUE (1u << 5) +#define OTG_UP2OCR_DPPUBE (1u << 6) +#define OTG_UP2OCR_DMPUBE (1u << 7) +#define OTG_UP2OCR_EXSP (1u << 8) +#define OTG_UP2OCR_EXSUS (1u << 9) +#define OTG_UP2OCR_IDON (1u << 10) +#define OTG_UP2OCR_HXS (1u << 16) +#define OTG_UP2OCR_HXOE (1u << 17) +#define OTG_UP2OCR_SEOS_SHIFT (24) + +/* fields and bits for UDCOTGISR */ +#define UDCOTGISR_IRIDF (1u << 0) +#define UDCOTGISR_IRIDR (1u << 1) +#define OTG_SETFEATURE (1u << 24) + +#define UDCOTGISR __REG(0x4060001C) /* UDC OTG Interrupt Status Register*/ +#define UP3OCR __REG(0x40600024) /* Port 3 control register */ + +#endif + diff --git a/drivers/usb/otg/pxa310_otg.c b/drivers/usb/otg/pxa310_otg.c new file mode 100644 index 00000000000000..b536e0c199c706 --- /dev/null +++ b/drivers/usb/otg/pxa310_otg.c @@ -0,0 +1,1054 @@ +/* #define DEBUG */ +#ifdef CONFIG_CPU_PXA310 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MACH_LITTLETON +#define ENABLE_USB_HEADSET +#endif + +#include +#include "pxa310_otg.h" + +#include "../gadget/pxa_comp.h" +#include "../gadget/pxa27x_udc.h" +/* special for headset*/ +#ifdef ENABLE_USB_HEADSET +#include +#include + +struct timer_list otg_carkit_timer; + + +static void otg_set_headset_volume(int headset_type); +static void otg_carkit_timer_callback(unsigned long data); + +#endif + +extern void enable_oscc_pout(void); +extern void disable_oscc_pout(void); + +extern void u2d_clk_enable(void); +extern void u2d_clk_restore(void); +extern void u2d_clk_set(int enable); +extern void u2d_irq_set(int en); +extern int get_u2d_bugs(void); +extern struct pxa27x_udc *get_the_controller(void); + +static int xcvr_init = 1; +static struct pxa_otg *p_pxa_otg; +/*---------------------------------------------------------------- + Special for ULPI +------------------------------------------------------------------*/ +static enum u2d_phy_mode xcvr_mode; + +static void reset_xcvr_init(void) +{ + xcvr_init = 1; +} + +static void ulpi_dat3_work(void) +{ + struct pxa27x_udc *dev = get_the_controller(); + unsigned long flags; + u32 u2dotgcr; + + if (!dev->ulpi_dat3_work) + return; + local_irq_save(flags); + /* enable u2d function */ + u2d_clk_set(1); + + dev->mach->ulpi_dat3(0); + enable_oscc_pout(); + + u2dotgcr = U2DOTGCR; + u2dotgcr |= U2DOTGCR_ULE; + U2DOTGCR = u2dotgcr; + u2dotgcr = U2DOTGCR; + + DMSG("%s\n", __func__); + u2d_irq_set(1); + + dev->ulpi_dat3_work = 0; + local_irq_restore(flags); +} + +static int ulpi_dat3_int_set(int enable) +{ + struct pxa27x_udc *dev = get_the_controller(); + int irq = gpio_to_irq(dev->ulpi_int); + unsigned long flags; + u32 u2dotgcr; + int err; + + local_irq_save(flags); + if (enable) { + if (dev->stp_gpio_irq_en) { + printk(KERN_ERR "re-enterance of %s\n", __func__); + goto done; + } + + dev->mach->ulpi_dat3(1); + err = gpio_request(dev->ulpi_int, "ULPI INT"); + if (err) { + gpio_free(dev->ulpi_int); + goto done; + } + gpio_direction_input(dev->ulpi_int); + + enable_irq(irq); + dev->stp_gpio_irq_en = 1; + + gpio_free(dev->ulpi_int); + u2d_irq_set(0); + u2dotgcr = U2DOTGCR; + u2dotgcr &= ~(U2DOTGCR_ULAF | U2DOTGCR_ULE); + U2DOTGCR = u2dotgcr; + u2dotgcr = U2DOTGCR; + + u2d_clk_set(0); + } else { + if (!dev->stp_gpio_irq_en) + goto done; + disable_irq(irq); + dev->ulpi_dat3_work = 1; + ulpi_dat3_work(); + dev->stp_gpio_irq_en = 0; + } + DMSG("%s %d, orig %d\n", __func__, enable, dev->stp_gpio_irq_en); + local_irq_restore(flags); +done: + return 0; +} + +static int ulpi_rtsm(void) +{ + struct pxa27x_udc *dev = get_the_controller(); + u32 u2dotgcr = U2DOTGCR; + int count = 100000; + + DMSG("%s, U2DOTGCR %x U2DOTGUSR %x\n", __func__, U2DOTGCR, U2DOTGUSR); + u2dotgcr |= U2DOTGCR_UTMID; + if ((U2DOTGUSR & 0xf0000000)) { + + /* switch to SYNCH mode first */ + u2dotgcr = U2DOTGCR; + u2dotgcr |= U2DOTGCR_UTMID; + u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF | U2DOTGCR_ULAF); + U2DOTGCR = u2dotgcr; + + u2dotgcr = U2DOTGCR; + u2dotgcr |= U2DOTGCR_RTSM; + U2DOTGCR = u2dotgcr; + + u2dotgcr = U2DOTGCR; + u2dotgcr |= U2DOTGCR_ULAF; + u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF); + U2DOTGCR = u2dotgcr; + u2dotgcr = U2DOTGCR; + + } + + while ((U2DOTGUSR & 0xf0000000) && (U2DOTGCR & U2DOTGCR_RTSM) && count) + count--; + + if (count <= 0) { + printk(KERN_ALERT "%s time out, reset_xcvr!!! USR %x\n", + __func__, U2DOTGUSR); + dev->mach->reset_xcvr(); + } + xcvr_mode = PRE_SYNCH; + DMSG("%s end, U2DOTGCR %x U2DOTGUSR %x\n", + __func__, U2DOTGCR, U2DOTGUSR); + + return 0; +} + +static enum u2d_phy_mode ulpi_get_phymode(void) +{ + enum u2d_phy_mode state; + + ulpi_dat3_int_set(0); + + state = (U2DOTGUSR & 0xF0000000) >> 28; + /* in case when set UDE it would enter LOWPOWER mode automatically + * if no SOFs longer than 3ms */ + if (state == LOWPOWER) + xcvr_mode = LOWPOWER; + + if ((state != xcvr_mode) && (xcvr_mode != PRE_SYNCH)) { + printk(KERN_DEBUG "ULPI mode %d not aligned, should be %d\n", + state, xcvr_mode); + xcvr_mode = state; + if (state) + ulpi_rtsm(); + } + + return xcvr_mode; +} + +static int ulpi_reg_read(u8 reg, u8 *value) +{ + int i = 50000; + enum u2d_phy_mode state = ulpi_get_phymode(); + + if ((state != SYNCH) && (state != PRE_SYNCH)) { + pr_debug(" not in SYNCH mode!!!"); + return -1; + } + + U2DOTGUCR = U2DOTGUCR_RUN | U2DOTGUCR_RNW | (reg << U2DOTGUCR_ADDR_S); + + while ((U2DOTGUCR & U2DOTGUCR_RUN) && i--) ; + + if (i <= 0) { + printk(KERN_DEBUG "Read ULPI register Time out," + " reg %x otgucr %x, usr %x ucr %x\n", + reg, U2DOTGUCR, U2DOTGUSR, U2DOTGCR); + return -1; + } + + *value = (u8)(U2DOTGUCR & U2DOTGUCR_RDATA); + + DMSG("read ulpi reg %x val %x\n", reg, *value); + return 0; + +} + +static int ulpi_reg_write(u8 reg, u8 value) +{ + int i = 50000; + enum u2d_phy_mode state = ulpi_get_phymode(); + + if ((state != SYNCH) && (state != PRE_SYNCH)) { + pr_debug(": not in SYNCH mode!!!"); + return -1; + } + + U2DOTGUCR = U2DOTGUCR_RUN | (reg << U2DOTGUCR_ADDR_S) \ + | (value << U2DOTGUCR_WDATA_S); + + while ((U2DOTGUCR & U2DOTGUCR_RUN) && i--) ; + + if (i <= 0) { + printk(KERN_DEBUG "Write ULPI register Time out," + " reg %x val %x\n", reg, (int)value); + return -1; + } + DMSG("write ulpi reg %x val %x\n", reg, (int)value); + + return 0; +} + +static int ulpi_set_phymode(enum u2d_phy_mode mode) +{ + u32 state; + u32 u2dotgcr; + u32 u2dp3cr; + + state = ulpi_get_phymode(); + ulpi_dat3_int_set(0); + u2dotgcr = U2DOTGCR; + u2dp3cr = U2DP3CR; + + if ((state == mode) && !xcvr_init) { + if (mode == SYNCH) + U2DOTGCR &= ~(U2DOTGCR_UTMID); + + if (mode == LOWPOWER) + if (get_u2d_bugs() & U2D_FIX_ULPI_STP) { + /* disable oscc reference clock, + * and u2d clock */ + disable_oscc_pout(); + + /* enable ULPI_DAT3 gpio interrupt */ + ulpi_dat3_int_set(1); + } + + return 0; + } + + if (xcvr_init == 1) + xcvr_init = 0; + + if ((state != SYNCH) && (state != PRE_SYNCH)) + ulpi_rtsm(); + + switch (mode) { + + case SYNCH: + /* disable D+/D- pulldown resistor */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DMPULLDOWN); + + /* Enable ID pullup */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_IDPULLUP); + + /* clear UDE and ULE before enable UTMI */ + U2DOTGICR = 0; + U2DCR &= ~U2DCR_UDE; + U2DOTGCR &= ~U2DOTGCR_ULE; + + /* enable the UTMI */ + u2dotgcr = U2DOTGCR; + u2dotgcr &= ~(U2DOTGCR_UTMID | U2DOTGCR_SMAF); + U2DOTGCR = u2dotgcr; + u2dotgcr = U2DOTGCR; + + /* enable the ULE again */ + U2DOTGCR |= U2DOTGCR_ULE; + U2DOTGICR = U2DOTGINT_DEFAULT; + + break; + + case SER_6PIN: + case SER_3PIN: + /* enable D+/D- pulldown resistor */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DMPULLDOWN); + + /* switch to serial mode */ + u2dp3cr &= ~(U2DP3CR_P2SS); + if (mode == SER_3PIN) + u2dp3cr |= 0x1 << U2DP3CR_P2SS_S; + U2DP3CR = u2dp3cr; + + /* set PHY into host mode */ + ulpi_reg_write(ULPI_FUNCTION_CONTROL_SET, 0x45); + + /* set ULPI PHY to serial mode */ + if (mode == SER_3PIN) + ulpi_reg_write(ULPI_INTERFACE_CONTROL, ULPI_IC_3PIN); + else + ulpi_reg_write(ULPI_INTERFACE_CONTROL, ULPI_IC_6PIN); + + /* enable serial mode */ + u2dotgcr |= U2DOTGCR_SMAF; + u2dotgcr &= ~(U2DOTGCR_ULAF | U2DOTGCR_CKAF); + U2DOTGCR = u2dotgcr; + pr_debug("U2DOTGCR %08X, U2DOTGICR %08X," + " U2DP3CR %08x, U2DOTGUSR %08X\n", + U2DOTGCR, U2DOTGICR, U2DP3CR, U2DOTGUSR); + break; + + case LOWPOWER: + /* enable D+/D- pulldown resistor */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DMPULLDOWN); + + /* Disable ID pullup */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_IDPULLUP); + + /* clear SuspendM in UPLI PHY */ + ulpi_reg_write(ULPI_FUNCTION_CONTROL_CLEAR, ULPI_FC_SUSPENDM); + + + if (get_u2d_bugs() & U2D_FIX_ULPI_STP) { + /* disable oscc reference clock, and u2d clock */ + disable_oscc_pout(); + + /* enable ULPI_DAT3 gpio interrupt */ + ulpi_dat3_int_set(1); + } + break; + + case CARKIT: +#ifdef ENABLE_USB_HEADSET + if (machine_is_littleton()) { + /* Enable Carkit mode */ + + /* disable D+/D- pulldown resistor */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, + ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, + ULPI_OC_DMPULLDOWN); + + /* Enable ID pullup */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_IDPULLUP); + + /* Configure Carkit Interrupts */ + ulpi_reg_write(ULPI_CARKIT_INT_ENABLE_SET, + ULPI_CK_IDFLOATRISE); + ulpi_reg_write(ULPI_CARKIT_INT_ENABLE_SET, + ULPI_CK_IDFLOATFALL); + ulpi_reg_write(ULPI_CARKIT_INT_ENABLE_SET, + ULPI_CK_RIDINTEN); + + /* ULPI Switch to Carkit Mode */ + + U2DOTGCR |= U2DOTGCR_UTMID; + + U2DOTGICR = U2DOTGINT_DEFAULT; + + U2DOTGCR |= U2DOTGCR_ULE; + + /* XCVR Carkit mode */ + ulpi_reg_write(ULPI_INTERFACE_CONTROL, ULPI_IC_CARKIT); + + /* Carkit Mode Alternate Function Select */ + u2dotgcr = U2DOTGCR; + u2dotgcr &= ~(U2DOTGCR_SMAF); + u2dotgcr &= ~(U2DOTGCR_ULAF); + u2dotgcr |= U2DOTGCR_CKAF; + U2DOTGCR = u2dotgcr; + } + break; +#endif + default: + printk(KERN_ERR "unsupported ULPI operation modes %d\n", mode); + return -1; + break; + } + + xcvr_mode = mode; + + return 0; +} + +static void dump_ulpi_regs(void) +{ + u8 val; + + ulpi_reg_read(ULPI_VENDOR_LOW, &val); + printk(KERN_DEBUG "vendor ID low %02X\n", val); + + ulpi_reg_read(ULPI_VENDOR_HIGH, &val); + printk(KERN_DEBUG "vendor ID high %02X\n", val); + + ulpi_reg_read(ULPI_PRODUCT_LOW, &val); + printk(KERN_DEBUG "vendor PRODUCT low %02X\n", val); + + ulpi_reg_read(ULPI_PRODUCT_HIGH, &val); + printk(KERN_DEBUG "vendor PRODUCT high %02X\n", val); + + ulpi_reg_read(ULPI_FUNCTION_CONTROL, &val); + printk(KERN_DEBUG "function control %02X\n", val); + + ulpi_reg_read(ULPI_INTERFACE_CONTROL, &val); + printk(KERN_DEBUG "interface control %02X\n", val); + + ulpi_reg_read(ULPI_OTG_CONTROL, &val); + printk(KERN_DEBUG "otg control %02X\n", val); + + ulpi_reg_read(ULPI_INT_RISE, &val); + printk(KERN_DEBUG "interrupt enable rising %02X\n", val); + + ulpi_reg_read(ULPI_INT_FALL, &val); + printk(KERN_DEBUG "interrupt enable falling %02X\n", val); + + ulpi_reg_read(ULPI_INT_STATUS, &val); + printk(KERN_DEBUG "interrupt status %02X\n", val); + + ulpi_reg_read(ULPI_INT_LATCH, &val); + printk(KERN_DEBUG "interrupt latch %02X\n", val); + +} + +/****************************************************************** + * headset special + ****************************************************************** + */ +#ifdef ENABLE_USB_HEADSET +/* Attempt to detect USB headset via USB Cable's Resitor ID */ +static int ulpi_detect_headset(void) +{ + u32 state = ulpi_get_phymode(); + u8 vendor_rid = 0; + int ret = 0; + int i = 0; + + if (xcvr_init) + return 0; + + if ((state != SYNCH) && (state != PRE_SYNCH)) + ulpi_rtsm(); + + /* disable RidConversionDone interrupt and RID Short Cycle */ + ulpi_reg_write(ULPI_VENDOR_RID_CONV, 0); + + /* perform conversion */ + ulpi_reg_write(ULPI_VENDOR_RID_CONV_SET, RID_CONV_START); + + /* Wait until RID Conversion is complete */ + for (i = 0; i < 10000; i++) { + ulpi_reg_read(ULPI_VENDOR_RID_CONV, &vendor_rid); + if (vendor_rid & RID_CONV_DONE) + break; + } + + pr_debug("USB Headset Vendor RID: 0x%08x\n", vendor_rid); + + vendor_rid &= RID_VALUE_MASK; + + if (vendor_rid == RID_0) + return ret; + + if (vendor_rid == RID_100K) { + printk(KERN_NOTICE "Headset detected RID = 100k ohm\n"); + /* May need adjustment based on headset */ + /* bin.yang@marvell.com change it base on the USB headset + of Motorola */ + ret = USB_HEADSET_MONO_MIC; + } + + if (vendor_rid == RID_200K) { + printk(KERN_NOTICE "Headset detected RID = 200k ohm\n"); + /* May need adjustment based on headset */ + ret = USB_HEADSET_STEREO; + } + + if (vendor_rid == RID_440K) { + printk(KERN_NOTICE "Headset detected RID = 440k ohm\n"); + /* May need adjustment based on headset */ + ret = USB_HEADSET_MONO_MIC; + } + + if (!ret) + printk(KERN_NOTICE "Headset configuration not available" + "for Vendor RID: 0x%08x\n", vendor_rid); + + return ret; +} + +/* Configure default volume controls for headset */ +static void otg_set_headset_volume(int headset_type) +{ + u32 state = ulpi_get_phymode(); + + if ((state != SYNCH) && (state != PRE_SYNCH)) + ulpi_rtsm(); + + /* Turn on stereo channels */ + micco_codec_write(MICCO_MUX_STEREO_CH1, 0x7f); + micco_codec_write(MICCO_MUX_STEREO_CH2, 0x7f); + + switch (headset_type) { + case USB_HEADSET_STEREO: + /* Enable Stereo */ + /* Enable carkit regs */ + /* Set ULPI DP/DM Pins to audio channels */ + ulpi_reg_write(ULPI_CARKIT_CONTROL_SET, ULPI_CK_SPKLEFTEN); + ulpi_reg_write(ULPI_CARKIT_CONTROL_SET, ULPI_CK_SPKRIGHTEN); + micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1, 0xd7); + /* Enable onboard MIC */ + micco_codec_write(MICCO_MIC_PGA, 0x2f); + printk(KERN_NOTICE "Stereo headset enabled\n"); + break; + + case USB_HEADSET_MONO_MIC: + /* Enable Mono */ + micco_codec_write(MICCO_STEREO_AMPLITUDE_CH1, 0x97); + /* Set ULPI DP/DM Pins to audio channels */ + ulpi_reg_write(ULPI_CARKIT_CONTROL_SET, ULPI_CK_SPKLEFTEN); + ulpi_reg_write(ULPI_CARKIT_CONTROL_SET, ULPI_CK_SPKMICEN); + micco_codec_write(MICCO_STEREO_AMPLITUDE_CH2, 0x3f); + /* Enable headset MIC */ + micco_codec_write(MICCO_MIC_PGA, 0x5f); + printk(KERN_NOTICE "Mono/mic headset enabled\n"); + break; + + default: + printk(KERN_NOTICE "Invalid headset type=%d\n", headset_type); + break; + } + + /* enable sidetone */ + micco_codec_write(MICCO_SIDETONE, 0x80); + return; +} + +/* Poll ULPI XCVR to determine if OTG device is removed */ +static void otg_carkit_timer_callback(unsigned long data) +{ + struct pxa_otg *pOtgHandle = p_pxa_otg; + u8 tmp_reg = 0; + u32 otgisr = 0; + + u2d_clk_enable(); + /* Disable OTG interrupts temporarily */ + otgisr = U2DOTGISR; + U2DOTGISR = 0; + ulpi_rtsm(); + U2DOTGISR = otgisr; + + /* Check if ID pin is floating */ + ulpi_reg_read(ULPI_CARKIT_INT_STATUS, &tmp_reg); + + if (tmp_reg & ULPI_CK_IDFLOAT) { + /* Disable headset configuration */ + printk(KERN_NOTICE "USB Headset removed!\n"); + + /* Disable speakers/mic on carkit regs */ + ulpi_reg_write(ULPI_CARKIT_CONTROL_CLEAR, ULPI_CK_SPKLEFTEN); + ulpi_reg_write(ULPI_CARKIT_CONTROL_CLEAR, ULPI_CK_SPKRIGHTEN); + ulpi_reg_write(ULPI_CARKIT_CONTROL_CLEAR, ULPI_CK_SPKMICEN); + + + /* Disable ID pullup */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_IDPULLUP); + + /* Configure Carkit Interrupts */ + ulpi_reg_write(ULPI_CARKIT_INT_ENABLE_CLEAR, + ULPI_CK_IDFLOATRISE); + ulpi_reg_write(ULPI_CARKIT_INT_ENABLE_CLEAR, + ULPI_CK_IDFLOATFALL); + ulpi_reg_write(ULPI_CARKIT_INT_ENABLE_CLEAR, ULPI_CK_RIDINTEN); + + pOtgHandle->pmic_ops->otg_set_vbus(VBUS_LOW); + pOtgHandle->pmic_ops->otg_set_vbus_ic(OTG_B_DEVICE); + + ulpi_set_phymode(LOWPOWER); + } else { + /* Return to carkit mode */ + ulpi_set_phymode(CARKIT); + otg_carkit_timer.expires = jiffies + + msecs_to_jiffies(OTG_CARKIT_POLL_DELAY); + add_timer(&otg_carkit_timer); + } + u2d_clk_restore(); + + return; +} +#endif + +static int get_headset_type(struct pxa_otg *pOtgHandle) +{ + int headset_type = 0; + +#ifdef ENABLE_USB_HEADSET + if (machine_is_littleton()) { + headset_type = ulpi_detect_headset(); + if (headset_type > 0) { + p_pxa_otg = pOtgHandle; + + /* initialize the headset module*/ + pOtgHandle->pmic_ops->otg_set_vbus(VBUS_HIGH); + otg_set_headset_volume(headset_type); + otg_carkit_timer.data = headset_type; + pOtgHandle->otg.default_a = OTG_B_DEVICE; + pOtgHandle->otg.state = OTG_STATE_B_IDLE; + } + } +#endif + return headset_type; +} + +static void headset_init(void) +{ +#ifdef ENABLE_USB_HEADSET + if (machine_is_littleton()) { + init_timer(&otg_carkit_timer); + otg_carkit_timer.data = 0; + otg_carkit_timer.function = otg_carkit_timer_callback; + } +#endif +} + +static void headset_deinit(void) +{ +#ifdef ENABLE_USB_HEADSET + if (machine_is_littleton()) + /* Delete Carkit polling timer */ + del_timer(&otg_carkit_timer); +#endif +} + +static void headset_polling(void) +{ +#ifdef ENABLE_USB_HEADSET + if (machine_is_littleton()) { + /* set carkit mode polling timer */ + otg_carkit_timer.expires = jiffies + + msecs_to_jiffies(OTG_CARKIT_POLL_DELAY); + add_timer(&otg_carkit_timer); + } +#endif +} + + +/*----------------------------------------------------------------*/ +/****************************************************************** + * Pxa3xx OTG transceiver functions + ****************************************************************** + */ +static int xceiv_mode = USB_OTG_OFF; +static int pxa310_otgx_get_mode(void) +{ + int state = ulpi_get_phymode(); + + switch (state) { + case LOWPOWER: + xceiv_mode = USB_OTG_LP; + break; + case SYNCH: + xceiv_mode = USB_INT_OTG_CLIENT_DP; + break; + case SER_6PIN: + xceiv_mode = USB_INT_OTG_HOST_DP; + break; + case CARKIT: + xceiv_mode = USB_OTG_CARKIT; + break; + case PRE_SYNCH: + xceiv_mode = USB_OTG_PRE_SYNCH; + break; + default: + break; + } + return xceiv_mode; +} + +/* Configures USB host port2 to the desired mode */ +static int pxa310_otgx_set_mode(int mode) +{ + int status = 0; + + switch (mode) { + + case USB_OTG_LP: + ulpi_set_phymode(LOWPOWER); + break; + case USB_INT_OTG_CLIENT_DP: + ulpi_set_phymode(SYNCH); + break; + + case USB_INT_OTG_HOST_DP: + ulpi_set_phymode(SER_6PIN); + break; + case USB_OTG_CARKIT: + ulpi_set_phymode(CARKIT); + break; + case USB_OTG_PRE_SYNCH: + ulpi_set_phymode(PRE_SYNCH); + break; + default: + status = OTG_INVALID_PARAMETER; + break; + } + + pr_debug("defore transceiver mode %d!\n", xceiv_mode); + xceiv_mode = mode; + pr_debug("set transceiver mode %d!\n", xceiv_mode); + return status; +} + +static void pxa310_otgx_init(void) +{ + ulpi_rtsm(); +} + +#define USB_OTGID_PIN MFP_PIN_GPIO106 +static enum otg_function pxa310_otgx_detect_default_func(void) +{ + struct pxa27x_udc *dev = get_the_controller(); + u8 tmp; + + /* enable OTG ID pullup register */ + ulpi_dat3_int_set(0); + ulpi_rtsm(); + if (ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_IDPULLUP)) + goto err; + if (ulpi_reg_read(ULPI_INT_STATUS, &tmp)) + goto err; + + /* enable ID detect */ + if (tmp & ULPI_INT_IDGND) + return OTG_B_DEVICE; + else + return OTG_A_DEVICE; + +err: + dev->mach->reset_xcvr(); + return OTG_B_DEVICE; +} + +/* Called to start data-line SRP + */ +static int pxa310_otgx_dp_session(void) +{ + char func_ctrl, otg_ctrl; + + ulpi_dat3_int_set(0); + ulpi_rtsm(); + ulpi_reg_read(ULPI_FUNCTION_CONTROL, &func_ctrl); + ulpi_reg_read(ULPI_OTG_CONTROL, &otg_ctrl); + + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DPPULLDOWN); + + /* Enable D+ pull-up resister to enable Data line SRP */ + ulpi_reg_write(ULPI_FUNCTION_CONTROL, 0x4 | ULPI_FC_SUSPENDM); + ulpi_dat3_int_set(0); + + mdelay(T_B_DATA_PLS); + + /* restore values to stop Data line SRP */ + ulpi_reg_write(ULPI_FUNCTION_CONTROL, (0x1 << 3) | ULPI_FC_SUSPENDM); + ulpi_reg_write(ULPI_OTG_CONTROL, otg_ctrl); + ulpi_reg_write(ULPI_FUNCTION_CONTROL, func_ctrl); + return 0; +} + +/* Called to start SRP + */ +static int pxa310_otgx_vbus_session(struct pxa_otg *pOtgHandle) +{ +#ifdef PXA3xx_OTG_VBUS_PMIC + pOtgHandle->pmic_ops->otg_set_vbus(VBUS_PULSE); + mdelay(T_B_SRP_INIT - T_B_DATA_PLS); + pOtgHandle->pmic_ops->otg_set_vbus(VBUS_LOW); +#else + ulpi_rtsm(); + + /* start VBUS pulse SRP */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DISCHRGVBUS); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_CHRGVBUS); + + /* stop VBUS pulse SRP */ + mdelay(T_B_SRP_INIT - T_B_DATA_PLS); + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_CHRGVBUS); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DISCHRGVBUS); + +#endif + return 0; +} + +static int pxa310_otgx_check_b_hnp(void) +{ + int ret = 0; + + u2d_clk_enable(); + if (U2DOTGCR_BHNP & U2DOTGCR) + ret = 1; + u2d_clk_restore(); + return ret; +} + +static int pxa310_otgx_check_vbus(void) +{ + int ret = 0; + + u2d_clk_enable(); + if (U2DOTGUSR & U2DOTGUSR_VV) + ret = 1; + u2d_clk_restore(); + return ret; +} + +#define U2DOTGRSM_IRQ (U2DOTGINT_RLS1 | U2DOTGINT_FLS0) + +static int pxa310_otgx_start_autoresume(void) +{ + u2d_clk_enable(); + pxa310_otgx_init(); + U2DOTGICR |= U2DOTGRSM_IRQ; + u2d_clk_restore(); + return 1; +} + +static int pxa310_otgx_drive_resume(void) +{ + u2d_clk_enable(); + pxa310_otgx_init(); + U2DOTGICR &= ~U2DOTGRSM_IRQ; + ulpi_reg_write(ULPI_INTERFACE_CONTROL_SET, 0x10); + pxa310_otgx_set_mode(USB_OTG_LP); + ulpi_dat3_int_set(0); + pxa310_otgx_init(); + ulpi_reg_write(ULPI_FUNCTION_CONTROL, 0x55); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DMPULLDOWN); + return 1; +} + +static struct otg_xceiv_ops pxa310_otg_xceiv_ops = { + .otgx_get_mode = pxa310_otgx_get_mode, + .otgx_set_mode = pxa310_otgx_set_mode, + .otgx_init = pxa310_otgx_init, + .otgx_detect_default_func = pxa310_otgx_detect_default_func, + .otgx_dp_session = pxa310_otgx_dp_session, + .otgx_vbus_session = pxa310_otgx_vbus_session, + .otgx_check_b_hnp = pxa310_otgx_check_b_hnp, + .otgx_check_vbus = pxa310_otgx_check_vbus, + .otgx_start_autoresume = pxa310_otgx_start_autoresume, + .otgx_drive_resume = pxa310_otgx_drive_resume, + .reset_xcvr_init = reset_xcvr_init, + .ulpi_dat3_work = ulpi_dat3_work, +}; + +struct otg_xceiv_ops *init_pxa310_otg_xceiv_ops(void) +{ + return &pxa310_otg_xceiv_ops; +} +/****************************************************************** + * Pxa3xx OTG controller functions + ****************************************************************** + */ +static int pxa310_otgc_init(void) +{ + u32 u2dotgcr; + + U2DOTGCR = U2DOTGCR_UTMID; + u2dotgcr = U2DOTGCR; + + u2dotgcr |= U2DOTGCR_ULE; + U2DOTGCR = u2dotgcr; + + u2dotgcr |= U2DOTGCR_ULAF; + u2dotgcr &= ~(U2DOTGCR_SMAF | U2DOTGCR_CKAF); + U2DOTGCR = u2dotgcr; + + headset_init(); + return 0; +} + +static void pxa310_otgc_interrupt_init(void) +{ + U2DOTGICR = U2DOTGINT_DEFAULT; + U2DOTGCR |= U2DOTGCR_OTGEN; +} + +/* This function is called when USB OTG interrupt happened + */ +static int pxa310_otgc_interrupt_handle(struct pxa_otg *pOtgHandle) +{ + u32 otgisr; + u8 interrupt_state = OTG_INT_INIT; + int state; + + state = pOtgHandle->xceiv_ops->otgx_get_mode(); + + if ((state != USB_INT_OTG_CLIENT_DP) && (state != USB_OTG_PRE_SYNCH)) { + pr_debug("state %d -> synch\n", state); + pOtgHandle->xceiv_ops->otgx_init(); + } + if (U2DOTGISR & U2DOTGINT_SI) + U2DOTGISR = U2DOTGINT_SI; + + pr_debug("otgicr %x otgisr %x otgusr %x\n", + U2DOTGICR, U2DOTGISR, U2DOTGUSR); + + otgisr = U2DOTGISR; + U2DOTGISR = otgisr; + + if ((otgisr & U2DOTGINT_RLS1)) { + interrupt_state |= OTG_INT_LS; + state = USB_INT_OTG_CLIENT_DP; + } else if (otgisr & U2DOTGINT_FLS0) + interrupt_state |= OTG_INT_LP_DIS; + + /* OTG SET FEATURE received */ + if (otgisr & U2DOTGINT_SF) { + U2DOTGISR = U2DOTGINT_SF; + if (U2DOTGCR & U2DOTGCR_BHNP) + pOtgHandle->otg.gadget->b_hnp_enable = 1; + + if (U2DOTGCR & U2DOTGCR_AHNP) + pOtgHandle->otg.gadget->a_hnp_support = 1; + + if (U2DOTGCR & U2DOTGCR_AALTHNP) + pOtgHandle->otg.gadget->a_alt_hnp_support = 1; + } + + if (U2DOTGUSR & U2DOTGUSR_VV) { + if (!pOtgHandle->otg_ctrl->b_sess_vld && + !pOtgHandle->otg_ctrl->a_vbus_vld) + interrupt_state |= OTG_INT_RVV; + } + else { + if (pOtgHandle->otg_ctrl->b_sess_vld || + pOtgHandle->otg_ctrl->a_vbus_vld) + interrupt_state |= OTG_INT_FVV; + } + + if (U2DOTGUSR & U2DOTGUSR_SV) { + if (pOtgHandle->otg.state == OTG_STATE_A_IDLE) + interrupt_state |= OTG_INT_RSV; + } + + /* mini-A is plugged in */ + if (otgisr & U2DOTGINT_FID) { + U2DOTGISR = U2DOTGINT_FID; + /* check the current of OTG ID from the PHY */ + if (U2DOTGUSR & U2DOTGUSR_ID) + goto out1; + + if (OTG_B_DEVICE == pOtgHandle->otg.default_a) { + if ((get_headset_type(pOtgHandle) > 0)) { + state = USB_OTG_CARKIT; + headset_polling(); + } else + interrupt_state |= OTG_INT_IDF; + } + + } +out1: + /* mini-A is plugged out */ + if (otgisr & U2DOTGINT_RID) { + U2DOTGISR = U2DOTGINT_RID; + + /* check the current of OTG ID from the PHY */ + if (!(U2DOTGUSR & U2DOTGUSR_ID)) + goto out2; + + if (OTG_A_DEVICE == pOtgHandle->otg.default_a) + interrupt_state |= OTG_INT_IDR; + } + +out2: + + if ((state != USB_INT_OTG_CLIENT_DP) + && (state != USB_OTG_PRE_SYNCH)) { + pr_debug("synch -> state %d \n ", state); + pOtgHandle->xceiv_ops->otgx_set_mode(state); + } + + /* set carkit mode polling timer */ + if (USB_OTG_CARKIT == state) + interrupt_state = 0; + + pr_debug("%s: interrupt type %x\n", __func__, interrupt_state); + return interrupt_state; +} + +static void pxa310_otgc_init_gadget(void) +{ + /* what if no gadget driver exists, OTG function is disabled + * as above? + */ + U2DOTGICR = 0; + U2DOTGISR = U2DOTGISR; +} + +static void pxa310_otgc_deinit(void) +{ + headset_deinit(); +} + +static struct otg_ctrl_ops pxa310_otg_ctrl_ops = { + .otgc_init = pxa310_otgc_init, + .otgc_interrupt_init = pxa310_otgc_interrupt_init, + .otgc_interrupt_handle = pxa310_otgc_interrupt_handle, + .otgc_init_gadget = pxa310_otgc_init_gadget, + .otgc_deinit = pxa310_otgc_deinit, +}; + +struct otg_ctrl_ops *init_pxa310_otg_ctrl_ops(void) +{ + return &pxa310_otg_ctrl_ops; +} +#endif /* CONFIG_CPU_PXA310 */ + diff --git a/drivers/usb/otg/pxa310_otg.h b/drivers/usb/otg/pxa310_otg.h new file mode 100644 index 00000000000000..fe27736491c3bc --- /dev/null +++ b/drivers/usb/otg/pxa310_otg.h @@ -0,0 +1,106 @@ +#ifndef __PXA310_USB_OTG_CONTROLLER__ +#define __PXA310_USB_OTG_CONTROLLER__ + +#define ULPI_VENDOR_LOW 0x0 +#define ULPI_VENDOR_HIGH 0x1 +#define ULPI_PRODUCT_LOW 0x2 +#define ULPI_PRODUCT_HIGH 0x3 +#define ULPI_FUNCTION_CONTROL 0x4 +#define ULPI_FUNCTION_CONTROL_CLEAR 0x6 +#define ULPI_FUNCTION_CONTROL_SET 0x5 +#define ULPI_INTERFACE_CONTROL 0x7 +#define ULPI_INTERFACE_CONTROL_SET 0x8 +#define ULPI_INTERFACE_CONTROL_CLEAR 0x9 +#define ULPI_OTG_CONTROL 0xA +#define ULPI_OTG_CONTROL_SET 0xB +#define ULPI_OTG_CONTROL_CLEAR 0xC +#define ULPI_INT_RISE 0xD +#define ULPI_INT_RISE_SET 0xE +#define ULPI_INT_RISE_CLEAR 0xF +#define ULPI_INT_FALL 0x10 +#define ULPI_INT_FALL_SET 0x11 +#define ULPI_INT_FALL_CLEAR 0x12 +#define ULPI_INT_STATUS 0x13 +#define ULPI_INT_LATCH 0x14 +#define ULPI_DEBUG 0x15 +#define ULPI_SCRATCH 0x16 +#define ULPI_SCRATCH_SET 0x17 +#define ULPI_SCRATCH_CLEAR 0x18 + +#ifdef CONFIG_MACH_LITTLETON +#define ULPI_CARKIT_CONTROL 0x19 +#define ULPI_CARKIT_CONTROL_SET 0x1A +#define ULPI_CARKIT_CONTROL_CLEAR 0x1B +#define ULPI_CARKIT_INT_ENABLE 0x1D +#define ULPI_CARKIT_INT_ENABLE_SET 0x1E +#define ULPI_CARKIT_INT_ENABLE_CLEAR 0x1F +#define ULPI_CARKIT_INT_STATUS 0x20 +#define ULPI_VENDOR_RID_CONV 0x36 +#define ULPI_VENDOR_RID_CONV_SET 0x37 +#define ULPI_VENDOR_RID_CONV_CLEAR 0x38 +#endif + +#define ULPI_FC_RESET (1 << 5) /* XCVR Reset */ +#define ULPI_FC_SUSPENDM (1 << 6) /* XCVR SuspendM, Low Power Mode */ + +#define ULPI_IC_6PIN (1 << 0) /* XCVR 6 pin mode */ +#define ULPI_IC_3PIN (1 << 1) /* XCVR 3 pin mode */ +#ifdef CONFIG_MACH_LITTLETON +#define ULPI_IC_CARKIT (1 << 2) /* Carkit mode */ +#endif +#define ULPI_IC_CLKSUSPENDM (1 << 3) /* Active low clock suspend */ + +#define ULPI_OC_IDPULLUP (1 << 0) /* ID Pull Up, enable sampling of ID line */ +#define ULPI_OC_DPPULLDOWN (1 << 1) /* Enable the 15K Ohm pull down resistor on D+ */ +#define ULPI_OC_DMPULLDOWN (1 << 2) /* Enable the 15K Ohm pull down resistor on D- */ +#define ULPI_OC_DISCHRGVBUS (1 << 3) /* Discharge Vbus */ +#define ULPI_OC_CHRGVBUS (1 << 4) /* Charge Vbus, for Vbus pulsing SRP */ +#define ULPI_OC_DRVVBUS (1 << 5) /* Drive 5V on Vbus */ +#define ULPI_OC_DRVVBUSEXT (1 << 6) /* Drive Vbus using external supply */ + +#define ULPI_INT_HOSTDISCON (1 << 0) /* Host Disconnect */ +#define ULPI_INT_VBUSVALID (1 << 1) /* Vbus Valid */ +#define ULPI_INT_SESSIONVALID (1 << 2) /* Session Valid */ +#define ULPI_INT_SESSIONEND (1 << 3) /* Session End */ +#define ULPI_INT_IDGND (1 << 4) /* current status of IDGND */ + +#define U2DOTGINT_DEFAULT (U2DOTGINT_RID | U2DOTGINT_FID | U2DOTGINT_SI \ + | U2DOTGINT_RVV | U2DOTGINT_FVV | U2DOTGINT_SF \ + | U2DOTGINT_RSV) + +#ifdef ENABLE_USB_HEADSET +/* Carkit Control Register Flags */ +#define ULPI_CK_SPKLEFTEN (1 << 4) /* Connect DM to Speaker Left Pin */ +#define ULPI_CK_SPKRIGHTEN (1 << 5) /* Connect DP to Speaker Right/Mic Pin */ +#define ULPI_CK_SPKMICEN (1 << 6) /* Connect DP to Speaker Right/Mic Pin */ + +/* Carkit Interrupt Enable Register Flags */ +#define ULPI_CK_IDFLOATRISE (1 << 0) /* Interrupt enabled when ID Pin transitions from Non-floating to Floating */ +#define ULPI_CK_IDFLOATFALL (1 << 1) /* Interrupt enabled when ID Pin transitions from Floating to Non-floating */ +#define ULPI_CK_RIDINTEN (1 << 5) /* Interrupt enabled when RidConvesionDone bit is asserted */ + +/* Carkit Rid Values */ +#define RID_VALUE_MASK 0x7 /* Mask for RidValue Bits on Vendor RID Conversion Reg */ +#define RID_0 0x0 /* Rid Value at 0K ohms */ +#define RID_100K 0x2 /* Rid Value at 100K ohms */ +#define RID_200K 0x3 /* Rid Value at 200K ohms */ +#define RID_440K 0x4 /* Rid Value at 440K ohms */ +#define RID_FLOATING 0x5 /* Rid Value indicates Floating */ +#define RID_ERROR 0x7 /* Rid Value indicates Error */ + +/* Carkit Vendor Rid Conversion Register Flags */ +#define RID_CONV_DONE (1 << 3) /* Automatically Asserted when RID conversion is finished */ +#define RID_CONV_START (1 << 4) /* Flag to allow Rid ADC to start conversion process */ + +/* Carkit Interrupt Status Register Flags */ +#define ULPI_CK_IDFLOAT 0x1 /* Asserted when ID pin is floating */ + +/* USB headset macros */ +#define USB_HEADSET_STEREO 1 +#define USB_HEADSET_MONO_MIC 2 + +/* Delay Carkit timer for 500 msec */ +#define OTG_CARKIT_POLL_DELAY 500 +#endif +#endif + diff --git a/drivers/usb/otg/pxa3xx_otg.c b/drivers/usb/otg/pxa3xx_otg.c new file mode 100644 index 00000000000000..6c26e10795255c --- /dev/null +++ b/drivers/usb/otg/pxa3xx_otg.c @@ -0,0 +1,1309 @@ +/* + * linux/drivers/usb/gadget/pxa_otg.c + * PXA3xx usb otg controller + * + * Copyright (C) 2005 Intel Corporation + * Copyright (C) 2007 Marvell International Ltd + * + * 2007.5.16 brown,mark added support for otg carkit mode + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define DEBUG +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#define DRIVER_DESC "USB OTG on Monahans" + +#ifdef CONFIG_CPU_PXA310 +extern struct otg_xceiv_ops *init_pxa310_otg_xceiv_ops(void); +extern struct otg_ctrl_ops *init_pxa310_otg_ctrl_ops(void); +#endif + +#ifdef CONFIG_CPU_PXA300 +extern struct otg_xceiv_ops *init_pxa300_otg_xceiv_ops(void); +extern struct otg_ctrl_ops *init_pxa300_otg_ctrl_ops(void); +#endif + +extern struct otg_pmic_ops *init_otg_pmic_ops(void); + +void pxa_otg_vbus_handle(int status); +static void pxa3xx_update_otg(struct pxa_otg *pxa_otg); +static void otg_a_idle(struct pxa_otg *pxa_otg); +static void otg_b_idle(struct pxa_otg *pxa_otg); +void pxa_otg_reset(void); + +static char *state_string[] = { + "undefined", + "b_idle", + "b_srp_init", + "b_peripheral", + "b_wait_acon", + "b_host", + "a_idle", + "a_wait_vrise", + "a_wait_bcon", + "a_host", + "a_suspend", + "a_peripheral", + "a_wait_vfall", + "a_vbus_err" +}; + +inline void pxa3xx_otg_require_bus(int status) +{ + pxa_otg_vbus_handle(status); +} +/****************************************************************** + * timer help functions + ****************************************************************** + */ +#define TIMER_NUM 5 +static struct timer_list timer_array[TIMER_NUM]; +static unsigned timer_state; + +/* register a timer + * parameters + * pxa_otg - OTG handle + * interval - delay of timer + * timer_callback - callback function when timer expired + */ +int otg_register_timer(struct pxa_otg *pxa_otg, \ + int interval, \ + void (*timer_callback)(unsigned long)) +{ + unsigned count, temp; + int ret; + struct timer_list *timer; + + /* find a free timer */ + ret = 0; + count = TIMER_NUM; + temp = timer_state; + while ((temp & 0x1) && count) { + temp = (temp >> 1); + count-- ; + ret++ ; + } + + if (ret >= TIMER_NUM) { + pr_err("No free timer\n"); + return -1; + } else { + timer = &timer_array[ret]; + timer_state |= (1 << ret) ; + } + + init_timer(timer); + timer->data = (unsigned long)pxa_otg; + timer->function = timer_callback; + timer->expires = jiffies + interval; + add_timer(timer); + + return ret; +} + +int otg_cancel_timer(int timer_id) +{ + if ((timer_id < 0) || (timer_id >= TIMER_NUM) + || !((1 << timer_id) & timer_state)) + return OTG_INVALID_PARAMETER; + + del_timer(&timer_array[timer_id]); + timer_state &= ~(1 << timer_id); + + return 0; +} + +/* Timer expired, call this functio to indicate the timer free + */ +int otg_timer_expired(int timer_id) +{ + if ((timer_id < 0) || (timer_id >= TIMER_NUM) + || !((1 << timer_id) & timer_state)) + return OTG_INVALID_PARAMETER; + + timer_state &= ~(1 << timer_id); + + return 0; +} + +int otg_timer_reset(void) +{ + int i; + + for (i = 0; i < TIMER_NUM; i++) + otg_cancel_timer(i); + return 0; +} + +/****************************************************************** + * OTG platform independent functions + ****************************************************************** + */ + +/* + * Set or clear field a_set_b_hnp_en + * mode - the desired mode for USB host port2 + */ +int otg_set_b_hnp(struct pxa_otg *pxa_otg, int set) +{ + if (set) + pxa_otg->otg_ctrl->a_set_b_hnp_en = 1; + else + pxa_otg->otg_ctrl->a_set_b_hnp_en = 0; + + return 0; +} + +/* called when timer a_aidl_bdis_tmr expires + */ +static void otg_timer_aidle_bdis(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + pxa_otg->otg_ctrl->a_aidl_bdis_timeout = 1; + pxa3xx_update_otg(pxa_otg); + otg_timer_expired(pxa_otg->otg_ctrl->a_aidl_bdis_tmr); + pxa_otg->otg_ctrl->a_aidl_bdis_tmr = -1; +} + +/* called when timer a_wait_bcon_tmr expires + */ +static void otg_timer_await_bcon(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + pxa_otg->otg_ctrl->a_wait_bcon_timeout = 1; + pxa3xx_update_otg(pxa_otg); + otg_timer_expired(pxa_otg->otg_ctrl->a_wait_bcon_tmr); + pxa_otg->otg_ctrl->a_wait_bcon_tmr = -1; + pr_info("B Device No Response!\n"); +} + +/* called when timer a_bidl_adis_tmr expires + */ +static void otg_timer_bidle_adis(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + pxa_otg->otg_ctrl->a_bidl_adis_timeout = 1; + pxa3xx_update_otg(pxa_otg); + otg_timer_expired(pxa_otg->otg_ctrl->a_bidl_adis_tmr); + pxa_otg->otg_ctrl->a_bidl_adis_tmr = -1; +} + +/* called when timer b_se0_srp_tmr expires + */ +static void otg_timer_se0_srp(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + otg_timer_expired(pxa_otg->otg_ctrl->b_se0_srp_tmr); + pxa_otg->otg_ctrl->b_se0_srp_tmr = -1; + pxa3xx_update_otg(pxa_otg); +} + +/* called when timer b_srp_fail_tmr expires + */ +static void otg_timer_srp_fail(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + otg_timer_expired(pxa_otg->otg_ctrl->b_srp_fail_tmr); + pxa_otg->otg_ctrl->b_srp_fail_tmr = -1; + + otg_timer_expired(pxa_otg->otg_ctrl->b_se0_srp_tmr); + pxa_otg->otg_ctrl->b_se0_srp_tmr = -1; + + if (pxa_otg->otg_ctrl->b_bus_req == 1) { + pr_info("A Device didn't response after sending Session!\n"); + pxa_otg->otg_ctrl->b_bus_req = 0; + } else + pr_info("A Device didn't response because the VBUS is low!\n"); +} + +/* called when timer b_ase0_brst_tmr expires */ +static void otg_timer_base0_brst(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + pr_debug(" otg state %s\n", + state_string[pxa_otg->otg.state]); + + pxa_otg->otg_ctrl->b_ase0_brst_timeout = 1; + pxa3xx_update_otg(pxa_otg); + + otg_timer_expired(pxa_otg->otg_ctrl->b_ase0_brst_tmr); + pxa_otg->otg_ctrl->b_ase0_brst_tmr = -1; + pr_info("As a client, A Device didn't response!\n"); +} + +static void otg_timer_drv_rsm(unsigned long data) +{ + struct pxa_otg *pxa_otg = (struct pxa_otg *)data; + + pxa_otg->xceiv_ops->otgx_set_mode(USB_INT_OTG_HOST_DP); + usb_bus_start_enum(pxa_otg->otg.host, pxa_otg->otg.host->otg_port); + pxa_otg->otg_ctrl->b_bus_resume = 1; + pxa3xx_update_otg(pxa_otg); + otg_timer_expired(pxa_otg->otg_ctrl->a_drv_rsm_tmr); + pxa_otg->otg_ctrl->a_drv_rsm_tmr = -1; +} +/* called when initialize fields in handle + */ +static void otg_init_handle(struct pxa_otg *pxa_otg) +{ + /* Initialize internal variables*/ + pxa_otg->otg_ctrl->a_set_b_hnp_en = 0; + pxa_otg->otg.gadget->a_hnp_support = 0; + pxa_otg->otg.gadget->a_alt_hnp_support = 0; + + /* Initialize OTG inputs*/ + pxa_otg->otg_ctrl->a_bus_drop = 0; + pxa_otg->otg_ctrl->a_bus_req = 0; + pxa_otg->otg_ctrl->a_bus_suspend = 0; + pxa_otg->otg_ctrl->a_vbus_vld = 0; + pxa_otg->otg_ctrl->a_conn = 0; + pxa_otg->otg_ctrl->b_bus_req = 0; + pxa_otg->otg_ctrl->b_sess_vld = 0; + pxa_otg->otg_ctrl->b_bus_suspend = 0; + pxa_otg->otg_ctrl->b_bus_resume = 0; + pxa_otg->otg_ctrl->b_conn = 0; + pxa_otg->otg_ctrl->a_suspend_req = 0; + + /* Initialize timer identifiers */ + pxa_otg->otg_ctrl->a_wait_vrise_tmr = -1; + pxa_otg->otg_ctrl->a_wait_bcon_tmr = -1; + pxa_otg->otg_ctrl->a_aidl_bdis_tmr = -1; + pxa_otg->otg_ctrl->b_ase0_brst_tmr = -1; + pxa_otg->otg_ctrl->b_se0_srp_tmr = -1; + pxa_otg->otg_ctrl->b_srp_fail_tmr = -1; + pxa_otg->otg_ctrl->a_srp_rspns_tmr = -1; + pxa_otg->otg_ctrl->a_bidl_adis_tmr = -1; + pxa_otg->otg_ctrl->a_drv_rsm_tmr = -1; + + otg_timer_reset(); +} + +/* called from irq handlers */ +static void otg_a_idle(struct pxa_otg *pxa_otg) +{ + if (pxa_otg->otg.state == OTG_STATE_A_IDLE) + return; + otg_init_handle(pxa_otg); + pxa_otg->otg.default_a = OTG_A_DEVICE; + pxa_otg->otg.state = OTG_STATE_A_IDLE; + + /* disable the device controller */ + usb_gadget_vbus_disconnect(pxa_otg->otg.gadget); + usb_gadget_disconnect(pxa_otg->otg.gadget); + + pr_debug(" --> %s\n", state_string[pxa_otg->otg.state]); + pr_info("mini A plug in," + " please require vbus or send SRP from B device!\n"); + +} + +/* called from irq handlers */ +static void otg_b_idle(struct pxa_otg *pxa_otg) +{ + if (pxa_otg->otg.state == OTG_STATE_B_IDLE) + return; + /* Disable usb pump */ + pxa_otg->pmic_ops->otg_set_vbus(VBUS_LOW); + + otg_init_handle(pxa_otg); + pxa_otg->otg.default_a = OTG_B_DEVICE; + pxa_otg->otg.state = OTG_STATE_B_IDLE; + pxa_otg->pmic_ops->otg_set_vbus_ic(pxa_otg->otg.default_a); + + pr_debug(" --> %s\n", state_string[pxa_otg->otg.state]); + +} + +static void otg_a_wait_vfail(struct pxa_otg *pxa_otg) +{ + /* Disable usb pump */ + pxa_otg->pmic_ops->otg_set_vbus(VBUS_LOW); + otg_cancel_timer(pxa_otg->otg_ctrl->a_wait_bcon_tmr); + pxa_otg->otg_ctrl->a_wait_bcon_tmr = -1; + usb_gadget_disconnect(pxa_otg->otg.gadget); + usb_gadget_vbus_disconnect(pxa_otg->otg.gadget); + pxa_otg->otg_ctrl->a_vbus_vld = 0; +} + +int otg_init(struct pxa_otg *pxa_otg) +{ + enum otg_function function; + + pxa_otg->ctrl_ops->otgc_init(pxa_otg->info); + /* Read the OTGID pin to detect whether there is a mini-A plug + * is alreadly inserted in receptacle.*/ + function = pxa_otg->xceiv_ops->otgx_detect_default_func(); + pr_info("otg default %s\n", function?"a device":"b device"); + + otg_init_handle(pxa_otg); + pxa_otg->ctrl_ops->otgc_interrupt_init(); + + pxa_otg->pmic_ops->otg_vbus_init(); + + pxa_otg->otg.state = OTG_STATE_UNDEFINED; + if (OTG_A_DEVICE == function) + otg_a_idle(pxa_otg); + else if (OTG_B_DEVICE == function) + otg_b_idle(pxa_otg); + + return 0; +} + +int otg_interrupt_handle(struct pxa_otg *pxa_otg) +{ + u8 interrupt_type; + struct otg_xceiv_ops *xceiv_ops = pxa_otg->xceiv_ops; + + interrupt_type = pxa_otg->ctrl_ops->otgc_interrupt_handle(pxa_otg); + + pr_debug("\tinterrupt type:%x otg state %s\n", + interrupt_type, state_string[pxa_otg->otg.state]); + + + /* mini-A is plugged in */ + if (interrupt_type & OTG_INT_IDF) { + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + pr_debug(" OTGID low, Mini-A plug in\n"); + otg_a_idle(pxa_otg); + } + + /* mini-A is plugged out */ + if (interrupt_type & OTG_INT_IDR) { +/* && !pxa_otg->otg_ctrl->b_sess_vld) { + * FIXME without it, maybe block the OPT */ + pr_debug(" OTGID high, Mini-A plug out\n"); + if (OTG_A_DEVICE == pxa_otg->otg.default_a) { + if (xceiv_ops->otgx_check_vbus()) + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + else + xceiv_ops->otgx_set_mode(USB_OTG_LP); + } + otg_b_idle(pxa_otg); + } + + if (interrupt_type & OTG_INT_RSV) + pxa_otg_vbus_handle(USBOTG_SRP_DETECT); + + if (interrupt_type & OTG_INT_RVV) + pxa_otg_vbus_handle(USBOTG_VBUS_VALID); + + if (interrupt_type & OTG_INT_FVV) + pxa_otg_vbus_handle(0); + + if (interrupt_type & OTG_INT_LS) { + xceiv_ops->otgx_drive_resume(); + if (pxa_otg->otg_ctrl->b_se0_srp_tmr == -1) { + pxa_otg->otg_ctrl->a_drv_rsm_tmr = + otg_register_timer(pxa_otg, T_A_DRV_RSM, + otg_timer_drv_rsm); + } + } + + if (interrupt_type & OTG_INT_LP_DIS) { + pxa_otg->otg_ctrl->b_conn = 0; + pxa3xx_update_otg(pxa_otg); + pxa_otg->udev = NULL; + } + + return 0; +} + +static int pxa_otg_start_srp(struct otg_transceiver *otg); +int otg_state_machine(struct pxa_otg *pxa_otg, int old_state) +{ + int timer_id; + + switch (old_state) { + case OTG_STATE_UNDEFINED: + break; + case OTG_STATE_B_IDLE: + /* If state is idle send SRP, tell host to wake up and + * power VBUS */ + if (pxa_otg->otg_ctrl->b_bus_req && + !(pxa_otg->otg_ctrl->b_sess_vld)) { + if (pxa_otg->otg_ctrl->b_se0_srp_tmr == -1) { + pxa_otg->otg_ctrl->b_se0_srp_tmr = + otg_register_timer(pxa_otg, + T_B_SE0_SRP, + otg_timer_se0_srp); + } + pxa_otg->otg.state = OTG_STATE_B_SRP_INIT; + pxa_otg_start_srp(&(pxa_otg->otg)); + pxa_otg->otg.state = OTG_STATE_B_IDLE; + if (pxa_otg->otg_ctrl->b_srp_fail_tmr == -1) { + pxa_otg->otg_ctrl->b_srp_fail_tmr = + otg_register_timer(pxa_otg, + T_B_SRP_FAIL, + otg_timer_srp_fail); + } + } + + if (pxa_otg->otg_ctrl->b_sess_vld) { + pxa_otg->otg.state = OTG_STATE_B_PERIPHERAL; + otg_cancel_timer(pxa_otg->otg_ctrl->b_se0_srp_tmr); + pxa_otg->otg_ctrl->b_se0_srp_tmr = -1; + otg_cancel_timer(pxa_otg->otg_ctrl->b_srp_fail_tmr); + pxa_otg->otg_ctrl->b_srp_fail_tmr = -1; + } + break; + case OTG_STATE_B_SRP_INIT: + break; + case OTG_STATE_B_PERIPHERAL: + if (!(pxa_otg->otg_ctrl->b_sess_vld)) { + pxa_otg->otg.state = OTG_STATE_B_IDLE; + if (pxa_otg->otg_ctrl->b_srp_fail_tmr == -1) { + pxa_otg->otg_ctrl->b_srp_fail_tmr = + otg_register_timer(pxa_otg, + T_B_SRP_FAIL, + otg_timer_srp_fail); + } + } + + if ((pxa_otg->otg_ctrl->a_bus_suspend) && + (pxa_otg->xceiv_ops->otgx_check_b_hnp())) { + if (pxa_otg->otg_ctrl->b_bus_req) { + pxa_otg->otg.state = OTG_STATE_B_WAIT_ACON; + /* Start timer Tb_ase0_brst_tmr */ + if (pxa_otg->otg_ctrl->b_ase0_brst_tmr == -1) { + pxa_otg->otg_ctrl->b_ase0_brst_tmr = + otg_register_timer(pxa_otg, + T_B_ASE0_BRST, + otg_timer_base0_brst); + } + } + } + + break; + case OTG_STATE_B_WAIT_ACON: + if (!(pxa_otg->otg_ctrl->b_sess_vld)) + pxa_otg->otg.state = OTG_STATE_B_IDLE; + + if (pxa_otg->otg_ctrl->b_ase0_brst_timeout) + pxa_otg->otg.state = OTG_STATE_B_PERIPHERAL; + + if (pxa_otg->otg_ctrl->a_conn) { + otg_cancel_timer(pxa_otg->otg_ctrl->b_ase0_brst_tmr); + pxa_otg->otg_ctrl->b_ase0_brst_tmr = -1; + pxa_otg->otg.state = OTG_STATE_B_HOST; + } + + break; + case OTG_STATE_B_HOST: + if (!(pxa_otg->otg_ctrl->a_conn) && pxa_otg->otg.gadget) + pxa_otg->otg.state = OTG_STATE_B_PERIPHERAL; + + if (!(pxa_otg->otg_ctrl->b_sess_vld)) + pxa_otg->otg.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_IDLE: + if (!(pxa_otg->otg_ctrl->a_bus_drop) && + (pxa_otg->otg_ctrl->a_bus_req || + pxa_otg->otg_ctrl->a_srp_det)) + pxa_otg->otg.state = OTG_STATE_A_WAIT_VRISE; + break; + case OTG_STATE_A_WAIT_VRISE: + if (pxa_otg->otg_ctrl->a_vbus_vld) + pxa_otg->otg.state = OTG_STATE_A_WAIT_BCON; + break; + case OTG_STATE_A_WAIT_BCON: + if (!(pxa_otg->otg_ctrl->a_vbus_vld)) { + otg_cancel_timer(pxa_otg->otg_ctrl->a_wait_bcon_tmr); + pxa_otg->otg_ctrl->a_wait_bcon_tmr = -1; + pxa_otg->otg.state = OTG_STATE_A_VBUS_ERR; + } + + if (pxa_otg->otg_ctrl->b_conn) { + otg_cancel_timer(pxa_otg->otg_ctrl->a_wait_bcon_tmr); + pxa_otg->otg_ctrl->a_wait_bcon_tmr = -1; + pxa_otg->otg.state = OTG_STATE_A_HOST; + } + + if (pxa_otg->otg_ctrl->a_wait_bcon_timeout) { + pxa_otg->otg.state = OTG_STATE_A_WAIT_VFALL; + pxa_otg->otg_ctrl->a_bus_req = 0; + } + break; + case OTG_STATE_A_HOST: + if (!(pxa_otg->otg_ctrl->a_vbus_vld)) + pxa_otg->otg.state = OTG_STATE_A_VBUS_ERR; + + if (pxa_otg->otg_ctrl->a_suspend_req) { + pxa_otg->otg.state = OTG_STATE_A_SUSPEND; + if ((pxa_otg->otg_ctrl->a_set_b_hnp_en) + && (pxa_otg->otg_ctrl->a_aidl_bdis_tmr == -1)) { + timer_id = otg_register_timer(pxa_otg, + T_A_AIDL_BDIS, + otg_timer_aidle_bdis); + if (timer_id < 0) + break; + + pxa_otg->otg_ctrl->a_aidl_bdis_tmr = timer_id; + } + } + + if (!(pxa_otg->otg_ctrl->b_conn)) + pxa_otg->otg.state = OTG_STATE_A_WAIT_BCON; + + break; + case OTG_STATE_A_SUSPEND: + if (!(pxa_otg->otg_ctrl->a_vbus_vld)) + pxa_otg->otg.state = OTG_STATE_A_VBUS_ERR; + + else if (pxa_otg->otg_ctrl->b_bus_resume || + pxa_otg->otg_ctrl->a_bus_req) { + pxa_otg->otg.state = OTG_STATE_A_HOST; + pxa_otg->xceiv_ops->otgx_set_mode(USB_INT_OTG_HOST_DP); + usb_bus_start_enum(pxa_otg->otg.host, + pxa_otg->otg.host->otg_port); + } else if (pxa_otg->otg_ctrl->a_aidl_bdis_timeout) + pxa_otg->otg.state = OTG_STATE_A_WAIT_VFALL; + + else if (!(pxa_otg->otg_ctrl->b_conn)) { + if (!pxa_otg->otg_ctrl->a_set_b_hnp_en) + pxa_otg->otg.state = OTG_STATE_A_WAIT_BCON; + else if (pxa_otg->otg_ctrl->a_bidl_adis_tmr == -1) { + pxa_otg->otg.state = OTG_STATE_A_PERIPHERAL; + timer_id = otg_register_timer(pxa_otg, \ + T_A_BIDL_ADIS, otg_timer_bidle_adis); + if (timer_id < 0) + break; + pxa_otg->otg_ctrl->a_bidl_adis_tmr = timer_id; + } + otg_cancel_timer(pxa_otg->otg_ctrl->a_aidl_bdis_tmr); + pxa_otg->otg_ctrl->a_aidl_bdis_tmr = -1; + } + + break; + case OTG_STATE_A_PERIPHERAL: + if (!(pxa_otg->otg_ctrl->a_vbus_vld)) + pxa_otg->otg.state = OTG_STATE_A_VBUS_ERR; +#if 0 + if ((pxa_otg->otg_ctrl->b_bus_suspend)) + pxa_otg->otg.state = OTG_STATE_A_WAIT_BCON; +#endif + if ((pxa_otg->otg_ctrl->b_conn)) { + otg_cancel_timer(pxa_otg->otg_ctrl->a_bidl_adis_tmr); + pxa_otg->otg_ctrl->a_bidl_adis_tmr = -1; + } + + if (pxa_otg->otg_ctrl->a_bidl_adis_timeout) + pxa_otg->otg.state = OTG_STATE_A_WAIT_BCON; + + break; + case OTG_STATE_A_WAIT_VFALL: + if (!(pxa_otg->otg_ctrl->b_conn) && + !pxa_otg->xceiv_ops->otgx_check_vbus()) + pxa_otg->otg.state = OTG_STATE_A_IDLE; + + if (pxa_otg->otg_ctrl->a_bus_req) + pxa_otg->otg.state = OTG_STATE_A_WAIT_VRISE; + + + break; + case OTG_STATE_A_VBUS_ERR: + if (pxa_otg->otg_ctrl->a_clr_err) { + pxa_otg->otg_ctrl->a_clr_err = 0; + pxa_otg->otg.state = OTG_STATE_A_WAIT_VFALL; + if (!(pxa_otg->otg_ctrl->b_conn)) + pxa_otg->otg.state = OTG_STATE_A_IDLE; + + if (pxa_otg->otg_ctrl->a_bus_req) + pxa_otg->otg.state = OTG_STATE_A_WAIT_VRISE; + } + break; + default: + break; + } + + return pxa_otg->otg.state; +} + +/****************************************************************** + * OTG interface functions + ****************************************************************** + */ +static struct pxa_otg *the_transceiver; +static struct otg_transceiver *xceiv; + +struct otg_transceiver *otg_get_transceiver(void) +{ + if (xceiv) + get_device(xceiv->dev); + + return xceiv; +} + +int otg_set_transceiver(struct otg_transceiver *x) +{ + if (xceiv && x) + return -EBUSY; + xceiv = x; + + return 0; +} + +/* When USB cable attached, call this function*/ +void pxa_otg_vbus_handle(int status) +{ + struct pxa_otg *pxa_otg; + struct pxa_otgc *otg_ctrl; + + if (the_transceiver) { + pxa_otg = the_transceiver; + otg_ctrl = pxa_otg->otg_ctrl; + pr_debug(" old otg state %s, require %d\n", + state_string[pxa_otg->otg.state], status); + otg_ctrl->b_sess_vld = (status & USBOTG_VBUS_VALID) ? 1 : 0; + otg_ctrl->a_vbus_vld = (status & USBOTG_VBUS_VALID) ? 1 : 0; + if (pxa_otg->otg.state == OTG_STATE_A_IDLE) + otg_ctrl->a_srp_det = (status & USBOTG_SRP_DETECT) ? + 1 : 0; + + pxa3xx_update_otg(pxa_otg); + + pr_debug(" current state %s\n", + state_string[pxa_otg->otg.state]); + + } else { + printk(KERN_ERR "%s failed to get transceiver\n", __func__); + } +} + +/* Called from pxa3xx udc interrupt handler to deal with otg interrupt*/ +static int pxa_otg_interrupt(struct otg_transceiver *otg) +{ + int old_default; + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + old_default = pxa_otg->otg.default_a; + otg_interrupt_handle(pxa_otg); + + /* This eradicate unnecessary suspend & reset interrupt */ + if (!old_default && pxa_otg->otg.default_a) + pxa_otg->udev = NULL; + else if (old_default && !pxa_otg->otg.default_a) + pxa_otg->udev = NULL; + + return 0; +} + +static int pxa_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + pxa_otg->otg.host = host; + + return 0; +} + +static int pxa_otg_set_peripheral(struct otg_transceiver *otg, + struct usb_gadget *gadget) +{ + unsigned long flags; + int ret; + struct pxa_otgc *otg_ctrl; + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + if (gadget == NULL) { + pxa_otg->ctrl_ops->otgc_init_gadget(); + pxa_otg->otg.gadget = NULL; + } else { + if (pxa_otg->otg.gadget) + return 0; + pxa_otg->otg.gadget = gadget; + otg_ctrl = pxa_otg->otg_ctrl; + local_irq_save(flags); + ret = otg_init(pxa_otg); + if (ret) { + pr_err("%s: failed to call otg_init:%d\n", + __FUNCTION__, ret); + local_irq_restore(flags); + return -EFAULT; + } + local_irq_restore(flags); + } + return 0; +} + +static int pxa_otg_set_power(struct otg_transceiver *otg, unsigned mA) +{ + return 0; +} + +static int pxa_otg_start_srp(struct otg_transceiver *otg) +{ + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + pxa_otg->xceiv_ops->otgx_dp_session(); + pxa_otg->xceiv_ops->otgx_vbus_session(pxa_otg); + + return 0; +} + +static int pxa_otg_start_hnp(struct otg_transceiver *otg) +{ + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + pr_debug("is called"); + /*otg_suspend_device*/ + pxa_otg->otg_ctrl->a_suspend_req = 1; + pxa3xx_update_otg(pxa_otg); + + return 0; +} + +static int pxa_otg_connect(struct otg_transceiver *otg, struct usb_device *udev) +{ + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + pr_debug("is called"); + /*otg_respond_connect*/ + if (!pxa_otg->otg_ctrl->a_conn && !pxa_otg->otg_ctrl->b_conn) { + pxa_otg->otg_ctrl->a_conn = 1; + pxa_otg->otg_ctrl->b_conn = 1; + pxa3xx_update_otg(pxa_otg); + } + + pr_debug("otg state:%s\n", state_string[pxa_otg->otg.state]); + if (udev) + pxa_otg->udev = udev; + return 0; +} + +static int pxa_otg_disconnect(struct otg_transceiver *otg) +{ + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver || !the_transceiver) + return -ENODEV; + + if (pxa_otg->otg.state == OTG_STATE_A_SUSPEND) + return 0; + + pr_debug("%s is called, state:%s\n", __func__, + state_string[pxa_otg->otg.state]); + /*otg_respond_disconnect*/ + + if (pxa_otg->otg_ctrl->a_conn || pxa_otg->otg_ctrl->b_conn) { + pxa_otg->otg_ctrl->a_conn = 0; + pxa_otg->otg_ctrl->b_conn = 0; + pxa3xx_update_otg(pxa_otg); + } + + pxa_otg->udev = NULL; + + pr_debug("otg state:%s\n", state_string[pxa_otg->otg.state]); + return 0; +} + +static int pxa_otg_host_suspend(struct otg_transceiver *otg) +{ + struct pxa_otg *pxa_otg = container_of(otg, \ + struct pxa_otg, otg); + + if (!otg || pxa_otg != the_transceiver) + return -ENODEV; + + pr_debug("%s otg state before:%s\n", __func__, + state_string[pxa_otg->otg.state]); + + /*otg_respond_suspend*/ + pxa_otg->otg_ctrl->b_bus_suspend = 1; + pxa_otg->otg_ctrl->a_bus_suspend = 1; + pxa3xx_update_otg(pxa_otg); + + + pr_debug("otg state after:%s\n", state_string[pxa_otg->otg.state]); + return 0; +} + +static void pxa3xx_update_otg(struct pxa_otg *pxa_otg) +{ + int state = pxa_otg->otg.state; + int timer_id; + struct otg_xceiv_ops *xceiv_ops = pxa_otg->xceiv_ops; + struct otg_pmic_ops *pmic_ops = pxa_otg->pmic_ops; + + if (pxa_otg->otg.host && pxa_otg->otg.host->b_hnp_enable) + otg_set_b_hnp(pxa_otg, 1); + + otg_state_machine(pxa_otg, state); + + /* change some inputs' state*/ + pxa_otg->otg_ctrl->b_bus_suspend = 0; + pxa_otg->otg_ctrl->a_bus_suspend = 0; + pxa_otg->otg_ctrl->a_srp_det = 0; + + pxa_otg->otg_ctrl->b_ase0_brst_timeout = 0; + pxa_otg->otg_ctrl->a_aidl_bdis_timeout = 0; + pxa_otg->otg_ctrl->a_bidl_adis_timeout = 0; + pxa_otg->otg_ctrl->a_wait_bcon_timeout = 0; + + if ((state != pxa_otg->otg.state)) { + switch (pxa_otg->otg.state) { + case OTG_STATE_B_IDLE: + if (pxa_otg->otg.gadget) { + usb_gadget_disconnect(pxa_otg->otg.gadget); + usb_gadget_vbus_disconnect(pxa_otg->otg.gadget); + } + if (pxa_otg->otg_ctrl->b_sess_vld) + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + else + xceiv_ops->otgx_set_mode(USB_OTG_LP); + pxa_otg->otg_ctrl->b_bus_req = 0; + if (pxa_otg->otg.host) + pxa_otg->otg.host->is_b_host = 0; + break; + case OTG_STATE_B_SRP_INIT: + break; + case OTG_STATE_B_PERIPHERAL: + if (pxa_otg->otg.host) + pxa_otg->otg.host->is_b_host = 0; + pxa_otg->otg_ctrl->b_bus_req = 1; + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + if (pxa_otg->otg.gadget) { + usb_gadget_connect(pxa_otg->otg.gadget); + usb_gadget_vbus_connect(pxa_otg->otg.gadget); + } + break; + case OTG_STATE_B_WAIT_ACON: + if (pxa_otg->otg.host) + pxa_otg->otg.host->is_b_host = 1; + if (pxa_otg->otg.gadget) { + usb_gadget_disconnect(pxa_otg->otg.gadget); + usb_gadget_vbus_disconnect(pxa_otg->otg.gadget); + } + xceiv_ops->otgx_set_mode(USB_INT_OTG_HOST_DP); + break; + case OTG_STATE_B_HOST: + usb_bus_start_enum(pxa_otg->otg.host, + pxa_otg->otg.host->otg_port); + if (pxa_otg->otg.host) + pxa_otg->otg.host->is_b_host = 1; + break; + case OTG_STATE_A_IDLE: + if (pxa_otg->otg.gadget) { + pxa_otg->otg.gadget->is_a_peripheral = 0; + usb_gadget_disconnect(pxa_otg->otg.gadget); + usb_gadget_vbus_disconnect(pxa_otg->otg.gadget); + } + otg_init_handle(pxa_otg); + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + break; + case OTG_STATE_A_WAIT_VRISE: + /* Enable USB PUMP */ + pmic_ops->otg_set_vbus(VBUS_HIGH); + pmic_ops->otg_set_vbus_ic(pxa_otg->otg.default_a); + mdelay(10); + if (xceiv_ops->otgx_check_vbus()) + pxa_otg_vbus_handle(USBOTG_VBUS_VALID); + break; + case OTG_STATE_A_WAIT_BCON: + if (pxa_otg->otg.gadget) { + pxa_otg->otg.gadget->is_a_peripheral = 0; + usb_gadget_disconnect(pxa_otg->otg.gadget); + /* remove reduntant vbus disconnect, + * already called in A_IDLE mode + usb_gadget_vbus_disconnect(pxa_otg->otg.gadget); + */ + } + otg_set_b_hnp(pxa_otg, 0); + if (state != OTG_STATE_A_HOST) + xceiv_ops->otgx_set_mode(USB_INT_OTG_HOST_DP); + if (pxa_otg->otg_ctrl->a_wait_bcon_tmr == -1) { + timer_id = otg_register_timer(pxa_otg, + T_A_WAIT_BCON, otg_timer_await_bcon); + if (timer_id < 0) + break; /* return OTG_TIMER_FAILED; */ + + pxa_otg->otg_ctrl->a_wait_bcon_tmr = timer_id; + } + break; + case OTG_STATE_A_HOST: + break; + case OTG_STATE_A_SUSPEND: + xceiv_ops->otgx_start_autoresume(); + break; + case OTG_STATE_A_PERIPHERAL: + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + if (pxa_otg->otg.gadget) { + pxa_otg->otg.gadget->is_a_peripheral = 1; + usb_gadget_vbus_connect(pxa_otg->otg.gadget); + usb_gadget_connect(pxa_otg->otg.gadget); + } + break; + case OTG_STATE_A_WAIT_VFALL: + if (pxa_otg->otg.gadget) + pxa_otg->otg.gadget->is_a_peripheral = 0; + xceiv_ops->otgx_set_mode(USB_INT_OTG_CLIENT_DP); + xceiv_ops->otgx_set_mode(USB_OTG_LP); + otg_a_wait_vfail(pxa_otg); + break; + case OTG_STATE_A_VBUS_ERR: + pmic_ops->otg_set_vbus(VBUS_LOW); + pr_info("VBUS ERR, please clear this err!\n"); + break; + default: + break; + } + } + pr_debug("%s:from %s to %s\n", __func__, state_string[state], + state_string[pxa_otg->otg.state]); +} + +#ifdef CONFIG_PROC_FS +static const char *proc_node_name = "driver/otg"; + +static int otg_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + struct pxa_otg *pxa_otg = _dev; + char *next = page; + unsigned size = count; + unsigned long flags; + int t; + + if (off != 0) + return 0; + + local_irq_save(flags); + + /* basic device status */ + t = scnprintf(next, size, DRIVER_DESC "\n"); + size -= t; + next += t; + + t = scnprintf(next, size, "otg state:%s bus required:%d\n", + state_string[pxa_otg->otg.state], + ((pxa_otg->otg.default_a == OTG_B_DEVICE) ? + pxa_otg->otg_ctrl->b_bus_req + : pxa_otg->otg_ctrl->a_bus_req)); + + size -= t; + next += t; + + local_irq_restore(flags); + *eof = 1; + return count - size; +} + +extern void dump_ulpi_regs(void); +extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg); +extern int usb_resume_both(struct usb_device *udev); +static int otg_proc_write(struct file *filp, const char *buffer, + unsigned long count, void *data) +{ + char kbuf[8]; + int index; + struct pxa_otg *pxa_otg = data; + struct otg_pmic_ops *pmic_ops = pxa_otg->pmic_ops; + + if (count >= 8) + return -EINVAL; + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + index = (int)simple_strtoul(kbuf, NULL, 10); + + switch (index) { + /* Device expects to use the bus */ + case 1: + if (rely_on_vbus) { + otg_init(pxa_otg); + if (pxa_otg->otg.default_a == OTG_A_DEVICE) { + pxa_otg->otg_ctrl->a_bus_req = 1; + + } + } + if (pxa_otg->otg.default_a == OTG_A_DEVICE) { + pxa_otg->otg_ctrl->a_bus_req = 1; + pxa_otg->otg_ctrl->a_bus_drop = 0; + } else + pxa_otg->otg_ctrl->b_bus_req = 1; + pxa3xx_update_otg(pxa_otg); + + break; + + /* After complete use usb device. Suspend the device connected */ + case 2: + break; + + case 3: + if (pxa_otg->otg.default_a == OTG_A_DEVICE) { + pxa3xx_pmic_set_pump(0); + pxa_otg->otg_ctrl->a_bus_drop = 1; + pxa_otg->otg_ctrl->a_bus_req = 0; + if (pxa_otg->otg_ctrl->a_vbus_vld) { + pxa_otg->otg.state = OTG_STATE_A_WAIT_VFALL; + otg_a_wait_vfail(pxa_otg); +#ifdef CONFIG_USB_PXA_U2O + //u2o_clear(base, U2xPORTSC, ~U2xPORTSC_PP); +#endif + } + pxa_otg->otg_ctrl->a_bus_drop = 0; + mdelay(200); + if (!pxa_otg->xceiv_ops->otgx_check_vbus()) { + pxa_otg->otg_ctrl->a_vbus_vld = 0; + otg_a_idle(pxa_otg); + pxa_otg->xceiv_ops->otgx_set_mode(USB_OTG_LP); + } + pxa3xx_pmic_set_pump(1); + } else { + pxa_otg->otg_ctrl->b_bus_req = 0; + pxa3xx_update_otg(pxa_otg); + } + break; + case 4: + if (pxa_otg->otg.state == OTG_STATE_A_VBUS_ERR) { + pxa_otg->otg_ctrl->a_clr_err = 1; + pxa3xx_update_otg(pxa_otg); + } + break; + + case 5: + pmic_ops->otg_set_vbus(VBUS_HIGH); + break; + case 6: + pmic_ops->otg_set_vbus(VBUS_LOW); + break; + default: + return -EINVAL; + } + return count; +} + +#define create_proc_files() \ + do { struct proc_dir_entry *ent; \ + ent = create_proc_entry(proc_node_name, 0, NULL); \ + if (ent) { \ + ent->data = pxa_otg; \ + ent->read_proc = otg_proc_read; \ + ent->write_proc = otg_proc_write; \ + } \ + } while (0); +#define remove_proc_files() \ + remove_proc_entry(proc_node_name, NULL) +#else +#define create_proc_files() do { } while (0) +#define remove_proc_files() do { } while (0) +#endif + +static int pxa_otg_probe(struct platform_device *pdev) +{ + struct pxa_otg *pxa_otg; + int status; + + if (the_transceiver) + return 0; + + pxa_otg = kzalloc(sizeof(struct pxa_otg), GFP_KERNEL); + if (!pxa_otg) { + pr_err("%s: failed to allocate memory!\n", + __FUNCTION__); + return -ENOMEM; + } + /* Initialize filed of otg */ + pxa_otg->otg.dev = &pdev->dev; + pxa_otg->otg.label = "pxa3xx-otg"; + pxa_otg->otg.set_host = pxa_otg_set_host; + pxa_otg->otg.set_peripheral = pxa_otg_set_peripheral; + pxa_otg->otg.set_power = pxa_otg_set_power; + pxa_otg->otg.start_srp = pxa_otg_start_srp; + pxa_otg->otg.start_hnp = pxa_otg_start_hnp; + pxa_otg->otg.disconnect = pxa_otg_disconnect; + pxa_otg->otg.connect = pxa_otg_connect; + pxa_otg->otg.host_suspend = pxa_otg_host_suspend; + pxa_otg->otg.state = OTG_STATE_UNDEFINED; + pxa_otg->otg.otg_interrupt = pxa_otg_interrupt; + pxa_otg->udev = NULL; + + pxa_otg->pmic_ops = init_otg_pmic_ops(); + +#ifdef CONFIG_USB_PXA_U2O + pxa_otg->xceiv_ops = init_pxa9xx_otg_xceiv_ops(); + pxa_otg->ctrl_ops = init_pxa9xx_otg_ctrl_ops(); +#endif + +#ifdef CONFIG_USB_OTG_PXA3xx_U2D + if (cpu_is_pxa310()) { + pxa_otg->xceiv_ops = init_pxa310_otg_xceiv_ops(); + pxa_otg->ctrl_ops = init_pxa310_otg_ctrl_ops(); + } else +#endif + { +#if defined(CONFIG_USB_OTG_PXA3xx_UDC) + pxa_otg->xceiv_ops = init_pxa300_otg_xceiv_ops(); + pxa_otg->ctrl_ops = init_pxa300_otg_ctrl_ops(); +#endif + } + + if (!pxa_otg->xceiv_ops || !pxa_otg->ctrl_ops) { + pr_err("%s: failed to set ops for ctrl_ops or xceiv_ops!\n", + __FUNCTION__); + kfree(pxa_otg); + return -EINVAL; + } + /* Allocate & initialize field of otg_ctrl */ + pxa_otg->otg_ctrl = kzalloc(sizeof(struct pxa_otgc), GFP_KERNEL); + if (!pxa_otg->otg_ctrl) { + pr_err("%s: failed to allocate memory for otg_ctrl!\n", + __FUNCTION__); + kfree(pxa_otg); + return -ENOMEM; + } + + the_transceiver = pxa_otg; + + pxa_otg->info = pdev->dev.platform_data; + +#if defined(CONFIG_MACH_ASPENITE) || defined(CONFIG_MACH_ZYLONITE2)\ + || defined(CONFIG_MACH_TETON_BGA) + if (machine_is_aspenite() || machine_is_zylonite2() || + machine_is_teton_bga()) { + struct pxa_usb_plat_info *info; + struct resource *res; + + info = (struct pxa_usb_plat_info *)pxa_otg->info; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "u2o"); + info->regbase = (unsigned)ioremap_nocache(res->start, res_size(res)); + if (!info->regbase) { + printk(KERN_ERR "Cannot get regbase 0x%x\n", + info->regbase); + return -ENOMEM; + } + printk("u2otg regbase 0x%x res->start %x pxa_otg->info %p\n", + info->regbase, res->start, pxa_otg->info); + + if (info->init_pmic_ops) { + pxa_otg->pmic_ops = (struct otg_pmic_ops *)info->init_pmic_ops(); + } + } +#endif + status = otg_set_transceiver(&pxa_otg->otg); + if (status < 0) + dev_err(&pdev->dev, "can't register transceiver, %d\n", + status); + + platform_set_drvdata(pdev, pxa_otg); + + create_proc_files(); + return 0; +} + +static int pxa_otg_remove(struct platform_device *pdev) +{ + struct pxa_otg *pxa_otg = platform_get_drvdata(pdev); + + remove_proc_files(); + + pxa_otg->ctrl_ops->otgc_deinit(); + kfree(pxa_otg->otg_ctrl); + kfree(pxa_otg); + the_transceiver = NULL; + + return 0; +} + +#ifdef CONFIG_PM +static int pxa_otg_suspend(struct platform_device *dev, pm_message_t state) +{ + return 0; +} + +static int pxa_otg_resume(struct platform_device *dev) +{ + return 0; +} +#endif + +static struct platform_driver pxa_otg_driver = { + .driver = { + .name = "pxa-otg", + }, + .probe = pxa_otg_probe, + .remove = pxa_otg_remove, +#ifdef CONFIG_PM + .suspend = pxa_otg_suspend, + .resume = pxa_otg_resume, +#endif +}; + +static int __init pxa_otg_init(void) +{ + return platform_driver_register(&pxa_otg_driver); +} + +static void __exit pxa_otg_exit(void) +{ + if (the_transceiver) + otg_set_transceiver(NULL); + platform_driver_unregister(&pxa_otg_driver); +} + +module_init(pxa_otg_init); +module_exit(pxa_otg_exit); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/otg/pxa3xx_otg_pmic.c b/drivers/usb/otg/pxa3xx_otg_pmic.c new file mode 100644 index 00000000000000..b7efe526afcb64 --- /dev/null +++ b/drivers/usb/otg/pxa3xx_otg_pmic.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +/****************************************************************** + * pmic special functions + ****************************************************************** + */ +static int otg_vbus_init(void) +{ + int status = 0; + +#ifdef PXA3xx_OTG_VBUS_PMIC + status = pxa3xx_pmic_set_pump(1); + if (status) + return OTG_I2C_ERROR; +#endif + return 0; +} + +/* Enable or disable USB Vbus + session_enable: whether support to start a session by using VBUS + */ +static int otg_set_vbus(int vbus_type) +{ + int status = 0; + +#ifdef PXA3xx_OTG_VBUS_PMIC + pr_debug("vbus_type %d\n", vbus_type); + switch (vbus_type) { + case VBUS_SRP: + status = pxa3xx_pmic_set_vbus_supply(1, 1); + break; + case VBUS_HIGH: + status = pxa3xx_pmic_set_vbus_supply(1, 0); + break; + case VBUS_LOW: + status = pxa3xx_pmic_set_vbus_supply(0, 0); + break; + default: + break; + } + if (status) + return OTG_I2C_ERROR; +#endif + return 0; +} + +static int otg_set_vbus_ic(int function) +{ + int status = 0; + +#ifdef PXA3xx_OTG_VBUS_PMIC + switch (function) { + case OTG_B_DEVICE: + status = pxa3xx_pmic_set_usbotg_b_mask(); + break; + case OTG_A_DEVICE: + status = pxa3xx_pmic_set_usbotg_a_mask(); + break; + default: + break; + } + if (status) + return OTG_I2C_ERROR; +#endif + return 0; +} + +static struct otg_pmic_ops pxa3xx_otg_pmic_ops = { + .otg_vbus_init = otg_vbus_init, + .otg_set_vbus = otg_set_vbus, + .otg_set_vbus_ic = otg_set_vbus_ic, +}; + +struct otg_pmic_ops *init_otg_pmic_ops(void) +{ + return &pxa3xx_otg_pmic_ops; +} + + diff --git a/drivers/usb/otg/pxa930_otg.c b/drivers/usb/otg/pxa930_otg.c new file mode 100644 index 00000000000000..7572fc9bb1ae22 --- /dev/null +++ b/drivers/usb/otg/pxa930_otg.c @@ -0,0 +1,331 @@ + +#ifdef CONFIG_CPU_PXA930 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pxa930_otg.h" + +#include "../gadget/pxa_comp.h" +#include "../gadget/pxa27x_udc.h" + +extern struct pxa27x_udc *get_the_controller(void); + +static int xcvr_init = 1; +/*---------------------------------------------------------------- + Special for ULPI +------------------------------------------------------------------*/ +static enum u2d_phy_mode xcvr_mode; + +static void reset_xcvr_init(void) +{ + xcvr_init = 1; +} + +static int ulpi_rtsm(void) +{ + struct pxa27x_udc *dev = get_the_controller(); + u32 u2dotgcr = U2DOTGCR; + int count = 100000; + + DMSG("%s, U2DOTGCR %x U2DOTGUSR %x\n", __func__, U2DOTGCR, U2DOTGUSR); + U2DOTGCR = 0; + U2DOTGCR |= U2DOTGCR_RTSM; + + u2dotgcr = U2DOTGCR; + U2DOTGCR = u2dotgcr; + + + while ((U2DOTGCR & U2DOTGCR_LPA) && count) count--; + + if (count <= 0) { + printk(KERN_ALERT "%s time out, reset_xcvr!!! USR %x\n", + __func__, U2DOTGCR); + dev->mach->reset_xcvr(); + } + + xcvr_mode = PRE_SYNCH; + DMSG("%s end, U2DOTGCR %x\n", __func__, U2DOTGCR); + + return 0; +} + +static enum u2d_phy_mode ulpi_get_phymode(void) +{ + enum u2d_phy_mode state; + + state = (U2DOTGCR & 0x8000) >> 12; + /* in case when set UDE it would enter LOWPOWER mode automatically + * if no SOFs longer than 3ms */ + if (state == LOWPOWER) + xcvr_mode = LOWPOWER; + + if ((state != xcvr_mode) && (xcvr_mode != PRE_SYNCH)) { + printk(KERN_DEBUG "ULPI mode %d not aligned, should be %d\n", + state, xcvr_mode); + xcvr_mode = state; + if (state) + ulpi_rtsm(); + } + + return xcvr_mode; +} + +static int ulpi_reg_read(u8 reg, u8 *value) +{ + int i = 50000; + enum u2d_phy_mode state = ulpi_get_phymode(); + + if ((state != SYNCH) && (state != PRE_SYNCH)) { + pr_debug(" not in SYNCH mode!!!"); + return -1; + } + + U2DOTGUCR = U2DOTGUCR_RUN | U2DOTGUCR_RNW | (reg << U2DOTGUCR_ADDR_S); + + while ((U2DOTGUCR & U2DOTGUCR_RUN) && i--) ; + + if (i <= 0) { + printk(KERN_DEBUG "Read ULPI register Time out," + " reg %x otgucr %x, usr %x ucr %x\n", + reg, U2DOTGUCR, U2DOTGUSR, U2DOTGCR); + return -1; + } + + *value = (u8)(U2DOTGUCR & U2DOTGUCR_RDATA); + + DMSG("read ulpi reg %x val %x\n", reg, *value); + return 0; + +} + +static int ulpi_reg_write(u8 reg, u8 value) +{ + int i = 50000; + enum u2d_phy_mode state = ulpi_get_phymode(); + + if ((state != SYNCH) && (state != PRE_SYNCH)) { + pr_debug(": not in SYNCH mode!!!"); + return -1; + } + + U2DOTGUCR = U2DOTGUCR_RUN | (reg << U2DOTGUCR_ADDR_S) \ + | (value << U2DOTGUCR_WDATA_S); + + while ((U2DOTGUCR & U2DOTGUCR_RUN) && i--) ; + + if (i <= 0) { + printk(KERN_DEBUG "Write ULPI register Time out," + " reg %x val %x\n", reg, (int)value); + return -1; + } + DMSG("write ulpi reg %x val %x\n", reg, (int)value); + + return 0; +} + +static int ulpi_set_phymode(enum u2d_phy_mode mode) +{ + u32 state; + u32 u2dotgcr; + + state = ulpi_get_phymode(); + u2dotgcr = U2DOTGCR; + + if ((state == mode) && !xcvr_init) { + if (mode == SYNCH) + U2DOTGCR &= ~(U2DOTGCR_UTMID); + + return 0; + } + + if (xcvr_init == 1) + xcvr_init = 0; + + if ((state != SYNCH) && (state != PRE_SYNCH)) + ulpi_rtsm(); + + switch (mode) { + + case SYNCH: + /* disable D+/D- pulldown resistor */ + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_CLEAR, ULPI_OC_DMPULLDOWN); + + /* Enable ID pullup */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_IDPULLUP); + + /* clear UDE and ULE before enable UTMI */ + U2DOTGCR &= ~(U2DOTGCR_IESI); + U2DCR &= ~U2DCR_UDE; + U2DOTGCR &= ~U2DOTGCR_ULE; + + /* enable the UTMI */ + u2dotgcr = U2DOTGCR; + u2dotgcr &= ~(U2DOTGCR_UTMID); + U2DOTGCR = u2dotgcr; + + /* enable the ULE again */ + U2DOTGCR |= U2DOTGCR_ULE; + + break; + + case LOWPOWER: + /* enable D+/D- pulldown resistor */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DPPULLDOWN); + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_DMPULLDOWN); + + /* Enable ID pullup */ + ulpi_reg_write(ULPI_OTG_CONTROL_SET, ULPI_OC_IDPULLUP); + + /* clear SuspendM in UPLI PHY */ + ulpi_reg_write(ULPI_FUNCTION_CONTROL_CLEAR, ULPI_FC_SUSPENDM); + + break; + + case CARKIT: + default: + printk(KERN_ERR "unsupported ULPI operation modes %d\n", mode); + return -1; + break; + } + + xcvr_mode = mode; + + return 0; +} + +static void dump_ulpi_regs(void) +{ + u8 val; + + ulpi_reg_read(ULPI_VENDOR_LOW, &val); + printk(KERN_DEBUG "vendor ID low %02X\n", val); + + ulpi_reg_read(ULPI_VENDOR_HIGH, &val); + printk(KERN_DEBUG "vendor ID high %02X\n", val); + + ulpi_reg_read(ULPI_PRODUCT_LOW, &val); + printk(KERN_DEBUG "vendor PRODUCT low %02X\n", val); + + ulpi_reg_read(ULPI_PRODUCT_HIGH, &val); + printk(KERN_DEBUG "vendor PRODUCT high %02X\n", val); + + ulpi_reg_read(ULPI_FUNCTION_CONTROL, &val); + printk(KERN_DEBUG "function control %02X\n", val); + + ulpi_reg_read(ULPI_INTERFACE_CONTROL, &val); + printk(KERN_DEBUG "interface control %02X\n", val); + + ulpi_reg_read(ULPI_OTG_CONTROL, &val); + printk(KERN_DEBUG "otg control %02X\n", val); + + ulpi_reg_read(ULPI_INT_RISE, &val); + printk(KERN_DEBUG "interrupt enable rising %02X\n", val); + + ulpi_reg_read(ULPI_INT_FALL, &val); + printk(KERN_DEBUG "interrupt enable falling %02X\n", val); + + ulpi_reg_read(ULPI_INT_STATUS, &val); + printk(KERN_DEBUG "interrupt status %02X\n", val); + + ulpi_reg_read(ULPI_INT_LATCH, &val); + printk(KERN_DEBUG "interrupt latch %02X\n", val); + +} + +/*----------------------------------------------------------------*/ +/****************************************************************** + * Pxa3xx OTG transceiver functions + ****************************************************************** + */ +static int xceiv_mode = USB_OTG_OFF; +static int pxa930_otgx_get_mode(void) +{ + int state = ulpi_get_phymode(); + + switch (state) { + case LOWPOWER: + xceiv_mode = USB_OTG_LP; + break; + case SYNCH: + xceiv_mode = USB_INT_OTG_CLIENT_DP; + break; + case SER_6PIN: + xceiv_mode = USB_INT_OTG_HOST_DP; + break; + case CARKIT: + xceiv_mode = USB_OTG_CARKIT; + break; + case PRE_SYNCH: + xceiv_mode = USB_OTG_PRE_SYNCH; + break; + default: + break; + } + return xceiv_mode; +} + +/* Configures USB host port2 to the desired mode */ +extern int pxa930_otgx_set_mode(int mode) +{ + int status = 0; + + switch (mode) { + + case USB_OTG_LP: + ulpi_set_phymode(LOWPOWER); + break; + case USB_INT_OTG_CLIENT_DP: + ulpi_set_phymode(SYNCH); + break; + + case USB_INT_OTG_HOST_DP: + ulpi_set_phymode(SER_6PIN); + break; + case USB_OTG_CARKIT: + ulpi_set_phymode(CARKIT); + break; + case USB_OTG_PRE_SYNCH: + ulpi_set_phymode(PRE_SYNCH); + break; + default: + status = OTG_INVALID_PARAMETER; + break; + } + + pr_debug("defore transceiver mode %d!\n", xceiv_mode); + xceiv_mode = mode; + pr_debug("set transceiver mode %d!\n", xceiv_mode); + return status; +} + +extern void pxa930_otgx_init(void) +{ + ulpi_rtsm(); +} + +static struct otg_xceiv_ops pxa930_otg_xceiv_ops = { + .otgx_get_mode = pxa930_otgx_get_mode, + .otgx_set_mode = pxa930_otgx_set_mode, + .otgx_init = pxa930_otgx_init, + .reset_xcvr_init = reset_xcvr_init, +}; + +struct otg_xceiv_ops *init_pxa930_otg_xceiv_ops(void) +{ + return &pxa930_otg_xceiv_ops; +} +#endif /* CONFIG_CPU_PXA930 */ diff --git a/drivers/usb/otg/pxa930_otg.h b/drivers/usb/otg/pxa930_otg.h new file mode 100644 index 00000000000000..503c0b39819dfc --- /dev/null +++ b/drivers/usb/otg/pxa930_otg.h @@ -0,0 +1,78 @@ +#ifndef __PXA310_USB_OTG_CONTROLLER__ +#define __PXA310_USB_OTG_CONTROLLER__ + +#define ULPI_VENDOR_LOW 0x0 +#define ULPI_VENDOR_HIGH 0x1 +#define ULPI_PRODUCT_LOW 0x2 +#define ULPI_PRODUCT_HIGH 0x3 +#define ULPI_FUNCTION_CONTROL 0x4 +#define ULPI_FUNCTION_CONTROL_CLEAR 0x6 +#define ULPI_FUNCTION_CONTROL_SET 0x5 +#define ULPI_INTERFACE_CONTROL 0x7 +#define ULPI_INTERFACE_CONTROL_SET 0x8 +#define ULPI_INTERFACE_CONTROL_CLEAR 0x9 +#define ULPI_OTG_CONTROL 0xA +#define ULPI_OTG_CONTROL_SET 0xB +#define ULPI_OTG_CONTROL_CLEAR 0xC +#define ULPI_INT_RISE 0xD +#define ULPI_INT_RISE_SET 0xE +#define ULPI_INT_RISE_CLEAR 0xF +#define ULPI_INT_FALL 0x10 +#define ULPI_INT_FALL_SET 0x11 +#define ULPI_INT_FALL_CLEAR 0x12 +#define ULPI_INT_STATUS 0x13 +#define ULPI_INT_LATCH 0x14 +#define ULPI_DEBUG 0x15 +#define ULPI_SCRATCH 0x16 +#define ULPI_SCRATCH_SET 0x17 +#define ULPI_SCRATCH_CLEAR 0x18 + +#ifdef CONFIG_MACH_LITTLETON +#define ULPI_CARKIT_CONTROL 0x19 +#define ULPI_CARKIT_CONTROL_SET 0x1A +#define ULPI_CARKIT_CONTROL_CLEAR 0x1B +#define ULPI_CARKIT_INT_ENABLE 0x1D +#define ULPI_CARKIT_INT_ENABLE_SET 0x1E +#define ULPI_CARKIT_INT_ENABLE_CLEAR 0x1F +#define ULPI_CARKIT_INT_STATUS 0x20 +#define ULPI_VENDOR_RID_CONV 0x36 +#define ULPI_VENDOR_RID_CONV_SET 0x37 +#define ULPI_VENDOR_RID_CONV_CLEAR 0x38 +#endif + +#define ULPI_FC_RESET (1 << 5) /* XCVR Reset */ +#define ULPI_FC_SUSPENDM (1 << 6) /* XCVR SuspendM, Low Power Mode */ + +#define ULPI_IC_6PIN (1 << 0) /* XCVR 6 pin mode */ +#define ULPI_IC_3PIN (1 << 1) /* XCVR 3 pin mode */ +#ifdef CONFIG_MACH_LITTLETON +#define ULPI_IC_CARKIT (1 << 2) /* Carkit mode */ +#endif +#define ULPI_IC_CLKSUSPENDM (1 << 3) /* Active low clock suspend */ + +#define ULPI_OC_IDPULLUP (1 << 0) /* ID Pull Up, enable sampling of ID line */ +#define ULPI_OC_DPPULLDOWN (1 << 1) /* Enable the 15K Ohm pull down resistor on D+ */ +#define ULPI_OC_DMPULLDOWN (1 << 2) /* Enable the 15K Ohm pull down resistor on D- */ +#define ULPI_OC_DISCHRGVBUS (1 << 3) /* Discharge Vbus */ +#define ULPI_OC_CHRGVBUS (1 << 4) /* Charge Vbus, for Vbus pulsing SRP */ +#define ULPI_OC_DRVVBUS (1 << 5) /* Drive 5V on Vbus */ +#define ULPI_OC_DRVVBUSEXT (1 << 6) /* Drive Vbus using external supply */ + +#define ULPI_INT_HOSTDISCON (1 << 0) /* Host Disconnect */ +#define ULPI_INT_VBUSVALID (1 << 1) /* Vbus Valid */ +#define ULPI_INT_SESSIONVALID (1 << 2) /* Session Valid */ +#define ULPI_INT_SESSIONEND (1 << 3) /* Session End */ +#define ULPI_INT_IDGND (1 << 4) /* current status of IDGND */ + +/* Carkit Control Register Flags */ +#define ULPI_CK_SPKLEFTEN (1 << 4) /* Connect DM to Speaker Left Pin */ +#define ULPI_CK_SPKRIGHTEN (1 << 5) /* Connect DP to Speaker Right/Mic Pin */ +#define ULPI_CK_SPKMICEN (1 << 6) /* Connect DP to Speaker Right/Mic Pin */ + +/* Carkit Interrupt Enable Register Flags */ +#define ULPI_CK_IDFLOATRISE (1 << 0) /* Interrupt enabled when ID Pin transitions from Non-floating to Floating */ +#define ULPI_CK_IDFLOATFALL (1 << 1) /* Interrupt enabled when ID Pin transitions from Floating to Non-floating */ +#define ULPI_CK_RIDINTEN (1 << 5) /* Interrupt enabled when RidConvesionDone bit is asserted */ + +#endif + diff --git a/drivers/usb/otg/pxa_u2o.c b/drivers/usb/otg/pxa_u2o.c new file mode 100644 index 00000000000000..bfe3b122fd5d63 --- /dev/null +++ b/drivers/usb/otg/pxa_u2o.c @@ -0,0 +1,477 @@ +#ifdef CONFIG_USB_OTG +#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 +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include "../gadget/mv/mvUsbDevApi.h" +#include "../gadget/mvUsb.h" + +#ifdef CONFIG_PXA3xx_DVFM +#include +#endif + +//#define DEBUG + +#define DRIVER_VERSION "05-May-2008" +#define DRIVER_DESC "Marvell USB 2.0 OTG Controller" +static const char driver_name [] = "pxa-u2otg"; +static const char driver_desc [] = DRIVER_DESC; + + +struct pxa_u2otg +{ + struct device *dev; + struct clk *clk; + unsigned regbase; + unsigned phybase; + int (*vbus_status) (unsigned base); + int (*vbus_detect) (void *func, int enable); + int (*usbid_detect)(struct otg_transceiver *); + int (*phy_init) (unsigned base); + int (*phy_deinit) (unsigned base); +}; + + +static struct pxa_u2otg *u2otg; +static struct work_struct pxa_otgc_work; +static struct workqueue_struct *pxa_otgc_work_queue; + +static void pxa_u2otg_init(void); + +extern struct mv_usb_dev *get_the_controller(void); +extern void mv_clock_enable(struct mv_usb_dev *, int); +extern void pxa3xx_otg_require_bus(int require); + +/*----------------------------------------------------------------*/ +static int u2otg_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t; + + local_irq_save(flags); + t = scnprintf(next, size, DRIVER_DESC "\n" + "%s\n\tU2xOTGSC = 0x%x\n", + driver_name, u2o_get(u2otg->regbase, U2xOTGSC)); + size -= t; + next += t; + + local_irq_restore(flags); + *eof = 1; + return count - size; +} + +static int u2otg_proc_write(struct file *filp, const char *buffer, + unsigned long count, void *data) +{ + char kbuf[8]; + int index; + + if (count >= 8) + return -EINVAL; + if (copy_from_user(kbuf, buffer, count)) + return -EFAULT; + index = (int)simple_strtoul(kbuf, NULL, 10); + + switch (index) { + default: + break; + } + return count; +} + +#define create_proc_files() \ + do { struct proc_dir_entry *ent;\ + ent = create_proc_entry("driver/u2otg", 0, NULL);\ + if (ent) { \ + ent->data = u2otg_dev; \ + ent->read_proc = u2otg_proc_read; \ + ent->write_proc = u2otg_proc_write; \ + } \ + } while (0); +#define remove_proc_files() \ + remove_proc_entry("driver/u2otg", NULL) + + +/****************************************************************** + * Pxa9xx OTG transceiver functions + ****************************************************************** + */ +static int xceiv_mode = USB_OTG_OFF; +static int pxa9xx_otgx_get_mode(void) +{ + return xceiv_mode; +} +int otg_is_client(void) +{ + struct mv_usb_dev *mv_dev = get_the_controller(); + return (mv_dev->transceiver->default_a != 1); +} +int otg_is_host(void) +{ + struct mv_usb_dev *mv_dev = get_the_controller(); + int ret = mv_dev->transceiver->state == OTG_STATE_A_WAIT_BCON; + struct pxa_otg *pxa_otg = container_of(mv_dev->transceiver, \ + struct pxa_otg, otg); + + ret |= mv_dev->transceiver->state == OTG_STATE_A_HOST; + ret |= (mv_dev->transceiver->state == OTG_STATE_A_IDLE) && pxa_otg->otg_ctrl->a_bus_req; + ret |= mv_dev->transceiver->state == OTG_STATE_A_WAIT_VFALL; + ret |= mv_dev->transceiver->state == OTG_STATE_A_WAIT_VRISE; + return ret; +} + +/* Configures USB 2.0 OTG controller to the desired mode */ +extern void pxa9xx_gadget_init(void); +extern void pxa9xx_ehci_set(int); +extern void set_vbus(int); + +static void pxa_u2o_host_init(struct work_struct *work) +{ + pxa9xx_ehci_set(1); +} + +static int pxa9xx_otgx_set_mode(int mode) +{ + int status = 0; + + pr_debug("%s set mode %d\n", __func__, mode); + switch (mode) { + + case USB_OTG_LP: + break; + case USB_INT_OTG_CLIENT_DP: + pxa_u2otg_init(); + u2otg->phy_init(u2otg->phybase); + pxa9xx_ehci_set(0); + pxa9xx_gadget_init(); + break; + + case USB_INT_OTG_HOST_DP: + pxa_u2otg_init(); + u2otg->phy_init(u2otg->phybase); + INIT_WORK(&pxa_otgc_work, + (void (*)(void *))pxa_u2o_host_init); + queue_work(pxa_otgc_work_queue, &pxa_otgc_work); + break; + case USB_OTG_PRE_SYNCH: + break; + default: + status = OTG_INVALID_PARAMETER; + break; + } + + xceiv_mode = mode; + return status; +} + + +static void pxa9xx_otgx_init(void) +{ + printk("%s not implemented\n", __func__); +} + +static int pxa9xx_otgx_dp_session(void) +{ + printk("%s not implemented\n", __func__); + return 0; +} +static int pxa9xx_otgx_vbus_session(struct pxa_otg *pOtgHandle) +{ + printk("%s not implemented\n", __func__); + return 0; +} +static int pxa9xx_otgx_check_b_hnp(void) +{ + printk("%s not implemented\n", __func__); + return 0; +} + +static int pxa9xx_otgx_check_vbus(void) +{ + if (u2otg->vbus_status) { + return u2otg->vbus_status(u2otg->regbase); + } + else { + printk(KERN_DEBUG "%s not implemented\n", __func__); + return 0; + } +} +static int pxa9xx_otgx_start_autoresume(void) +{ + printk("%s not implemented\n", __func__); + return 0; +} +static int pxa9xx_otgx_drive_resume(void) +{ + printk("%s not implemented\n", __func__); + return 0; +} + +static enum otg_function pxa9xx_otgx_detect_default_func(void) +{ + struct mv_usb_dev *mv_dev = get_the_controller(); + + if (u2otg->usbid_detect) { + if (u2otg->usbid_detect(mv_dev->transceiver)) { + return OTG_B_DEVICE; + } + return OTG_A_DEVICE; + } + + if (rely_on_vbus) + mv_clock_enable(mv_dev, 1); + + /* enable OTG ID pullup register */ + u2o_set(u2otg->regbase, U2xOTGSC, U2xOTGSC_IDPU); + mdelay(3); + + /* ID detect */ + if (u2o_get(u2otg->regbase, U2xOTGSC) & U2xOTGSC_ID) { + if (rely_on_vbus) + mv_clock_enable(mv_dev, 0); + return OTG_B_DEVICE; + } + + return OTG_A_DEVICE; +} + +static struct otg_xceiv_ops pxa9xx_otg_xceiv_ops = { + .otgx_get_mode = pxa9xx_otgx_get_mode, + .otgx_set_mode = pxa9xx_otgx_set_mode, + .otgx_init = pxa9xx_otgx_init, + .otgx_detect_default_func = pxa9xx_otgx_detect_default_func, + .otgx_dp_session = pxa9xx_otgx_dp_session, + .otgx_vbus_session = pxa9xx_otgx_vbus_session, + .otgx_check_b_hnp = pxa9xx_otgx_check_b_hnp, + .otgx_check_vbus = pxa9xx_otgx_check_vbus, + .otgx_start_autoresume = pxa9xx_otgx_start_autoresume, + .otgx_drive_resume = pxa9xx_otgx_drive_resume, +}; + +struct otg_xceiv_ops *init_pxa9xx_otg_xceiv_ops(void) +{ + return &pxa9xx_otg_xceiv_ops; +} + +/****************************************************************** + * Pxa9xx OTG controller functions + ****************************************************************** + */ +static void pxa_otgc_interrupt_init(void) +{ + +} + +static int pxa_otgc_set_mode(int mode) +{ + printk("%s %d not implemented\n", __func__, mode); + return 0; +} + +static int pxa_otgc_reset(void) +{ + int delay, tmp; + + pr_debug("%s line %d\n", __func__, __LINE__); + u2o_clear(u2otg->regbase, U2xUSBCMD, U2xUSBCMD_RS); + u2o_set(u2otg->regbase, U2xUSBCMD, U2xUSBCMD_RST); + delay = 10000; + while ((u2o_get(u2otg->regbase, U2xUSBCMD) & U2xUSBCMD_RST) && delay--); + if (delay <= 0) { + printk("%s reset timeout\n", __func__); + } + + u2o_write(u2otg->regbase, U2xUSBINTR, 0); + u2o_clear(u2otg->regbase, U2xUSBMODE, 0x3); + tmp = u2o_get(u2otg->regbase, U2xUSBSTS); + u2o_write(u2otg->regbase, U2xUSBSTS, tmp); + pr_debug("%s USBCMD %x USBSTS %x USBINTR %x\n", + __func__, u2o_get(u2otg->regbase, U2xUSBCMD), + u2o_get(u2otg->regbase, U2xUSBSTS), u2o_get(u2otg->regbase, U2xUSBINTR)); + return 0; +} + +static int pxa_otgc_interrupt_handle(struct pxa_otg *pOtgHandle) +{ + unsigned long flags; + u8 int_type = OTG_INT_INIT; + u32 status, tmp; + + pr_debug("%s line %d\n", __func__, __LINE__); + if (u2otg->usbid_detect) { + struct mv_usb_dev *mv_dev = get_the_controller(); + + pr_debug("%s id_detect\n", __func__); + if (u2otg->usbid_detect(mv_dev->transceiver)) { + int_type |= OTG_INT_IDR; + } else { + int_type |= OTG_INT_IDF; + } + if (u2otg->vbus_status(u2otg->regbase) == VBUS_LOW) { + /* VBUS off, U2O access not permitted yet*/ + pr_debug("%s line %d return int_type %x\n", + __func__, __LINE__, int_type); + return int_type; + } + } + + status = u2o_get(u2otg->regbase, U2xOTGSC); + status = (status & U2xOTGSC_IS_MASK) & + ((u2o_get(u2otg->regbase, U2xOTGSC) & U2xOTGSC_IE_MASK)>>8); + pr_debug("%s, status %x\n", __func__, status); + + tmp = u2o_get(u2otg->regbase, U2xOTGSC); + u2o_set(u2otg->regbase, U2xOTGSC, tmp); + if (!status) + return OTG_INT_INIT; + local_irq_save(flags); + + pxa_otgc_reset(); + + if (status & U2xOTGSC_IDIS) { + if (u2o_get(u2otg->regbase, U2xOTGSC) & U2xOTGSC_ID) { + int_type |= OTG_INT_IDR; + } else { + int_type |= OTG_INT_IDF; + } + } + + if (status & (U2xOTGSC_BSVIS | U2xOTGSC_ASVIS | U2xOTGSC_BSEIS)) { + if (u2o_get(u2otg->regbase, U2xOTGSC) & (U2xOTGSC_BSV)) { + int_type |= OTG_INT_RVV; + } else if (u2o_get(u2otg->regbase, U2xOTGSC) & (U2xOTGSC_BSE)) { + int_type |= OTG_INT_FVV; + } + } + + local_irq_restore(flags); + return int_type; +} + +static void pxa_otgc_init_gadget(void) +{ + u32 tmp; + + if (u2otg->usbid_detect && u2otg->vbus_status) { + if (u2otg->vbus_status(u2otg->regbase) == VBUS_LOW) { + printk("%s vbus low, return\n", __func__); + return; + } + } + u2o_clear(u2otg->regbase, U2xOTGSC, U2xOTGSC_IE_MASK); + tmp = u2o_get(u2otg->regbase, U2xOTGSC); + u2o_set(u2otg->regbase, U2xOTGSC, tmp); +} + +static void pxa_u2otg_init(void) +{ + if (u2otg->usbid_detect && u2otg->vbus_status) { + if (u2otg->vbus_status(u2otg->regbase) == VBUS_LOW) { + printk("%s vbus low, return\n", __func__); + return; + } + } + + pr_debug("%s U2xUSBCMD %x U2xOTGSC %x\n", __func__, + u2o_get(u2otg->regbase, U2xUSBCMD), + u2o_get(u2otg->regbase, U2xOTGSC)); + pxa_otgc_reset(); + + if (u2otg->vbus_detect) { + u2o_set(u2otg->regbase, U2xOTGSC, U2xOTGSC_IDIE | U2xOTGSC_IDPU); + } else { + u2o_set(u2otg->regbase, U2xOTGSC, U2xOTGSC_IDIE | U2xOTGSC_IDPU | + U2xOTGSC_BSVIE | U2xOTGSC_BSEIE | + U2xOTGSC_AVVIE | U2xOTGSC_ASVIE); + } +} + +static int pxa_otgc_init(void *_data) +{ + struct pxa_usb_plat_info *info = _data; + + u2otg = kmalloc(sizeof(struct pxa_u2otg), GFP_ATOMIC); + if (info->vbus_status) { + u2otg->vbus_status = info->vbus_status; + } + u2otg->regbase = info->regbase; + u2otg->phybase = info->phybase; + u2otg->phy_init = info->phy_init; + u2otg->phy_deinit = info->phy_deinit; + u2otg->usbid_detect = info->usbid_detect; + u2otg->vbus_detect = info->vbus_detect; + + printk("u2otg->regbase %x\n", u2otg->regbase); + + pxa_u2otg_init(); + + pxa_otgc_work_queue = create_workqueue("pxa_otgc_work_queue"); + + /* create_proc_files(); */ + return 0; +} + +static void pxa_otgc_deinit(void) +{ + printk("%s\n", __func__); +} + +static struct otg_ctrl_ops pxa_otg_ctrl_ops = { + .otgc_init = pxa_otgc_init, + .otgc_deinit = pxa_otgc_deinit, + .otgc_interrupt_init = pxa_otgc_interrupt_init, + .otgc_interrupt_handle = pxa_otgc_interrupt_handle, + .otgc_init_gadget = pxa_otgc_init_gadget, +}; + +struct otg_ctrl_ops *init_pxa9xx_otg_ctrl_ops(void) +{ + return &pxa_otg_ctrl_ops; +} + +#endif /* CONFIG_CPU_PXA935 */ diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ddc2af2ba45bc6..d828e82b39ebfb 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -99,7 +99,8 @@ obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o obj-$(CONFIG_FB_PXA) += pxafb.o -obj-$(CONFIG_FB_PXA168) += pxa168fb.o +obj-$(CONFIG_FB_PXA168) += pxa168fb.o pxa168fb_ovly.o +obj-$(CONFIG_FB_PXA910) += pxa910fb.o pxa910fb_ovly.o obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index c025c84601b01d..f162f5353a8d97 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -251,6 +251,14 @@ config BACKLIGHT_SAHARA If you have a Tabletkiosk Sahara Touch-iT, say y to enable the backlight driver. +config BACKLIGHT_PORTOFINO + tristate "Marvell PORTOFINO(PM8606) Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && PORTOFINO + default y + help + If you have a LCD backlight connected to the WLED output of PORTOFINO + WLED output, say Y here to enable this driver. + config BACKLIGHT_WM831X tristate "WM831x PMIC Backlight Driver" depends on BACKLIGHT_CLASS_DEVICE && MFD_WM831X diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 09d1f14d6257ac..653bc02c639d23 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o obj-$(CONFIG_BACKLIGHT_MBP_NVIDIA) += mbp_nvidia_bl.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o +obj-$(CONFIG_BACKLIGHT_PORTOFINO) += portofino_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o diff --git a/drivers/video/backlight/portofino_bl.c b/drivers/video/backlight/portofino_bl.c new file mode 100644 index 00000000000000..147b30d6cfa5eb --- /dev/null +++ b/drivers/video/backlight/portofino_bl.c @@ -0,0 +1,179 @@ +/* + * Backlight driver for Marvell Semiconductor Portofio(PM8606) + * + * Copyright (C) 2009 Marvell International Ltd. + * + * + * 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 + + +#define REG_WLED_DUTY(id) (0x2 + ((id) << 1)) + +#define REG_WLED_CTRL(id) (0x3 + ((id) << 1)) +#define PORTOFINO_WLED_FULL_DUTY 1 +#define PORTOFINO_WLED_CURRENT_MASK 0x3e +#define PORTOFINO_WLED_CURRENT(x) (((x) << 1) & PORTOFINO_WLED_CURRENT_MASK) +#define PORTOFINO_WLED_CURRENT_GET(x) (((x) >> 1) & 0x1f) + +#define PORTOFINO_MAX_BRIGHTNESS 0x1f + +struct portofino_backlight_data { + struct device *portofino_dev; + int id; +}; + +static int portofino_backlight_set(struct backlight_device *bl, int brightness) +{ + struct portofino_backlight_data *data = bl_get_data(bl); + int ret = 0; + u8 val; + + ret = portofino_read(REG_WLED_CTRL(data->id), &val); + val &= ~PORTOFINO_WLED_CURRENT_MASK; + val |= (u8) PORTOFINO_WLED_CURRENT(brightness); + ret = portofino_write(REG_WLED_CTRL(data->id), val); + if (ret){ + bl->props.brightness = 0; + printk("Failed to update backlight!\n"); + return ret; + } + + return 0; +} + +static int portofino_backlight_update_status(struct backlight_device *bl) +{ + int brightness = bl->props.brightness; + + if (bl->props.power != FB_BLANK_UNBLANK) + brightness = 0; + + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + brightness = 0; + + return portofino_backlight_set(bl, brightness); +} + +static int portofino_backlight_get_brightness(struct backlight_device *bl) +{ + struct portofino_backlight_data *data = bl_get_data(bl); + int ret; + u8 val; + ret = portofino_read(REG_WLED_CTRL(data->id), &val); + if(ret){ + printk("Failed to get backlight power! set as 0\n"); + return 0; + } + return (int)PORTOFINO_WLED_CURRENT_GET(val); +} + +static struct backlight_ops portofino_backlight_ops = { + .update_status = portofino_backlight_update_status, + .get_brightness = portofino_backlight_get_brightness, +}; + +static int __init portofino_backlight_probe(struct platform_device *pdev) +{ + struct portofino_backlight_data *data; + struct backlight_device *bl; + int max_brightness, ret = -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + max_brightness = PORTOFINO_MAX_BRIGHTNESS; + + if(pdev->id < 0 || pdev->id > 2) + goto probe; + data->id = pdev->id; + data->portofino_dev = pdev->dev.parent; + + bl = backlight_device_register(pdev->name, data->portofino_dev, + data, &portofino_backlight_ops); + if (IS_ERR(bl)) { + ret = PTR_ERR(bl); + goto probe; + } + + bl->props.max_brightness = max_brightness; + bl->props.brightness = max_brightness >> 1; + + platform_set_drvdata(pdev, bl); + backlight_update_status(bl); + dev_notice(&pdev->dev, "backlight detected at WLED id %d\n", data->id); + return 0; + +probe: + dev_err(&pdev->dev, "failed to register backlight\n"); + kfree(data); + return ret; +} + +static int portofino_backlight_remove(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + struct portofino_backlight_data *data = bl_get_data(bl); + + backlight_device_unregister(bl); + kfree(data); + return 0; +} + +#ifdef CONFIG_PM +static int portofino_backlight_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + return portofino_backlight_set(bl, 0); +} + +static int portofino_backlight_resume(struct platform_device *pdev) +{ + struct backlight_device *bl = platform_get_drvdata(pdev); + + backlight_update_status(bl); + return 0; +} +#else +#define portofino_backlight_suspend NULL +#define portofino_backlight_resume NULL +#endif + +static struct platform_driver portofino_backlight_driver = { + .driver = { + .name = "portofino-bl", + .owner = THIS_MODULE, + }, + .probe = portofino_backlight_probe, + .remove = portofino_backlight_remove, + .suspend = portofino_backlight_suspend, + .resume = portofino_backlight_resume, +}; + +static int __init portofino_backlight_init(void) +{ + return platform_driver_register(&portofino_backlight_driver); +} +module_init(portofino_backlight_init); + +static void __exit portofino_backlight_exit(void) +{ + platform_driver_unregister(&portofino_backlight_driver); +} +module_exit(portofino_backlight_exit); + +MODULE_DESCRIPTION("Backlight Driver for Marvell Semiconductor Portofino PM8606"); +MODULE_AUTHOR("Jun Nie "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:portofino-backlight"); diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c index c91a7f70f7b086..fbf554bea73dfc 100644 --- a/drivers/video/pxa168fb.c +++ b/drivers/video/pxa168fb.c @@ -5,6 +5,7 @@ * All rights reserved. * * 2009-02-16 adapted from original version for PXA168/910 + * Green Wan * Jun Nie * * This file is subject to the terms and conditions of the GNU General Public @@ -13,83 +14,265 @@ */ #include +#include #include #include +#include #include #include +#include #include #include #include #include #include +#include #include #include #include #include #include -#include