summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget13
-rw-r--r--Documentation/ABI/testing/sysfs-devices-platform-ab5500-core-adc20
-rw-r--r--Documentation/DocBook/Makefile7
-rw-r--r--Documentation/DocBook/gpio.tmpl112
-rw-r--r--Documentation/DocBook/i2c.tmpl116
-rw-r--r--Documentation/DocBook/prcmu-fw-api.tmpl109
-rw-r--r--Documentation/DocBook/ste_timed_vibra.tmpl278
-rw-r--r--Documentation/DocBook/stmpe.tmpl115
-rwxr-xr-x[-rw-r--r--]Documentation/DocBook/stylesheet.xsl28
-rw-r--r--Documentation/DocBook/ux500_usb.tmpl151
-rw-r--r--Documentation/trace/stm-trace.txt193
-rw-r--r--Makefile6
-rw-r--r--arch/arm/Kconfig17
-rw-r--r--arch/arm/Kconfig.debug9
-rw-r--r--arch/arm/common/Makefile1
-rw-r--r--arch/arm/common/boottime.c46
-rw-r--r--arch/arm/configs/u8500_android_defconfig344
-rw-r--r--arch/arm/configs/u8500_defconfig255
-rw-r--r--arch/arm/include/asm/cacheflush.h15
-rw-r--r--arch/arm/include/asm/delay.h11
-rw-r--r--arch/arm/include/asm/elf.h4
-rw-r--r--arch/arm/include/asm/io.h6
-rw-r--r--arch/arm/include/asm/outercache.h14
-rw-r--r--arch/arm/include/asm/setup.h21
-rw-r--r--arch/arm/include/asm/smp_twd.h8
-rw-r--r--arch/arm/kernel/armksyms.c4
-rw-r--r--arch/arm/kernel/elf.c6
-rw-r--r--arch/arm/kernel/hw_breakpoint.c25
-rw-r--r--arch/arm/kernel/machine_kexec.c13
-rw-r--r--arch/arm/kernel/process.c11
-rw-r--r--arch/arm/kernel/return_address.c4
-rw-r--r--arch/arm/kernel/smp.c4
-rw-r--r--arch/arm/kernel/smp_twd.c24
-rw-r--r--arch/arm/lib/delay.S69
-rw-r--r--arch/arm/lib/delay.c81
-rw-r--r--arch/arm/mach-ux500/Kconfig128
-rw-r--r--arch/arm/mach-ux500/Makefile68
-rw-r--r--arch/arm/mach-ux500/board-mop500-bm.c517
-rw-r--r--arch/arm/mach-ux500/board-mop500-bm.h25
-rw-r--r--arch/arm/mach-ux500/board-mop500-cyttsp.c250
-rw-r--r--arch/arm/mach-ux500/board-mop500-pins.c1100
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.c350
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.h8
-rw-r--r--arch/arm/mach-ux500/board-mop500-sdi.c105
-rw-r--r--arch/arm/mach-ux500/board-mop500-stm.c447
-rw-r--r--arch/arm/mach-ux500/board-mop500-stuib.c89
-rw-r--r--arch/arm/mach-ux500/board-mop500-u8500uib.c40
-rw-r--r--arch/arm/mach-ux500/board-mop500-uib.c178
-rw-r--r--arch/arm/mach-ux500/board-mop500-vibra.c54
-rw-r--r--arch/arm/mach-ux500/board-mop500.c1100
-rw-r--r--arch/arm/mach-ux500/board-mop500.h24
-rw-r--r--arch/arm/mach-ux500/board-pins-sleep-force.c269
-rw-r--r--arch/arm/mach-ux500/board-pins-sleep-force.h38
-rw-r--r--arch/arm/mach-ux500/board-u5500-bm.c497
-rw-r--r--arch/arm/mach-ux500/board-u5500-bm.h26
-rw-r--r--arch/arm/mach-ux500/board-u5500-cyttsp.c144
-rw-r--r--arch/arm/mach-ux500/board-u5500-pins.c225
-rw-r--r--arch/arm/mach-ux500/board-u5500-regulators.c221
-rw-r--r--arch/arm/mach-ux500/board-u5500-sdi.c232
-rw-r--r--arch/arm/mach-ux500/board-u5500.c752
-rw-r--r--arch/arm/mach-ux500/board-u5500.h35
-rw-r--r--arch/arm/mach-ux500/board-ux500-usb.h13
-rw-r--r--arch/arm/mach-ux500/cache-l2x0.c17
-rw-r--r--arch/arm/mach-ux500/clock-db5500.c745
-rw-r--r--arch/arm/mach-ux500/clock-db8500.c1171
-rw-r--r--arch/arm/mach-ux500/clock-debug.c237
-rw-r--r--arch/arm/mach-ux500/clock.c888
-rw-r--r--arch/arm/mach-ux500/clock.h265
-rw-r--r--arch/arm/mach-ux500/cpu-db5500.c88
-rw-r--r--arch/arm/mach-ux500/cpu-db8500.c113
-rw-r--r--arch/arm/mach-ux500/cpu-db8500.h56
-rw-r--r--arch/arm/mach-ux500/cpu-db9500.c28
-rw-r--r--arch/arm/mach-ux500/cpu.c63
-rw-r--r--arch/arm/mach-ux500/cpuidle.c171
-rw-r--r--arch/arm/mach-ux500/dbx500_dump.c233
-rw-r--r--arch/arm/mach-ux500/devices-common.c78
-rw-r--r--arch/arm/mach-ux500/devices-common.h48
-rw-r--r--arch/arm/mach-ux500/devices-db5500.c301
-rw-r--r--arch/arm/mach-ux500/devices-db5500.h43
-rw-r--r--arch/arm/mach-ux500/devices-db8500.c551
-rw-r--r--arch/arm/mach-ux500/devices-db8500.h5
-rw-r--r--arch/arm/mach-ux500/devices.c45
-rw-r--r--arch/arm/mach-ux500/dma-db5500.c147
-rw-r--r--arch/arm/mach-ux500/dma-db8500.c315
-rw-r--r--arch/arm/mach-ux500/hotplug.c32
-rw-r--r--arch/arm/mach-ux500/hwreg.c736
-rw-r--r--arch/arm/mach-ux500/id.c31
-rw-r--r--arch/arm/mach-ux500/include/mach/context.h92
-rw-r--r--arch/arm/mach-ux500/include/mach/db5500-regs.h1
-rw-r--r--arch/arm/mach-ux500/include/mach/db8500-regs.h7
-rw-r--r--arch/arm/mach-ux500/include/mach/devices.h22
-rw-r--r--arch/arm/mach-ux500/include/mach/gpio.h24
-rw-r--r--arch/arm/mach-ux500/include/mach/hardware.h27
-rw-r--r--arch/arm/mach-ux500/include/mach/id.h65
-rw-r--r--arch/arm/mach-ux500/include/mach/irqs-board-mop500.h4
-rw-r--r--arch/arm/mach-ux500/include/mach/irqs-board-u5500.h11
-rw-r--r--arch/arm/mach-ux500/include/mach/irqs-db5500.h31
-rw-r--r--arch/arm/mach-ux500/include/mach/irqs-db9540.h58
-rw-r--r--arch/arm/mach-ux500/include/mach/irqs.h31
-rw-r--r--arch/arm/mach-ux500/include/mach/pm-timer.h30
-rw-r--r--arch/arm/mach-ux500/include/mach/pm.h109
-rw-r--r--arch/arm/mach-ux500/include/mach/prcmu-debug.h31
-rw-r--r--arch/arm/mach-ux500/include/mach/reboot_reasons.h44
-rw-r--r--arch/arm/mach-ux500/include/mach/setup.h10
-rw-r--r--arch/arm/mach-ux500/include/mach/ste-dma40-db5500.h (renamed from arch/arm/mach-ux500/ste-dma40-db5500.h)12
-rw-r--r--arch/arm/mach-ux500/include/mach/ste-dma40-db8500.h (renamed from arch/arm/mach-ux500/ste-dma40-db8500.h)21
-rw-r--r--arch/arm/mach-ux500/include/mach/suspend.h20
-rw-r--r--arch/arm/mach-ux500/include/mach/timex.h1
-rw-r--r--arch/arm/mach-ux500/include/mach/usb.h12
-rw-r--r--arch/arm/mach-ux500/l2x0-prefetch.c160
-rw-r--r--arch/arm/mach-ux500/mbox-db5500.c565
-rw-r--r--arch/arm/mach-ux500/pins-db8500.h72
-rw-r--r--arch/arm/mach-ux500/pins.c252
-rw-r--r--arch/arm/mach-ux500/pins.h46
-rw-r--r--arch/arm/mach-ux500/platsmp.c4
-rw-r--r--arch/arm/mach-ux500/pm/Kconfig70
-rw-r--r--arch/arm/mach-ux500/pm/Makefile12
-rw-r--r--arch/arm/mach-ux500/pm/context-db5500.c407
-rw-r--r--arch/arm/mach-ux500/pm/context-db8500.c456
-rw-r--r--arch/arm/mach-ux500/pm/context-db9540.c651
-rw-r--r--arch/arm/mach-ux500/pm/context.c1002
-rw-r--r--arch/arm/mach-ux500/pm/context_arm.S409
-rw-r--r--arch/arm/mach-ux500/pm/performance.c224
-rw-r--r--arch/arm/mach-ux500/pm/pm.c218
-rw-r--r--arch/arm/mach-ux500/pm/prcmu-qos-power.c711
-rw-r--r--arch/arm/mach-ux500/pm/runtime.c514
-rw-r--r--arch/arm/mach-ux500/pm/scu.h25
-rw-r--r--arch/arm/mach-ux500/pm/suspend.c281
-rw-r--r--arch/arm/mach-ux500/pm/suspend_dbg.c165
-rw-r--r--arch/arm/mach-ux500/pm/suspend_dbg.h63
-rw-r--r--arch/arm/mach-ux500/pm/timer.c193
-rw-r--r--arch/arm/mach-ux500/pm/usecase_gov.c962
-rw-r--r--arch/arm/mach-ux500/prcc.h20
-rw-r--r--arch/arm/mach-ux500/prcmu-debug.c1041
-rw-r--r--arch/arm/mach-ux500/product.c134
-rw-r--r--arch/arm/mach-ux500/product.h26
-rw-r--r--arch/arm/mach-ux500/reboot_reasons.c79
-rw-r--r--arch/arm/mach-ux500/regulator-u5500.h20
-rw-r--r--arch/arm/mach-ux500/test/Kconfig6
-rw-r--r--arch/arm/mach-ux500/test/Makefile1
-rw-r--r--arch/arm/mach-ux500/test/pwr.c828
-rw-r--r--arch/arm/mach-ux500/timer.c32
-rw-r--r--arch/arm/mach-ux500/uart-db8500.c225
-rw-r--r--arch/arm/mach-ux500/usb.c86
-rw-r--r--arch/arm/mm/cache-fa.S18
-rw-r--r--arch/arm/mm/cache-l2x0.c13
-rw-r--r--arch/arm/mm/cache-v3.S18
-rw-r--r--arch/arm/mm/cache-v4.S18
-rw-r--r--arch/arm/mm/cache-v4wb.S18
-rw-r--r--arch/arm/mm/cache-v4wt.S18
-rw-r--r--arch/arm/mm/cache-v6.S18
-rw-r--r--arch/arm/mm/cache-v7.S94
-rw-r--r--arch/arm/mm/mmu.c14
-rw-r--r--arch/arm/mm/proc-macros.S2
-rw-r--r--arch/arm/mm/proc-v7.S2
-rw-r--r--arch/arm/plat-nomadik/include/plat/mtu.h2
-rw-r--r--arch/arm/plat-nomadik/include/plat/pincfg.h19
-rw-r--r--arch/arm/plat-nomadik/include/plat/ste_dma40.h29
-rw-r--r--arch/arm/plat-nomadik/timer.c32
-rw-r--r--arch/arm/tools/mach-types2796
-rw-r--r--block/partitions/Kconfig19
-rw-r--r--block/partitions/Makefile1
-rwxr-xr-xblock/partitions/blkdev_parts.c127
-rwxr-xr-xblock/partitions/blkdev_parts.h14
-rw-r--r--block/partitions/check.c4
-rw-r--r--drivers/amba/bus.c12
-rw-r--r--drivers/clocksource/Kconfig20
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/clksrc-dbx500-prcmu.c23
-rw-r--r--drivers/clocksource/db5500-mtimer.c67
-rw-r--r--drivers/cpufreq/Makefile3
-rw-r--r--drivers/cpufreq/cpufreq.c21
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c167
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c170
-rw-r--r--drivers/cpufreq/dbx500-cpufreq.c340
-rw-r--r--drivers/dma/ste_dma40.c68
-rw-r--r--drivers/dma/ste_dma40_ll.c26
-rw-r--r--drivers/dma/ste_dma40_ll.h4
-rw-r--r--drivers/gpio/Kconfig2
-rw-r--r--drivers/gpio/gpio-ab8500.c287
-rw-r--r--drivers/gpio/gpio-nomadik.c124
-rw-r--r--drivers/hwmon/Kconfig38
-rw-r--r--drivers/hwmon/Makefile3
-rw-r--r--drivers/hwmon/ab5500.c212
-rw-r--r--drivers/hwmon/ab8500.c184
-rw-r--r--drivers/hwmon/abx500.c698
-rw-r--r--drivers/hwmon/abx500.h95
-rw-r--r--drivers/hwmon/dbx500.c402
-rw-r--r--drivers/hwmon/hwmon.c21
-rw-r--r--drivers/i2c/busses/i2c-nomadik.c57
-rw-r--r--drivers/leds/Kconfig8
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-ab5500.c811
-rw-r--r--drivers/leds/leds-lm3530.c25
-rw-r--r--drivers/leds/leds-lp5521.c8
-rw-r--r--drivers/leds/leds-pwm.c61
-rw-r--r--drivers/mfd/Kconfig46
-rw-r--r--drivers/mfd/Makefile4
-rw-r--r--drivers/mfd/ab5500-core.c72
-rw-r--r--drivers/mfd/ab5500-gpadc.c1256
-rw-r--r--drivers/mfd/ab5500-power.c96
-rw-r--r--drivers/mfd/ab8500-core.c72
-rw-r--r--drivers/mfd/ab8500-debugfs.c1245
-rw-r--r--drivers/mfd/ab8500-denc.c539
-rw-r--r--drivers/mfd/ab8500-gpadc.c90
-rw-r--r--drivers/mfd/ab8500-i2c.c1
-rw-r--r--drivers/mfd/ab8500-sysctrl.c135
-rw-r--r--drivers/mfd/abx500-core.c16
-rw-r--r--drivers/mfd/db5500-prcmu-regs.h141
-rw-r--r--drivers/mfd/db5500-prcmu.c2055
-rw-r--r--drivers/mfd/db8500-prcmu.c141
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h17
-rw-r--r--drivers/mfd/stmpe.c18
-rw-r--r--drivers/mfd/tc35892.c503
-rw-r--r--drivers/mfd/tc3589x.c131
-rw-r--r--drivers/mfd/tps6105x.c1
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Kconfig.stm120
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/ab8500-pwm.c211
-rw-r--r--drivers/misc/bh1780gli.c192
-rw-r--r--drivers/misc/stm.c850
-rw-r--r--drivers/mmc/card/block.c110
-rw-r--r--drivers/mmc/card/mmc_test.c180
-rw-r--r--drivers/mmc/core/core.c35
-rw-r--r--drivers/mmc/core/core.h1
-rw-r--r--drivers/mmc/core/host.c1
-rw-r--r--drivers/mmc/host/mmci.c478
-rw-r--r--drivers/mmc/host/mmci.h9
-rw-r--r--drivers/net/ethernet/smsc/smsc911x.c51
-rw-r--r--drivers/power/Kconfig19
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/ab5500_btemp.c994
-rw-r--r--drivers/power/ab5500_charger.c1820
-rw-r--r--drivers/power/ab5500_fg.c1954
-rw-r--r--drivers/power/ab8500_btemp.c19
-rw-r--r--drivers/power/ab8500_charger.c198
-rw-r--r--drivers/power/ab8500_fg.c12
-rw-r--r--drivers/power/abx500_chargalg.c7
-rw-r--r--drivers/regulator/Kconfig8
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/ab5500.c650
-rw-r--r--drivers/regulator/ab8500-debug.c2083
-rw-r--r--drivers/regulator/ab8500-debug.h80
-rw-r--r--drivers/regulator/ab8500-ext.c451
-rw-r--r--drivers/regulator/ab8500.c1052
-rw-r--r--drivers/regulator/core.c56
-rw-r--r--drivers/regulator/db5500-prcmu.c334
-rw-r--r--drivers/regulator/dbx500-prcmu.c167
-rw-r--r--drivers/rtc/Kconfig8
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-ab.c485
-rw-r--r--drivers/rtc/rtc-ab8500.c53
-rw-r--r--drivers/rtc/rtc-pl031.c14
-rw-r--r--drivers/spi/spi-pl022.c17
-rw-r--r--drivers/staging/android/Kconfig18
-rw-r--r--drivers/staging/android/Makefile2
-rw-r--r--drivers/staging/android/ab5500-timed-vibra.c490
-rw-r--r--drivers/staging/android/ste_timed_vibra.c431
-rw-r--r--drivers/tty/serial/Kconfig8
-rw-r--r--drivers/tty/serial/amba-pl011.c434
-rw-r--r--drivers/usb/core/driver.c78
-rw-r--r--drivers/usb/core/hcd.c3
-rw-r--r--drivers/usb/core/hub.c286
-rw-r--r--drivers/usb/core/notify.c7
-rw-r--r--drivers/usb/core/otg_whitelist.h68
-rw-r--r--drivers/usb/core/usb.h8
-rw-r--r--drivers/usb/gadget/Kconfig11
-rw-r--r--drivers/usb/gadget/epautoconf.c6
-rw-r--r--drivers/usb/gadget/f_mass_storage.c1
-rw-r--r--drivers/usb/gadget/f_rndis.c4
-rw-r--r--drivers/usb/musb/Kconfig13
-rw-r--r--drivers/usb/musb/musb_core.c63
-rw-r--r--drivers/usb/musb/musb_core.h17
-rw-r--r--drivers/usb/musb/musb_debugfs.c1
-rw-r--r--drivers/usb/musb/musb_gadget.c33
-rw-r--r--drivers/usb/musb/musb_gadget_ep0.c54
-rw-r--r--drivers/usb/musb/musb_host.c102
-rw-r--r--drivers/usb/musb/musb_regs.h4
-rw-r--r--drivers/usb/musb/musb_virthub.c2
-rw-r--r--drivers/usb/musb/ux500.c515
-rw-r--r--drivers/usb/musb/ux500_dma.c125
-rw-r--r--drivers/usb/otg/Kconfig9
-rw-r--r--drivers/usb/otg/Makefile1
-rw-r--r--drivers/usb/otg/ab5500-usb.c681
-rw-r--r--drivers/usb/otg/ab8500-usb.c1014
-rw-r--r--drivers/watchdog/Kconfig16
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/mpcore_wdt.c98
-rw-r--r--drivers/watchdog/ux500_wdt.c454
-rw-r--r--include/Kbuild1
-rw-r--r--include/linux/ab5500-vibra.h57
-rw-r--r--include/linux/amba/bus.h20
-rw-r--r--include/linux/boottime.h89
-rw-r--r--include/linux/clksrc-db5500-mtimer.h17
-rw-r--r--include/linux/cpufreq-dbx500.h16
-rw-r--r--include/linux/cpufreq.h2
-rw-r--r--include/linux/gpio/nomadik.h (renamed from arch/arm/plat-nomadik/include/plat/gpio-nomadik.h)1
-rw-r--r--include/linux/hwmon.h6
-rw-r--r--include/linux/kexec.h2
-rw-r--r--include/linux/led-lm3530.h9
-rw-r--r--include/linux/leds-ab5500.h35
-rw-r--r--include/linux/leds_pwm.h3
-rw-r--r--include/linux/mfd/ab8500/denc-regs.h357
-rw-r--r--include/linux/mfd/ab8500/denc.h82
-rw-r--r--include/linux/mfd/ab8500/pwmleds.h20
-rw-r--r--include/linux/mfd/abx500.h9
-rw-r--r--include/linux/mfd/abx500/ab5500-bm.h116
-rw-r--r--include/linux/mfd/abx500/ab5500-gpadc.h71
-rw-r--r--include/linux/mfd/abx500/ab5500.h27
-rw-r--r--include/linux/mfd/abx500/ab8500-bm.h1
-rw-r--r--include/linux/mfd/abx500/ab8500-gpio.h78
-rw-r--r--include/linux/mfd/abx500/ab8500-sysctrl.h5
-rw-r--r--include/linux/mfd/abx500/ab8500.h54
-rw-r--r--include/linux/mfd/db5500-prcmu.h207
-rw-r--r--include/linux/mfd/db8500-prcmu.h93
-rw-r--r--include/linux/mfd/dbx500-prcmu.h71
-rw-r--r--include/linux/mfd/stmpe.h2
-rw-r--r--include/linux/mfd/tc35892.h146
-rw-r--r--include/linux/mfd/tc3589x.h61
-rw-r--r--include/linux/mmc/host.h17
-rw-r--r--include/linux/pwm.h10
-rw-r--r--include/linux/regulator/ab5500.h34
-rw-r--r--include/linux/regulator/ab8500-debug.h21
-rw-r--r--include/linux/regulator/ab8500.h47
-rw-r--r--include/linux/regulator/db5500-prcmu.h27
-rw-r--r--include/linux/regulator/dbx500-prcmu.h92
-rw-r--r--include/linux/ste_timed_vibra.h41
-rw-r--r--include/linux/usb.h12
-rw-r--r--include/linux/usb/ch9.h12
-rw-r--r--include/linux/usb/gadget.h7
-rw-r--r--include/linux/usb/otg.h5
-rw-r--r--include/trace/Kbuild1
-rw-r--r--include/trace/stm.h228
-rw-r--r--init/Kconfig9
-rw-r--r--init/Makefile1
-rw-r--r--init/boottime.c475
-rw-r--r--init/main.c6
-rw-r--r--kernel/irq/chip.c8
-rw-r--r--kernel/kexec.c12
-rw-r--r--kernel/power/suspend.c37
-rw-r--r--kernel/printk.c12
-rw-r--r--kernel/trace/trace.c11
-rw-r--r--kernel/trace/trace_events.c3
-rw-r--r--kernel/trace/trace_sched_switch.c10
-rwxr-xr-xscripts/setlocalversion42
346 files changed, 56987 insertions, 3554 deletions
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget b/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget
index d548eaac230..3bf505e7227 100644
--- a/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget
+++ b/Documentation/ABI/testing/sysfs-devices-platform-_UDC_-gadget
@@ -19,3 +19,16 @@ Description:
Possible values are:
1 -> ignore the FUA flag
0 -> obey the FUA flag
+
+What: /sys/devices/platform/_UDC_/gadget/host_request
+Date: December 2010
+Contact: Pavan Kondeti <pkondeti@...>
+Description:
+ OTG 2.0 compliant host keeps polling OTG2.0 peripheral
+ for host role. Set host_request flag, which tells host
+ to give up the host role to peripheral.
+
+ 1 -> host role is requested
+ 0 -> no effect (automatically cleared upon reset/disconnect)
+
+ (_UDC_ is the name of the USB Device Controller driver)
diff --git a/Documentation/ABI/testing/sysfs-devices-platform-ab5500-core-adc b/Documentation/ABI/testing/sysfs-devices-platform-ab5500-core-adc
new file mode 100644
index 00000000000..fcfc0ed26fb
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-platform-ab5500-core-adc
@@ -0,0 +1,20 @@
+What: /sys/devices/platform/ab5500-core.0/ab5500-adc.0/adc0volt
+Date: Nov 2011
+KernelVersion: 3.0
+Contact: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+Description: The adc0volt attribute allows the userspace to read the
+ voltage of the device connected to the General Purpose
+ Analog to Digital Converter (GPADC) channel-0. Voltage
+ conversion from analog to digital happens only when this
+ attribute is read. GPADC block is present in AB5500 chip
+ and has input voltage range of 0-1.8 volt for GPADC Ch-0.
+ It provides result of the converted voltage in 10 bits.
+ Other GPADC channels attributes may appear in this path
+ later. For minimum and maximum input voltage range for
+ each channel please refer to the ST-Ericssons AB5500
+ datasheet. An example usage of GPADC can be an ALS device
+ connected to the channel and user space adapts the
+ LCD backlight brightness based on ambient light value
+ read from the attribute.
+Users: HAL.
+
diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
index 66725a3d30d..ae3d4c348c1 100644
--- a/Documentation/DocBook/Makefile
+++ b/Documentation/DocBook/Makefile
@@ -14,7 +14,12 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
80211.xml debugobjects.xml sh.xml regulator.xml \
alsa-driver-api.xml writing-an-alsa-driver.xml \
- tracepoint.xml drm.xml media_api.xml
+ tracepoint.xml drm.xml media_api.xml \
+ shrm.xml touchp.xml \
+ tc_keypad.xml prcmu-fw-api.xml cg2900_fm_radio.xml \
+ synaptics_rmi4_touchp.xml db5500_keypad.xml \
+ u5500_LogicalMailbox.xml cg2900.xml \
+ lsm303dlh.xml ske_keypad.xml ste_ff_vibra.xml ste_timed_vibra.xml ux500_usb.xml
include $(srctree)/Documentation/DocBook/media/Makefile
diff --git a/Documentation/DocBook/gpio.tmpl b/Documentation/DocBook/gpio.tmpl
new file mode 100644
index 00000000000..b69c2770210
--- /dev/null
+++ b/Documentation/DocBook/gpio.tmpl
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="GPIO">
+ <bookinfo>
+ <title>GPIO1B</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Alessandro</firstname>
+ <surname>Rubini</surname>
+ <affiliation>
+ <address>
+ <email>rubini@unipv.it</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Prafulla</firstname>
+ <surname>WADASKAR</surname>
+ <affiliation>
+ <address>
+ <email>prafulla.wadaskar@st.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2008-2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <!-- Do NOT remove the legal notice below -->
+
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This Documentation describes the API's provided by the GPIO controller Driver.
+ </para>
+ <para>
+ Only the API specific to the Ux500 platform is listed here. For the generic GPIO
+ API, see <filename>Documentation/gpio.txt</filename> in the kernel source tree.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title> Public Interface </title>
+ <para>
+ This Section lists the API's provided by the GPIO controller driver to client drivers.
+ </para>
+ <para>
+ Only the API specific to the Ux500 platform is listed here. For the generic GPIO
+ API, see <filename>Documentation/gpio.txt</filename> in the kernel source tree.
+ </para>
+!Earch/arm/plat-nomadik/gpio.c
+ </chapter>
+</book>
diff --git a/Documentation/DocBook/i2c.tmpl b/Documentation/DocBook/i2c.tmpl
new file mode 100644
index 00000000000..8a4cb49204e
--- /dev/null
+++ b/Documentation/DocBook/i2c.tmpl
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="I2C">
+ <bookinfo>
+ <title>I2C</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Srinidhi</firstname>
+ <surname>Kasagar</surname>
+ <affiliation>
+ <address>
+ <email>srinidhi.kasagar@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Sachin</firstname>
+ <surname>Verma</surname>
+ <affiliation>
+ <address>
+ <email>sachin.verma@st.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2009-2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <!-- Do NOT remove the legal notice below -->
+
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This Documentation describes the API's provided by the I2C controller Driver.
+ Since this driver registers the transferfunction with kernel framework, there
+ are only private functions in this I2C bus driver. This driver currently
+ works only in master mode and does 7 bit adderssing only. There is no support
+ for 10 bit addressing. The driver currently supports standard mode (100KHz)
+ and Fast mode (400KHz) operation.
+ </para>
+ </chapter>
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ Not Applicable
+ </para>
+ </chapter>
+
+ <chapter id="private">
+ <title>Private Functions</title>
+ <para>
+ This Section lists the functions used internally by the I2C controller driver.
+ </para>
+!Idrivers/i2c/busses/i2c-nomadik.c
+ </chapter>
+
+</book>
diff --git a/Documentation/DocBook/prcmu-fw-api.tmpl b/Documentation/DocBook/prcmu-fw-api.tmpl
new file mode 100644
index 00000000000..445a277933c
--- /dev/null
+++ b/Documentation/DocBook/prcmu-fw-api.tmpl
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="STw4500">
+ <bookinfo>
+ <title>PRCMU Driver</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Sudeep Karkada</firstname>
+ <surname>Nagesha</surname>
+ <affiliation>
+ <address>
+ <email>sudeepkarkada.nagesha@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2009-2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <!-- Do NOT remove the legal notice below -->
+
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This documentation describes the API provided by the PRCMU firmware interface driver.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="enum">
+ <title>Enumerations</title>
+ <para>
+ This chapter contains the autogenerated documentation of the structures
+ and enumerations which are used in the PRCMU firmware interface driver.
+ It is also required by the client drivers.
+ </para>
+!Iinclude/linux/mfd/dbx500-prcmu.h
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ This chapter contains the autogenerated documentation of the kernel
+ API functions which are exported to the client drivers.
+ </para>
+!Edrivers/mfd/db8500-prcmu.c
+ </chapter>
+
+
+ </book>
diff --git a/Documentation/DocBook/ste_timed_vibra.tmpl b/Documentation/DocBook/ste_timed_vibra.tmpl
new file mode 100644
index 00000000000..ae9e62c9fb1
--- /dev/null
+++ b/Documentation/DocBook/ste_timed_vibra.tmpl
@@ -0,0 +1,278 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="STE-Timed-Output-Vibrator-API-Guide">
+ <bookinfo>
+ <title>Timed Output Vibrator Driver</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Marcin</firstname>
+ <surname>Mielczarczyk</surname>
+ <affiliation>
+ <address>
+ <email>marcin.mielczarczyk@tieto.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+ </legalnotice>
+ </bookinfo>
+
+ <toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This documentation describes the implementation of ST-Ericsson's
+ Timed Output Vibrator driver for the ST-Ericsson Linux platforms.
+ </para>
+ </chapter>
+
+ <chapter id="gettingstarted">
+ <title>Getting Started</title>
+ <para>
+ There are no special compilation flags needed to build the
+ Timed Output Vibrator driver.
+ </para>
+
+ <section id="basic-tutorial">
+ <title>Basic Tutorial</title>
+ <para>
+ Before using this driver few parameters shall be defined in the platform data structure:
+ <itemizedlist>
+ <listitem><para>Boost level - vibrator speed in the the startup stage</para></listitem>
+ <listitem><para>Boost time - vibrator startup period</para></listitem>
+ <listitem><para>On level - vibrator moderate speed</para></listitem>
+ <listitem><para>On time - vibrator moderate period</para></listitem>
+ <listitem><para>Off level - vibrator speed in the stop stage</para></listitem>
+ <listitem><para>Off time - vibrator stop period</para></listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ NOTE: If the time elapsing between the ON and OFF sequence is smaller than the 'boost' period,
+ then OFF is delayed until the 'boost' expires. If the time elapsing between the OFF and
+ ON sequence is smaller than the 'off' period, then ON is delayed until the 'off' period expires.
+ The ON request when vibrator is in 'boost' or 'on' stage is ignored.
+ The OFF request when vibrator is in the 'off' stage is ignored.
+ </para>
+ <para>
+ To enable the Timed Output Vibrator driver using Kconfig, go to
+ <constant> Device Drivers -&gt; Staging -&gt; Android </constant>
+ and enable the following:
+ </para>
+ <itemizedlist>
+ <listitem><para>ST-Ericsson Timed Output Vibrator driver</para></listitem>
+ </itemizedlist>
+ </section>
+
+ </chapter>
+
+ <chapter id="concepts">
+ <title>Concepts</title>
+ <para>
+ Vibrator driver registers as timed output class device (implemented for Android).
+ </para>
+ </chapter>
+
+ <chapter id="tasks">
+ <title>Tasks</title>
+ <para>
+ Timed Output Vibrator can be controlled from user space using following device:
+ <itemizedlist>
+ <listitem><para>/sys/class/timed_output/vibrator/enable</para></listitem>
+ </itemizedlist>
+ To turn on the vibrator for 1s, following command should be executed:
+ <itemizedlist>
+ <listitem><para>echo 1000 &gt; /sys/class/timed_output/vibrator/enable</para></listitem>
+ </itemizedlist>
+ To turn off the vibrator if enabled, then the following command should be executed:
+ <itemizedlist>
+ <listitem><para>echo 0 &gt; /sys/class/timed_output/vibrator/enable</para></listitem>
+ </itemizedlist>
+ To get the remaining time, the following command should be executed:
+ <itemizedlist>
+ <listitem><para>cat /sys/class/timed_output/vibrator/enable</para></listitem>
+ </itemizedlist>
+ </para>
+
+ </chapter>
+
+ <chapter id="driver-configuration">
+ <title>Driver Configuration and Interaction</title>
+ <para>
+ There are no configuration parameters for Timed Output Vibrator Driver.
+ </para>
+ <section id="driver-implemented-operations">
+ <title>Implemented operations in driver</title>
+ <para>
+ All available operations are provided by Timed Output class driver.
+ </para>
+ <para>
+ <table>
+ <title> Supported device driver operations </title>
+ <tgroup cols="2"><tbody>
+ <row><entry> enable </entry> <entry> Calls vibra_enable() function which controls timer and workqueue </entry> </row>
+ <row><entry> get_time </entry> <entry> Calls vibra_get_time() function which returns time remaining to the end of vibration </entry> </row>
+ </tbody></tgroup>
+ </table>
+ </para>
+
+ </section>
+ <section id="driver-loading">
+ <title>Driver loading parameters</title>
+ <para>
+ Not Applicable.
+ </para>
+ </section>
+ <section id="driver-ioctl">
+ <title>Driver IO Control</title>
+ <para>
+ Not Applicable.
+ </para>
+ </section>
+
+ <section id="driver-sysfs">
+ <title>Driver Interaction with Sysfs</title>
+ <para>
+ Timed Output Vibrator driver is available in sysfs as <filename>/sys/class/timed_output/vibrator</filename>
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>enable</term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term>Direction</term>
+ <listitem><para>read-write</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Attribute file type</term>
+ <listitem>
+ <para>Text file</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>File path</term>
+ <listitem><para><filename>/sys/class/timed_output/vibrator/enable</filename></para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Description</term>
+ <listitem>
+ <para>
+ Vibrator is enabled for given period of time on file write (in miliseconds).
+ </para>
+ <para>
+ When written 0, vibrator stops.
+ </para>
+ <para>
+ When file is read, it gives remaining time to disable vibrator (in miliseconds).
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </section>
+ <section id="driver-proc">
+ <title>Driver Interaction using /proc filesystem</title>
+ <para>
+ Not Applicable.
+ </para>
+
+ </section>
+
+ <section id="driver-other">
+ <title>Other means for Driver Interaction</title>
+ <para>
+ Not Applicable.
+ </para>
+ </section>
+
+ <section id="driver-node">
+ <title>Driver Node File</title>
+ <para>
+ Not aplicable.
+ </para>
+ </section>
+
+
+ </chapter>
+
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None.</term>
+ <listitem>
+ <para>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </para>
+ </chapter>
+
+<chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ Not Applicable.
+ </para>
+</chapter>
+
+<chapter id="internal-functions">
+ <title>Internal Functions Provided</title>
+ <para>
+ This chapter contains the autogenerated documentation of the internal functions.
+ </para>
+!Edrivers/staging/android/ste_timed_vibra.c
+</chapter>
+
+</book>
diff --git a/Documentation/DocBook/stmpe.tmpl b/Documentation/DocBook/stmpe.tmpl
new file mode 100644
index 00000000000..9e64a00f6b3
--- /dev/null
+++ b/Documentation/DocBook/stmpe.tmpl
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="STMPE MFD devices">
+ <bookinfo>
+ <title>STMPE IO-Port Expander guide</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Rabin</firstname>
+ <surname>Vincent</surname>
+ <affiliation>
+ <address>
+ <email>rabin.vincent@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This documentation describes the driver for STMicroelectronics
+ STMPExxxx port expander devices.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None.</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ List of public interfaces in stmpe driver
+ </para>
+!Edrivers/mfd/stmpe.c
+ </chapter>
+
+ <chapter id="private">
+ <title>Private Functions</title>
+ <para>
+ STMPE Keypad driver
+ STMPE GPIO driver
+ </para>
+ <section id="stmpe-keypad.c">
+ <title>stmpe-keypad.c</title>
+!Idrivers/input/keyboard/stmpe-keypad.c
+ </section>
+ </chapter>
+
+ <chapter id="Other">
+ <title>Other Data Structures</title>
+ <para>
+ This Section lists some of the Data structure used by the stmpe driver and client drivers.
+ </para>
+!Iinclude/linux/mfd/stmpe.h
+!Idrivers/mfd/stmpe.h
+</chapter>
+</book>
diff --git a/Documentation/DocBook/stylesheet.xsl b/Documentation/DocBook/stylesheet.xsl
index 85b25275196..b2769ce5c8f 100644..100755
--- a/Documentation/DocBook/stylesheet.xsl
+++ b/Documentation/DocBook/stylesheet.xsl
@@ -1,10 +1,18 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<stylesheet xmlns="http://www.w3.org/1999/XSL/Transform" version="1.0">
-<param name="chunk.quietly">1</param>
-<param name="funcsynopsis.style">ansi</param>
-<param name="funcsynopsis.tabular.threshold">80</param>
-<param name="callout.graphics">0</param>
-<!-- <param name="paper.type">A4</param> -->
-<param name="generate.section.toc.level">2</param>
-<param name="use.id.as.filename">1</param>
-</stylesheet>
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+ <xsl:param name="use.id.as.filename" select="'1'"/>
+ <xsl:param name="admon.graphics" select="'1'"/>
+ <xsl:param name="admon.graphics.path"></xsl:param>
+ <xsl:param name="chunk.section.depth" select="2"></xsl:param>
+ <xsl:param name="chunk.quietly">1</xsl:param>
+ <xsl:param name="html.stylesheet"
+ select="'style.css'"/>
+ <xsl:param name="section.autolabel" select="1"/>
+ <xsl:param name="table.section.depth" select="1"/>
+ <xsl:param name="toc.section.depth" select="5"/>
+ <xsl:template name="user.header.content">
+ <link href="../style.css" title="walsh" rel="stylesheet" type="text/css"/>
+ </xsl:template>
+</xsl:stylesheet> \ No newline at end of file
diff --git a/Documentation/DocBook/ux500_usb.tmpl b/Documentation/DocBook/ux500_usb.tmpl
new file mode 100644
index 00000000000..71b744386d4
--- /dev/null
+++ b/Documentation/DocBook/ux500_usb.tmpl
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="USB-FUNCTION-Guide">
+ <bookinfo>
+ <title>USB Driver Function guide</title>
+
+ <authorgroup>
+ <author>
+ <firstname>Praveena</firstname>
+ <surname>Nadahally</surname>
+ <affiliation>
+ <address>
+ <email>praveen.nadahally@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>Rajaram</firstname>
+ <surname>Ragupathy</surname>
+ <affiliation>
+ <address>
+ <email>ragupathy.rajaram@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ <author>
+ <firstname>SakethRam</firstname>
+ <surname>Bommisetti</surname>
+ <affiliation>
+ <address>
+ <email>sakethram.bommisetti@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2011</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Connectivity</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <para>
+ This documentation 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.
+ </para>
+
+ <para>
+ 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.
+ </para>
+
+ <para>
+ 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
+ </para>
+
+ <para>
+ For more details see the file COPYING in the source
+ distribution of Linux.
+ </para>
+
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This documentation describes ST-Ericsson's adaptation on USB external DMA and communication between Mentor USB IP controller and the USB
+ transreceiver
+ </para>
+ </chapter>
+
+ <chapter id="concepts">
+ <title>Concepts</title>
+ <!-- Do NOT change the chapter id or title! -->
+ <para>
+ In ST-Ericsson's USB driver, the open source linux gadget stack and Mentor IP USB 2.0 driver is used. Since the USB Transceiver and Mentor USB IP controller are on different hardware, API's are defined for the communication between them. These API's are available in ux500.c file.
+ The ST-Ericsson's USB driver doesn't have the internal DMA dedicated for USB. So, the external system DMA is used. The integration of external DMA with the mentor chip is available in ux500_dma.c file.
+ Changes have been made in the musb_core.c file where endpoints are configured as per the platform and also integrated DMA specific chnages in the musb_gadget.c file.
+ <!-- TODO: A brief introduction about the concepts
+ which are introduced by the driver.
+ Remove this chapter completely if there are no
+ special concepts introduced by this driver.
+ Do NOT change the chapter id or title! -->
+ <!-- TODO: This guideline for this chapter may be extended
+ during the user-guide guidelines drop. -->
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <!-- Do NOT change the chapter id or title! -->
+ <para>
+ <variablelist>
+
+ <varlistentry>
+ <term>None.</term>
+ <listitem>
+ <para>
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </para>
+ </chapter>
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ The musb driver doesn't export any functions.
+ </para>
+ </chapter>
+ <chapter id="intfunctions">
+ <title>Internal Functions Provided</title>
+ <para>
+ List of internal functions
+ </para>
+ <!-- Do NOT change the chapter id or title! -->
+ <!-- TODO: Replace with link to appropriate headerfile(s),
+ source file(s), or both. One per row, ensure the
+ exclamation mark is on the first column! If no
+ appropriate header or source file exist describing a public interface,
+ replace the inclusion with a paragraph containing the text
+ "Not Applicable"-->
+ <section id="ux500_dma.c">
+ <title>ux500_dma.c</title>
+!Idrivers/usb/musb/ux500_dma.c
+ </section>
+ <section id="ux500.c">
+ <title>ux500.c</title>
+!Idrivers/usb/musb/ux500.c
+ </section>
+</chapter>
+</book>
diff --git a/Documentation/trace/stm-trace.txt b/Documentation/trace/stm-trace.txt
new file mode 100644
index 00000000000..cd73c2b87b7
--- /dev/null
+++ b/Documentation/trace/stm-trace.txt
@@ -0,0 +1,193 @@
+ MIPI System Trace Module driver
+ ===============================
+
+Copyright (C) ST-Ericsson SA 2011
+ Authors: Pierre Peiffer <pierre dot peiffer at stericsson dot com>
+ Philippe Langlais <philippe dot langlais at linaro dot org>
+ License: The GNU Free Documentation License, Version 1.2
+ (dual licensed under the GPL v2)
+
+Hardware overview
+=================
+ This hardware collects and provides simple tracepoints,
+ so a system processor (in our case the main ARM CPU,
+ or some small CPUs and DSPs) can write some data,
+ up to 8 bytes, into a register and out comes a log entry
+ with a time stamp (20ns resolution) on one of 256 channels. Also
+ hardware tracepoints are supported.
+
+ This module external interface is a pad on the chip
+ which complies to the MIPI System Trace Protocol v1.0
+ (see http://www.mipi.org/specifications/debug)
+ and the actual trace output can be read by an
+ electronic probe, not by software so it cannot be intercepted by
+ the CPU and reach Linux userspace.
+
+ Bandwidth depends on number of lines & bus frequency (for example on ux500
+ SoC 4 lines at max 100MHz eg max 400Mbit/s shared between 7 cores).
+ Transmit FIFO size: 256 samples up to 8 bytes.
+ On ux500 platform there is 2 contiguous STM blocks (eg 512 channels)
+
+Software Overview
+=================
+ Write atomicity and write order on STM trace channels is ensured by the fact
+ we try to allocate one channel by execution thread (no concurrent access).
+ There is 2 modes one lossless but intrusive aka Software mode and
+ another lossy mode less intrusive aka Hardware mode, by default
+ all sources are configured in Hardware mode and enabled.
+ The end of data packet is marked by a time stamp on latest byte(s) only.
+
+Kernel API
+----------
+ Configuration functions:
+ output trace clock frequency, trace mode, output port configuration
+ and enable/disable STM trace sources
+ Expose a debugfs interface too for STM trace control
+
+ Alloc/free STM trace channel functions
+
+ Set of low level atomic trace functions for 1, 2, 4 or 8 bytes
+ with & w/o time stamp
+
+ Higher level lockless trace functions:
+ stm_trace_buffer:
+ allocate a channel in 128 highest channels available
+ output the trace buffer with arbitrary length
+ (latest byte(s) automatically time stamped) then free the channel
+ stm_trace_buffer_onchannel:
+ use given channel to output the trace buffer
+ with arbitrary length (latest byte(s) automatically time stamped)
+
+ File IO output console like interface (open, close, write)
+
+ See <trace/stm.h> & drivers/misc/stm.c for more detail
+
+debugfs API
+-----------
+clockdiv:
+ This is used to set or display the current clock divisor
+ that is configured
+
+connection:
+ This is used to set or display the current output connector
+ that is configured (common values, 0 not connected, 1 for default
+ connection, 3 on Ux500 for APE MIPI34 connection)
+
+free_channels:
+ This is used to display the total number of free channels
+
+masters_enable:
+ This sets or displays whether the STM trace sources
+ are activated. Each bits represent the state of corresponding source:
+ 0 for disable or 1 to enable it.
+
+masters_modes:
+ This sets or displays the STM trace sources modes.
+ Each bits represent the mode of corresponding source:
+ 0 for Sofware lossless mode or 1 for Hardware lossy mode.
+
+User API
+--------
+ IOCTLs or debugfs for controls
+ 2 levels API for tracing:
+ - Standard write function, in this case a channel is automatically
+ allocated at first write, after you can channel number
+ with IOCTL STM_GET_CHANNEL_NO
+ - mmap for direct access of all STM trace channels port plus
+ a set of IOCTLs for alloc/free channels, in this case you can
+ write your own lib to easiest its usage
+
+Examples of using the STM
+=========================
+First mount debugfs with:
+mount -t debugfs none /sys/kernel/debug
+
+In a shell scipt
+----------------
+It's as easy as:
+ echo "My trace point" > /dev/stm
+
+To avoid trace overflow, increase STM clock by decreasing the clockdiv with:
+ echo 1 >/sys/kernel/debug/stm/clockdiv # now use DIV2 instead of default DIV8
+If not enough you can disable some sources with:
+ echo YourEnableSources > /sys/kernel/debug/stm/masters_enable
+If always not enough the ultime intrusive way is to change the sources mode
+and set the corresponding sources in Software mode (set corresponding source
+bit to 0) with:
+ echo YourModeSources > /sys/kernel/debug/stm/masters_modes
+ (be aware some source doesn't support Software mode => keep it in HW mode)
+
+NB: on Ux500 platform, first you have to configure STM output port to switch
+APE tracing on MIPI34 connector with:
+ echo 3 > /sys/kernel/debug/stm/connection
+
+In C language
+-------------
+
+The easy way more intrusive (with STM buffer recopy):
+
+#include <trace/stm.h>
+
+int fd, i;
+char buf[1024]; // Try to align this buffer on 64 bits if possible
+
+ fd = open("/dev/stm", O_WRONLY);
+ snprintf(buf, 1024, "STM0 Hello world\n");
+ write(fd, buf, strlen(buf));
+ ioctl(fd, STM_GET_CHANNEL_NO, &i);
+ snprintf(buf, 1024, "Use channel #%d\n", i);
+ write(fd, buf, strlen(buf));
+ close(fd);
+
+NB: You can call open("/dev/stm", O_WRONLY) as many times as necessary
+to allocate a different channel to avoid concurrency in your
+multithreaded application.
+
+The more efficient way, use mmap'ed STM channels memory (to put in a lib):
+
+#include <trace/stm.h>
+
+int fd, i, c, l, maxChannels;
+char buf[1024]; // Try to align this buffer on 64 bits if possible
+volatile struct stm_channel *channels; // mmap'ed channels area
+
+ fd = open("/dev/stm", O_RDWR);
+ ioctl(fd0, STM_GET_NB_MAX_CHANNELS, &maxChannels);
+ channels = (struct stm_channel *)mmap(0, maxChannels*sizeof(*channels),
+ PROT_WRITE, MAP_SHARED, fd, 0);
+ assert(channels != MAP_FAILED);
+
+ if (!ioctl(fd, STM_GET_FREE_CHANNEL, &c)) {
+ l = snprintf(buf, 1024, "STM0 Hello world on channel #%d\n", c);
+ // lazy implementation you have to send buffer by 8 Bytes when possible
+ // and be sure you don't share this channel with others threads
+ for (i=0; i<l; i++) {
+ channels[c].stamp8 = buf[i];
+ }
+ ioctl(fd, STM_RELEASE_CHANNEL, c);
+ }
+ munmap((void *)channels, maxChannels*sizeof(*channels));
+ close(fd);
+
+Kernel Internal usages
+======================
+Dynamically channels dedicated for the kernel are allocated
+in the 128 highest ones
+
+Via menuconfig you can:
+- Duplicate printk output on a STM dedicated channel (255)
+- Have realtime ftrace output to a STM dedicated channel (254),
+ if corresponding TRACER is enabled
+- Have realtime sched context switch & sched wakeup output on dedicated channels
+ (253, 252), if corresponding TRACER is enabled
+- Have Stack Trace on dedicated channels (251)
+- Duplicate trace_printk output on dedicated channels (250 & 249)
+
+
+And in the future:
+------------------
+- Use it in standard kernel tracing infrastucture,
+ possibilities:
+ - Insert other STM trace calls before trace ring buffer write
+ - Substitute time stamping & trace ring buffer by STM
+
diff --git a/Makefile b/Makefile
index a6879630a3e..f75dfdba153 100644
--- a/Makefile
+++ b/Makefile
@@ -443,7 +443,7 @@ asm-generic:
no-dot-config-targets := clean mrproper distclean \
cscope gtags TAGS tags help %docs check% coccicheck \
include/linux/version.h headers_% archheaders archscripts \
- kernelversion %src-pkg
+ kernelrelease kernelversion %src-pkg
config-targets := 0
mixed-targets := 0
@@ -947,7 +947,7 @@ $(vmlinux-dirs): prepare scripts
# Store (new) KERNELRELASE string in include/config/kernel.release
include/config/kernel.release: include/config/auto.conf FORCE
$(Q)rm -f $@
- $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" > $@
+ $(Q)echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion -s $(srctree) -t v$(KERNELVERSION))" > $@
# Things we need to do before we recursively start building the kernel
@@ -1466,7 +1466,7 @@ checkstack:
$(PERL) $(src)/scripts/checkstack.pl $(CHECKSTACK_ARCH)
kernelrelease:
- @echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
+ @echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion -s $(srctree) -t v$(KERNELVERSION))"
kernelversion:
@echo $(KERNELVERSION)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 36586dba6fa..2e1b250151a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -73,6 +73,10 @@ config KTIME_SCALAR
bool
default y
+config KTIME_SCALAR
+ bool
+ default y
+
config HAVE_TCM
bool
select GENERIC_ALLOCATOR
@@ -933,9 +937,11 @@ config ARCH_U8500
select GENERIC_CLOCKEVENTS
select CLKDEV_LOOKUP
select ARCH_REQUIRE_GPIOLIB
+ select HAVE_CLK
select ARCH_HAS_CPUFREQ
select HAVE_SMP
select MIGHT_HAVE_CACHE_L2X0
+ select NOMADIK_GPIO
help
Support for ST-Ericsson's Ux500 architecture
@@ -1609,7 +1615,7 @@ config LOCAL_TIMERS
config ARCH_NR_GPIO
int
default 1024 if ARCH_SHMOBILE || ARCH_TEGRA
- default 355 if ARCH_U8500
+ default 366 if ARCH_U8500
default 264 if MACH_H4700
default 0
help
@@ -1756,7 +1762,9 @@ source "mm/Kconfig"
config FORCE_MAX_ZONEORDER
int "Maximum zone order" if ARCH_SHMOBILE
range 11 64 if ARCH_SHMOBILE
+ depends on SA1111 || UX500_SOC_DB8500
default "9" if SA1111
+ default "12" if UX500_SOC_DB8500
default "11"
help
The kernel memory allocator divides physically contiguous memory
@@ -2071,6 +2079,13 @@ config KEXEC
initially work for you. It may help to enable device hotplugging
support.
+config CRASH_SWRESET
+ bool "Perform a software reset at a panic (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ depends on KEXEC
+ help
+ If no crash kernel has been loaded, perform a SW reset as plan B.
+
config ATAGS_PROC
bool "Export atags in procfs"
depends on KEXEC
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 85348a09d65..6ca82db8d21 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -318,6 +318,15 @@ config EARLY_PRINTK
kernel low-level debugging functions. Add earlyprintk to your
kernel parameters to enable this console.
+config PRINTK_LL
+ bool "Use printascii in printk"
+ depends on DEBUG_LL
+ help
+ Say Y here if you want to have printk send its output via the
+ kernel low-level debugging functions. This is useful if you
+ are debugging code that executes before the earlyprintk console
+ is initialized.
+
config OC_ETM
bool "On-chip ETM and ETB"
depends on ARM_AMBA
diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile
index 215816f1775..d5b5aa2fe3c 100644
--- a/arch/arm/common/Makefile
+++ b/arch/arm/common/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_ARCH_IXP2000) += uengine.o
obj-$(CONFIG_ARCH_IXP23XX) += uengine.o
obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o
obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o
+obj-$(CONFIG_BOOTTIME) += boottime.o
diff --git a/arch/arm/common/boottime.c b/arch/arm/common/boottime.c
new file mode 100644
index 00000000000..73e9e04ed37
--- /dev/null
+++ b/arch/arm/common/boottime.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009-2010
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Store boot times measured during for example u-boot startup.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/boottime.h>
+#include <linux/string.h>
+#include <asm/setup.h>
+
+static u32 bootloader_idle;
+static u32 bootloader_total;
+
+static int __init boottime_parse_tag(const struct tag *tag)
+{
+ int i;
+ char buff[BOOTTIME_MAX_NAME_LEN];
+
+ bootloader_idle = tag->u.boottime.idle;
+ bootloader_total = tag->u.boottime.total;
+
+ for (i = 0; i < tag->u.boottime.num; i++) {
+ snprintf(buff, BOOTTIME_MAX_NAME_LEN, "%s+0x0/0x0",
+ tag->u.boottime.entry[i].name);
+ buff[BOOTTIME_MAX_NAME_LEN - 1] = '\0';
+ boottime_mark_wtime(buff, tag->u.boottime.entry[i].time);
+ }
+
+ return 0;
+}
+
+__tagtable(ATAG_BOOTTIME, boottime_parse_tag);
+
+int boottime_bootloader_idle(void)
+{
+ if (bootloader_total == 0)
+ return 0;
+
+ return (int) ((bootloader_idle) / (bootloader_total / 100));
+}
diff --git a/arch/arm/configs/u8500_android_defconfig b/arch/arm/configs/u8500_android_defconfig
new file mode 100644
index 00000000000..62d360df4f4
--- /dev/null
+++ b/arch/arm/configs/u8500_android_defconfig
@@ -0,0 +1,344 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_BOOTTIME=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_BLKDEV_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_DEFAULT_DEADLINE=y
+CONFIG_ARCH_U8500=y
+CONFIG_UX500_SOC_DB8500=y
+CONFIG_MACH_HREFV60=y
+CONFIG_MACH_U8520=y
+CONFIG_MACH_SNOWBALL=y
+CONFIG_MACH_U9540=y
+CONFIG_UX500_GPIO_KEYS=y
+CONFIG_DBX500_PRCMU_DEBUG=y
+CONFIG_DB8500_PWR_TEST=y
+# CONFIG_UX500_USECASE_GOVERNOR is not set
+CONFIG_DISPLAY_GENERIC_DSI_PRIMARY_VSYNC=y
+CONFIG_DB8500_MLOADER=y
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+CONFIG_HIGHMEM=y
+CONFIG_CMDLINE="root=/dev/ram0 init=init rw console=ttyAMA2,115200n8 mem=256M initrd=0x800000,72M"
+CONFIG_KEXEC=y
+CONFIG_CRASH_SWRESET=y
+CONFIG_CRASH_DUMP=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
+CONFIG_U8500_CPUIDLE_DEEPEST_STATE=2
+CONFIG_UX500_CPUIDLE_DEBUG=y
+CONFIG_FPE_NWFPE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_WAKELOCK=y
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET6_XFRM_MODE_BEET is not set
+# CONFIG_IPV6_SIT is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_ULOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_PHONET=y
+CONFIG_NET_SCHED=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
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+CONFIG_CFG80211_REG_DEBUG=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_PM=y
+CONFIG_RFKILL_LEDS=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_RFKILL_REGULATOR=y
+CONFIG_RFKILL_GPIO=y
+CONFIG_NET_9P=y
+CONFIG_CAIF=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_STANDALONE is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=73728
+CONFIG_AB8500_PWM=y
+CONFIG_SENSORS_BH1780=y
+CONFIG_STE_TRACE_MODEM=y
+CONFIG_DISPDEV=y
+CONFIG_U8500_SIM_DETECT=y
+CONFIG_STM_TRACE=y
+CONFIG_STM_DEFAULT_MASTERS_MODES=0x20
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_CAIF_TTY=m
+CONFIG_CAIF_HSI=m
+CONFIG_SMSC911X=y
+CONFIG_SMSC_PHY=y
+CONFIG_PPP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_ASYNC=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_NOMADIK_SKE=y
+CONFIG_KEYBOARD_TC3589X=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_CYTTSP_CORE=y
+CONFIG_TOUCHSCREEN_CYTTSP_SPI=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AB8500_ACCDET=y
+CONFIG_INPUT_AB8500_PONKEY=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_NOMADIK=y
+CONFIG_I2C=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_NOMADIK=y
+CONFIG_SPI=y
+CONFIG_SPI_PL022=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_TC3589X=y
+CONFIG_GPIO_AB8500=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_AB8500_BM=y
+CONFIG_SENSORS_AB8500=y
+CONFIG_SENSORS_DBX500=y
+CONFIG_SENSORS_LSM303DLH=y
+CONFIG_SENSORS_LSM303DLHC=y
+CONFIG_SENSORS_L3G4200D=y
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_UX500_WATCHDOG_DEBUG=y
+CONFIG_MFD_TC3589X=y
+CONFIG_AB5500_CORE=y
+CONFIG_AB8500_CORE=y
+CONFIG_MFD_DB8500_PRCMU=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DEBUG=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_AB8500=y
+CONFIG_REGULATOR_DB8500_PRCMU=y
+CONFIG_REGULATOR_AB8500_DEBUG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+CONFIG_USB_VIDEO_CLASS=y
+CONFIG_RADIO_CG2900=y
+CONFIG_GPU_MALI=y
+CONFIG_FB=y
+CONFIG_FB_MCDE=y
+CONFIG_MCDE_FB_AVOID_REALLOC=y
+CONFIG_MCDE_DISPLAY_GENERIC_DSI=y
+CONFIG_MCDE_DISPLAY_SAMSUNG_S6D16D0=y
+CONFIG_MCDE_DISPLAY_SONY_ACX424AKP_DSI=y
+CONFIG_MCDE_DISPLAY_AV8100=y
+CONFIG_MCDE_DISPLAY_HDMI_FB_AUTO_CREATE=y
+CONFIG_AV8100_HWTRIG_I2SDAT3=y
+CONFIG_FB_B2R2=y
+CONFIG_B2R2_PLUG_CONF=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_UX500=y
+CONFIG_SND_SOC_UX500_AB5500=y
+CONFIG_SND_SOC_UX500_AB8500=y
+CONFIG_SND_SOC_UX500_CG29XX=y
+CONFIG_SND_SOC_UX500_AV8100=y
+CONFIG_HIDRAW=y
+CONFIG_USB=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_MON=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_UX500=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_USB_G_ANDROID=y
+CONFIG_AB8500_USB=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_BLOCK_MINORS=32
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_ARMMMCI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_LM3530=y
+CONFIG_LEDS_LP5521=y
+CONFIG_LEDS_PWM=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_AB=y
+CONFIG_RTC_DRV_AB8500=y
+CONFIG_DMADEVICES=y
+CONFIG_STE_DMA40=y
+CONFIG_STAGING=y
+CONFIG_AB5500_SIM=y
+CONFIG_CG2900=y
+CONFIG_CG2900_CHIP=y
+CONFIG_STLC2690_CHIP=y
+CONFIG_CG2900_UART=y
+CONFIG_CG2900_AUDIO=y
+CONFIG_CG2900_TEST=y
+CONFIG_BT_CG2900=y
+CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_EARLY_INIT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_CW1200=m
+CONFIG_CW1200_USE_GPIO_IRQ=y
+CONFIG_CW1200_DEBUGFS=y
+CONFIG_U8500_MMIO=y
+CONFIG_U8500_CM=y
+CONFIG_U8500_FLASH=y
+CONFIG_HSEM_U8500=y
+CONFIG_MODEM_U8500=y
+CONFIG_U8500_SHRM=y
+CONFIG_U8500_SHRM_MODEM_SILENT_RESET=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_QUOTA=y
+CONFIG_QFMT_V2=y
+CONFIG_AUTOFS4_FS=m
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HFS_FS=m
+CONFIG_BEFS_FS=m
+CONFIG_CRAMFS=m
+CONFIG_VXFS_FS=m
+CONFIG_MINIX_FS=m
+CONFIG_ROMFS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+CONFIG_9P_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_UX500=y
+CONFIG_CRYPTO_DEV_UX500_HASH=y
+CONFIG_CRC7=y
+CONFIG_LIBCRC32C=m
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index 7e84f453e8a..30e88eb8985 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -1,117 +1,322 @@
CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
# CONFIG_SWAP is not set
CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RELAY=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_BOOTTIME=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
-# CONFIG_LBDAF is not set
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_BLKDEV_PARTITION=y
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SUN_PARTITION=y
+CONFIG_DEFAULT_DEADLINE=y
CONFIG_ARCH_U8500=y
+CONFIG_UX500_SOC_DB8500=y
CONFIG_MACH_HREFV60=y
+CONFIG_MACH_U8520=y
CONFIG_MACH_SNOWBALL=y
-CONFIG_MACH_U5500=y
+CONFIG_MACH_U9540=y
+CONFIG_MACH_U8500_SNOWBALL=y
CONFIG_MACH_UX500_DT=y
+CONFIG_UX500_GPIO_KEYS=y
+CONFIG_DBX500_PRCMU_DEBUG=y
+CONFIG_DB8500_PWR_TEST=y
+CONFIG_DB8500_MLOADER=y
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA is not set
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
+CONFIG_SCHED_MC=y
CONFIG_NR_CPUS=2
CONFIG_PREEMPT=y
CONFIG_AEABI=y
-CONFIG_CMDLINE="root=/dev/ram0 console=ttyAMA2,115200n8"
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_CMDLINE="root=/dev/ram0 init=init rw console=ttyAMA2,115200n8 mem=256M initrd=0x800000,72M"
+CONFIG_KEXEC=y
+CONFIG_CRASH_SWRESET=y
+CONFIG_CRASH_DUMP=y
CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_IDLE=y
CONFIG_VFP=y
CONFIG_NEON=y
CONFIG_PM_RUNTIME=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
+CONFIG_NET_KEY=y
CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET6_XFRM_MODE_BEET is not set
+# CONFIG_IPV6_SIT is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_ULOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP6_NF_IPTABLES=y
CONFIG_PHONET=y
-# CONFIG_WIRELESS is not set
+CONFIG_NET_SCHED=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
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+CONFIG_CFG80211_REG_DEBUG=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=y
CONFIG_CAIF=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_STANDALONE is not set
+CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_BLK_DEV_RAM_SIZE=73728
CONFIG_AB8500_PWM=y
CONFIG_SENSORS_BH1780=y
+CONFIG_STE_TRACE_MODEM=y
+CONFIG_DISPDEV=y
+CONFIG_U8500_SIM_DETECT=y
+CONFIG_STM_TRACE=y
+CONFIG_STM_DEFAULT_MASTERS_MODES=0x20
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_CAIF_TTY=m
+CONFIG_CAIF_HSI=m
CONFIG_SMSC911X=y
CONFIG_SMSC_PHY=y
+CONFIG_PPP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_ASYNC=y
# CONFIG_WLAN is not set
-# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+# CONFIG_INPUT_MOUSEDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_KEYBOARD_ATKBD is not set
CONFIG_KEYBOARD_GPIO=y
-CONFIG_KEYBOARD_NOMADIK=y
-CONFIG_KEYBOARD_STMPE=y
+CONFIG_KEYBOARD_NOMADIK_SKE=y
CONFIG_KEYBOARD_TC3589X=y
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_TOUCHSCREEN=y
-CONFIG_TOUCHSCREEN_BU21013=y
+CONFIG_TOUCHSCREEN_CYTTSP_CORE=y
+CONFIG_TOUCHSCREEN_CYTTSP_SPI=y
CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AB8500_ACCDET=y
CONFIG_INPUT_AB8500_PONKEY=y
-# CONFIG_SERIO is not set
+CONFIG_INPUT_UINPUT=y
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_AMBA_PL011=y
CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_NOMADIK=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_NOMADIK=y
CONFIG_SPI=y
CONFIG_SPI_PL022=y
-CONFIG_GPIO_STMPE=y
+CONFIG_GPIO_SYSFS=y
CONFIG_GPIO_TC3589X=y
+CONFIG_GPIO_AB8500=y
CONFIG_POWER_SUPPLY=y
CONFIG_AB8500_BM=y
CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL=y
-CONFIG_MFD_STMPE=y
+CONFIG_SENSORS_AB8500=y
+CONFIG_SENSORS_DBX500=y
+CONFIG_SENSORS_LSM303DLH=y
+CONFIG_SENSORS_LSM303DLHC=y
+CONFIG_SENSORS_L3G4200D=y
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+CONFIG_UX500_WATCHDOG_DEBUG=y
CONFIG_MFD_TC3589X=y
CONFIG_AB5500_CORE=y
CONFIG_AB8500_CORE=y
+CONFIG_MFD_DB8500_PRCMU=y
CONFIG_REGULATOR=y
+CONFIG_REGULATOR_DEBUG=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
CONFIG_REGULATOR_AB8500=y
-# CONFIG_HID_SUPPORT is not set
+CONFIG_REGULATOR_DB8500_PRCMU=y
+CONFIG_REGULATOR_AB8500_DEBUG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_VIDEO_CAPTURE_DRIVERS is not set
+CONFIG_RADIO_CG2900=y
+CONFIG_DRM=y
+CONFIG_GPU_MALI=y
+CONFIG_FB=y
+CONFIG_FB_MCDE=y
+CONFIG_MCDE_FB_AVOID_REALLOC=y
+CONFIG_MCDE_DISPLAY_SAMSUNG_S6D16D0=y
+CONFIG_MCDE_DISPLAY_SONY_ACX424AKP_DSI=y
+CONFIG_MCDE_DISPLAY_AV8100=y
+# CONFIG_MCDE_DISPLAY_HDMI_FB_AUTO_CREATE is not set
+CONFIG_AV8100_HWTRIG_I2SDAT3=y
+CONFIG_FB_B2R2=y
+CONFIG_B2R2_PLUG_CONF=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_UX500=y
+CONFIG_SND_SOC_UX500_AB5500=y
+CONFIG_SND_SOC_UX500_AB8500=y
+CONFIG_SND_SOC_UX500_CG29XX=y
+CONFIG_SND_SOC_UX500_AV8100=y
+CONFIG_HIDRAW=y
+CONFIG_USB=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_MON=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_UX500=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_FILE_STORAGE=m
+CONFIG_USB_MASS_STORAGE=m
+CONFIG_USB_G_SERIAL=m
+CONFIG_USB_CDC_COMPOSITE=m
+CONFIG_USB_G_MULTI=m
+# CONFIG_USB_G_MULTI_RNDIS is not set
+CONFIG_USB_G_HID=m
CONFIG_AB8500_USB=y
CONFIG_MMC=y
-CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_BLOCK_MINORS=32
+# CONFIG_MMC_BLOCK_BOUNCE is not set
CONFIG_MMC_ARMMMCI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
-CONFIG_LEDS_LM3530=y
-CONFIG_LEDS_LP5521=y
+CONFIG_LEDS_PWM=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_AB=y
CONFIG_RTC_DRV_AB8500=y
-CONFIG_RTC_DRV_PL031=y
CONFIG_DMADEVICES=y
CONFIG_STE_DMA40=y
CONFIG_STAGING=y
+CONFIG_AB5500_SIM=y
+CONFIG_CG2900=y
+CONFIG_CG2900_CHIP=y
+CONFIG_STLC2690_CHIP=y
+CONFIG_CG2900_UART=y
+CONFIG_CG2900_AUDIO=y
+CONFIG_CG2900_TEST=y
+CONFIG_BT_CG2900=y
CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4=y
+CONFIG_CW1200=m
+CONFIG_CW1200_USE_GPIO_IRQ=y
+CONFIG_CW1200_DEBUGFS=y
+CONFIG_U8500_MMIO=y
+CONFIG_U8500_CM=y
+CONFIG_U8500_FLASH=y
CONFIG_HSEM_U8500=y
+CONFIG_MODEM_U8500=y
+CONFIG_U8500_SHRM=y
+CONFIG_U8500_SHRM_MODEM_SILENT_RESET=y
CONFIG_EXT2_FS=y
CONFIG_EXT2_FS_XATTR=y
CONFIG_EXT2_FS_POSIX_ACL=y
CONFIG_EXT2_FS_SECURITY=y
CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_QUOTA=y
+CONFIG_QFMT_V2=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_CONFIGFS_FS=m
-# CONFIG_MISC_FILESYSTEMS is not set
-CONFIG_NFS_FS=y
-CONFIG_ROOT_NFS=y
+CONFIG_9P_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_FS=y
-CONFIG_DEBUG_KERNEL=y
-# CONFIG_SCHED_DEBUG is not set
+CONFIG_UNUSED_SYMBOLS=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
-# CONFIG_FTRACE is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
+CONFIG_KEYS=y
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_UX500=y
+CONFIG_CRYPTO_DEV_UX500_HASH=y
+CONFIG_CRC7=y
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index d5d8d5c7268..a4bf3199819 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -84,6 +84,14 @@
* - kaddr - page address
* - size - region size
*
+ * clean_dcache_all()
+ *
+ * Cleans the entire d-cache.
+ *
+ * flush_dcache_all()
+ *
+ * Flushes the entire d-cache.
+ *
* DMA Cache Coherency
* ===================
*
@@ -104,6 +112,9 @@ struct cpu_cache_fns {
void (*coherent_user_range)(unsigned long, unsigned long);
void (*flush_kern_dcache_area)(void *, size_t);
+ void (*clean_dcache_all)(void);
+ void (*flush_dcache_all)(void);
+
void (*dma_map_area)(const void *, size_t, int);
void (*dma_unmap_area)(const void *, size_t, int);
@@ -124,6 +135,8 @@ extern struct cpu_cache_fns cpu_cache;
#define __cpuc_coherent_kern_range cpu_cache.coherent_kern_range
#define __cpuc_coherent_user_range cpu_cache.coherent_user_range
#define __cpuc_flush_dcache_area cpu_cache.flush_kern_dcache_area
+#define __cpuc_clean_dcache_all cpu_cache.clean_dcache_all
+#define __cpuc_flush_dcache_all cpu_cache.flush_dcache_all
/*
* These are private to the dma-mapping API. Do not use directly.
@@ -144,6 +157,8 @@ extern void __cpuc_flush_user_range(unsigned long, unsigned long, unsigned int);
extern void __cpuc_coherent_kern_range(unsigned long, unsigned long);
extern void __cpuc_coherent_user_range(unsigned long, unsigned long);
extern void __cpuc_flush_dcache_area(void *, size_t);
+extern void __cpuc_clean_dcache_all(void);
+extern void __cpuc_flush_dcache_all(void);
/*
* These are private to the dma-mapping API. Do not use directly.
diff --git a/arch/arm/include/asm/delay.h b/arch/arm/include/asm/delay.h
index b2deda18154..91063a3976f 100644
--- a/arch/arm/include/asm/delay.h
+++ b/arch/arm/include/asm/delay.h
@@ -8,7 +8,7 @@
#include <asm/param.h> /* HZ */
-extern void __delay(int loops);
+extern void __delay(unsigned long loops);
/*
* This function intentionally does not exist; if you see references to
@@ -40,5 +40,14 @@ extern void __const_udelay(unsigned long);
__const_udelay((n) * ((2199023U*HZ)>>11))) : \
__udelay(n))
+extern void (*delay_fn)(unsigned long);
+
+static inline void set_delay_fn(void (*fn)(unsigned long))
+{
+ delay_fn = fn;
+}
+
+extern void read_current_timer_delay_loop(unsigned long loops);
+
#endif /* defined(_ARM_DELAY_H) */
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index 38050b1c480..cd644aade4c 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -96,8 +96,8 @@ struct elf32_hdr;
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
-extern int elf_check_arch(const struct elf32_hdr *);
-#define elf_check_arch elf_check_arch
+extern int arm_elf_check_arch(const struct elf32_hdr *);
+#define elf_check_arch(x) arm_elf_check_arch((const struct elf32_hdr *)(x))
#define vmcore_elf64_check_arch(x) (0)
diff --git a/arch/arm/include/asm/io.h b/arch/arm/include/asm/io.h
index 9af5563dd3e..d9a9a19cb68 100644
--- a/arch/arm/include/asm/io.h
+++ b/arch/arm/include/asm/io.h
@@ -63,6 +63,12 @@ extern void __raw_readsl(const void __iomem *addr, void *data, int longlen);
#define MT_DEVICE_CACHED 2
#define MT_DEVICE_WC 3
/*
+ * NOTE : U8500 v1.0/ED cut specific hack.
+ * look at the commit message for more details
+ */
+#define MT_BACKUP_RAM 4
+
+/*
* types 4 onwards can be found in asm/mach/map.h and are undefined
* for ioremap
*/
diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h
index 53426c66352..e76b9eae8e4 100644
--- a/arch/arm/include/asm/outercache.h
+++ b/arch/arm/include/asm/outercache.h
@@ -33,6 +33,8 @@ struct outer_cache_fns {
#ifdef CONFIG_OUTER_CACHE_SYNC
void (*sync)(void);
#endif
+ void (*prefetch_enable)(void);
+ void (*prefetch_disable)(void);
void (*set_debug)(unsigned long);
void (*resume)(void);
};
@@ -81,6 +83,18 @@ static inline void outer_resume(void)
outer_cache.resume();
}
+static inline void outer_prefetch_enable(void)
+{
+ if (outer_cache.prefetch_enable)
+ outer_cache.prefetch_enable();
+}
+
+static inline void outer_prefetch_disable(void)
+{
+ if (outer_cache.prefetch_disable)
+ outer_cache.prefetch_disable();
+}
+
#else
static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
diff --git a/arch/arm/include/asm/setup.h b/arch/arm/include/asm/setup.h
index 23ebc0c82a3..ea1384fea05 100644
--- a/arch/arm/include/asm/setup.h
+++ b/arch/arm/include/asm/setup.h
@@ -143,6 +143,23 @@ struct tag_memclk {
__u32 fmemclk;
};
+/* for automatic boot timing testcases */
+#define ATAG_BOOTTIME 0x41000403
+#define BOOTTIME_MAX_NAME_LEN 64
+#define BOOTTIME_MAX 10
+
+struct boottime_entry {
+ u32 time; /* in us */
+ u8 name[BOOTTIME_MAX_NAME_LEN];
+};
+
+struct tag_boottime {
+ struct boottime_entry entry[BOOTTIME_MAX];
+ u32 idle; /* in us */
+ u32 total; /* in us */
+ u8 num;
+};
+
struct tag {
struct tag_header hdr;
union {
@@ -165,6 +182,10 @@ struct tag {
* DC21285 specific
*/
struct tag_memclk memclk;
+ /*
+ * Boot time
+ */
+ struct tag_boottime boottime;
} u;
};
diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h
index 0f01f4677bd..e0faaeb30ea 100644
--- a/arch/arm/include/asm/smp_twd.h
+++ b/arch/arm/include/asm/smp_twd.h
@@ -42,4 +42,12 @@ static inline void twd_local_timer_of_register(void)
}
#endif
+#if defined(CONFIG_HOTPLUG) || defined(CONFIG_CPU_IDLE)
+void twd_save(void);
+void twd_restore(void);
+#else
+static inline void twd_save(void) { }
+static inline void twd_restore(void) { }
+#endif
+
#endif
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index b57c75e0b01..f1a50f37efd 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -48,10 +48,6 @@ extern void __aeabi_ulcmp(void);
extern void fpundefinstr(void);
- /* platform dependent support */
-EXPORT_SYMBOL(__udelay);
-EXPORT_SYMBOL(__const_udelay);
-
/* networking */
EXPORT_SYMBOL(csum_partial);
EXPORT_SYMBOL(csum_partial_copy_from_user);
diff --git a/arch/arm/kernel/elf.c b/arch/arm/kernel/elf.c
index d0d1e83150c..6602eae1527 100644
--- a/arch/arm/kernel/elf.c
+++ b/arch/arm/kernel/elf.c
@@ -5,11 +5,13 @@
#include <linux/elf.h>
#include <asm/system_info.h>
-int elf_check_arch(const struct elf32_hdr *x)
+int arm_elf_check_arch(const struct elf32_hdr *x)
{
unsigned int eflags;
/* Make sure it's an ARM executable */
+ if (x->e_ident[EI_CLASS] != ELF_CLASS)
+ return 0;
if (x->e_machine != EM_ARM)
return 0;
@@ -36,7 +38,7 @@ int elf_check_arch(const struct elf32_hdr *x)
}
return 1;
}
-EXPORT_SYMBOL(elf_check_arch);
+EXPORT_SYMBOL(arm_elf_check_arch);
void elf_set_personality(const struct elf32_hdr *x)
{
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index ba386bd9410..180dd9f5b19 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -853,6 +853,25 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
return ret;
}
+static int hw_breakpoint_undef(struct pt_regs *regs, unsigned int instr)
+{
+ int reg = (instr >> 12) & 15;
+
+ /* Fake sticky power-down cleared */
+ regs->uregs[reg] = 0;
+ regs->ARM_pc += 4;
+
+ return 0;
+}
+
+static struct undef_hook hw_breakpoint_hook = {
+ .instr_mask = 0xffff0fff,
+ .instr_val = 0xee110e95,
+ .cpsr_mask = MODE_MASK,
+ .cpsr_val = SVC_MODE,
+ .fn = hw_breakpoint_undef,
+};
+
/*
* One-time initialisation.
*/
@@ -899,6 +918,10 @@ static void reset_ctrl_regs(void *unused)
/*
* Ensure sticky power-down is clear (i.e. debug logic is
* powered up).
+ *
+ * This could raise an undefined instruction exception. If it
+ * does, it is fixed up with an undef hook which constructs
+ * a fake value with the sticky power-down bit cleared.
*/
asm volatile("mrc p14, 0, %0, c1, c5, 4" : "=r" (dbg_power));
if ((dbg_power & 0x1) == 0)
@@ -986,6 +1009,8 @@ static int __init arch_hw_breakpoint_init(void)
*/
register_undef_hook(&debug_reg_hook);
+ register_undef_hook(&hw_breakpoint_hook);
+
/*
* Reset the breakpoint resources. We assume that a halting
* debugger will leave the world in a nice state for us.
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c
index dfcdb9f7c12..4f868fc5174 100644
--- a/arch/arm/kernel/machine_kexec.c
+++ b/arch/arm/kernel/machine_kexec.c
@@ -47,6 +47,7 @@ void machine_crash_nonpanic_core(void *unused)
printk(KERN_DEBUG "CPU %u will stop doing anything useful since another CPU has crashed\n",
smp_processor_id());
crash_save_cpu(&regs, smp_processor_id());
+ atomic_notifier_call_chain(&crash_percpu_notifier_list, 0, NULL);
flush_cache_all();
atomic_dec(&waiting_for_crash_ipi);
@@ -83,6 +84,8 @@ void machine_crash_shutdown(struct pt_regs *regs)
local_irq_disable();
+ atomic_notifier_call_chain(&crash_percpu_notifier_list, 0, NULL);
+
atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
smp_call_function(machine_crash_nonpanic_core, NULL, false);
msecs = 1000; /* Wait at most a second for the other cpus to stop */
@@ -138,3 +141,13 @@ void machine_kexec(struct kimage *image)
soft_restart(reboot_code_buffer_phys);
}
+
+void machine_crash_swreset(void)
+{
+ printk(KERN_INFO "Software reset on panic!\n");
+
+ flush_cache_all();
+ outer_flush_all();
+ outer_disable();
+ arm_pm_restart(0, NULL);
+}
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 2b7b017a20c..5b644d3e831 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -212,8 +212,17 @@ void cpu_idle(void)
leds_event(led_idle_start);
while (!need_resched()) {
#ifdef CONFIG_HOTPLUG_CPU
- if (cpu_is_offline(smp_processor_id()))
+ if (cpu_is_offline(smp_processor_id())) {
+
+ /* NOTE : preempt_count() should be 0 for dying CPU
+ * as the CPU will use this very thread when
+ * it is alive
+ */
+ if (preempt_count())
+ preempt_enable_no_resched();
+
cpu_die();
+ }
#endif
/*
diff --git a/arch/arm/kernel/return_address.c b/arch/arm/kernel/return_address.c
index 8085417555d..0697db65efa 100644
--- a/arch/arm/kernel/return_address.c
+++ b/arch/arm/kernel/return_address.c
@@ -58,10 +58,6 @@ void *return_address(unsigned int level)
#else /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) */
-#if defined(CONFIG_ARM_UNWIND)
-#warning "TODO: return_address should use unwind tables"
-#endif
-
void *return_address(unsigned int level)
{
return NULL;
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index 8f464465977..d0029d1ef7e 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -275,8 +275,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
notify_cpu_starting(cpu);
- calibrate_delay();
-
smp_store_cpu_info(cpu);
/*
@@ -423,7 +421,7 @@ static void ipi_timer(void)
}
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
-static void smp_timer_broadcast(const struct cpumask *mask)
+void smp_timer_broadcast(const struct cpumask *mask)
{
smp_cross_call(mask, IPI_TIMER);
}
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index fef42b21cec..33e4a0456c3 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -33,6 +33,9 @@ static void __iomem *twd_base;
static struct clk *twd_clk;
static unsigned long twd_timer_rate;
+static DEFINE_PER_CPU(u32, twd_ctrl);
+static DEFINE_PER_CPU(u32, twd_load);
+
static struct clock_event_device __percpu **twd_evt;
static int twd_ppi;
@@ -345,3 +348,24 @@ out:
WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
}
#endif
+
+#if defined(CONFIG_HOTPLUG) || defined(CONFIG_CPU_IDLE)
+void twd_save(void)
+{
+ int this_cpu = smp_processor_id();
+
+ per_cpu(twd_ctrl, this_cpu) = __raw_readl(twd_base + TWD_TIMER_CONTROL);
+ per_cpu(twd_load, this_cpu) = __raw_readl(twd_base + TWD_TIMER_LOAD);
+
+}
+
+void twd_restore(void)
+{
+ int this_cpu = smp_processor_id();
+
+ __raw_writel(per_cpu(twd_ctrl, this_cpu),
+ twd_base + TWD_TIMER_CONTROL);
+ __raw_writel(per_cpu(twd_load, this_cpu),
+ twd_base + TWD_TIMER_LOAD);
+}
+#endif
diff --git a/arch/arm/lib/delay.S b/arch/arm/lib/delay.S
deleted file mode 100644
index 3c9a05c8d20..00000000000
--- a/arch/arm/lib/delay.S
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * linux/arch/arm/lib/delay.S
- *
- * Copyright (C) 1995, 1996 Russell King
- *
- * 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 <linux/linkage.h>
-#include <asm/assembler.h>
-#include <asm/param.h>
- .text
-
-.LC0: .word loops_per_jiffy
-.LC1: .word (2199023*HZ)>>11
-
-/*
- * r0 <= 2000
- * lpj <= 0x01ffffff (max. 3355 bogomips)
- * HZ <= 1000
- */
-
-ENTRY(__udelay)
- ldr r2, .LC1
- mul r0, r2, r0
-ENTRY(__const_udelay) @ 0 <= r0 <= 0x7fffff06
- mov r1, #-1
- ldr r2, .LC0
- ldr r2, [r2] @ max = 0x01ffffff
- add r0, r0, r1, lsr #32-14
- mov r0, r0, lsr #14 @ max = 0x0001ffff
- add r2, r2, r1, lsr #32-10
- mov r2, r2, lsr #10 @ max = 0x00007fff
- mul r0, r2, r0 @ max = 2^32-1
- add r0, r0, r1, lsr #32-6
- movs r0, r0, lsr #6
- moveq pc, lr
-
-/*
- * loops = r0 * HZ * loops_per_jiffy / 1000000
- *
- * Oh, if only we had a cycle counter...
- */
-
-@ Delay routine
-ENTRY(__delay)
- subs r0, r0, #1
-#if 0
- movls pc, lr
- subs r0, r0, #1
- movls pc, lr
- subs r0, r0, #1
- movls pc, lr
- subs r0, r0, #1
- movls pc, lr
- subs r0, r0, #1
- movls pc, lr
- subs r0, r0, #1
- movls pc, lr
- subs r0, r0, #1
- movls pc, lr
- subs r0, r0, #1
-#endif
- bhi __delay
- mov pc, lr
-ENDPROC(__udelay)
-ENDPROC(__const_udelay)
-ENDPROC(__delay)
diff --git a/arch/arm/lib/delay.c b/arch/arm/lib/delay.c
new file mode 100644
index 00000000000..b8d636e8ef8
--- /dev/null
+++ b/arch/arm/lib/delay.c
@@ -0,0 +1,81 @@
+/*
+ * Originally from linux/arch/arm/lib/delay.S
+ *
+ * Copyright (C) 1995, 1996 Russell King
+ *
+ * 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 <linux/module.h>
+#include <linux/delay.h>
+#include <linux/timex.h>
+
+/*
+ * Oh, if only we had a cycle counter...
+ */
+static void delay_loop(unsigned long loops)
+{
+ asm volatile(
+ "1: subs %0, %0, #1 \n"
+ " bhi 1b \n"
+ : /* No output */
+ : "r" (loops)
+ );
+}
+
+#ifdef ARCH_HAS_READ_CURRENT_TIMER
+/*
+ * Assumes read_current_timer() is monotonically increasing
+ * across calls and wraps at most once within MAX_UDELAY_MS.
+ */
+void read_current_timer_delay_loop(unsigned long loops)
+{
+ unsigned long bclock, now;
+
+ read_current_timer(&bclock);
+ do {
+ read_current_timer(&now);
+ } while ((now - bclock) < loops);
+}
+#endif
+
+void (*delay_fn)(unsigned long) = delay_loop;
+
+/*
+ * loops = usecs * HZ * loops_per_jiffy / 1000000
+ */
+void __delay(unsigned long loops)
+{
+ delay_fn(loops);
+}
+EXPORT_SYMBOL(__delay);
+
+/*
+ * 0 <= xloops <= 0x7fffff06
+ * loops_per_jiffy <= 0x01ffffff (max. 3355 bogomips)
+ */
+void __const_udelay(unsigned long xloops)
+{
+ unsigned long lpj;
+ unsigned long loops;
+
+ xloops >>= 14; /* max = 0x01ffffff */
+ lpj = loops_per_jiffy >> 10; /* max = 0x0001ffff */
+ loops = lpj * xloops; /* max = 0x00007fff */
+ loops >>= 6; /* max = 2^32-1 */
+
+ if (likely(loops))
+ __delay(loops);
+}
+EXPORT_SYMBOL(__const_udelay);
+
+/*
+ * usecs <= 2000
+ * HZ <= 1000
+ */
+void __udelay(unsigned long usecs)
+{
+ __const_udelay(usecs * ((2199023UL*HZ)>>11));
+}
+EXPORT_SYMBOL(__udelay);
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index ef7099eea0f..a2af24dd3b9 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -4,18 +4,26 @@ config UX500_SOC_COMMON
bool
default y
select ARM_GIC
- select HAS_MTU
+ select NOMADIK_GPIO
select PL310_ERRATA_753970
select ARM_ERRATA_754322
select ARM_ERRATA_764369
select CACHE_L2X0
+ select HAS_MTU
+
+config UX500_SOC_DBX500
+ depends on UX500_SOC_DB5500 || UX500_SOC_DB8500
+ bool
config UX500_SOC_DB5500
bool
select MFD_DB5500_PRCMU
+ select REGULATOR_DB5500_PRCMU
+ select UX500_SOC_DBX500
config UX500_SOC_DB8500
bool
+ select UX500_SOC_DBX500
select MFD_DB8500_PRCMU
select REGULATOR
select REGULATOR_DB8500_PRCMU
@@ -39,6 +47,13 @@ config MACH_HREFV60
Include support for the HREFv60 new development platform.
Includes HREFv70, v71 etc.
+config MACH_U8520
+ bool "U8520 Development platform"
+ depends on UX500_SOC_DB8500
+ select MACH_U8500
+ help
+ Include support for the 8520 development platform.
+
config MACH_SNOWBALL
bool "U8500 Snowball platform"
select MACH_MOP500
@@ -51,6 +66,13 @@ config MACH_U5500
help
Include support for the U5500 development platform.
+config MACH_U9540
+ bool "U9540 Development platform"
+ depends on UX500_SOC_DB8500
+ select MACH_U8500
+ help
+ Include support for the U9540 development platform.
+
config UX500_AUTO_PLATFORM
def_bool y
depends on !MACH_U5500
@@ -67,6 +89,50 @@ config MACH_UX500_DT
endmenu
+choice
+ prompt "Ux500 UIB Keylayout"
+ default KEYLAYOUT_LAYOUT1
+
+config KEYLAYOUT_LAYOUT1
+ bool "UIB Keylayout 1; for generic users"
+ help
+ Supported keylayout for some numerics, power/call buttons,
+ volume control etc
+
+config KEYLAYOUT_LAYOUT2
+ bool "UIB Keylayout 2; for connectivity users"
+ help
+ Supports keylayout numerics 0-9, left/right/up/down/back/
+ enter keys and special character "."(dot)
+
+endchoice
+
+choice
+ prompt "DBx500 sched_clock"
+
+config DBX500_SCHED_CLOCK_PRCMU
+ bool "PRCMU Timer sched_clock"
+ depends on CLKSRC_DBX500_PRCMU
+ select CLKSRC_DBX500_PRCMU_SCHED_CLOCK
+ help
+ Use the always on PRCMU Timer as sched_clock
+
+config DB5500_MTIMER_SCHED_CLOCK
+ bool "MTIMER sched_clock"
+ depends on CLKSRC_DB5500_MTIMER
+ select CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+ help
+ Use the always on MTIMER as sched_clock
+
+config DBX500_MTU_SCHED_CLOCK
+ bool "MTU sched_clock"
+ depends on HAS_MTU
+ select NOMADIK_MTU_SCHED_CLOCK
+ help
+ Use the Multi Timer Unit as the sched_clock.
+
+endchoice
+
config UX500_DEBUG_UART
int "Ux500 UART to use for low-level debug"
default 2
@@ -74,6 +140,12 @@ config UX500_DEBUG_UART
Choose the UART on which kernel low-level debug messages should be
output.
+config UX500_GPIO_KEYS
+ bool "Use gpio-keys for proximity and hal sensors"
+ depends on KEYBOARD_GPIO
+ help
+ Add proximity and hal sensors as a gpio keyboard.
+
config U5500_MODEM_IRQ
bool "Modem IRQ support"
depends on UX500_SOC_DB5500
@@ -81,11 +153,57 @@ config U5500_MODEM_IRQ
help
Add support for handling IRQ:s from modem side
-config U5500_MBOX
- bool "Mailbox support"
- depends on U5500_MODEM_IRQ
+config DBX500_PRCMU_DEBUG
+ bool "DBX500 PRCMU debug"
+ depends on ((MFD_DB5500_PRCMU || MFD_DB8500_PRCMU) && DEBUG_FS)
+ help
+ Add support for PRCMU debug
+
+config TEE_UX500
+ bool "Trusted Execution Environment (TEE) ux500 hardware support"
+ depends on TEE_SUPPORT
+ default y
+ help
+ Adds TEE hardware support for ux500 platforms.
+
+config TEE_SVP
+ bool "Trusted Execution Environment (TEE) ux500 SVP support"
+ depends on TEE_SUPPORT && UX500_SVP
+ default y
+ help
+ Adds TEE support for SVP in ux500 platforms.
+
+config UX500_DEBUG_HWREG
+ bool "Debug hardware registers from userspace"
+ depends on (DEBUG_FS && UX500_SOC_DB8500)
+ help
+ Adds various debug files to access registers.
+ This should never ever be used for anything else than debugging.
+
+config UX500_DEBUG_NO_LAUTERBACH
+ bool "Disable clocks needed for Lauterbach debugging"
+ help
+ Disable clocks needed for Lauterbach debugging at boot.
+ If yes, you will reduce the power consumption.
+
+config UX500_L2X0_PREFETCH_CTRL
+ bool "PL310 prefetch control"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && \
+ (TEE_UX500 && CACHE_L2X0)
default y
help
- Add support for U5500 mailbox communication with modem side
+ Adds interface to control instruction and data prefetch.
+ Communication with Trustzone is done through TEE driver.
+
+config UX500_DB_DUMP
+ bool "DBx500 register dump on crash"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500)
+ default y
+ help
+ Reads specific DBx500 register in case of kernel crash
+ and saves it.
+
+source "arch/arm/mach-ux500/pm/Kconfig"
+source "arch/arm/mach-ux500/test/Kconfig"
endif
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index 465b9ec9510..165435edeed 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -1,20 +1,70 @@
#
-# Makefile for the linux kernel, U8500 machine.
+# Makefile for the linux kernel, UX500 machine.
#
-obj-y := clock.o cpu.o devices.o devices-common.o \
- id.o usb.o timer.o
+obj-y := clock.o cpu.o devices.o \
+ devices-common.o id.o pins.o \
+ usb.o reboot_reasons.o timer.o \
+ uart-db8500.o clock-debug.o
+obj-y += pm/ test/
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
-obj-$(CONFIG_UX500_SOC_DB5500) += cpu-db5500.o dma-db5500.o
-obj-$(CONFIG_UX500_SOC_DB8500) += cpu-db8500.o devices-db8500.o
+obj-$(CONFIG_CPU_IDLE) += cpuidle.o
+
+
+ifeq ($(CONFIG_UX500_SOC_DB5500), y)
+obj-$(CONFIG_UX500_SOC_DBX500) += cpu-db5500.o dma-db5500.o \
+ devices-db5500.o clock-db5500.o
+board-mcde-objs += board-u5500-mcde.o
+endif
+ifeq ($(CONFIG_UX500_SOC_DB8500), y)
+obj-$(CONFIG_UX500_SOC_DBX500) += cpu-db8500.o devices-db8500.o \
+ clock-db8500.o dma-db8500.o cpu-db9500.o
+board-mcde-objs += board-mop500-mcde.o
+endif
obj-$(CONFIG_MACH_MOP500) += board-mop500.o board-mop500-sdi.o \
board-mop500-regulators.o \
board-mop500-uib.o board-mop500-stuib.o \
- board-mop500-u8500uib.o \
- board-mop500-pins.o
-obj-$(CONFIG_MACH_U5500) += board-u5500.o board-u5500-sdi.o
+ board-mop500-u8500uib.o board-mop500-pins.o \
+ board-mop500-bm.o \
+ board-pins-sleep-force.o
+obj-$(CONFIG_ANDROID_STE_TIMED_VIBRA) += board-mop500-vibra.o
+obj-$(CONFIG_MACH_U5500) += board-u5500.o board-u5500-sdi.o \
+ board-u5500-regulators.o \
+ board-u5500-pins.o
+obj-$(CONFIG_U5500_MMIO) += board-u5500-mmio.o
+obj-$(CONFIG_U8500_MMIO) += board-mop500-mmio.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_U5500_MODEM_IRQ) += modem-irq-db5500.o
-obj-$(CONFIG_U5500_MBOX) += mbox-db5500.o
+obj-$(CONFIG_TEE_UX500) += tee_ux500.o product.o
+obj-$(CONFIG_TEE_SVP) += tee_service_svp.o
+obj-$(CONFIG_TEE_SVP) += tee_ta_start_modem_svp.o
+obj-$(CONFIG_DB8500_MLOADER) += mloader-db8500.o
+obj-$(CONFIG_U5500_MLOADER) += mloader-db5500.o
+obj-$(CONFIG_UX500_DEBUG_HWREG) += hwreg.o
+obj-$(CONFIG_HWMEM) += hwmem-int.o
+obj-$(CONFIG_UX500_L2X0_PREFETCH_CTRL) += l2x0-prefetch.o
+obj-$(CONFIG_AB5500_BM) += board-u5500-bm.o
+obj-$(CONFIG_DBX500_PRCMU_DEBUG) += prcmu-debug.o
+obj-$(CONFIG_UX500_DB_DUMP) += dbx500_dump.o
+
+obj-$(CONFIG_HWMEM) += dcache.o
+ifdef CONFIG_STM_TRACE
+obj-$(CONFIG_MACH_MOP500) += board-mop500-stm.o
+endif
+ifdef CONFIG_SENSORS_LSM303DLH
+obj-$(CONFIG_MACH_MOP500) += board-mop500-sensors.o
+endif
+obj-$(CONFIG_FB_MCDE) += board-mcde.o
+ifdef CONFIG_STM_MSP_SPI
+obj-$(CONFIG_MACH_MOP500) += board-mop500-msp.o
+endif
+ifdef CONFIG_CW1200
+obj-$(CONFIG_MACH_MOP500) += board-mop500-wlan.o
+obj-$(CONFIG_MACH_U5500) += board-u5500-wlan.o
+endif
+ifdef CONFIG_TOUCHSCREEN_CYTTSP_SPI
+obj-$(CONFIG_MACH_MOP500) += board-mop500-cyttsp.o
+obj-$(CONFIG_MACH_U5500) += board-u5500-cyttsp.o
+endif
diff --git a/arch/arm/mach-ux500/board-mop500-bm.c b/arch/arm/mach-ux500/board-mop500-bm.c
new file mode 100644
index 00000000000..afdc6ee59a3
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-bm.c
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * U8500 board specific charger and battery initialization parameters.
+ *
+ * Author: Johan Palsson <johan.palsson@stericsson.com> for ST-Ericsson.
+ * Author: Johan Gardsmark <johan.gardsmark@stericsson.com> for ST-Ericsson.
+ *
+ */
+
+#include <linux/power_supply.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/mfd/ab8500/pwmleds.h>
+#include "board-mop500-bm.h"
+
+#ifdef CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL
+/*
+ * These are the defined batteries that uses a NTC and ID resistor placed
+ * inside of the battery pack.
+ * Note that the res_to_temp table must be strictly sorted by falling resistance
+ * values to work.
+ */
+static struct abx500_res_to_temp temp_tbl_A[] = {
+ {-5, 53407},
+ { 0, 48594},
+ { 5, 43804},
+ {10, 39188},
+ {15, 34870},
+ {20, 30933},
+ {25, 27422},
+ {30, 24347},
+ {35, 21694},
+ {40, 19431},
+ {45, 17517},
+ {50, 15908},
+ {55, 14561},
+ {60, 13437},
+ {65, 12500},
+};
+static struct abx500_res_to_temp temp_tbl_B[] = {
+ {-5, 165418},
+ { 0, 159024},
+ { 5, 151921},
+ {10, 144300},
+ {15, 136424},
+ {20, 128565},
+ {25, 120978},
+ {30, 113875},
+ {35, 107397},
+ {40, 101629},
+ {45, 96592},
+ {50, 92253},
+ {55, 88569},
+ {60, 85461},
+ {65, 82869},
+};
+static struct abx500_v_to_cap cap_tbl_A[] = {
+ {4171, 100},
+ {4114, 95},
+ {4009, 83},
+ {3947, 74},
+ {3907, 67},
+ {3863, 59},
+ {3830, 56},
+ {3813, 53},
+ {3791, 46},
+ {3771, 33},
+ {3754, 25},
+ {3735, 20},
+ {3717, 17},
+ {3681, 13},
+ {3664, 8},
+ {3651, 6},
+ {3635, 5},
+ {3560, 3},
+ {3408, 1},
+ {3247, 0},
+};
+static struct abx500_v_to_cap cap_tbl_B[] = {
+ {4161, 100},
+ {4124, 98},
+ {4044, 90},
+ {4003, 85},
+ {3966, 80},
+ {3933, 75},
+ {3888, 67},
+ {3849, 60},
+ {3813, 55},
+ {3787, 47},
+ {3772, 30},
+ {3751, 25},
+ {3718, 20},
+ {3681, 16},
+ {3660, 14},
+ {3589, 10},
+ {3546, 7},
+ {3495, 4},
+ {3404, 2},
+ {3250, 0},
+};
+#endif
+static struct abx500_v_to_cap cap_tbl[] = {
+ {4186, 100},
+ {4163, 99},
+ {4114, 95},
+ {4068, 90},
+ {3990, 80},
+ {3926, 70},
+ {3898, 65},
+ {3866, 60},
+ {3833, 55},
+ {3812, 50},
+ {3787, 40},
+ {3768, 30},
+ {3747, 25},
+ {3730, 20},
+ {3705, 15},
+ {3699, 14},
+ {3684, 12},
+ {3672, 9},
+ {3657, 7},
+ {3638, 6},
+ {3556, 4},
+ {3424, 2},
+ {3317, 1},
+ {3094, 0},
+};
+
+/*
+ * Note that the res_to_temp table must be strictly sorted by falling
+ * resistance values to work.
+ */
+static struct abx500_res_to_temp temp_tbl[] = {
+ {-5, 214834},
+ { 0, 162943},
+ { 5, 124820},
+ {10, 96520},
+ {15, 75306},
+ {20, 59254},
+ {25, 47000},
+ {30, 37566},
+ {35, 30245},
+ {40, 24520},
+ {45, 20010},
+ {50, 16432},
+ {55, 13576},
+ {60, 11280},
+ {65, 9425},
+};
+
+#ifdef CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL
+/*
+ * Note that the batres_vs_temp table must be strictly sorted by falling
+ * temperature values to work.
+ */
+static struct batres_vs_temp temp_to_batres_tbl[] = {
+ { 40, 120},
+ { 30, 135},
+ { 20, 165},
+ { 10, 230},
+ { 00, 325},
+ {-10, 445},
+ {-20, 595},
+};
+#else
+/*
+ * Note that the batres_vs_temp table must be strictly sorted by falling
+ * temperature values to work.
+ */
+#ifdef CONFIG_AB8500_9100_LI_ION_BATTERY
+#define BATRES 180
+#else
+#define BATRES 300
+#endif
+static struct batres_vs_temp temp_to_batres_tbl[] = {
+ { 60, BATRES},
+ { 30, BATRES},
+ { 20, BATRES},
+ { 10, BATRES},
+ { 00, BATRES},
+ {-10, BATRES},
+ {-20, BATRES},
+};
+#endif
+static const struct abx500_battery_type bat_type[] = {
+ [BATTERY_UNKNOWN] = {
+ /* First element always represent the UNKNOWN battery */
+ .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+ .resis_high = 0,
+ .resis_low = 0,
+ .battery_resistance = 300,
+#ifdef CONFIG_AB8500_9100_LI_ION_BATTERY
+ .charge_full_design = 2600,
+#else
+ .charge_full_design = 612,
+#endif
+ .nominal_voltage = 3700,
+#ifdef CONFIG_AB8500_9100_LI_ION_BATTERY
+ .termination_vol = 4150,
+#else
+ .termination_vol = 4050,
+#endif
+ .termination_curr = 200,
+#ifdef CONFIG_AB8500_9100_LI_ION_BATTERY
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 520,
+ .normal_vol_lvl = 4200,
+#else
+ .recharge_vol = 3990,
+ .normal_cur_lvl = 400,
+ .normal_vol_lvl = 4100,
+#endif
+ .maint_a_cur_lvl = 400,
+ .maint_a_vol_lvl = 4050,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 400,
+ .maint_b_vol_lvl = 4000,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl),
+ .batres_tbl = temp_to_batres_tbl,
+ },
+
+#ifdef CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 53407,
+ .resis_low = 12500,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3600,
+ .termination_vol = 4150,
+ .termination_curr = 80,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_A),
+ .r_to_t_tbl = temp_tbl_A,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_A),
+ .v_to_cap_tbl = cap_tbl_A,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl),
+ .batres_tbl = temp_to_batres_tbl,
+
+ },
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 165418,
+ .resis_low = 82869,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3600,
+ .termination_vol = 4150,
+ .termination_curr = 80,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B),
+ .r_to_t_tbl = temp_tbl_B,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B),
+ .v_to_cap_tbl = cap_tbl_B,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl),
+ .batres_tbl = temp_to_batres_tbl,
+ },
+#else
+/*
+ * These are the batteries that doesn't have an internal NTC resistor to measure
+ * its temperature. The temperature in this case is measure with a NTC placed
+ * near the battery but on the PCB.
+ */
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 76000,
+ .resis_low = 53000,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl),
+ .batres_tbl = temp_to_batres_tbl,
+ },
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LION,
+ .resis_high = 30000,
+ .resis_low = 10000,
+ .battery_resistance = 300,
+ .charge_full_design = 950,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl),
+ .batres_tbl = temp_to_batres_tbl,
+ },
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LION,
+ .resis_high = 95000,
+ .resis_low = 76001,
+ .battery_resistance = 300,
+ .charge_full_design = 950,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4130,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4100,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ .n_batres_tbl_elements = ARRAY_SIZE(temp_to_batres_tbl),
+ .batres_tbl = temp_to_batres_tbl,
+ },
+#endif
+};
+
+static char *ab8500_charger_supplied_to[] = {
+ "ab8500_chargalg",
+ "ab8500_fg",
+ "ab8500_btemp",
+};
+
+static char *ab8500_btemp_supplied_to[] = {
+ "ab8500_chargalg",
+ "ab8500_fg",
+};
+
+static char *ab8500_fg_supplied_to[] = {
+ "ab8500_chargalg",
+ "ab8500_usb",
+};
+
+static char *ab8500_chargalg_supplied_to[] = {
+ "ab8500_fg",
+};
+
+struct abx500_charger_platform_data ab8500_charger_plat_data = {
+ .supplied_to = ab8500_charger_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab8500_charger_supplied_to),
+ .autopower_cfg = false,
+};
+
+struct abx500_btemp_platform_data ab8500_btemp_plat_data = {
+ .supplied_to = ab8500_btemp_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab8500_btemp_supplied_to),
+};
+
+struct abx500_fg_platform_data ab8500_fg_plat_data = {
+ .supplied_to = ab8500_fg_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab8500_fg_supplied_to),
+};
+
+struct abx500_chargalg_platform_data ab8500_chargalg_plat_data = {
+ .supplied_to = ab8500_chargalg_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab8500_chargalg_supplied_to),
+};
+
+static struct ab8500_led_pwm leds_pwm_data[] = {
+ [0] = {
+ .pwm_id = 1,
+ .blink_en = 1,
+ },
+ [1] = {
+ .pwm_id = 2,
+ .blink_en = 0,
+ },
+ [2] = {
+ .pwm_id = 3,
+ .blink_en = 0,
+ },
+};
+
+struct ab8500_pwmled_platform_data ab8500_pwmled_plat_data = {
+ .num_pwm = 3,
+ .leds = leds_pwm_data,
+};
+
+static const struct abx500_bm_capacity_levels cap_levels = {
+ .critical = 2,
+ .low = 10,
+ .normal = 70,
+ .high = 95,
+ .full = 100,
+};
+
+static const struct abx500_fg_parameters fg = {
+ .recovery_sleep_timer = 10,
+ .recovery_total_time = 100,
+ .init_timer = 1,
+ .init_discard_time = 5,
+ .init_total_time = 40,
+ .high_curr_time = 60,
+ .accu_charging = 30,
+ .accu_high_curr = 30,
+ .high_curr_threshold = 50,
+ .lowbat_threshold = 3100,
+ .battok_falling_th_sel0 = 2860,
+ .battok_raising_th_sel1 = 2860,
+ .user_cap_limit = 15,
+ .maint_thres = 97,
+};
+
+static const struct abx500_maxim_parameters maxi_params = {
+ .ena_maxi = true,
+ .chg_curr = 910,
+ .wait_cycles = 10,
+ .charger_curr_step = 100,
+};
+
+static const struct abx500_bm_charger_parameters chg = {
+ .usb_volt_max = 5500,
+ .usb_curr_max = 1500,
+ .ac_volt_max = 7500,
+ .ac_curr_max = 1500,
+};
+
+struct abx500_bm_data ab8500_bm_data = {
+ .temp_under = 3,
+ .temp_low = 8,
+ .temp_high = 43,
+ .temp_over = 48,
+ .main_safety_tmr_h = 4,
+ .temp_interval_chg = 20,
+ .temp_interval_nochg = 120,
+ .usb_safety_tmr_h = 4,
+ .bkup_bat_v = BUP_VCH_SEL_2P6V,
+ .bkup_bat_i = BUP_ICH_SEL_150UA,
+#ifdef CONFIG_AB8500_9100_LI_ION_BATTERY
+ .no_maintenance = true,
+#else
+ .no_maintenance = false,
+#endif
+#ifdef CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL
+ .adc_therm = ABx500_ADC_THERM_BATCTRL,
+#else
+ .adc_therm = ABx500_ADC_THERM_BATTEMP,
+#endif
+#ifdef CONFIG_AB8500_9100_LI_ION_BATTERY
+ .chg_unknown_bat = true,
+#else
+ .chg_unknown_bat = false,
+#endif
+ .enable_overshoot = false,
+ .fg_res = 100,
+ .cap_levels = &cap_levels,
+ .bat_type = bat_type,
+ .n_btypes = ARRAY_SIZE(bat_type),
+ .batt_id = 0,
+ .interval_charging = 5,
+ .interval_not_charging = 120,
+ .temp_hysteresis = 3,
+ .gnd_lift_resistance = 34,
+ .maxi = &maxi_params,
+ .chg_params = &chg,
+ .fg_params = &fg,
+};
diff --git a/arch/arm/mach-ux500/board-mop500-bm.h b/arch/arm/mach-ux500/board-mop500-bm.h
new file mode 100644
index 00000000000..61d6bc71cc3
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-bm.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * U8500 board specific charger and battery initialization parameters.
+ *
+ * Author: Johan Palsson <johan.palsson@stericsson.com> for ST-Ericsson.
+ * Author: Johan Gardsmark <johan.gardsmark@stericsson.com> for ST-Ericsson.
+ *
+ */
+
+#ifndef __BOARD_MOP500_BM_H
+#define __BOARD_MOP500_BM_H
+
+#include <linux/mfd/abx500/ab8500-bm.h>
+
+extern struct abx500_charger_platform_data ab8500_charger_plat_data;
+extern struct abx500_btemp_platform_data ab8500_btemp_plat_data;
+extern struct abx500_fg_platform_data ab8500_fg_plat_data;
+extern struct abx500_chargalg_platform_data ab8500_chargalg_plat_data;
+extern struct abx500_bm_data ab8500_bm_data;
+extern struct ab8500_pwmled_platform_data ab8500_pwmled_plat_data;
+
+#endif
diff --git a/arch/arm/mach-ux500/board-mop500-cyttsp.c b/arch/arm/mach-ux500/board-mop500-cyttsp.c
new file mode 100644
index 00000000000..1551355f50d
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-cyttsp.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Author: Avinash A <avinash.a@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/input/cyttsp.h>
+#include <linux/delay.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/i2c.h>
+#ifdef CONFIG_U8500_FLASH
+#include <../drivers/staging/camera_flash/adp1653_plat.h>
+#endif
+#include <linux/input/matrix_keypad.h>
+#include <linux/mfd/tc3589x.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/amba/pl022.h>
+#include <plat/pincfg.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/irqs-db8500.h>
+#include <asm/mach-types.h>
+#include "pins-db8500.h"
+#include "board-mop500.h"
+#include "devices-db8500.h"
+
+#define NUM_SSP_CLIENTS 10
+
+#ifdef CONFIG_U8500_FLASH
+static struct adp1653_platform_data __initdata adp1653_pdata_u8500_uib = {
+ .irq_no = CAMERA_FLASH_INT_PIN
+};
+#endif
+
+static struct i2c_board_info __initdata mop500_i2c2_devices_u8500_r3[] = {
+#ifdef CONFIG_U8500_FLASH
+ {
+ I2C_BOARD_INFO("adp1653", 0x30),
+ .platform_data = &adp1653_pdata_u8500_uib
+ }
+#endif
+};
+
+/* cyttsp_gpio_board_init : configures the touch panel. */
+static int cyttsp_plat_init(void)
+{
+ int ret;
+
+ ret = gpio_direction_output(CYPRESS_SLAVE_SELECT_GPIO, 1);
+ if (ret < 0) {
+ pr_err("slave select gpio direction failed\n");
+ gpio_free(CYPRESS_SLAVE_SELECT_GPIO);
+ return ret;
+ }
+ return 0;
+}
+
+/* cyttsp_gpio_board_exit : deconfigures the touch panel. */
+static void cyttsp_plat_exit(void)
+{
+ gpio_direction_output(CYPRESS_SLAVE_SELECT_GPIO, 0);
+}
+
+static struct pl022_ssp_controller mop500_spi2_data = {
+ .bus_id = SPI023_2_CONTROLLER,
+ .num_chipselect = NUM_SSP_CLIENTS,
+};
+
+static int cyttsp_wakeup(void)
+{
+ int ret;
+
+ ret = gpio_request(CYPRESS_TOUCH_INT_PIN, "Wakeup_pin");
+ if (ret < 0) {
+ pr_err("touch gpio failed\n");
+ return ret;
+ }
+ ret = gpio_direction_output(CYPRESS_TOUCH_INT_PIN, 1);
+ if (ret < 0) {
+ pr_err("touch gpio direction failed\n");
+ goto out;
+ }
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 0);
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 1);
+ /*
+ * To wake up the controller from sleep
+ * state the interrupt pin needs to be
+ * pulsed twice with a delay greater
+ * than 2 micro seconds.
+ */
+ udelay(3);
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 0);
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 1);
+ ret = gpio_direction_input(CYPRESS_TOUCH_INT_PIN);
+ if (ret < 0) {
+ pr_err("touch gpio direction IN config failed\n");
+ goto out;
+ }
+out:
+ gpio_free(CYPRESS_TOUCH_INT_PIN);
+ return 0;
+}
+struct cyttsp_platform_data cyttsp_platdata = {
+ .maxx = 480,
+ .maxy = 854,
+ .use_hndshk = 0,
+ /* set active distance */
+ .act_dist = CY_ACT_DIST_DFLT,
+ .act_intrvl = CY_ACT_INTRVL_DFLT, /* Active refresh interval; ms */
+ .tch_tmout = CY_TCH_TMOUT_DFLT, /* Active touch timeout; ms */
+ .lp_intrvl = CY_LP_INTRVL_DFLT, /* Low power refresh interval; ms */
+ .init = cyttsp_plat_init,
+ .exit = cyttsp_plat_exit,
+ .name = CY_SPI_NAME,
+ .irq_gpio = CYPRESS_TOUCH_INT_PIN,
+/* .rst_gpio = CYPRESS_TOUCH_RST_GPIO, Notavailable in mainline */
+};
+
+static void cyttsp_spi_cs_control(u32 command)
+{
+ if (command == SSP_CHIP_SELECT)
+ gpio_set_value(CYPRESS_SLAVE_SELECT_GPIO, 0);
+ else if (command == SSP_CHIP_DESELECT)
+ gpio_set_value(CYPRESS_SLAVE_SELECT_GPIO, 1);
+}
+
+static struct pl022_config_chip cyttsp_ssp_config_chip = {
+ .com_mode = INTERRUPT_TRANSFER,
+ .iface = SSP_INTERFACE_MOTOROLA_SPI,
+ /* we can act as master only */
+ .hierarchy = SSP_MASTER,
+ .slave_tx_disable = 0,
+ .rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
+ .tx_lev_trig = SSP_TX_16_OR_MORE_EMPTY_LOC,
+ .ctrl_len = SSP_BITS_16,
+ .wait_state = SSP_MWIRE_WAIT_ZERO,
+ .duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
+ .cs_control = cyttsp_spi_cs_control,
+};
+
+static struct spi_board_info cypress_spi_devices[] = {
+ {
+ .modalias = CY_SPI_NAME,
+ .controller_data = &cyttsp_ssp_config_chip,
+ .platform_data = &cyttsp_platdata,
+ .max_speed_hz = 1000000,
+ .bus_num = SPI023_2_CONTROLLER,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ }
+};
+
+/*
+ * TC35893
+ */
+static const unsigned int sony_keymap[] = {
+ KEY(3, 1, KEY_END),
+ KEY(4, 1, KEY_HOME),
+ KEY(6, 4, KEY_VOLUMEDOWN),
+ KEY(4, 2, KEY_EMAIL),
+ KEY(3, 3, KEY_RIGHT),
+ KEY(2, 5, KEY_BACKSPACE),
+
+ KEY(6, 7, KEY_MENU),
+ KEY(5, 0, KEY_ENTER),
+ KEY(4, 3, KEY_0),
+ KEY(3, 4, KEY_DOT),
+ KEY(5, 2, KEY_UP),
+ KEY(3, 5, KEY_DOWN),
+
+ KEY(4, 5, KEY_SEND),
+ KEY(0, 5, KEY_BACK),
+ KEY(6, 2, KEY_VOLUMEUP),
+ KEY(1, 3, KEY_SPACE),
+ KEY(7, 6, KEY_LEFT),
+ KEY(5, 5, KEY_SEARCH),
+};
+
+static struct matrix_keymap_data sony_keymap_data = {
+ .keymap = sony_keymap,
+ .keymap_size = ARRAY_SIZE(sony_keymap),
+};
+
+static struct tc3589x_keypad_platform_data tc35893_data = {
+ .krow = TC_KPD_ROWS,
+ .kcol = TC_KPD_COLUMNS,
+ .debounce_period = TC_KPD_DEBOUNCE_PERIOD,
+ .settle_time = TC_KPD_SETTLE_TIME,
+ .irqtype = IRQF_TRIGGER_FALLING,
+ .enable_wakeup = true,
+ .keymap_data = &sony_keymap_data,
+ .no_autorepeat = true,
+};
+
+static struct tc3589x_platform_data tc3589x_keypad_data = {
+ .block = TC3589x_BLOCK_KEYPAD,
+ .keypad = &tc35893_data,
+ .irq_base = MOP500_EGPIO_IRQ_BASE,
+};
+
+static struct i2c_board_info __initdata mop500_i2c0_devices_u8500[] = {
+ {
+ I2C_BOARD_INFO("tc3589x", 0x44),
+ .platform_data = &tc3589x_keypad_data,
+ .irq = NOMADIK_GPIO_TO_IRQ(64),
+ .flags = I2C_CLIENT_WAKE,
+ },
+};
+
+static void mop500_cyttsp_init(void)
+{
+ int ret = 0;
+
+ /*
+ * Enable the alternative C function
+ * in the PRCMU register
+ */
+ prcmu_enable_spi2();
+ ret = gpio_request(CYPRESS_SLAVE_SELECT_GPIO, "slave_select_gpio");
+ if (ret < 0)
+ pr_err("slave select gpio failed\n");
+ spi_register_board_info(cypress_spi_devices,
+ ARRAY_SIZE(cypress_spi_devices));
+}
+
+void __init mop500_u8500uib_r3_init()
+{
+ mop500_cyttsp_init();
+ db8500_add_spi2(NULL, &mop500_spi2_data);
+ nmk_config_pin((GPIO64_GPIO | PIN_INPUT_PULLUP), false);
+
+#ifdef CONFIG_U8500_FLASH
+ if (machine_is_hrefv60() || machine_is_u8520()) {
+ adp1653_pdata_u8500_uib.enable_gpio =
+ HREFV60_CAMERA_FLASH_ENABLE;
+ } else {
+ adp1653_pdata_u8500_uib.enable_gpio =
+ GPIO_CAMERA_FLASH_ENABLE;
+ }
+#endif
+ mop500_uib_i2c_add(0, mop500_i2c0_devices_u8500,
+ ARRAY_SIZE(mop500_i2c0_devices_u8500));
+ mop500_uib_i2c_add(0, mop500_i2c0_devices_u8500,
+ ARRAY_SIZE(mop500_i2c0_devices_u8500));
+ mop500_uib_i2c_add(2, mop500_i2c2_devices_u8500_r3,
+ ARRAY_SIZE(mop500_i2c2_devices_u8500_r3));
+}
diff --git a/arch/arm/mach-ux500/board-mop500-pins.c b/arch/arm/mach-ux500/board-mop500-pins.c
index f5413dca532..342954fdecf 100644
--- a/arch/arm/mach-ux500/board-mop500-pins.c
+++ b/arch/arm/mach-ux500/board-mop500-pins.c
@@ -7,109 +7,50 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/bug.h>
+#include <linux/string.h>
#include <asm/mach-types.h>
#include <plat/pincfg.h>
-#include <plat/gpio-nomadik.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/mfd/abx500/ab8500-gpio.h>
+
#include <mach/hardware.h>
+#include <mach/suspend.h>
#include "pins-db8500.h"
+#include "pins.h"
+#include "board-mop500.h"
+#include "board-pins-sleep-force.h"
+
+enum custom_pin_cfg_t {
+ PINS_FOR_DEFAULT,
+ PINS_FOR_U9500,
+};
+
+static enum custom_pin_cfg_t pinsfor;
static pin_cfg_t mop500_pins_common[] = {
- /* I2C */
- GPIO147_I2C0_SCL,
- GPIO148_I2C0_SDA,
- GPIO16_I2C1_SCL,
- GPIO17_I2C1_SDA,
- GPIO10_I2C2_SDA,
- GPIO11_I2C2_SCL,
- GPIO229_I2C3_SDA,
- GPIO230_I2C3_SCL,
-
- /* MSP0 */
+ /* uMSP0 */
GPIO12_MSP0_TXD,
GPIO13_MSP0_TFS,
GPIO14_MSP0_TCK,
GPIO15_MSP0_RXD,
/* MSP2: HDMI */
- GPIO193_MSP2_TXD,
- GPIO194_MSP2_TCK,
- GPIO195_MSP2_TFS,
+ GPIO193_MSP2_TXD | PIN_INPUT_PULLDOWN,
+ GPIO194_MSP2_TCK | PIN_INPUT_PULLDOWN,
+ GPIO195_MSP2_TFS | PIN_INPUT_PULLDOWN,
GPIO196_MSP2_RXD | PIN_OUTPUT_LOW,
+ /* LCD TE0 */
+ GPIO68_LCD_VSI0 | PIN_INPUT_PULLUP,
+
/* Touch screen INTERFACE */
GPIO84_GPIO | PIN_INPUT_PULLUP, /* TOUCH_INT1 */
/* STMPE1601/tc35893 keypad IRQ */
GPIO218_GPIO | PIN_INPUT_PULLUP,
- /* MMC0 (MicroSD card) */
- GPIO18_MC0_CMDDIR | PIN_OUTPUT_HIGH,
- GPIO19_MC0_DAT0DIR | PIN_OUTPUT_HIGH,
- GPIO20_MC0_DAT2DIR | PIN_OUTPUT_HIGH,
-
- GPIO22_MC0_FBCLK | PIN_INPUT_NOPULL,
- GPIO23_MC0_CLK | PIN_OUTPUT_LOW,
- GPIO24_MC0_CMD | PIN_INPUT_PULLUP,
- GPIO25_MC0_DAT0 | PIN_INPUT_PULLUP,
- GPIO26_MC0_DAT1 | PIN_INPUT_PULLUP,
- GPIO27_MC0_DAT2 | PIN_INPUT_PULLUP,
- GPIO28_MC0_DAT3 | PIN_INPUT_PULLUP,
-
- /* SDI1 (SDIO) */
- GPIO208_MC1_CLK | PIN_OUTPUT_LOW,
- GPIO209_MC1_FBCLK | PIN_INPUT_NOPULL,
- GPIO210_MC1_CMD | PIN_INPUT_PULLUP,
- GPIO211_MC1_DAT0 | PIN_INPUT_PULLUP,
- GPIO212_MC1_DAT1 | PIN_INPUT_PULLUP,
- GPIO213_MC1_DAT2 | PIN_INPUT_PULLUP,
- GPIO214_MC1_DAT3 | PIN_INPUT_PULLUP,
-
- /* MMC2 (On-board DATA INTERFACE eMMC) */
- GPIO128_MC2_CLK | PIN_OUTPUT_LOW,
- GPIO129_MC2_CMD | PIN_INPUT_PULLUP,
- GPIO130_MC2_FBCLK | PIN_INPUT_NOPULL,
- GPIO131_MC2_DAT0 | PIN_INPUT_PULLUP,
- GPIO132_MC2_DAT1 | PIN_INPUT_PULLUP,
- GPIO133_MC2_DAT2 | PIN_INPUT_PULLUP,
- GPIO134_MC2_DAT3 | PIN_INPUT_PULLUP,
- GPIO135_MC2_DAT4 | PIN_INPUT_PULLUP,
- GPIO136_MC2_DAT5 | PIN_INPUT_PULLUP,
- GPIO137_MC2_DAT6 | PIN_INPUT_PULLUP,
- GPIO138_MC2_DAT7 | PIN_INPUT_PULLUP,
-
- /* MMC4 (On-board STORAGE INTERFACE eMMC) */
- GPIO197_MC4_DAT3 | PIN_INPUT_PULLUP,
- GPIO198_MC4_DAT2 | PIN_INPUT_PULLUP,
- GPIO199_MC4_DAT1 | PIN_INPUT_PULLUP,
- GPIO200_MC4_DAT0 | PIN_INPUT_PULLUP,
- GPIO201_MC4_CMD | PIN_INPUT_PULLUP,
- GPIO202_MC4_FBCLK | PIN_INPUT_NOPULL,
- GPIO203_MC4_CLK | PIN_OUTPUT_LOW,
- GPIO204_MC4_DAT7 | PIN_INPUT_PULLUP,
- GPIO205_MC4_DAT6 | PIN_INPUT_PULLUP,
- GPIO206_MC4_DAT5 | PIN_INPUT_PULLUP,
- GPIO207_MC4_DAT4 | PIN_INPUT_PULLUP,
-
- /* SKE keypad */
- GPIO153_KP_I7,
- GPIO154_KP_I6,
- GPIO155_KP_I5,
- GPIO156_KP_I4,
- GPIO157_KP_O7,
- GPIO158_KP_O6,
- GPIO159_KP_O5,
- GPIO160_KP_O4,
- GPIO161_KP_I3,
- GPIO162_KP_I2,
- GPIO163_KP_I1,
- GPIO164_KP_I0,
- GPIO165_KP_O3,
- GPIO166_KP_O2,
- GPIO167_KP_O1,
- GPIO168_KP_O0,
-
/* UART */
/* uart-0 pins gpio configuration should be
* kept intact to prevent glitch in tx line
@@ -128,10 +69,6 @@ static pin_cfg_t mop500_pins_common[] = {
GPIO30_U2_TXD | PIN_OUTPUT_HIGH,
GPIO31_U2_CTSn | PIN_INPUT_PULLUP,
GPIO32_U2_RTSn | PIN_OUTPUT_HIGH,
-
- /* Display & HDMI HW sync */
- GPIO68_LCD_VSI0 | PIN_INPUT_PULLUP,
- GPIO69_LCD_VSI1 | PIN_INPUT_PULLUP,
};
static pin_cfg_t mop500_pins_default[] = {
@@ -141,10 +78,13 @@ static pin_cfg_t mop500_pins_default[] = {
GPIO145_SSP0_RXD | PIN_PULL_DOWN,
GPIO146_SSP0_TXD,
+ /* XENON Flashgun INTERFACE */
+ GPIO6_IP_GPIO0 | PIN_INPUT_PULLUP,/* XENON_FLASH_ID */
+ GPIO7_IP_GPIO1 | PIN_INPUT_PULLUP,/* XENON_READY */
GPIO217_GPIO | PIN_INPUT_PULLUP, /* TC35892 IRQ */
- /* SDI0 (MicroSD card) */
+ /* sdi0 (removable MMC/SD/SDIO cards) not handled by pm_runtime */
GPIO21_MC0_DAT31DIR | PIN_OUTPUT_HIGH,
/* UART */
@@ -156,13 +96,11 @@ static pin_cfg_t mop500_pins_default[] = {
static pin_cfg_t hrefv60_pins[] = {
/* WLAN */
- GPIO4_GPIO | PIN_INPUT_PULLUP,/* WLAN_IRQ */
GPIO85_GPIO | PIN_OUTPUT_LOW,/* WLAN_ENA */
/* XENON Flashgun INTERFACE */
GPIO6_IP_GPIO0 | PIN_INPUT_PULLUP,/* XENON_FLASH_ID */
GPIO7_IP_GPIO1 | PIN_INPUT_PULLUP,/* XENON_READY */
- GPIO170_GPIO | PIN_OUTPUT_LOW, /* XENON_CHARGE */
/* Assistant LED INTERFACE */
GPIO21_GPIO | PIN_OUTPUT_LOW, /* XENON_EN1 */
@@ -173,7 +111,7 @@ static pin_cfg_t hrefv60_pins[] = {
GPIO32_GPIO | PIN_INPUT_PULLDOWN, /* Magnetometer DRDY */
/* Display Interface */
- GPIO65_GPIO | PIN_OUTPUT_LOW, /* DISP1 RST */
+ GPIO65_GPIO | PIN_OUTPUT_HIGH, /* DISP1 NO RST */
GPIO66_GPIO | PIN_OUTPUT_LOW, /* DISP2 RST */
/* Touch screen INTERFACE */
@@ -215,11 +153,8 @@ static pin_cfg_t hrefv60_pins[] = {
/* DiPro Sensor Interface */
GPIO139_GPIO | PIN_INPUT_PULLUP, /* DIPRO_INT */
- /* HAL SWITCH INTERFACE */
- GPIO145_GPIO | PIN_INPUT_PULLDOWN,/* HAL_SW */
-
/* Audio Amplifier Interface */
- GPIO149_GPIO | PIN_OUTPUT_LOW, /* VAUDIO_HF_EN */
+ GPIO149_GPIO | PIN_OUTPUT_HIGH, /* VAUDIO_HF_EN, enable MAX8968 */
/* GBF INTERFACE */
GPIO171_GPIO | PIN_OUTPUT_LOW, /* GBF_ENA_RESET */
@@ -231,10 +166,29 @@ static pin_cfg_t hrefv60_pins[] = {
GPIO82_GPIO | PIN_INPUT_PULLUP, /* ACC_INT1 */
GPIO83_GPIO | PIN_INPUT_PULLUP, /* ACC_INT2 */
- /* Proximity Sensor */
- GPIO217_GPIO | PIN_INPUT_PULLUP,
+ /* SD card detect */
+ GPIO95_GPIO | PIN_INPUT_PULLUP,
+};
+static pin_cfg_t u9500_pins[] = {
+ GPIO4_U1_RXD | PIN_INPUT_PULLUP,
+ GPIO5_U1_TXD | PIN_OUTPUT_HIGH,
+ GPIO144_GPIO | PIN_INPUT_PULLUP,/* WLAN_IRQ */
+ /* HSI */
+ GPIO219_HSIR_FLA0 | PIN_INPUT_PULLDOWN,
+ GPIO220_HSIR_DAT0 | PIN_INPUT_PULLDOWN,
+ GPIO221_HSIR_RDY0 | PIN_OUTPUT_LOW,
+ GPIO222_HSIT_FLA0 | PIN_OUTPUT_LOW,
+ GPIO223_HSIT_DAT0 | PIN_OUTPUT_LOW,
+ GPIO224_HSIT_RDY0 | PIN_INPUT_PULLDOWN,
+ GPIO225_HSIT_CAWAKE0 | PIN_INPUT_PULLDOWN, /* CA_WAKE0 */
+ GPIO226_GPIO | PIN_OUTPUT_HIGH, /* AC_WAKE0 */
+};
+
+static pin_cfg_t u8500_pins[] = {
+ GPIO226_GPIO | PIN_OUTPUT_LOW, /* WLAN_PMU_EN */
+ GPIO4_GPIO | PIN_INPUT_PULLUP,/* WLAN_IRQ */
};
static pin_cfg_t snowball_pins[] = {
@@ -247,6 +201,9 @@ static pin_cfg_t snowball_pins[] = {
/* MMC0: MicroSD card */
GPIO21_MC0_DAT31DIR | PIN_OUTPUT_HIGH,
+ /* USER PB pin */
+ GPIO32_GPIO | PIN_INPUT_PULLUP,
+
/* MMC2: LAN */
GPIO86_SM_ADQ0,
GPIO87_SM_ADQ1,
@@ -275,15 +232,929 @@ static pin_cfg_t snowball_pins[] = {
/* RSTn_LAN */
GPIO141_GPIO | PIN_OUTPUT_HIGH,
+
+ /* Accelerometer/Magnetometer */
+ GPIO163_GPIO | PIN_INPUT_PULLUP, /* ACCEL_IRQ1 */
+ GPIO164_GPIO | PIN_INPUT_PULLUP, /* ACCEL_IRQ2 */
+ GPIO165_GPIO | PIN_INPUT_PULLUP, /* MAG_DRDY */
+
+ /* WLAN/GBF */
+ GPIO161_GPIO | PIN_OUTPUT_LOW, /* WLAN_PMU_EN */
+ GPIO171_GPIO | PIN_OUTPUT_HIGH,/* GBF_ENA */
+ GPIO215_GPIO | PIN_OUTPUT_LOW,/* WLAN_ENA */
+ GPIO216_GPIO | PIN_INPUT_PULLUP,/* WLAN_IRQ */
+};
+
+/*
+ * I2C
+ */
+
+static UX500_PINS(mop500_pins_i2c0,
+ GPIO147_I2C0_SCL |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+ GPIO148_I2C0_SDA |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+);
+
+static UX500_PINS(mop500_pins_i2c1,
+ GPIO16_I2C1_SCL |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+ GPIO17_I2C1_SDA |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+);
+
+static UX500_PINS(mop500_pins_i2c2,
+ GPIO10_I2C2_SDA |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+ GPIO11_I2C2_SCL |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+);
+
+static UX500_PINS(mop500_pins_i2c3,
+ GPIO229_I2C3_SDA |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+ GPIO230_I2C3_SCL |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+);
+
+static UX500_PINS(mop500_pins_mcde_tvout,
+ GPIO78_LCD_D8,
+ GPIO79_LCD_D9,
+ GPIO80_LCD_D10,
+ GPIO81_LCD_D11,
+ GPIO150_LCDA_CLK,
+);
+
+static UX500_PINS(mop500_pins_mcde_hdmi,
+ GPIO69_LCD_VSI1 | PIN_INPUT_PULLUP,
+);
+
+static UX500_PINS(mop500_pins_ske,
+ GPIO153_KP_I7 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO154_KP_I6 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO155_KP_I5 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO156_KP_I4 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO161_KP_I3 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO162_KP_I2 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO163_KP_I1 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO164_KP_I0 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO157_KP_O7 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO158_KP_O6 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO159_KP_O5 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO160_KP_O4 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO165_KP_O3 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO166_KP_O2 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO167_KP_O1 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO168_KP_O0 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+);
+
+/* sdi0 (removable MMC/SD/SDIO cards) */
+static UX500_PINS(mop500_pins_sdi0,
+ GPIO18_MC0_CMDDIR | PIN_OUTPUT_HIGH,
+ GPIO19_MC0_DAT0DIR | PIN_OUTPUT_HIGH,
+ GPIO20_MC0_DAT2DIR | PIN_OUTPUT_HIGH,
+
+ GPIO22_MC0_FBCLK | PIN_INPUT_NOPULL,
+ GPIO23_MC0_CLK | PIN_OUTPUT_LOW,
+ GPIO24_MC0_CMD | PIN_INPUT_PULLUP,
+ GPIO25_MC0_DAT0 | PIN_INPUT_PULLUP,
+ GPIO26_MC0_DAT1 | PIN_INPUT_PULLUP,
+ GPIO27_MC0_DAT2 | PIN_INPUT_PULLUP,
+ GPIO28_MC0_DAT3 | PIN_INPUT_PULLUP,
+);
+
+/* sdi1 (WLAN CW1200) */
+static UX500_PINS(mop500_pins_sdi1,
+ GPIO208_MC1_CLK | PIN_OUTPUT_LOW,
+ GPIO209_MC1_FBCLK | PIN_INPUT_NOPULL,
+ GPIO210_MC1_CMD | PIN_INPUT_PULLUP,
+ GPIO211_MC1_DAT0 | PIN_INPUT_PULLUP,
+ GPIO212_MC1_DAT1 | PIN_INPUT_PULLUP,
+ GPIO213_MC1_DAT2 | PIN_INPUT_PULLUP,
+ GPIO214_MC1_DAT3 | PIN_INPUT_PULLUP,
+);
+
+/* sdi2 (POP eMMC) */
+static UX500_PINS(mop500_pins_sdi2,
+ GPIO128_MC2_CLK | PIN_OUTPUT_LOW,
+ GPIO129_MC2_CMD | PIN_INPUT_PULLUP,
+ GPIO130_MC2_FBCLK | PIN_INPUT_NOPULL,
+ GPIO131_MC2_DAT0 | PIN_INPUT_PULLUP,
+ GPIO132_MC2_DAT1 | PIN_INPUT_PULLUP,
+ GPIO133_MC2_DAT2 | PIN_INPUT_PULLUP,
+ GPIO134_MC2_DAT3 | PIN_INPUT_PULLUP,
+ GPIO135_MC2_DAT4 | PIN_INPUT_PULLUP,
+ GPIO136_MC2_DAT5 | PIN_INPUT_PULLUP,
+ GPIO137_MC2_DAT6 | PIN_INPUT_PULLUP,
+ GPIO138_MC2_DAT7 | PIN_INPUT_PULLUP,
+);
+
+/* sdi4 (PCB eMMC) */
+static UX500_PINS(mop500_pins_sdi4,
+ GPIO197_MC4_DAT3 | PIN_INPUT_PULLUP,
+ GPIO198_MC4_DAT2 | PIN_INPUT_PULLUP,
+ GPIO199_MC4_DAT1 | PIN_INPUT_PULLUP,
+ GPIO200_MC4_DAT0 | PIN_INPUT_PULLUP,
+ GPIO201_MC4_CMD | PIN_INPUT_PULLUP,
+ GPIO202_MC4_FBCLK | PIN_INPUT_NOPULL,
+ GPIO203_MC4_CLK | PIN_OUTPUT_LOW,
+ GPIO204_MC4_DAT7 | PIN_INPUT_PULLUP,
+ GPIO205_MC4_DAT6 | PIN_INPUT_PULLUP,
+ GPIO206_MC4_DAT5 | PIN_INPUT_PULLUP,
+ GPIO207_MC4_DAT4 | PIN_INPUT_PULLUP,
+);
+
+/* USB */
+static UX500_PINS(mop500_pins_usb,
+ GPIO256_USB_NXT,
+ GPIO257_USB_STP | PIN_OUTPUT_HIGH,
+ GPIO258_USB_XCLK,
+ GPIO259_USB_DIR,
+ GPIO260_USB_DAT7,
+ GPIO261_USB_DAT6,
+ GPIO262_USB_DAT5,
+ GPIO263_USB_DAT4,
+ GPIO264_USB_DAT3,
+ GPIO265_USB_DAT2,
+ GPIO266_USB_DAT1,
+ GPIO267_USB_DAT0,
+);
+
+/* SPI2 */
+static UX500_PINS(mop500_pins_spi2,
+ GPIO216_GPIO | PIN_OUTPUT_HIGH,
+ GPIO218_SPI2_RXD | PIN_INPUT_PULLDOWN,
+ GPIO215_SPI2_TXD | PIN_OUTPUT_LOW,
+ GPIO217_SPI2_CLK | PIN_OUTPUT_LOW,
+);
+
+static UX500_PINS(mop500_pins_sensors1p_v60,
+ GPIO217_GPIO| PIN_INPUT_PULLUP |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+ GPIO145_GPIO | PIN_INPUT_PULLDOWN |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+ GPIO139_GPIO | PIN_INPUT_PULLUP |
+ PIN_SLPM_GPIO | PIN_SLPM_INPUT_NOPULL,
+);
+
+static UX500_PINS(mop500_pins_sensors1p,
+ PIN_CFG_INPUT(GPIO_PROX_SENSOR, GPIO, NOPULL),
+ PIN_CFG_INPUT(GPIO_HAL_SENSOR, GPIO, NOPULL),
+);
+
+static struct ux500_pin_lookup mop500_runtime_pins[] = {
+ PIN_LOOKUP("mcde-tvout", &mop500_pins_mcde_tvout),
+ PIN_LOOKUP("av8100-hdmi", &mop500_pins_mcde_hdmi),
+ PIN_LOOKUP("nmk-i2c.0", &mop500_pins_i2c0),
+ PIN_LOOKUP("nmk-i2c.1", &mop500_pins_i2c1),
+ PIN_LOOKUP("nmk-i2c.2", &mop500_pins_i2c2),
+ PIN_LOOKUP("nmk-i2c.3", &mop500_pins_i2c3),
+ PIN_LOOKUP("sdi0", &mop500_pins_sdi0),
+ PIN_LOOKUP("sdi1", &mop500_pins_sdi1),
+ PIN_LOOKUP("sdi2", &mop500_pins_sdi2),
+ PIN_LOOKUP("sdi4", &mop500_pins_sdi4),
+ PIN_LOOKUP("musb-ux500.0", &mop500_pins_usb),
+ PIN_LOOKUP("spi2", &mop500_pins_spi2),
+};
+
+static struct ux500_pin_lookup mop500_runtime_pins_v60[] = {
+ PIN_LOOKUP("ske", &mop500_pins_ske),
+ PIN_LOOKUP("gpio-keys.0", &mop500_pins_sensors1p_v60),
+};
+
+static struct ux500_pin_lookup mop500_runtime_pins_pre_v60[] = {
+ PIN_LOOKUP("ske", &mop500_pins_ske),
+ PIN_LOOKUP("gpio-keys.0", &mop500_pins_sensors1p),
+};
+
+/*
+ * Sleep pin configuration for u8500 platform.
+ * If another HW is used the GPIO's must be configured
+ * correctly when entering sleep for optimal power
+ * consumption.
+ */
+static pin_cfg_t mop500_pins_common_power_save_bank0[] = {
+ GPIO0_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO1_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO2_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO3_GPIO | PIN_SLPM_DIR_OUTPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO4_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO5_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO6_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO7_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO8_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO9_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 10-11 - I2C2 */
+ GPIO10_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO11_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO12_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO13_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO14_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO15_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ /* 16-17 - I2C1 */
+ GPIO16_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO17_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO18_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO19_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO20_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO21_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO22_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO23_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO24_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO25_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO26_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO27_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO28_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO29_U2_RXD | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO30_U2_TXD | PIN_SLPM_DIR_OUTPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO31_U2_CTSn | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank0_href60[] = {
+ GPIO0_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO1_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO2_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO3_GPIO | PIN_SLPM_DIR_OUTPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO4_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO5_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO6_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO7_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO8_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO9_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 10-11 - I2C2 */
+ GPIO10_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO11_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO12_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO13_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO14_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO15_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ /* 16-17 - I2C1 */
+ GPIO16_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO17_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO18_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO19_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO20_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO21_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO22_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO23_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO24_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO25_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO26_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO27_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO28_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO29_U2_RXD | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO30_U2_TXD | PIN_SLPM_DIR_OUTPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO31_U2_CTSn | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank1[] = {
+ GPIO32_U2_RTSn | PIN_SLPM_DIR_OUTPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO33_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO34_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO35_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO36_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank1_snowball[] = {
+ /*GPIO32 is used as USER_PB input in snowball*/
+ GPIO32_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_PDIS_ENABLED,
+ GPIO33_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO34_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO35_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO36_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
};
+static pin_cfg_t mop500_pins_common_power_save_bank2[] = {
+ GPIO64_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO65_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO66_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO67_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO68_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO69_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO70_STMAPE_CLK | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO71_STMAPE_DAT3 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO72_STMAPE_DAT2 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO73_STMAPE_DAT1 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO74_STMAPE_DAT0 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO75_U2_RXD | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO76_U2_TXD | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO77_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO78_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO79_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO80_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO81_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO82_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO83_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO84_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO85_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO86_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO87_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO88_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO89_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO90_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO91_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO92_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO93_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO94_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO95_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank2_href60[] = {
+ GPIO64_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO65_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO66_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO67_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO68_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO69_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO70_STMAPE_CLK | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO71_STMAPE_DAT3 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO72_STMAPE_DAT2 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO73_STMAPE_DAT1 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO74_STMAPE_DAT0 | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_DISABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO75_U2_RXD | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO76_U2_TXD | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO77_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO78_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO79_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO80_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO81_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO82_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO83_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO84_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO85_GPIO,
+ GPIO86_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO87_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO88_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO89_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO90_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO91_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO92_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO93_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO94_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO95_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank3[] = {
+ GPIO96_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO97_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank4[] = {
+ GPIO128_MC2_CLK | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO129_MC2_CMD | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO130_MC2_FBCLK | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO131_MC2_DAT0 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO132_MC2_DAT1 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO133_MC2_DAT2 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO134_MC2_DAT3 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO135_MC2_DAT4 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO136_MC2_DAT5 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO137_MC2_DAT6 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO138_MC2_DAT7 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO139_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO140_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO141_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO142_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO143_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO144_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 145 - HAL sensor (on v60 and later) */
+ GPIO145_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO146_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ /* 147-148 - I2C0 */
+ GPIO147_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO148_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO149_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO150_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO151_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO152_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO153_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO154_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO155_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO156_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO157_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO158_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO159_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank4_snowball[] = {
+ GPIO128_MC2_CLK | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO129_MC2_CMD | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO130_MC2_FBCLK | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO131_MC2_DAT0 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO132_MC2_DAT1 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO133_MC2_DAT2 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO134_MC2_DAT3 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO135_MC2_DAT4 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO136_MC2_DAT5 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO137_MC2_DAT6 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO138_MC2_DAT7 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO139_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO140_GPIO | PIN_SLPM_DIR_INPUT,
+ /* 141 - (RSTn_LAN) Keep this high to avoid smsc reset at suspend */
+ GPIO141_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO142_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO143_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO144_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 145 - HAL sensor (on v60 and later) */
+ GPIO145_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO146_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ /* 147-148 - I2C0 */
+ GPIO147_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO148_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO149_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO150_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO151_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO152_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO153_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO154_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO155_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO156_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO157_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO158_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO159_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank4_u9500_uibr3[] = {
+ GPIO128_MC2_CLK | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO129_MC2_CMD | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO130_MC2_FBCLK | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO131_MC2_DAT0 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO132_MC2_DAT1 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO133_MC2_DAT2 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO134_MC2_DAT3 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO135_MC2_DAT4 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO136_MC2_DAT5 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO137_MC2_DAT6 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO138_MC2_DAT7 | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO139_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO140_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO141_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO142_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO143_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO144_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 145 - HAL sensor (on v60 and later) */
+ GPIO145_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ /* 147-148 - I2C0 */
+ GPIO147_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO148_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO149_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO150_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO151_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO152_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO153_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO154_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO155_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO156_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO157_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO158_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO159_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank5[] = {
+ GPIO160_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO161_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO162_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO163_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO164_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO165_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO166_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO167_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO168_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO169_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO170_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO171_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank5_href60[] = {
+ GPIO160_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO161_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO162_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO163_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO164_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO165_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO166_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO167_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO168_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO169_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO170_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO171_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank6[] = {
+ GPIO192_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO193_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO194_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO195_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO196_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO197_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO198_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO199_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO200_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO201_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO202_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO203_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO204_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO205_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO206_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO207_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO208_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO209_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO210_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO211_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO212_GPIO,
+ GPIO213_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO214_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO215_GPIO,
+
+ GPIO216_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO217_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO218_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO219_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO220_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO221_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO222_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO223_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank6_href60[] = {
+ GPIO192_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO193_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO194_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO195_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO196_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO197_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO198_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO199_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO200_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO201_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO202_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO203_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO204_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO205_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO206_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO207_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO208_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO209_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO210_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO211_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO212_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO213_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO214_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO215_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO216_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 217 - Proximity */
+ GPIO217_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO218_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO219_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO220_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO221_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO222_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO223_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank6_u9500[] = {
+ GPIO192_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO193_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO194_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO195_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO196_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO197_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO198_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO199_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO200_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO201_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO202_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO203_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO204_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO205_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO206_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO207_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO208_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO209_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO210_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO211_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO212_GPIO,
+ GPIO213_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO214_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO215_GPIO,
+
+ GPIO216_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO217_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO218_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO219_GPIO | PIN_SLPM_INPUT_PULLDOWN,
+
+ GPIO220_GPIO | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO221_GPIO | PIN_SLPM_OUTPUT_LOW,
+ GPIO222_GPIO | PIN_SLPM_OUTPUT_LOW,
+ GPIO223_GPIO | PIN_SLPM_OUTPUT_LOW,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank7[] = {
+ GPIO224_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO225_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO226_GPIO | PIN_SLPM_DIR_OUTPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO227_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO228_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 229-230 - I2C3 */
+ GPIO229_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO230_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank7_href60[] = {
+ GPIO224_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO225_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO226_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO227_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO228_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ /* 229-230 - I2C3 */
+ GPIO229_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO230_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank7_u9500[] = {
+ GPIO224_GPIO | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO225_GPIO | PIN_SLPM_INPUT_PULLDOWN | PIN_SLPM_WAKEUP_ENABLE,
+ GPIO226_GPIO | PIN_SLPM_OUTPUT_LOW,
+ GPIO227_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+
+ GPIO228_GPIO | PIN_SLPM_OUTPUT_LOW | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO229_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO230_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+};
+
+static pin_cfg_t mop500_pins_common_power_save_bank8[] = {
+ GPIO256_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO257_GPIO | PIN_SLPM_OUTPUT_HIGH | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_DISABLED,
+ GPIO258_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO259_GPIO | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+
+ GPIO260_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO261_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO262_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO263_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+
+ GPIO264_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO265_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO266_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+ GPIO267_GPIO | PIN_SLPM_DIR_INPUT | PIN_SLPM_WAKEUP_ENABLE | PIN_SLPM_PDIS_ENABLED,
+};
+
+static void mop500_pins_suspend_force(void)
+{
+ if (machine_is_hrefv60() || machine_is_u8520() ||
+ machine_is_u9540())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank0_href60,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank0_href60));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank0,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank0));
+
+ if (machine_is_snowball())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank1_snowball,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank1_snowball));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank1,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank1));
+
+ if (machine_is_hrefv60() || machine_is_u8520() ||
+ machine_is_u9540())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank2_href60,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank2_href60));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank2,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank2));
+
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank3,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank3));
+
+
+ if (pins_for_u9500() && uib_is_u8500uibr3())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank4_u9500_uibr3,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank4_u9500_uibr3));
+ else if (machine_is_snowball())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank4_snowball,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank4_snowball));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank4,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank4));
+
+ if (machine_is_hrefv60() || machine_is_u8520() ||
+ machine_is_u9540())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank5_href60,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank5_href60));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank5,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank5));
+
+ if (pins_for_u9500())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank6_u9500,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank6_u9500));
+ else if (machine_is_hrefv60() || machine_is_u8520() ||
+ machine_is_u9540())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank6_href60,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank6_href60));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank6,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank6));
+
+ if (pins_for_u9500())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank7_u9500,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank7_u9500));
+ else if (machine_is_hrefv60() || machine_is_u8520() ||
+ machine_is_u9540())
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank7_href60,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank7_href60));
+ else
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank7,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank7));
+
+ sleep_pins_config_pm(mop500_pins_common_power_save_bank8,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank8));
+}
+
+/*
+ * This function is called to force gpio power save
+ * mux settings during suspend.
+ * This is a temporary solution until all drivers are
+ * controlling their pin settings when in inactive mode.
+ */
+static void mop500_pins_suspend_force_mux(void)
+{
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank0,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank0));
+
+ if (machine_is_snowball())
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank1_snowball,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank1_snowball));
+ else
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank1,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank1));
+
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank2,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank2));
+
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank3,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank3));
+
+ if (machine_is_snowball())
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank4_snowball,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank4_snowball));
+ else
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank4,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank4));
+
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank5,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank5));
+
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank6,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank6));
+
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank7,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank7));
+
+ sleep_pins_config_pm_mux(mop500_pins_common_power_save_bank8,
+ ARRAY_SIZE(mop500_pins_common_power_save_bank8));
+}
+
+/*
+ * passing "pinsfor=" in kernel cmdline allows for custom
+ * configuration of GPIOs on u8500 derived boards.
+ */
+static int __init early_pinsfor(char *p)
+{
+ pinsfor = PINS_FOR_DEFAULT;
+
+ if (strcmp(p, "u9500-21") == 0)
+ pinsfor = PINS_FOR_U9500;
+
+ return 0;
+}
+early_param("pinsfor", early_pinsfor);
+
+int pins_for_u9500(void)
+{
+ if (pinsfor == PINS_FOR_U9500)
+ return 1;
+
+ return 0;
+}
+
+static UX500_PINS(mop500_offchip_gpio_cfg,
+ /*
+ * Workaround for auto shutdown of 3.2MHz oscillator during
+ * deep sleep. APESPICSn/GPIO37 must be floating on the board
+ * to use this fix.
+ */
+ AB8500_PIN_GPIO37 | PIN_OUTPUT_HIGH,
+);
+
void __init mop500_pins_init(void)
{
nmk_config_pins(mop500_pins_common,
ARRAY_SIZE(mop500_pins_common));
+ ux500_pins_add(mop500_runtime_pins, ARRAY_SIZE(mop500_runtime_pins));
+
+ ux500_pins_add(mop500_runtime_pins_pre_v60,
+ ARRAY_SIZE(mop500_runtime_pins_pre_v60));
+
+ switch (pinsfor) {
+ case PINS_FOR_U9500:
+ nmk_config_pins(u9500_pins, ARRAY_SIZE(u9500_pins));
+ break;
+
+ case PINS_FOR_DEFAULT:
+ nmk_config_pins(u8500_pins, ARRAY_SIZE(u8500_pins));
+ default:
+ break;
+ }
+
nmk_config_pins(mop500_pins_default,
ARRAY_SIZE(mop500_pins_default));
+
+ suspend_set_pins_force_fn(mop500_pins_suspend_force,
+ mop500_pins_suspend_force_mux);
}
void __init snowball_pins_init(void)
@@ -291,8 +1162,14 @@ void __init snowball_pins_init(void)
nmk_config_pins(mop500_pins_common,
ARRAY_SIZE(mop500_pins_common));
- nmk_config_pins(snowball_pins,
- ARRAY_SIZE(snowball_pins));
+ ux500_pins_add(mop500_runtime_pins, ARRAY_SIZE(mop500_runtime_pins));
+
+ nmk_config_pins(u8500_pins, ARRAY_SIZE(u8500_pins));
+
+ nmk_config_pins(snowball_pins, ARRAY_SIZE(snowball_pins));
+
+ suspend_set_pins_force_fn(mop500_pins_suspend_force,
+ mop500_pins_suspend_force_mux);
}
void __init hrefv60_pins_init(void)
@@ -300,6 +1177,35 @@ void __init hrefv60_pins_init(void)
nmk_config_pins(mop500_pins_common,
ARRAY_SIZE(mop500_pins_common));
+ ux500_pins_add(mop500_runtime_pins, ARRAY_SIZE(mop500_runtime_pins));
+
+ ux500_pins_add(mop500_runtime_pins_v60,
+ ARRAY_SIZE(mop500_runtime_pins_v60));
+
nmk_config_pins(hrefv60_pins,
ARRAY_SIZE(hrefv60_pins));
+
+ switch (pinsfor) {
+ case PINS_FOR_U9500:
+ nmk_config_pins(u9500_pins, ARRAY_SIZE(u9500_pins));
+ break;
+
+ case PINS_FOR_DEFAULT:
+ nmk_config_pins(u8500_pins, ARRAY_SIZE(u8500_pins));
+ default:
+ break;
+ }
+
+ suspend_set_pins_force_fn(mop500_pins_suspend_force,
+ mop500_pins_suspend_force_mux);
+}
+
+static int __init mop500_offchip_gpio_init(void)
+{
+ if (machine_is_hrefv60() || machine_is_u8520() || machine_is_u9540())
+ ux500_offchip_gpio_init(&mop500_offchip_gpio_cfg);
+
+ return 0;
}
+/* Let gpio chip drivers initialize. */
+late_initcall(mop500_offchip_gpio_init);
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index 52426a42578..44fbb68b15a 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -11,8 +11,52 @@
#include <linux/kernel.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
+#include <mach/id.h> /* to identify older boards for fixes */
+#include <asm/mach-types.h>
#include "board-mop500-regulators.h"
+#ifdef CONFIG_REGULATOR_FIXED_VOLTAGE
+/*
+ * GPIO regulator controlled by the ab8500 GPIO16
+ */
+static struct regulator_consumer_supply gpio_wlan_vbat_consumers[] = {
+ /* for cg2900 chip */
+ REGULATOR_SUPPLY("vdd", "cg2900-uart.0"),
+ /* for cw1200 chip */
+ REGULATOR_SUPPLY("vdd", "cw1200_wlan"),
+};
+
+struct regulator_init_data gpio_wlan_vbat_regulator = {
+ .constraints = {
+ .name = "WLAN-VBAT",
+ .min_uV = 3600000,
+ .max_uV = 3600000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(gpio_wlan_vbat_consumers),
+ .consumer_supplies = gpio_wlan_vbat_consumers,
+};
+
+/*
+ * GPIO regulator controlled by the ab8500 GPIO26
+ */
+static struct regulator_consumer_supply gpio_en_3v3_consumers[] = {
+ /* for LAN chip */
+ REGULATOR_SUPPLY("vdd33a", "smsc911x.0"),
+};
+
+struct regulator_init_data gpio_en_3v3_regulator = {
+ .constraints = {
+ .name = "EN-3V3",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(gpio_en_3v3_consumers),
+ .consumer_supplies = gpio_en_3v3_consumers,
+};
+#endif
+
/*
* TPS61052 regulator
*/
@@ -38,21 +82,37 @@ struct regulator_init_data tps61052_regulator = {
};
static struct regulator_consumer_supply ab8500_vaux1_consumers[] = {
- /* External displays, connector on board 2v5 power supply */
- REGULATOR_SUPPLY("vaux12v5", "mcde.0"),
+ /* Main display, u8500 R3 uib */
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ /* Main display, u8500 uib and ST uib */
+ REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.0"),
+ /* Secondary display, ST uib */
+ REGULATOR_SUPPLY("vdd1", "samsung_s6d16d0.1"),
/* SFH7741 proximity sensor */
REGULATOR_SUPPLY("vcc", "gpio-keys.0"),
/* BH1780GLS ambient light sensor */
REGULATOR_SUPPLY("vcc", "2-0029"),
/* lsm303dlh accelerometer */
- REGULATOR_SUPPLY("vdd", "3-0018"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.0"),
/* lsm303dlh magnetometer */
- REGULATOR_SUPPLY("vdd", "3-001e"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.1"),
/* Rohm BU21013 Touchscreen devices */
REGULATOR_SUPPLY("avdd", "3-005c"),
REGULATOR_SUPPLY("avdd", "3-005d"),
/* Synaptics RMI4 Touchscreen device */
REGULATOR_SUPPLY("vdd", "3-004b"),
+ /* L3G4200D Gyroscope device */
+ REGULATOR_SUPPLY("vdd", "l3g4200d"),
+ /* Proximity and Hal sensor device */
+ REGULATOR_SUPPLY("vdd", "sensor1p.0"),
+ /* Ambient light sensor device */
+ REGULATOR_SUPPLY("vdd", "3-0029"),
+ /* Pressure sensor device */
+ REGULATOR_SUPPLY("vdd", "2-005c"),
+ /* Cypress TrueTouch Touchscreen device */
+ REGULATOR_SUPPLY("vcpin", "spi8.0"),
+ /* Camera device */
+ REGULATOR_SUPPLY("vaux12v5", "mmio_camera"),
};
static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
@@ -60,9 +120,16 @@ static struct regulator_consumer_supply ab8500_vaux2_consumers[] = {
REGULATOR_SUPPLY("vmmc", "sdi4"),
/* AB8500 audio codec */
REGULATOR_SUPPLY("vcc-N2158", "ab8500-codec.0"),
+ /* AB8500 accessory detect 1 */
+ REGULATOR_SUPPLY("vcc-N2158", "ab8500-acc-det.0"),
+ /* AB8500 Tv-out device */
+ REGULATOR_SUPPLY("vcc-N2158", "mcde_tv_ab8500.4"),
+ /* AV8100 HDMI device */
+ REGULATOR_SUPPLY("vcc-N2158", "av8100_hdmi.3"),
};
static struct regulator_consumer_supply ab8500_vaux3_consumers[] = {
+ REGULATOR_SUPPLY("v-SD-STM", "stm"),
/* External MMC slot power */
REGULATOR_SUPPLY("vmmc", "sdi0"),
};
@@ -72,26 +139,30 @@ static struct regulator_consumer_supply ab8500_vtvout_consumers[] = {
REGULATOR_SUPPLY("vtvout", "ab8500-denc.0"),
/* Internal general-purpose ADC */
REGULATOR_SUPPLY("vddadc", "ab8500-gpadc.0"),
+ /* ADC for charger */
+ REGULATOR_SUPPLY("vddadc", "ab8500-charger.0"),
+ /* AB8500 Tv-out device */
+ REGULATOR_SUPPLY("vtvout", "mcde_tv_ab8500.4"),
};
static struct regulator_consumer_supply ab8500_vaud_consumers[] = {
/* AB8500 audio-codec main supply */
- REGULATOR_SUPPLY("vaud", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-audio", NULL),
};
static struct regulator_consumer_supply ab8500_vamic1_consumers[] = {
/* AB8500 audio-codec Mic1 supply */
- REGULATOR_SUPPLY("vamic1", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-amic1", NULL),
};
static struct regulator_consumer_supply ab8500_vamic2_consumers[] = {
/* AB8500 audio-codec Mic2 supply */
- REGULATOR_SUPPLY("vamic2", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-amic2", NULL),
};
static struct regulator_consumer_supply ab8500_vdmic_consumers[] = {
/* AB8500 audio-codec DMic supply */
- REGULATOR_SUPPLY("vdmic", "ab8500-codec.0"),
+ REGULATOR_SUPPLY("v-dmic", NULL),
};
static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
@@ -102,74 +173,85 @@ static struct regulator_consumer_supply ab8500_vintcore_consumers[] = {
};
static struct regulator_consumer_supply ab8500_vana_consumers[] = {
- /* External displays, connector on board, 1v8 power supply */
- REGULATOR_SUPPLY("vsmps2", "mcde.0"),
+ /* DB8500 DSI */
+ REGULATOR_SUPPLY("vdddsi1v2", "mcde"),
+ /* DB8500 CSI */
+ REGULATOR_SUPPLY("vddcsi1v2", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab8500_sysclkreq_2_consumers[] = {
+ /* CG2900 device */
+ REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"),
+};
+
+static struct regulator_consumer_supply ab8500_sysclkreq_4_consumers[] = {
+ /* CW1200 device */
+ REGULATOR_SUPPLY("wlan_1v8", "cw1200_wlan.0"),
};
/* ab8500 regulator register initialization */
-struct ab8500_regulator_reg_init
-ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
+static struct ab8500_regulator_reg_init ab8500_reg_init[] = {
/*
* VanaRequestCtrl = HP/LP depending on VxRequest
* VextSupply1RequestCtrl = HP/LP depending on VxRequest
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL2, 0xf0, 0x00),
/*
* VextSupply2RequestCtrl = HP/LP depending on VxRequest
* VextSupply3RequestCtrl = HP/LP depending on VxRequest
* Vaux1RequestCtrl = HP/LP depending on VxRequest
* Vaux2RequestCtrl = HP/LP depending on VxRequest
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL3, 0xff, 0x00),
/*
* Vaux3RequestCtrl = HP/LP depending on VxRequest
* SwHPReq = Control through SWValid disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUREQUESTCTRL4, 0x07, 0x00),
/*
* VanaSysClkReq1HPValid = disabled
* Vaux1SysClkReq1HPValid = disabled
* Vaux2SysClkReq1HPValid = disabled
* Vaux3SysClkReq1HPValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID1, 0xe8, 0x00),
/*
* VextSupply1SysClkReq1HPValid = disabled
* VextSupply2SysClkReq1HPValid = disabled
* VextSupply3SysClkReq1HPValid = SysClkReq1 controlled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x40),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQ1HPVALID2, 0x70, 0x40),
/*
* VanaHwHPReq1Valid = disabled
* Vaux1HwHPreq1Valid = disabled
* Vaux2HwHPReq1Valid = disabled
* Vaux3HwHPReqValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID1, 0xe8, 0x00),
/*
* VextSupply1HwHPReq1Valid = disabled
* VextSupply2HwHPReq1Valid = disabled
* VextSupply3HwHPReq1Valid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ1VALID2, 0x07, 0x00),
/*
* VanaHwHPReq2Valid = disabled
* Vaux1HwHPReq2Valid = disabled
* Vaux2HwHPReq2Valid = disabled
* Vaux3HwHPReq2Valid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID1, 0xe8, 0x00),
/*
* VextSupply1HwHPReq2Valid = disabled
* VextSupply2HwHPReq2Valid = disabled
* VextSupply3HwHPReq2Valid = HWReq2 controlled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x04),
+ INIT_REGULATOR_REGISTER(AB8500_REGUHWHPREQ2VALID2, 0x07, 0x04),
/*
* VanaSwHPReqValid = disabled
* Vaux1SwHPReqValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID1, 0xa0, 0x00),
/*
* Vaux2SwHPReqValid = disabled
* Vaux3SwHPReqValid = disabled
@@ -177,7 +259,7 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* VextSupply2SwHPReqValid = disabled
* VextSupply3SwHPReqValid = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSWHPREQVALID2, 0x1f, 0x00),
/*
* SysClkReq2Valid1 = SysClkReq2 controlled
* SysClkReq3Valid1 = disabled
@@ -187,7 +269,7 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* SysClkReq7Valid1 = disabled
* SysClkReq8Valid1 = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0x2a),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID1, 0xfe, 0x2a),
/*
* SysClkReq2Valid2 = disabled
* SysClkReq3Valid2 = disabled
@@ -197,7 +279,7 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* SysClkReq7Valid2 = disabled
* SysClkReq8Valid2 = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0x20),
+ INIT_REGULATOR_REGISTER(AB8500_REGUSYSCLKREQVALID2, 0xfe, 0x20),
/*
* VTVoutEna = disabled
* Vintcore12Ena = disabled
@@ -205,66 +287,62 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* Vintcore12LP = inactive (HP)
* VTVoutLP = inactive (HP)
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0x10),
+ INIT_REGULATOR_REGISTER(AB8500_REGUMISC1, 0xfe, 0x10),
/*
* VaudioEna = disabled
* VdmicEna = disabled
* Vamic1Ena = disabled
* Vamic2Ena = disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_VAUDIOSUPPLY, 0x1e, 0x00),
/*
* Vamic1_dzout = high-Z when Vamic1 is disabled
* Vamic2_dzout = high-Z when Vamic2 is disabled
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRL1VAMIC, 0x03, 0x00),
/*
- * VPll = Hw controlled
+ * VPll = Hw controlled (NOTE! PRCMU bits)
* VanaRegu = force off
*/
- INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x02),
+ INIT_REGULATOR_REGISTER(AB8500_VPLLVANAREGU, 0x0f, 0x02),
/*
* VrefDDREna = disabled
* VrefDDRSleepMode = inactive (no pulldown)
*/
- INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_VREFDDR, 0x03, 0x00),
/*
- * VextSupply1Regu = HW control
- * VextSupply2Regu = HW control
- * VextSupply3Regu = HW control
+ * VextSupply1Regu = force LP
+ * VextSupply2Regu = force OFF
+ * VextSupply3Regu = force HP (-> STBB2=LP and TPS=LP)
* ExtSupply2Bypass = ExtSupply12LPn ball is 0 when Ena is 0
* ExtSupply3Bypass = ExtSupply3LPn ball is 0 when Ena is 0
*/
- INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0x2a),
+ INIT_REGULATOR_REGISTER(AB8500_EXTSUPPLYREGU, 0xff, 0x13),
/*
* Vaux1Regu = force HP
* Vaux2Regu = force off
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x01),
- /*
- * Vaux3regu = force off
- */
- INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_VAUX12REGU, 0x0f, 0x01),
/*
- * Vsmps1 = 1.15V
+ * Vaux3Regu = force off
*/
- INIT_REGULATOR_REGISTER(AB8500_VSMPS1SEL1, 0x24),
+ INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3REGU, 0x03, 0x00),
/*
- * Vaux1Sel = 2.5 V
+ * Vaux1Sel = 2.8 V
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x08),
+ INIT_REGULATOR_REGISTER(AB8500_VAUX1SEL, 0x0f, 0x0C),
/*
* Vaux2Sel = 2.9 V
*/
- INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0d),
+ INIT_REGULATOR_REGISTER(AB8500_VAUX2SEL, 0x0f, 0x0d),
/*
* Vaux3Sel = 2.91 V
*/
- INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07),
+ INIT_REGULATOR_REGISTER(AB8500_VRF1VAUX3SEL, 0x07, 0x07),
/*
* VextSupply12LP = disabled (no LP)
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRL2SPARE, 0x01, 0x00),
/*
* Vaux1Disch = short discharge time
* Vaux2Disch = short discharge time
@@ -273,23 +351,24 @@ ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS] = {
* VTVoutDisch = short discharge time
* VaudioDisch = short discharge time
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH, 0xfc, 0x00),
/*
* VanaDisch = short discharge time
* VdmicPullDownEna = pulldown disabled when Vdmic is disabled
* VdmicDisch = short discharge time
*/
- INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x00),
+ INIT_REGULATOR_REGISTER(AB8500_REGUCTRLDISCH2, 0x16, 0x00),
};
/* AB8500 regulators */
-struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
+static struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
/* supplies to the display/camera */
[AB8500_LDO_AUX1] = {
+ .supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-DISPLAY",
- .min_uV = 2500000,
- .max_uV = 2900000,
+ .min_uV = 2800000,
+ .max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
REGULATOR_CHANGE_STATUS,
.boot_on = 1, /* display is on at boot */
@@ -306,24 +385,32 @@ struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
},
/* supplies to the on-board eMMC */
[AB8500_LDO_AUX2] = {
+ .supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-eMMC1",
.min_uV = 1100000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
- REGULATOR_CHANGE_STATUS,
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux2_consumers),
.consumer_supplies = ab8500_vaux2_consumers,
},
/* supply for VAUX3, supplies to SDcard slots */
[AB8500_LDO_AUX3] = {
+ .supply_regulator = "ab8500-ext-supply3",
.constraints = {
.name = "V-MMC-SD",
.min_uV = 1100000,
.max_uV = 3300000,
.valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
- REGULATOR_CHANGE_STATUS,
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vaux3_consumers),
.consumer_supplies = ab8500_vaux3_consumers,
@@ -377,18 +464,167 @@ struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS] = {
[AB8500_LDO_INTCORE] = {
.constraints = {
.name = "V-INTCORE",
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .min_uV = 1250000,
+ .max_uV = 1350000,
+ .input_uV = 1800000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE |
+ REGULATOR_CHANGE_DRMS,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vintcore_consumers),
.consumer_supplies = ab8500_vintcore_consumers,
},
- /* supply for U8500 CSI/DSI, VANA LDO */
+ /* supply for U8500 CSI-DSI, VANA LDO */
[AB8500_LDO_ANA] = {
.constraints = {
- .name = "V-CSI/DSI",
+ .name = "V-CSI-DSI",
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ab8500_vana_consumers),
.consumer_supplies = ab8500_vana_consumers,
},
+ /* sysclkreq 2 pin */
+ [AB8500_SYSCLKREQ_2] = {
+ .constraints = {
+ .name = "V-SYSCLKREQ-2",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_sysclkreq_2_consumers),
+ .consumer_supplies = ab8500_sysclkreq_2_consumers,
+ },
+ /* sysclkreq 4 pin */
+ [AB8500_SYSCLKREQ_4] = {
+ .constraints = {
+ .name = "V-SYSCLKREQ-4",
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_sysclkreq_4_consumers),
+ .consumer_supplies = ab8500_sysclkreq_4_consumers,
+ },
};
+
+/* supply for VextSupply3 */
+static struct regulator_consumer_supply ab8500_ext_supply3_consumers[] = {
+ /* SIM supply for 3 V SIM cards */
+ REGULATOR_SUPPLY("vinvsim", "sim-detect.0"),
+};
+
+/* extended configuration for VextSupply2, only used for HREFP_V20 boards */
+static struct ab8500_ext_regulator_cfg ab8500_ext_supply2 = {
+ .hwreq = true,
+};
+
+/*
+ * AB8500 external regulators
+ */
+static struct regulator_init_data ab8500_ext_regulators[] = {
+ /* fixed Vbat supplies VSMPS1_EXT_1V8 */
+ [AB8500_EXT_SUPPLY1] = {
+ .constraints = {
+ .name = "ab8500-ext-supply1",
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .initial_mode = REGULATOR_MODE_IDLE,
+ .boot_on = 1,
+ .always_on = 1,
+ },
+ },
+ /* fixed Vbat supplies VSMPS2_EXT_1V36 and VSMPS5_EXT_1V15 */
+ [AB8500_EXT_SUPPLY2] = {
+ .constraints = {
+ .name = "ab8500-ext-supply2",
+ .min_uV = 1360000,
+ .max_uV = 1360000,
+ },
+ },
+ /* fixed Vbat supplies VSMPS3_EXT_3V4 and VSMPS4_EXT_3V4 */
+ [AB8500_EXT_SUPPLY3] = {
+ .constraints = {
+ .name = "ab8500-ext-supply3",
+ .min_uV = 3400000,
+ .max_uV = 3400000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .boot_on = 1,
+ },
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab8500_ext_supply3_consumers),
+ .consumer_supplies = ab8500_ext_supply3_consumers,
+ },
+};
+
+struct ab8500_regulator_platform_data ab8500_regulator_plat_data = {
+ .reg_init = ab8500_reg_init,
+ .num_reg_init = ARRAY_SIZE(ab8500_reg_init),
+ .regulator = ab8500_regulators,
+ .num_regulator = ARRAY_SIZE(ab8500_regulators),
+ .ext_regulator = ab8500_ext_regulators,
+ .num_ext_regulator = ARRAY_SIZE(ab8500_ext_regulators),
+};
+
+static void ab8500_modify_reg_init(int id, u8 mask, u8 value)
+{
+ int i;
+
+ for (i = ARRAY_SIZE(ab8500_reg_init) - 1; i >= 0; i--) {
+ if (ab8500_reg_init[i].id == id) {
+ u8 initval = ab8500_reg_init[i].value;
+ initval = (initval & ~mask) | (value & mask);
+ ab8500_reg_init[i].value = initval;
+
+ BUG_ON(mask & ~ab8500_reg_init[i].mask);
+ return;
+ }
+ }
+
+ BUG_ON(1);
+}
+
+void mop500_regulator_init(void)
+{
+ struct regulator_init_data *regulator;
+
+ /*
+ * Temporarily turn on Vaux2 on 8520 machine
+ */
+ if (machine_is_u8520()) {
+ /* Vaux2 initialized to be on */
+ ab8500_modify_reg_init(AB8500_VAUX12REGU, 0x0f, 0x05);
+
+ /* Vaux2 always on */
+ regulator = &ab8500_ext_regulators[AB8500_LDO_AUX2];
+ regulator->constraints.always_on = 1;
+ }
+
+ /*
+ * Handle AB8500_EXT_SUPPLY2 on HREFP_V20_V50 boards (do it for
+ * all HREFP_V20 boards)
+ */
+ if (cpu_is_u8500v20()) {
+ /* VextSupply2RequestCtrl = HP/OFF depending on VxRequest */
+ ab8500_modify_reg_init(AB8500_REGUREQUESTCTRL3, 0x01, 0x01);
+
+ /* VextSupply2SysClkReq1HPValid = SysClkReq1 controlled */
+ ab8500_modify_reg_init(AB8500_REGUSYSCLKREQ1HPVALID2,
+ 0x20, 0x20);
+
+ /* VextSupply2 = force HP at initialization */
+ ab8500_modify_reg_init(AB8500_EXTSUPPLYREGU, 0x0c, 0x04);
+
+ /* enable VextSupply2 during platform active */
+ regulator = &ab8500_ext_regulators[AB8500_EXT_SUPPLY2];
+ regulator->constraints.always_on = 1;
+
+ /* disable VextSupply2 in suspend */
+ regulator = &ab8500_ext_regulators[AB8500_EXT_SUPPLY2];
+ regulator->constraints.state_mem.disabled = 1;
+ regulator->constraints.state_standby.disabled = 1;
+
+ /* enable VextSupply2 HW control (used in suspend) */
+ regulator->driver_data = (void *)&ab8500_ext_supply2;
+ }
+}
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h
index 94992158d96..b5fc81a3649 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.h
+++ b/arch/arm/mach-ux500/board-mop500-regulators.h
@@ -14,9 +14,11 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
-extern struct ab8500_regulator_reg_init
-ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS];
-extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS];
+extern struct ab8500_regulator_platform_data ab8500_regulator_plat_data;
extern struct regulator_init_data tps61052_regulator;
+extern struct regulator_init_data gpio_wlan_vbat_regulator;
+extern struct regulator_init_data gpio_en_3v3_regulator;
+
+void mop500_regulator_init(void);
#endif
diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c
index 920251cf834..828b230dd27 100644
--- a/arch/arm/mach-ux500/board-mop500-sdi.c
+++ b/arch/arm/mach-ux500/board-mop500-sdi.c
@@ -11,15 +11,16 @@
#include <linux/amba/mmci.h>
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
#include <asm/mach-types.h>
#include <plat/ste_dma40.h>
#include <mach/devices.h>
#include <mach/hardware.h>
+#include <mach/ste-dma40-db8500.h>
#include "devices-db8500.h"
#include "board-mop500.h"
-#include "ste-dma40-db8500.h"
/*
* v2 has a new version of this block that need to be forced, the number found
@@ -37,26 +38,55 @@ static int sdi0_vsel = -1;
static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios)
{
+ static unsigned char power_mode = MMC_POWER_ON;
+ static unsigned char signal_voltage = MMC_SIGNAL_VOLTAGE_330;
+
+ if (signal_voltage == ios->signal_voltage)
+ goto do_power;
+
+ /*
+ * We need to re-init the levelshifter when switching I/O voltage level.
+ * Max discharge time according to ST6G3244ME spec is 1 ms.
+ */
+ if (power_mode == MMC_POWER_ON) {
+ power_mode = MMC_POWER_OFF;
+ gpio_direction_output(sdi0_en, 0);
+ msleep(1);
+ }
+
+ switch (ios->signal_voltage) {
+ case MMC_SIGNAL_VOLTAGE_330:
+ gpio_direction_output(sdi0_vsel, 0);
+ break;
+ case MMC_SIGNAL_VOLTAGE_180:
+ gpio_direction_output(sdi0_vsel, 1);
+ break;
+ default:
+ pr_warning("Non supported signal voltage for levelshifter.\n");
+ break;
+ }
+
+ signal_voltage = ios->signal_voltage;
+
+do_power:
+ if (power_mode == ios->power_mode)
+ return 0;
+
switch (ios->power_mode) {
case MMC_POWER_UP:
+ break;
case MMC_POWER_ON:
- /*
- * Level shifter voltage should depend on vdd to when deciding
- * on either 1.8V or 2.9V. Once the decision has been made the
- * level shifter must be disabled and re-enabled with a changed
- * select signal in order to switch the voltage. Since there is
- * no framework support yet for indicating 1.8V in vdd, use the
- * default 2.9V.
- */
- gpio_direction_output(sdi0_vsel, 0);
gpio_direction_output(sdi0_en, 1);
+ /* Max settling time according to ST6G3244ME spec is 100 us. */
+ udelay(100);
break;
case MMC_POWER_OFF:
- gpio_direction_output(sdi0_vsel, 0);
gpio_direction_output(sdi0_en, 0);
break;
}
+ power_mode = ios->power_mode;
+
return 0;
}
@@ -64,29 +94,34 @@ static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios)
struct stedma40_chan_cfg mop500_sdi0_dma_cfg_rx = {
.mode = STEDMA40_MODE_LOGICAL,
.dir = STEDMA40_PERIPH_TO_MEM,
- .src_dev_type = DB8500_DMA_DEV29_SD_MM0_RX,
+ .src_dev_type = DB8500_DMA_DEV1_SD_MMC0_RX,
.dst_dev_type = STEDMA40_DEV_DST_MEMORY,
.src_info.data_width = STEDMA40_WORD_WIDTH,
.dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .use_fixed_channel = true,
+ .phy_channel = 0,
};
static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = {
.mode = STEDMA40_MODE_LOGICAL,
.dir = STEDMA40_MEM_TO_PERIPH,
.src_dev_type = STEDMA40_DEV_SRC_MEMORY,
- .dst_dev_type = DB8500_DMA_DEV29_SD_MM0_TX,
+ .dst_dev_type = DB8500_DMA_DEV1_SD_MMC0_TX,
.src_info.data_width = STEDMA40_WORD_WIDTH,
.dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .use_fixed_channel = true,
+ .phy_channel = 0,
};
#endif
static struct mmci_platform_data mop500_sdi0_data = {
.ios_handler = mop500_sdi0_ios_handler,
- .ocr_mask = MMC_VDD_29_30,
.f_max = 50000000,
.capabilities = MMC_CAP_4_BIT_DATA |
MMC_CAP_SD_HIGHSPEED |
- MMC_CAP_MMC_HIGHSPEED,
+ MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_UHS_SDR12 |
+ MMC_CAP_UHS_SDR25,
.gpio_wp = -1,
.sigdir = MCI_ST_FBCLKEN |
MCI_ST_CMDDIREN |
@@ -121,14 +156,6 @@ static void sdi0_configure(struct device *parent)
db8500_add_sdi0(parent, &mop500_sdi0_data, U8500_SDI_V2_PERIPHID);
}
-void mop500_sdi_tc35892_init(struct device *parent)
-{
- mop500_sdi0_data.gpio_cd = GPIO_SDMMC_CD;
- sdi0_en = GPIO_SDMMC_EN;
- sdi0_vsel = GPIO_SDMMC_1V8_3V_SEL;
- sdi0_configure(parent);
-}
-
/*
* SDI1 (SDIO WLAN)
*/
@@ -193,7 +220,9 @@ static struct mmci_platform_data mop500_sdi2_data = {
.ocr_mask = MMC_VDD_165_195,
.f_max = 50000000,
.capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |
- MMC_CAP_MMC_HIGHSPEED,
+ MMC_CAP_MMC_HIGHSPEED |
+ MMC_CAP_ERASE,
+ .capabilities2 = MMC_CAP2_NO_SLEEP_CMD,
.gpio_cd = -1,
.gpio_wp = -1,
#ifdef CONFIG_STE_DMA40
@@ -228,7 +257,6 @@ static struct stedma40_chan_cfg mop500_sdi4_dma_cfg_tx = {
#endif
static struct mmci_platform_data mop500_sdi4_data = {
- .ocr_mask = MMC_VDD_29_30,
.f_max = 50000000,
.capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED,
@@ -241,6 +269,16 @@ static struct mmci_platform_data mop500_sdi4_data = {
#endif
};
+void mop500_sdi_tc35892_init(struct device *parent)
+{
+ mop500_sdi0_data.gpio_cd = GPIO_SDMMC_CD;
+ sdi0_en = GPIO_SDMMC_EN;
+ sdi0_vsel = GPIO_SDMMC_1V8_3V_SEL;
+ sdi0_configure(parent);
+ /* WLAN SDIO channel */
+ db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID);
+}
+
void __init mop500_sdi_init(struct device *parent)
{
/* PoP:ed eMMC */
@@ -267,6 +305,8 @@ void __init snowball_sdi_init(struct device *parent)
sdi0_en = SNOWBALL_SDMMC_EN_GPIO;
sdi0_vsel = SNOWBALL_SDMMC_1V8_3V_GPIO;
sdi0_configure(parent);
+ /* WLAN SDIO channel */
+ db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID);
}
void __init hrefv60_sdi_init(struct device *parent)
@@ -283,3 +323,18 @@ void __init hrefv60_sdi_init(struct device *parent)
/* WLAN SDIO channel */
db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID);
}
+
+void __init mach_u8520_sdi_init(struct device *parent)
+{
+ /* PoP:ed eMMC */
+ db8500_add_sdi2(parent, &mop500_sdi2_data, U8500_SDI_V2_PERIPHID);
+ /* On-board eMMC */
+ db8500_add_sdi4(parent, &mop500_sdi4_data, U8500_SDI_V2_PERIPHID);
+ /* External Micro SD slot */
+ mop500_sdi0_data.gpio_cd = U8520_SDMMC_CD_GPIO;
+ sdi0_en = U8520_SDMMC_EN_GPIO;
+ sdi0_vsel = U8520_SDMMC_1V8_3V_GPIO;
+ sdi0_configure(parent);
+ /* WLAN SDIO channel */
+ db8500_add_sdi1(parent, &mop500_sdi1_data, U8500_SDI_V2_PERIPHID);
+}
diff --git a/arch/arm/mach-ux500/board-mop500-stm.c b/arch/arm/mach-ux500/board-mop500-stm.c
new file mode 100644
index 00000000000..1bef2a01873
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-stm.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson
+ *
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * Author: Olivier Germain <olivier.germain@stericsson.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 <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/mach-types.h>
+#include <plat/pincfg.h>
+#include <mach/devices.h>
+#include <asm/io.h>
+#include <trace/stm.h>
+#include "pins-db8500.h"
+
+#define HREFV60_SDMMC_EN_GPIO 169
+#define HREFV60_SDMMC_1V8_3V_GPIO 5
+
+#define U8520_SDMMC_EN_GPIO 78
+#define U8520_SDMMC_1V8_3V_GPIO 5
+
+#define STM_DEVICE (&u8500_stm_device.dev)
+#define STM_ERR(msg) dev_err(STM_DEVICE, msg)
+#define STM_WARN(msg) dev_warn(STM_DEVICE, msg)
+
+static struct regulator *regulator_aux3;
+static enum stm_connection_type
+ stm_current_connection = STM_STE_INVALID_CONNECTION;
+
+static pin_cfg_t mop500_stm_mipi34_pins[] = {
+ GPIO70_STMAPE_CLK | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO71_STMAPE_DAT3 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO72_STMAPE_DAT2 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO73_STMAPE_DAT1 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO74_STMAPE_DAT0 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO75_U2_RXD | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO76_U2_TXD | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+};
+
+static pin_cfg_t mop500_stm_mipi60_pins[] = {
+ GPIO153_U2_RXD,
+ GPIO154_U2_TXD,
+ GPIO155_STMAPE_CLK,
+ GPIO156_STMAPE_DAT3,
+ GPIO157_STMAPE_DAT2,
+ GPIO158_STMAPE_DAT1,
+ GPIO159_STMAPE_DAT0,
+};
+
+static pin_cfg_t mop500_stm_ape_microsd_pins[] = {
+ GPIO23_MS_CLK | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO24_MS_BS | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO25_MS_DAT0 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO26_MS_DAT1 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO27_MS_DAT2 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO28_MS_DAT3 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+};
+
+static pin_cfg_t mop500_ske_pins[] = {
+ GPIO153_KP_I7 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO154_KP_I6 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO155_KP_I5 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO156_KP_I4 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO161_KP_I3 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO162_KP_I2 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO163_KP_I1 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO164_KP_I0 | PIN_INPUT_PULLDOWN | PIN_SLPM_INPUT_PULLUP,
+ GPIO157_KP_O7 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO158_KP_O6 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO159_KP_O5 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO160_KP_O4 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO165_KP_O3 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO166_KP_O2 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO167_KP_O1 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO168_KP_O0 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+};
+
+static pin_cfg_t mop500_stm_modem_microsd_pins[] = {
+ GPIO18_GPIO | PIN_OUTPUT_LOW,
+ GPIO19_GPIO | PIN_OUTPUT_HIGH,
+ GPIO20_GPIO | PIN_OUTPUT_HIGH,
+ GPIO22_GPIO | PIN_INPUT_PULLUP,
+ GPIO23_STMMOD_CLK | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO24_UARTMOD_RXD | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO25_STMMOD_DAT0 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO26_STMMOD_DAT1 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO27_STMMOD_DAT2 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+ GPIO28_STMMOD_DAT3 | PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP,
+};
+
+/* sdi0 (removable MMC/SD/SDIO cards) */
+static pin_cfg_t mop500_sdi0_pins[] = {
+ GPIO18_MC0_CMDDIR | PIN_OUTPUT_HIGH,
+ GPIO19_MC0_DAT0DIR | PIN_OUTPUT_HIGH,
+ GPIO20_MC0_DAT2DIR | PIN_OUTPUT_HIGH,
+
+ GPIO22_MC0_FBCLK | PIN_INPUT_NOPULL,
+ GPIO23_MC0_CLK | PIN_OUTPUT_LOW,
+ GPIO24_MC0_CMD | PIN_INPUT_PULLUP,
+ GPIO25_MC0_DAT0 | PIN_INPUT_PULLUP,
+ GPIO26_MC0_DAT1 | PIN_INPUT_PULLUP,
+ GPIO27_MC0_DAT2 | PIN_INPUT_PULLUP,
+ GPIO28_MC0_DAT3 | PIN_INPUT_PULLUP,
+};
+
+static int stm_ste_disable_ape_on_mipi60(void)
+{
+ int retval;
+
+ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_stm_mipi60_pins));
+ if (retval)
+ STM_ERR("Failed to disable MIPI60\n");
+ else {
+ retval = nmk_config_pins(ARRAY_AND_SIZE(mop500_ske_pins));
+ if (retval)
+ STM_ERR("Failed to enable SKE gpio\n");
+ }
+ return retval;
+}
+
+static int stm_enable_ape_microsd(void)
+{
+ int retval;
+
+ /*
+ * Configure STM APE on GPIO23,GPIO28,GPIO27,GPIO26,GPIO25
+ * On HREF board an external SD buffer exist (ST6G3244ME)
+ * to perform level conversion from 1.8v to 3.3V on SD card signals
+ * When STM is redirected on micro SD connector GPIO18,GP19,GPIO20
+ * are configured in standard GPIO mode and are used to configure
+ * direction on external SD buffer ST6G3244ME.
+ */
+
+ retval = nmk_config_pins(ARRAY_AND_SIZE(mop500_stm_ape_microsd_pins));
+ if (retval)
+ STM_ERR("Failed to enable STM APE on MICRO SD\n");
+
+ /* Enable altC1 on GPIO23-28 (STMAPE) */
+ prcmu_enable_stm_ape();
+
+ return retval;
+}
+
+static int stm_disable_ape_microsd(void)
+{
+ int retval;
+
+ /* Disable altC1 on GPIO23-28 (STMAPE) */
+ prcmu_disable_stm_ape();
+
+ /* Reconfigure GPIO for SD */
+ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_sdi0_pins));
+ if (retval)
+ STM_ERR("Failed to disable STM APE on MICRO SD "
+ "and to reconfigure GPIO for SD\n");
+
+ return retval;
+}
+
+static int stm_enable_modem_microsd(void)
+{
+ int retval;
+
+ /*
+ * Configure STM APE on GPIO23,GPIO28,GPIO27,GPIO26,GPIO25
+ * On HREF board an external SD buffer exist (ST6G3244ME)
+ * to perform level conversion from 1.8v to 3.3V on SD card
+ * signals. When STM is redirected on micro SD connector
+ * GPIO18,GP19,GPIO20 are configured in standard GPIO mode
+ * and are used to configure direction on external SD buffer
+ * ST6G3244ME.
+ */
+
+ retval = nmk_config_pins(ARRAY_AND_SIZE(mop500_stm_modem_microsd_pins));
+ if (retval)
+ STM_ERR("Failed to enable STM MODEM on MICRO SD\n");
+
+ return retval;
+}
+
+static int stm_disable_modem_microsd(void)
+{
+ int retval;
+
+ /* Reconfigure GPIO for SD */
+ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_sdi0_pins));
+ if (retval)
+ STM_ERR("Failed to disable STM MODEM on MICRO SD "
+ "and to reconfigure GPIO for SD\n");
+
+ return retval;
+}
+
+/* Enable or disable micro sd card buffers on HREF */
+static void control_level_shifter_for_microsd(int gpio_dir)
+{
+ int gpio[2];
+
+ if (machine_is_hrefv60() || machine_is_u9540()) {
+ gpio[0] = HREFV60_SDMMC_EN_GPIO;
+ gpio[1] = HREFV60_SDMMC_1V8_3V_GPIO;
+ } else if (machine_is_u8520()) {
+ gpio[0] = U8520_SDMMC_EN_GPIO;
+ gpio[1] = U8520_SDMMC_1V8_3V_GPIO;
+ } else {
+ gpio[0] = MOP500_EGPIO(17);
+ gpio[1] = MOP500_EGPIO(18);
+ }
+
+ /* Select the default 2.9V and enable / disable level shifter */
+ gpio_direction_output(gpio[1], 0);
+ gpio_direction_output(gpio[0], gpio_dir);
+}
+
+/* Enable micro sd card buffers on HREF */
+static int enable_level_shifter_for_microsd(void)
+{
+ control_level_shifter_for_microsd(1);
+ STM_WARN("Level Shifter for SD card connector on.\n");
+ return 0;
+}
+
+/* Disable micro sd card buffers on HREF */
+static int disable_level_shifter_for_microsd(void)
+{
+ control_level_shifter_for_microsd(0);
+ STM_WARN("Level Shifter for SD card connector off.\n");
+ return 0;
+}
+
+/* Enable VAUX3 to power on buffer on STM MICRO SD cable */
+static int enable_vaux3_for_microsd_cable(void)
+{
+ int error;
+
+ regulator_aux3 = regulator_get(&u8500_stm_device.dev, "v-SD-STM");
+
+ if (IS_ERR(regulator_aux3)) {
+ error = PTR_ERR(regulator_aux3);
+ STM_ERR("Failed to get regulator, supply: v-SD-STM\n");
+ return error;
+ }
+
+ error = regulator_enable(regulator_aux3);
+
+ if (error) {
+ STM_ERR("Unable to enable regulator on SD card connector\n");
+ return error;
+ }
+
+ STM_WARN("Regulator on SD card connector power on.\n");
+ return error;
+}
+
+/* Disable VAUX3 to power off buffer on STM MICRO SD cable */
+static int disable_vaux3_for_microsd_cable(void)
+{
+ int error = 0;
+
+ error = regulator_disable(regulator_aux3);
+
+ if (regulator_aux3)
+ regulator_put(regulator_aux3);
+
+ STM_WARN("Regulator for stm on SD card connector power off.\n");
+
+ return error;
+
+}
+
+static int stm_ste_connection(enum stm_connection_type con_type)
+{
+ int retval = -EINVAL;
+
+ /* Check if connection type has been changed */
+ if (con_type == stm_current_connection)
+ return 0;
+
+ if (con_type != STM_DISCONNECT) {
+ /* Always enable MIPI34 GPIO pins */
+ retval = nmk_config_pins(
+ ARRAY_AND_SIZE(mop500_stm_mipi34_pins));
+ if (retval) {
+ STM_ERR("Failed to enable MIPI34\n");
+ goto stm_ste_connection_error;
+ }
+ }
+
+ switch (con_type) {
+ case STM_DEFAULT_CONNECTION:
+ case STM_STE_MODEM_ON_MIPI34_NONE_ON_MIPI60:
+ /* Enable altC3 on GPIO70-74 (STMMOD) & GPIO75-76 (UARTMOD) */
+ prcmu_enable_stm_mod_uart();
+ retval = stm_ste_disable_ape_on_mipi60();
+ break;
+
+ case STM_STE_APE_ON_MIPI34_NONE_ON_MIPI60:
+ /* Disable altC3 on GPIO70-74 (STMMOD) & GPIO75-76 (UARTMOD) */
+ prcmu_disable_stm_mod_uart();
+ retval = stm_ste_disable_ape_on_mipi60();
+ break;
+
+ case STM_STE_MODEM_ON_MIPI34_APE_ON_MIPI60:
+ /* Enable altC3 on GPIO70-74 (STMMOD) and GPIO75-76 (UARTMOD) */
+ prcmu_enable_stm_mod_uart();
+ /* Enable APE on MIPI60 */
+ retval = nmk_config_pins_sleep(ARRAY_AND_SIZE(mop500_ske_pins));
+ if (retval)
+ STM_ERR("Failed to disable SKE GPIO\n");
+ else {
+ retval = nmk_config_pins(
+ ARRAY_AND_SIZE(mop500_stm_mipi60_pins));
+ if (retval)
+ STM_ERR("Failed to enable MIPI60\n");
+ }
+ break;
+
+ case STM_STE_MODEM_ON_MICROSD:
+ /* Disable APE on micro SD */
+ retval = stm_disable_ape_microsd();
+ /* Enable modem on micro SD */
+ if (!retval)
+ retval = stm_enable_modem_microsd();
+ /* Enable SD card buffer and regulator on href */
+ if (!retval && (stm_current_connection
+ != STM_STE_APE_ON_MICROSD)) {
+ enable_level_shifter_for_microsd();
+ enable_vaux3_for_microsd_cable();
+ }
+ break;
+
+ case STM_STE_APE_ON_MICROSD:
+ /* Disable modem on micro SD */
+ retval = stm_disable_modem_microsd();
+ /* Enable ape on micro SD */
+ if (!retval)
+ retval = stm_enable_ape_microsd();
+ /* Enable SD card buffer and regulator on href */
+ if (!retval && (stm_current_connection
+ != STM_STE_MODEM_ON_MICROSD)) {
+ enable_level_shifter_for_microsd();
+ enable_vaux3_for_microsd_cable();
+ }
+ break;
+
+ case STM_DISCONNECT:
+ retval = nmk_config_pins_sleep(
+ ARRAY_AND_SIZE(mop500_stm_mipi34_pins));
+ if (retval)
+ STM_ERR("Failed to disable MIPI34\n");
+
+ retval = stm_ste_disable_ape_on_mipi60();
+ if (retval)
+ STM_ERR("Failed to disable MIPI60\n");
+
+ retval = stm_disable_modem_microsd();
+ if (retval)
+ STM_ERR("Failed to disable modem on microsd\n");
+
+ retval = stm_disable_ape_microsd();
+ if (retval)
+ STM_ERR("Failed to disable ape on microsd\n");
+ break;
+
+ default:
+ STM_ERR("Bad connection type\n");
+ goto stm_ste_connection_error;
+ }
+
+ /* Disable power for microsd */
+ if ((stm_current_connection == STM_STE_MODEM_ON_MICROSD)
+ || (stm_current_connection == STM_STE_APE_ON_MICROSD)) {
+ if ((con_type != STM_STE_MODEM_ON_MICROSD)
+ && (con_type != STM_STE_APE_ON_MICROSD)) {
+ disable_vaux3_for_microsd_cable();
+ disable_level_shifter_for_microsd();
+ }
+ }
+
+ stm_current_connection = con_type;
+
+stm_ste_connection_error:
+ return retval;
+}
+
+/* Possible STM sources (masters) on ux500 */
+enum stm_master {
+ STM_ARM0 = 0,
+ STM_ARM1 = 1,
+ STM_SVA = 2,
+ STM_SIA = 3,
+ STM_SIA_XP70 = 4,
+ STM_PRCMU = 5,
+ STM_MCSBAG = 9
+};
+
+#define STM_ENABLE_ARM0 BIT(STM_ARM0)
+#define STM_ENABLE_ARM1 BIT(STM_ARM1)
+#define STM_ENABLE_SVA BIT(STM_SVA)
+#define STM_ENABLE_SIA BIT(STM_SIA)
+#define STM_ENABLE_SIA_XP70 BIT(STM_SIA_XP70)
+#define STM_ENABLE_PRCMU BIT(STM_PRCMU)
+#define STM_ENABLE_MCSBAG BIT(STM_MCSBAG)
+
+/*
+ * These are the channels used by NMF and some external softwares
+ * expect the NMF traces to be output on these channels
+ * For legacy reason, we need to reserve them.
+ */
+static const s16 stm_channels_reserved[] = {
+ 100, /* NMF MPCEE channel */
+ 101, /* NMF CM channel */
+ 151, /* NMF HOSTEE channel */
+};
+
+/* On Ux500 we 2 consecutive STMs therefore 512 channels available */
+static struct stm_platform_data stm_pdata = {
+ .regs_phys_base = U8500_STM_REG_BASE,
+ .channels_phys_base = U8500_STM_BASE,
+ .id_mask = 0x000fffff, /* Ignore revisions differences */
+ .channels_reserved = stm_channels_reserved,
+ .channels_reserved_sz = ARRAY_SIZE(stm_channels_reserved),
+ /* Enable all except MCSBAG */
+ .masters_enabled = STM_ENABLE_ARM0 | STM_ENABLE_ARM1 |
+ STM_ENABLE_SVA | STM_ENABLE_PRCMU |
+ STM_ENABLE_SIA | STM_ENABLE_SIA_XP70,
+ /* Provide function for MIPI34/MIPI60 STM connection */
+ .stm_connection = stm_ste_connection,
+};
+
+struct platform_device u8500_stm_device = {
+ .name = "stm",
+ .id = -1,
+ .dev = {
+ .platform_data = &stm_pdata,
+ },
+};
diff --git a/arch/arm/mach-ux500/board-mop500-stuib.c b/arch/arm/mach-ux500/board-mop500-stuib.c
index e01bec73b6d..6256f3375c4 100644
--- a/arch/arm/mach-ux500/board-mop500-stuib.c
+++ b/arch/arm/mach-ux500/board-mop500-stuib.c
@@ -11,33 +11,70 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
+#ifdef CONFIG_U8500_FLASH
+#include <../drivers/staging/camera_flash/adp1653_plat.h>
+#endif
#include <linux/input/matrix_keypad.h>
#include <asm/mach-types.h>
#include "board-mop500.h"
-/* STMPE/SKE keypad use this key layout */
+/*
+ * ux500 keymaps
+ *
+ * Organized row-wise as on the UIB, starting at the top-left
+ *
+ * we support two key layouts, specific to requirements. The first
+ * keylayout includes controls for power/volume a few generic keys;
+ * the second key layout contains the full numeric layout, enter/back/left
+ * buttons along with a "."(dot), specifically for connectivity testing
+ */
static const unsigned int mop500_keymap[] = {
+#if defined(CONFIG_KEYLAYOUT_LAYOUT1)
KEY(2, 5, KEY_END),
- KEY(4, 1, KEY_POWER),
+ KEY(4, 1, KEY_HOME),
KEY(3, 5, KEY_VOLUMEDOWN),
- KEY(1, 3, KEY_3),
+ KEY(1, 3, KEY_EMAIL),
KEY(5, 2, KEY_RIGHT),
- KEY(5, 0, KEY_9),
+ KEY(5, 0, KEY_BACKSPACE),
KEY(0, 5, KEY_MENU),
KEY(7, 6, KEY_ENTER),
KEY(4, 5, KEY_0),
- KEY(6, 7, KEY_2),
+ KEY(6, 7, KEY_DOT),
KEY(3, 4, KEY_UP),
KEY(3, 3, KEY_DOWN),
KEY(6, 4, KEY_SEND),
KEY(6, 2, KEY_BACK),
KEY(4, 2, KEY_VOLUMEUP),
- KEY(5, 5, KEY_1),
+ KEY(5, 5, KEY_SPACE),
KEY(4, 3, KEY_LEFT),
+ KEY(3, 2, KEY_SEARCH),
+#elif defined(CONFIG_KEYLAYOUT_LAYOUT2)
+ KEY(2, 5, KEY_RIGHT),
+ KEY(4, 1, KEY_ENTER),
+ KEY(3, 5, KEY_MENU),
+ KEY(1, 3, KEY_3),
+ KEY(5, 2, KEY_6),
+ KEY(5, 0, KEY_9),
+
+ KEY(0, 5, KEY_UP),
+ KEY(7, 6, KEY_DOWN),
+ KEY(4, 5, KEY_0),
+ KEY(6, 7, KEY_2),
+ KEY(3, 4, KEY_5),
+ KEY(3, 3, KEY_8),
+
+ KEY(6, 4, KEY_LEFT),
+ KEY(6, 2, KEY_BACK),
+ KEY(4, 2, KEY_KPDOT),
+ KEY(5, 5, KEY_1),
+ KEY(4, 3, KEY_4),
KEY(3, 2, KEY_7),
+#else
+#warning "No keypad layout defined."
+#endif
};
static const struct matrix_keymap_data mop500_keymap_data = {
@@ -73,6 +110,24 @@ static struct i2c_board_info __initdata mop500_i2c0_devices_stuib[] = {
},
};
+#ifdef CONFIG_U8500_FLASH
+/*
+ * Config data for the flash
+ */
+static struct adp1653_platform_data __initdata adp1653_pdata_u8500_uib = {
+ .irq_no = CAMERA_FLASH_INT_PIN
+};
+#endif
+
+static struct i2c_board_info __initdata mop500_i2c2_devices_stuib[] = {
+#ifdef CONFIG_U8500_FLASH
+ {
+ I2C_BOARD_INFO("adp1653", 0x30),
+ .platform_data = &adp1653_pdata_u8500_uib
+ }
+#endif
+};
+
/*
* BU21013 ROHM touchscreen interface on the STUIBs
*/
@@ -111,6 +166,7 @@ static int bu21013_gpio_board_init(int reset_pin)
__func__);
return retval;
}
+ gpio_set_value_cansleep(reset_pin, 1);
}
return retval;
@@ -133,7 +189,8 @@ static int bu21013_gpio_board_exit(int reset_pin)
__func__);
return retval;
}
- gpio_set_value(reset_pin, 0);
+ gpio_set_value_cansleep(reset_pin, 0);
+ gpio_free(reset_pin);
}
bu21013_devices--;
@@ -196,11 +253,11 @@ static struct bu21013_platform_device tsc_plat2_device = {
static struct i2c_board_info __initdata u8500_i2c3_devices_stuib[] = {
{
- I2C_BOARD_INFO("bu21013_tp", 0x5C),
+ I2C_BOARD_INFO("bu21013_ts", 0x5C),
.platform_data = &tsc_plat_device,
},
{
- I2C_BOARD_INFO("bu21013_tp", 0x5D),
+ I2C_BOARD_INFO("bu21013_ts", 0x5D),
.platform_data = &tsc_plat2_device,
},
@@ -208,18 +265,28 @@ static struct i2c_board_info __initdata u8500_i2c3_devices_stuib[] = {
void __init mop500_stuib_init(void)
{
- if (machine_is_hrefv60()) {
+ if (machine_is_hrefv60() || machine_is_u8520()) {
tsc_plat_device.cs_pin = HREFV60_TOUCH_RST_GPIO;
tsc_plat2_device.cs_pin = HREFV60_TOUCH_RST_GPIO;
+#ifdef CONFIG_U8500_FLASH
+ adp1653_pdata_u8500_uib.enable_gpio =
+ HREFV60_CAMERA_FLASH_ENABLE;
+#endif
} else {
tsc_plat_device.cs_pin = GPIO_BU21013_CS;
tsc_plat2_device.cs_pin = GPIO_BU21013_CS;
-
+#ifdef CONFIG_U8500_FLASH
+ adp1653_pdata_u8500_uib.enable_gpio =
+ GPIO_CAMERA_FLASH_ENABLE;
+#endif
}
mop500_uib_i2c_add(0, mop500_i2c0_devices_stuib,
ARRAY_SIZE(mop500_i2c0_devices_stuib));
+ mop500_uib_i2c_add(2, mop500_i2c2_devices_stuib,
+ ARRAY_SIZE(mop500_i2c2_devices_stuib));
+
mop500_uib_i2c_add(3, u8500_i2c3_devices_stuib,
ARRAY_SIZE(u8500_i2c3_devices_stuib));
}
diff --git a/arch/arm/mach-ux500/board-mop500-u8500uib.c b/arch/arm/mach-ux500/board-mop500-u8500uib.c
index ead91c968ff..0b77ee29982 100644
--- a/arch/arm/mach-ux500/board-mop500-u8500uib.c
+++ b/arch/arm/mach-ux500/board-mop500-u8500uib.c
@@ -8,10 +8,21 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#ifdef CONFIG_U8500_FLASH
+#include <../drivers/staging/camera_flash/adp1653_plat.h>
+#endif
#include <linux/interrupt.h>
+#ifdef CONFIG_SENSORS_LSM303DLH
+#include <linux/lsm303dlh.h>
+#endif
+#ifdef CONFIG_SENSORS_L3G4200D
+#include <linux/l3g4200d.h>
+#endif
#include <linux/mfd/tc3589x.h>
#include <linux/input/matrix_keypad.h>
+#include <asm/mach-types.h>
+#include <linux/gpio.h>
#include <mach/irqs.h>
#include "board-mop500.h"
@@ -20,12 +31,28 @@
struct i2c_board_info __initdata __weak mop500_i2c3_devices_u8500[] = {
};
+#ifdef CONFIG_U8500_FLASH
+static struct adp1653_platform_data __initdata adp1653_pdata_u8500_uib = {
+ .irq_no = CAMERA_FLASH_INT_PIN
+};
+#endif
+
+static struct i2c_board_info __initdata mop500_i2c2_devices_u8500[] = {
+#ifdef CONFIG_U8500_FLASH
+ {
+ I2C_BOARD_INFO("adp1653", 0x30),
+ .platform_data = &adp1653_pdata_u8500_uib
+ }
+#endif
+};
+
+
/*
* TC35893
*/
static const unsigned int u8500_keymap[] = {
KEY(3, 1, KEY_END),
- KEY(4, 1, KEY_POWER),
+ KEY(4, 1, KEY_HOME),
KEY(6, 4, KEY_VOLUMEDOWN),
KEY(4, 2, KEY_EMAIL),
KEY(3, 3, KEY_RIGHT),
@@ -86,4 +113,15 @@ void __init mop500_u8500uib_init(void)
mop500_uib_i2c_add(0, mop500_i2c0_devices_u8500,
ARRAY_SIZE(mop500_i2c0_devices_u8500));
+#ifdef CONFIG_U8500_FLASH
+ if (machine_is_hrefv60() || machine_is_u8520())
+ adp1653_pdata_u8500_uib.enable_gpio =
+ HREFV60_CAMERA_FLASH_ENABLE;
+ else
+ adp1653_pdata_u8500_uib.enable_gpio =
+ GPIO_CAMERA_FLASH_ENABLE;
+#endif
+
+ mop500_uib_i2c_add(2, mop500_i2c2_devices_u8500,
+ ARRAY_SIZE(mop500_i2c2_devices_u8500));
}
diff --git a/arch/arm/mach-ux500/board-mop500-uib.c b/arch/arm/mach-ux500/board-mop500-uib.c
index 5af36aa56c0..908c5d973da 100644
--- a/arch/arm/mach-ux500/board-mop500-uib.c
+++ b/arch/arm/mach-ux500/board-mop500-uib.c
@@ -1,4 +1,5 @@
/*
+
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
@@ -10,13 +11,22 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+#include <linux/regulator/consumer.h>
#include <mach/hardware.h>
+#include <asm/mach-types.h>
+
+#include "pins.h"
#include "board-mop500.h"
enum mop500_uib {
STUIB,
U8500UIB,
+ U8500UIB_R3,
+ NO_UIB,
};
struct uib {
@@ -25,6 +35,8 @@ struct uib {
void (*init)(void);
};
+static u8 type_of_uib = NO_UIB;
+
static struct uib __initdata mop500_uibs[] = {
[STUIB] = {
.name = "ST-UIB",
@@ -36,9 +48,16 @@ static struct uib __initdata mop500_uibs[] = {
.option = "u8500uib",
.init = mop500_u8500uib_init,
},
+#ifdef CONFIG_TOUCHSCREEN_CYTTSP_SPI
+ [U8500UIB_R3] = {
+ .name = "U8500-UIBR3",
+ .option = "u8500uibr3",
+ .init = mop500_u8500uib_r3_init,
+ },
+#endif
};
-static struct uib *mop500_uib;
+static struct uib __initdata *mop500_uib;
static int __init mop500_uib_setup(char *str)
{
@@ -64,7 +83,7 @@ __setup("uib=", mop500_uib_setup);
* The UIBs are detected after the I2C host controllers are registered, so
* i2c_register_board_info() can't be used.
*/
-void mop500_uib_i2c_add(int busnum, struct i2c_board_info *info,
+void mop500_uib_i2c_add(int busnum, struct i2c_board_info const *info,
unsigned n)
{
struct i2c_adapter *adap;
@@ -90,26 +109,137 @@ void mop500_uib_i2c_add(int busnum, struct i2c_board_info *info,
static void __init __mop500_uib_init(struct uib *uib, const char *why)
{
pr_info("%s (%s)\n", uib->name, why);
+
+ if (strcmp("stuib", uib->option) == 0)
+ type_of_uib = STUIB;
+ else if (strcmp("u8500uib", uib->option) == 0)
+ type_of_uib = U8500UIB;
+ else if (strcmp("u8500uibr3", uib->option) == 0)
+ type_of_uib = U8500UIB_R3;
+
uib->init();
}
+int uib_is_stuib(void)
+{
+ return (type_of_uib == STUIB);
+}
+
+int uib_is_u8500uib(void)
+{
+ return (type_of_uib == U8500UIB);
+}
+
+int uib_is_u8500uibr3(void)
+{
+ return (type_of_uib == U8500UIB_R3);
+}
+
+
+#ifdef CONFIG_UX500_GPIO_KEYS
+static struct gpio_keys_button mop500_gpio_keys[] = {
+ {
+ .desc = "SFH7741 Proximity Sensor",
+ .type = EV_SW,
+ .code = SW_FRONT_PROXIMITY,
+ .active_low = 0,
+ .can_disable = 1,
+ },
+ {
+ .desc = "HED54XXU11 Hall Effect Sensor",
+ .type = EV_SW,
+ .code = SW_LID, /* FIXME arbitrary usage */
+ .active_low = 0,
+ .can_disable = 1,
+ }
+};
+
+static struct regulator *gpio_keys_regulator;
+static int mop500_gpio_keys_activate(struct device *dev);
+static void mop500_gpio_keys_deactivate(struct device *dev);
+
+static struct gpio_keys_platform_data mop500_gpio_keys_data = {
+ .buttons = mop500_gpio_keys,
+ .nbuttons = ARRAY_SIZE(mop500_gpio_keys),
+ .enable = mop500_gpio_keys_activate,
+ .disable = mop500_gpio_keys_deactivate,
+};
+
+static struct platform_device mop500_gpio_keys_device = {
+ .name = "gpio-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &mop500_gpio_keys_data,
+ },
+};
+
+static int mop500_gpio_keys_activate(struct device *dev)
+{
+ gpio_keys_regulator = regulator_get(&mop500_gpio_keys_device.dev,
+ "vcc");
+ if (IS_ERR(gpio_keys_regulator)) {
+ dev_err(&mop500_gpio_keys_device.dev, "no regulator\n");
+ return PTR_ERR(gpio_keys_regulator);
+ }
+ regulator_enable(gpio_keys_regulator);
+
+ /*
+ * Please be aware that the start-up time of the SFH7741 is
+ * 120 ms and during that time the output is undefined.
+ */
+
+ return 0;
+}
+
+static void mop500_gpio_keys_deactivate(struct device *dev)
+{
+ if (!IS_ERR(gpio_keys_regulator)) {
+ regulator_disable(gpio_keys_regulator);
+ regulator_put(gpio_keys_regulator);
+ }
+}
+
+static __init void mop500_gpio_keys_init(void)
+{
+ struct ux500_pins *gpio_keys_pins = ux500_pins_get("gpio-keys.0");
+
+ if (gpio_keys_pins == NULL) {
+ pr_err("gpio_keys: Fail to get pins\n");
+ return;
+ }
+
+ ux500_pins_enable(gpio_keys_pins);
+ if (type_of_uib == U8500UIB_R3)
+ mop500_gpio_keys[0].gpio = PIN_NUM(gpio_keys_pins->cfg[2]);
+ else
+ mop500_gpio_keys[0].gpio = PIN_NUM(gpio_keys_pins->cfg[0]);
+ mop500_gpio_keys[1].gpio = PIN_NUM(gpio_keys_pins->cfg[1]);
+}
+#else
+static inline void mop500_gpio_keys_init(void) { }
+#endif
+
+/* add any platform devices here - TODO */
+static struct platform_device *mop500_uib_platform_devs[] __initdata = {
+#ifdef CONFIG_UX500_GPIO_KEYS
+ &mop500_gpio_keys_device,
+#endif
+};
+
/*
* Detect the UIB attached based on the presence or absence of i2c devices.
*/
static int __init mop500_uib_init(void)
{
- struct uib *uib = mop500_uib;
+ struct uib *uib = mop500_uibs;
struct i2c_adapter *i2c0;
+ struct i2c_adapter *i2c3;
int ret;
- if (!cpu_is_u8500())
+ /* snowball and non u8500 cpus dont have uib */
+ if (!cpu_is_u8500() || machine_is_snowball())
return -ENODEV;
- if (uib) {
- __mop500_uib_init(uib, "from uib= boot argument");
- return 0;
- }
-
i2c0 = i2c_get_adapter(0);
if (!i2c0) {
__mop500_uib_init(&mop500_uibs[STUIB],
@@ -121,14 +251,32 @@ static int __init mop500_uib_init(void)
ret = i2c_smbus_xfer(i2c0, 0x44, 0, I2C_SMBUS_WRITE, 0,
I2C_SMBUS_QUICK, NULL);
i2c_put_adapter(i2c0);
+ i2c3 = i2c_get_adapter(3);
+ if (!i2c3) {
+ __mop500_uib_init(&mop500_uibs[STUIB],
+ "fallback, could not get i2c3");
+ return -ENODEV;
+ }
- if (ret == 0)
- uib = &mop500_uibs[U8500UIB];
- else
- uib = &mop500_uibs[STUIB];
-
+ if (ret == 0) {
+ ret = i2c_smbus_xfer(i2c3, 0x4B, 0, I2C_SMBUS_WRITE, 0,
+ I2C_SMBUS_QUICK, NULL);
+ i2c_put_adapter(i2c3);
+ if (ret == 0)
+ uib = &mop500_uibs[U8500UIB];
+ else
+ uib = &mop500_uibs[U8500UIB_R3];
+ } else {
+ ret = i2c_smbus_xfer(i2c3, 0x5C, 0, I2C_SMBUS_WRITE, 0,
+ I2C_SMBUS_QUICK, NULL);
+ i2c_put_adapter(i2c3);
+ if (ret == 0)
+ uib = &mop500_uibs[STUIB];
+ }
__mop500_uib_init(uib, "detected");
-
+ mop500_gpio_keys_init();
+ platform_add_devices(mop500_uib_platform_devs,
+ ARRAY_SIZE(mop500_uib_platform_devs));
return 0;
}
diff --git a/arch/arm/mach-ux500/board-mop500-vibra.c b/arch/arm/mach-ux500/board-mop500-vibra.c
new file mode 100644
index 00000000000..911ecf26b5a
--- /dev/null
+++ b/arch/arm/mach-ux500/board-mop500-vibra.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 ST-Ericsson SA
+ *
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <asm/mach-types.h>
+
+#include <linux/ste_timed_vibra.h>
+#include <sound/ux500_ab8500_ext.h>
+
+/* For details check ste_timed_vibra docbook */
+static struct ste_timed_vibra_platform_data rotary_vibra_plat_data = {
+ .is_linear_vibra = false,
+ .boost_level = 100,
+ .boost_time = 60,
+ .on_level = 50,
+ .off_level = 50,
+ .off_time = 60,
+ .timed_vibra_control = ux500_ab8500_audio_pwm_vibra,
+};
+
+static struct ste_timed_vibra_platform_data linear_vibra_plat_data = {
+ .is_linear_vibra = true,
+ .boost_level = 80,
+ .boost_time = 40,
+ .on_level = 80,
+ .off_level = 80,
+ .off_time = 0,
+ .timed_vibra_control = ux500_ab8500_audio_pwm_vibra,
+};
+
+/* Timed output vibrator device */
+static struct platform_device ux500_vibra_device = {
+ .name = "ste_timed_output_vibra",
+};
+
+void __init mop500_vibra_init(void)
+{
+ int ret;
+
+ if (machine_is_hrefv60() || machine_is_u8520() || machine_is_u9540())
+ ux500_vibra_device.dev.platform_data = &linear_vibra_plat_data;
+ else
+ ux500_vibra_device.dev.platform_data = &rotary_vibra_plat_data;
+
+ ret = platform_device_register(&ux500_vibra_device);
+ if (ret < 0)
+ pr_err("vibra dev register failed");
+}
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 77d03c1fbd0..ea703097be6 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -15,25 +15,41 @@
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl022.h>
#include <linux/amba/serial.h>
#include <linux/spi/spi.h>
+#ifdef CONFIG_HSI
+#include <linux/hsi/hsi.h>
+#endif
#include <linux/mfd/abx500/ab8500.h>
#include <linux/regulator/ab8500.h>
#include <linux/mfd/tc3589x.h>
-#include <linux/mfd/tps6105x.h>
#include <linux/mfd/abx500/ab8500-gpio.h>
+#include <linux/regulator/fixed.h>
#include <linux/leds-lp5521.h>
#include <linux/input.h>
#include <linux/smsc911x.h>
#include <linux/gpio_keys.h>
#include <linux/delay.h>
+#include <linux/mfd/ab8500/denc.h>
+#ifdef CONFIG_STM_MSP_SPI
+#include <linux/spi/stm_msp.h>
+#endif
+#include <linux/leds_pwm.h>
+#include <linux/pwm_backlight.h>
+#include <linux/gpio/nomadik.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/leds.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#ifdef CONFIG_INPUT_AB8500_ACCDET
+#include <linux/input/abx500-accdet.h>
+#endif
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/hardware/gic.h>
@@ -41,23 +57,52 @@
#include <plat/i2c.h>
#include <plat/ste_dma40.h>
#include <plat/pincfg.h>
-#include <plat/gpio-nomadik.h>
#include <mach/hardware.h>
#include <mach/setup.h>
#include <mach/devices.h>
#include <mach/irqs.h>
+#include <mach/ste-dma40-db8500.h>
+#ifdef CONFIG_U8500_SIM_DETECT
+#include <mach/sim_detect.h>
+#endif
+#ifdef CONFIG_CRYPTO_DEV_UX500
+#include <mach/crypto-ux500.h>
+#endif
+#include <mach/pm.h>
+#ifdef CONFIG_AV8100
+#include <video/av8100.h>
+#endif
+#ifdef CONFIG_KEYBOARD_NOMADIK_SKE
+#include <plat/ske.h>
+#include "pins.h"
+#endif
+
+#include "cpu-db8500.h"
#include "pins-db8500.h"
-#include "ste-dma40-db8500.h"
#include "devices-db8500.h"
#include "board-mop500.h"
#include "board-mop500-regulators.h"
+#include "board-mop500-bm.h"
+#if defined(CONFIG_CW1200) || defined(CONFIG_CW1200_MODULE)
+#include "board-mop500-wlan.h"
+#endif
+
+#define PRCM_DEBUG_NOPWRDOWN_VAL 0x194
+#define ARM_DEBUG_NOPOWER_DOWN_REQ 1
+
+#ifdef CONFIG_AB8500_DENC
+static struct ab8500_denc_platform_data ab8500_denc_pdata = {
+ .ddr_enable = true,
+ .ddr_little_endian = false,
+};
+#endif
static struct gpio_led snowball_led_array[] = {
{
.name = "user_led",
- .default_trigger = "none",
+ .default_trigger = "heartbeat",
.gpio = 142,
},
};
@@ -75,85 +120,156 @@ static struct platform_device snowball_led_dev = {
};
static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
- .gpio_base = MOP500_AB8500_PIN_GPIO(1),
+ .gpio_base = AB8500_PIN_GPIO1,
.irq_base = MOP500_AB8500_VIR_GPIO_IRQ_BASE,
- /* config_reg is the initial configuration of ab8500 pins.
+ /*
+ * config_reg is the initial configuration of ab8500 pins.
* The pins can be configured as GPIO or alt functions based
* on value present in GpioSel1 to GpioSel6 and AlternatFunction
* register. This is the array of 7 configuration settings.
* One has to compile time decide these settings. Below is the
* explanation of these setting
- * GpioSel1 = 0x00 => Pins GPIO1 to GPIO8 are not used as GPIO
- * GpioSel2 = 0x1E => Pins GPIO10 to GPIO13 are configured as GPIO
- * GpioSel3 = 0x80 => Pin GPIO24 is configured as GPIO
- * GpioSel4 = 0x01 => Pin GPIo25 is configured as GPIO
- * GpioSel5 = 0x7A => Pins GPIO34, GPIO36 to GPIO39 are conf as GPIO
- * GpioSel6 = 0x00 => Pins GPIO41 & GPIo42 are not configured as GPIO
+ * GpioSel1 = 0x0F => Pin GPIO1 (SysClkReq2)
+ * Pin GPIO2 (SysClkReq3)
+ * Pin GPIO3 (SysClkReq4)
+ * Pin GPIO4 (SysClkReq6) are configured as GPIO
+ * GpioSel2 = 0x9E => Pins GPIO10 to GPIO13 are configured as GPIO
+ * GpioSel3 = 0x80 => Pin GPIO24 (SysClkReq7) is configured as GPIO
+ * GpioSel4 = 0x01 => Pin GPIO25 (SysClkReq8) is configured as GPIO
+ * GpioSel5 = 0x78 => Pin GPIO36 (ApeSpiClk)
+ * Pin GPIO37 (ApeSpiCSn)
+ * Pin GPIO38 (ApeSpiDout)
+ * Pin GPIO39 (ApeSpiDin) are configured as GPIO
+ * GpioSel6 = 0x02 => Pin GPIO42 (SysClkReq5) is configured as GPIO
* AlternaFunction = 0x00 => If Pins GPIO10 to 13 are not configured
* as GPIO then this register selectes the alternate fucntions
*/
- .config_reg = {0x00, 0x1E, 0x80, 0x01,
- 0x7A, 0x00, 0x00},
+ .config_reg = {0x0F, 0x9E, 0x80, 0x01, 0x78, 0x02, 0x00},
+
+ /*
+ * config_direction allows for the initial GPIO direction to
+ * be set. For Snowball we set GPIO26 to output.
+ */
+ .config_direction = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00},
+
+ /*
+ * config_pullups allows for the intial configuration of the
+ * GPIO pullup/pulldown configuration.
+ */
+ .config_pullups = {0xE0, 0x01, 0x00, 0x00, 0x00, 0x00},
+};
+
+static struct ab8500_gpio_platform_data ab8505_gpio_pdata = {
+ .gpio_base = AB8500_PIN_GPIO1,
+ .irq_base = MOP500_AB8500_VIR_GPIO_IRQ_BASE,
+ /*
+ * config_reg is the initial configuration of ab8500 pins.
+ * The pins can be configured as GPIO or alt functions based
+ * on value present in GpioSel1 to GpioSel6 and AlternatFunction
+ * register. This is the array of 7 configuration settings.
+ * One has to compile time decide these settings. Below is the
+ * explanation of these setting
+ * GpioSel1 = 0x0F => Pin GPIO1 (SysClkReq2)
+ * Pin GPIO2 (SysClkReq3)
+ * Pin GPIO3 (SysClkReq4)
+ * Pin GPIO4 (SysClkReq6) are configured as GPIO
+ * GpioSel2 = 0x9E => Pins GPIO10 to GPIO13 are configured as GPIO
+ * GpioSel3 = 0x80 => Pin GPIO24 (SysClkReq7) is configured as GPIO
+ * GpioSel4 = 0x01 => Pin GPIO25 (SysClkReq8) is configured as GPIO
+ * GpioSel5 = 0x78 => Pin GPIO36 (ApeSpiClk)
+ * Pin GPIO37 (ApeSpiCSn)
+ * Pin GPIO38 (ApeSpiDout)
+ * Pin GPIO39 (ApeSpiDin) are configured as GPIO
+ * GpioSel6 = 0x02 => Pin GPIO42 (SysClkReq5) is configured as GPIO
+ * AlternaFunction = 0x00 => If Pins GPIO10 to 13 are not configured
+ * as GPIO then this register selectes the alternate functions
+ * GpioSel7 = 0x22 => Pins GPIO50 to GPIO52 are configured as GPIO.
+ */
+ .config_reg = {0x0F, 0x9E, 0x80, 0x01, 0x7A, 0x02, 0x22},
+
+ /*
+ * config_direction allows for the initial GPIO direction to
+ * be set. For Snowball we set GPIO26 to output.
+ */
+ .config_direction = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00},
+
+ /*
+ * config_pullups allows for the intial configuration of the
+ * GPIO pullup/pulldown configuration.
+ * GPIO13(GpioPud2) = 1 and GPIO50(GpioPud7) = 1.
+ */
+ .config_pullups = {0xE0, 0x11, 0x00, 0x00, 0x00, 0x00, 0x02},
+};
+
+static struct ab8500_sysctrl_platform_data ab8500_sysctrl_pdata = {
+ /*
+ * SysClkReq1RfClkBuf - SysClkReq8RfClkBuf
+ * The initial values should not be changed because of the way
+ * the system works today
+ */
+ .initial_req_buf_config
+ = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
};
+#ifdef CONFIG_INPUT_AB8500_ACCDET
+static struct abx500_accdet_platform_data ab8500_accdet_pdata = {
+ .btn_keycode = KEY_MEDIA,
+ .accdet1_dbth = ACCDET1_TH_1200mV | ACCDET1_DB_70ms,
+ .accdet2122_th = ACCDET21_TH_1000mV | ACCDET22_TH_1000mV,
+ .video_ctrl_gpio = AB8500_PIN_GPIO35,
+};
+#endif
+
static struct gpio_keys_button snowball_key_array[] = {
{
- .gpio = 32,
- .type = EV_KEY,
- .code = KEY_1,
- .desc = "userpb",
+ .gpio = 32,
+ .type = EV_KEY,
+ .code = KEY_1,
+ .desc = "userpb",
.active_low = 1,
.debounce_interval = 50,
- .wakeup = 1,
+ .wakeup = 1,
},
{
- .gpio = 151,
- .type = EV_KEY,
- .code = KEY_2,
- .desc = "extkb1",
+ .gpio = 151,
+ .type = EV_KEY,
+ .code = KEY_2,
+ .desc = "extkb1",
.active_low = 1,
.debounce_interval = 50,
- .wakeup = 1,
+ .wakeup = 1,
},
{
- .gpio = 152,
- .type = EV_KEY,
- .code = KEY_3,
- .desc = "extkb2",
+ .gpio = 152,
+ .type = EV_KEY,
+ .code = KEY_3,
+ .desc = "extkb2",
.active_low = 1,
.debounce_interval = 50,
- .wakeup = 1,
+ .wakeup = 1,
},
{
- .gpio = 161,
- .type = EV_KEY,
- .code = KEY_4,
- .desc = "extkb3",
+ .gpio = 162,
+ .type = EV_KEY,
+ .code = KEY_5,
+ .desc = "extkb4",
.active_low = 1,
.debounce_interval = 50,
- .wakeup = 1,
- },
- {
- .gpio = 162,
- .type = EV_KEY,
- .code = KEY_5,
- .desc = "extkb4",
- .active_low = 1,
- .debounce_interval = 50,
- .wakeup = 1,
+ .wakeup = 1,
},
};
static struct gpio_keys_platform_data snowball_key_data = {
- .buttons = snowball_key_array,
+ .buttons = snowball_key_array,
.nbuttons = ARRAY_SIZE(snowball_key_array),
};
static struct platform_device snowball_key_dev = {
- .name = "gpio-keys",
- .id = -1,
- .dev = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
.platform_data = &snowball_key_data,
+ .pm_domain = &ux500_dev_power_domain,
}
};
@@ -179,21 +295,45 @@ static struct resource sbnet_res[] = {
};
static struct platform_device snowball_sbnet_dev = {
- .name = "smsc911x",
+ .name = "smsc911x",
.num_resources = ARRAY_SIZE(sbnet_res),
.resource = sbnet_res,
- .dev = {
+ .dev = {
.platform_data = &snowball_sbnet_cfg,
},
};
+#ifdef CONFIG_MODEM_U8500
+static struct platform_device u8500_modem_dev = {
+ .name = "u8500-modem",
+ .id = 0,
+ .dev = {
+ .platform_data = NULL,
+ },
+};
+#endif
+
static struct ab8500_platform_data ab8500_platdata = {
.irq_base = MOP500_AB8500_IRQ_BASE,
- .regulator_reg_init = ab8500_regulator_reg_init,
- .num_regulator_reg_init = ARRAY_SIZE(ab8500_regulator_reg_init),
- .regulator = ab8500_regulators,
- .num_regulator = ARRAY_SIZE(ab8500_regulators),
+ .regulator = &ab8500_regulator_plat_data,
+#ifdef CONFIG_AB8500_DENC
+ .denc = &ab8500_denc_pdata,
+#endif
+ .battery = &ab8500_bm_data,
+ .charger = &ab8500_charger_plat_data,
+ .btemp = &ab8500_btemp_plat_data,
+ .fg = &ab8500_fg_plat_data,
+ .chargalg = &ab8500_chargalg_plat_data,
.gpio = &ab8500_gpio_pdata,
+ .sysctrl = &ab8500_sysctrl_pdata,
+ .pwmled = &ab8500_pwmled_plat_data,
+#ifdef CONFIG_INPUT_AB8500_ACCDET
+ .accdet = &ab8500_accdet_pdata,
+#endif
+#ifdef CONFIG_PM
+ .pm_power_off = true,
+#endif
+ .thermal_time_out = 20, /* seconds */
};
static struct resource ab8500_resources[] = {
@@ -214,14 +354,203 @@ struct platform_device ab8500_device = {
.resource = ab8500_resources,
};
+struct platform_device ab8505_device = {
+ .name = "ab8505-i2c",
+ .id = 0,
+ .dev = {
+ .platform_data = &ab8500_platdata,
+ },
+ .num_resources = 1,
+ .resource = ab8500_resources,
+};
+
+#ifdef CONFIG_KEYBOARD_NOMADIK_SKE
+
+/*
+ * Nomadik SKE keypad
+ */
+#define ROW_PIN_I0 164
+#define ROW_PIN_I1 163
+#define ROW_PIN_I2 162
+#define ROW_PIN_I3 161
+#define ROW_PIN_I4 156
+#define ROW_PIN_I5 155
+#define ROW_PIN_I6 154
+#define ROW_PIN_I7 153
+#define COL_PIN_O0 168
+#define COL_PIN_O1 167
+#define COL_PIN_O2 166
+#define COL_PIN_O3 165
+#define COL_PIN_O4 160
+#define COL_PIN_O5 159
+#define COL_PIN_O6 158
+#define COL_PIN_O7 157
+
+static int ske_kp_rows[] = {
+ ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3,
+ ROW_PIN_I4, ROW_PIN_I5, ROW_PIN_I6, ROW_PIN_I7,
+};
+static int ske_kp_cols[] = {
+ COL_PIN_O0, COL_PIN_O1, COL_PIN_O2, COL_PIN_O3,
+ COL_PIN_O4, COL_PIN_O5, COL_PIN_O6, COL_PIN_O7,
+};
+
/*
- * TPS61052
+ * ske_set_gpio_row: request and set gpio rows
*/
+static int ske_set_gpio_row(int gpio)
+{
+ int ret;
+ ret = gpio_request(gpio, "ske-kp");
+ if (ret < 0) {
+ pr_err("ske_set_gpio_row: gpio request failed\n");
+ return ret;
+ }
+
+ ret = gpio_direction_output(gpio, 1);
+ if (ret < 0) {
+ pr_err("ske_set_gpio_row: gpio direction failed\n");
+ gpio_free(gpio);
+ }
-static struct tps6105x_platform_data mop500_tps61052_data = {
- .mode = TPS6105X_MODE_VOLTAGE,
- .regulator_data = &tps61052_regulator,
+ return ret;
+}
+
+/*
+ * ske_kp_init - enable the gpio configuration
+ */
+static int ske_kp_init(void)
+{
+ struct ux500_pins *pins;
+ int ret, i;
+
+ pins = ux500_pins_get("ske");
+ if (pins)
+ ux500_pins_enable(pins);
+
+ for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
+ ret = ske_set_gpio_row(ske_kp_rows[i]);
+ if (ret < 0) {
+ pr_err("ske_kp_init: failed init\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ske_kp_exit(void)
+{
+ struct ux500_pins *pins;
+ int i;
+
+ pins = ux500_pins_get("ske");
+ if (pins)
+ ux500_pins_disable(pins);
+
+ for (i = 0; i < SKE_KPD_MAX_ROWS; i++)
+ gpio_free(ske_kp_rows[i]);
+
+ return 0;
+}
+
+static const unsigned int mop500_ske_keymap[] = {
+#if defined(CONFIG_KEYLAYOUT_LAYOUT1)
+ KEY(2, 5, KEY_END),
+ KEY(4, 1, KEY_HOME),
+ KEY(3, 5, KEY_VOLUMEDOWN),
+ KEY(1, 3, KEY_EMAIL),
+ KEY(5, 2, KEY_RIGHT),
+ KEY(5, 0, KEY_BACKSPACE),
+
+ KEY(0, 5, KEY_MENU),
+ KEY(7, 6, KEY_ENTER),
+ KEY(4, 5, KEY_0),
+ KEY(6, 7, KEY_DOT),
+ KEY(3, 4, KEY_UP),
+ KEY(3, 3, KEY_DOWN),
+
+ KEY(6, 4, KEY_SEND),
+ KEY(6, 2, KEY_BACK),
+ KEY(4, 2, KEY_VOLUMEUP),
+ KEY(5, 5, KEY_SPACE),
+ KEY(4, 3, KEY_LEFT),
+ KEY(3, 2, KEY_SEARCH),
+#elif defined(CONFIG_KEYLAYOUT_LAYOUT2)
+ KEY(2, 5, KEY_RIGHT),
+ KEY(4, 1, KEY_ENTER),
+ KEY(3, 5, KEY_MENU),
+ KEY(1, 3, KEY_3),
+ KEY(5, 2, KEY_6),
+ KEY(5, 0, KEY_9),
+
+ KEY(0, 5, KEY_UP),
+ KEY(7, 6, KEY_DOWN),
+ KEY(4, 5, KEY_0),
+ KEY(6, 7, KEY_2),
+ KEY(3, 4, KEY_5),
+ KEY(3, 3, KEY_8),
+
+ KEY(6, 4, KEY_LEFT),
+ KEY(6, 2, KEY_BACK),
+ KEY(4, 2, KEY_KPDOT),
+ KEY(5, 5, KEY_1),
+ KEY(4, 3, KEY_4),
+ KEY(3, 2, KEY_7),
+#else
+#warning "No keypad layout defined."
+#endif
+};
+
+static struct matrix_keymap_data mop500_ske_keymap_data = {
+ .keymap = mop500_ske_keymap,
+ .keymap_size = ARRAY_SIZE(mop500_ske_keymap),
+};
+
+
+
+static struct ske_keypad_platform_data mop500_ske_keypad_data = {
+ .init = ske_kp_init,
+ .exit = ske_kp_exit,
+ .gpio_input_pins = ske_kp_rows,
+ .gpio_output_pins = ske_kp_cols,
+ .keymap_data = &mop500_ske_keymap_data,
+ .no_autorepeat = true,
+ .krow = SKE_KPD_MAX_ROWS, /* 8x8 matrix */
+ .kcol = SKE_KPD_MAX_COLS,
+ .debounce_ms = 20, /* in timeout period */
+ .switch_delay = 200, /* in jiffies */
+};
+
+#endif
+
+
+#ifdef CONFIG_REGULATOR_FIXED_VOLTAGE
+/*
+ * GPIO-regulator wlan vbat data
+ */
+static struct fixed_voltage_config snowball_gpio_wlan_vbat_data = {
+ .supply_name = "WLAN-VBAT",
+ .gpio = SNOWBALL_EN_3V6_GPIO,
+ .microvolts = 3600000,
+ .enable_high = 1,
+ .init_data = &gpio_wlan_vbat_regulator,
+ .startup_delay = 3500, /* Startup time */
+};
+
+/*
+ * GPIO-regulator en 3v3 vbat data
+ */
+
+static struct fixed_voltage_config snowball_gpio_en_3v3_data = {
+ .supply_name = "EN-3V3",
+ .gpio = SNOWBALL_EN_3V3_ETH_GPIO,
+ .microvolts = 3300000,
+ .enable_high = 1,
+ .init_data = &gpio_en_3v3_regulator,
+ .startup_delay = 5000, /* 1200us according to data sheet */
};
+#endif
/*
* TC35892
@@ -249,53 +578,64 @@ static struct tc3589x_platform_data mop500_tc35892_data = {
};
static struct lp5521_led_config lp5521_pri_led[] = {
- [0] = {
- .chan_nr = 0,
- .led_current = 0x2f,
- .max_current = 0x5f,
- },
- [1] = {
- .chan_nr = 1,
- .led_current = 0x2f,
- .max_current = 0x5f,
- },
- [2] = {
- .chan_nr = 2,
- .led_current = 0x2f,
- .max_current = 0x5f,
- },
+ [0] = {
+ .chan_nr = 0,
+ .led_current = 0x2f,
+ .max_current = 0x5f,
+ },
+ [1] = {
+ .chan_nr = 1,
+ .led_current = 0x2f,
+ .max_current = 0x5f,
+ },
+ [2] = {
+ .chan_nr = 2,
+ .led_current = 0x2f,
+ .max_current = 0x5f,
+ },
};
+#ifdef CONFIG_AV8100
+static struct av8100_platform_data av8100_plat_data = {
+ .irq = NOMADIK_GPIO_TO_IRQ(192),
+ .reset = MOP500_HDMI_RST_GPIO,
+ .inputclk_id = "sysclk2",
+ .regulator_pwr_id = "hdmi_1v8",
+ .alt_powerupseq = true,
+ .mclk_freq = 3, /* MCLK_RNG_31_38 */
+};
+#endif
+
static struct lp5521_platform_data __initdata lp5521_pri_data = {
- .label = "lp5521_pri",
- .led_config = &lp5521_pri_led[0],
- .num_channels = 3,
- .clock_mode = LP5521_CLOCK_EXT,
+ .label = "lp5521_pri",
+ .led_config = &lp5521_pri_led[0],
+ .num_channels = 3,
+ .clock_mode = LP5521_CLOCK_EXT,
};
static struct lp5521_led_config lp5521_sec_led[] = {
- [0] = {
- .chan_nr = 0,
- .led_current = 0x2f,
- .max_current = 0x5f,
- },
- [1] = {
- .chan_nr = 1,
- .led_current = 0x2f,
- .max_current = 0x5f,
- },
- [2] = {
- .chan_nr = 2,
- .led_current = 0x2f,
- .max_current = 0x5f,
- },
+ [0] = {
+ .chan_nr = 0,
+ .led_current = 0x2f,
+ .max_current = 0x5f,
+ },
+ [1] = {
+ .chan_nr = 1,
+ .led_current = 0x2f,
+ .max_current = 0x5f,
+ },
+ [2] = {
+ .chan_nr = 2,
+ .led_current = 0x2f,
+ .max_current = 0x5f,
+ },
};
static struct lp5521_platform_data __initdata lp5521_sec_data = {
- .label = "lp5521_sec",
- .led_config = &lp5521_sec_led[0],
- .num_channels = 3,
- .clock_mode = LP5521_CLOCK_EXT,
+ .label = "lp5521_sec",
+ .led_config = &lp5521_sec_led[0],
+ .num_channels = 3,
+ .clock_mode = LP5521_CLOCK_EXT,
};
static struct i2c_board_info __initdata mop500_i2c0_devices[] = {
@@ -304,15 +644,26 @@ static struct i2c_board_info __initdata mop500_i2c0_devices[] = {
.irq = NOMADIK_GPIO_TO_IRQ(217),
.platform_data = &mop500_tc35892_data,
},
- /* I2C0 devices only available prior to HREFv60 */
+#ifdef CONFIG_AV8100
{
- I2C_BOARD_INFO("tps61052", 0x33),
- .platform_data = &mop500_tps61052_data,
+ I2C_BOARD_INFO("av8100", 0x70),
+ .platform_data = &av8100_plat_data,
},
+#endif
+ /* I2C0 devices only available prior to HREFv60 */
};
#define NUM_PRE_V60_I2C0_DEVICES 1
+static struct i2c_board_info __initdata snowball_i2c0_devices[] = {
+#ifdef CONFIG_AV8100
+ {
+ I2C_BOARD_INFO("av8100", 0x70),
+ .platform_data = &av8100_plat_data,
+ },
+#endif
+};
+
static struct i2c_board_info __initdata mop500_i2c2_devices[] = {
{
/* lp5521 LED driver, 1st device */
@@ -353,13 +704,13 @@ static struct nmk_i2c_controller u8500_i2c##id##_data = { \
/*
* The board uses 4 i2c controllers, initialize all of
* them with slave data setup time of 250 ns,
- * Tx & Rx FIFO threshold values as 8 and standard
+ * Tx & Rx FIFO threshold values as 1 and standard
* mode of operation
*/
-U8500_I2C_CONTROLLER(0, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
-U8500_I2C_CONTROLLER(1, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
-U8500_I2C_CONTROLLER(2, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
-U8500_I2C_CONTROLLER(3, 0xe, 1, 8, 100000, 200, I2C_FREQ_MODE_FAST);
+U8500_I2C_CONTROLLER(0, 0xe, 1, 8, 400000, 200, I2C_FREQ_MODE_FAST);
+U8500_I2C_CONTROLLER(1, 0xe, 1, 8, 400000, 200, I2C_FREQ_MODE_FAST);
+U8500_I2C_CONTROLLER(2, 0xe, 1, 8, 400000, 200, I2C_FREQ_MODE_FAST);
+U8500_I2C_CONTROLLER(3, 0xe, 1, 8, 400000, 200, I2C_FREQ_MODE_FAST);
static void __init mop500_i2c_init(struct device *parent)
{
@@ -369,60 +720,267 @@ static void __init mop500_i2c_init(struct device *parent)
db8500_add_i2c3(parent, &u8500_i2c3_data);
}
-static struct gpio_keys_button mop500_gpio_keys[] = {
- {
- .desc = "SFH7741 Proximity Sensor",
- .type = EV_SW,
- .code = SW_FRONT_PROXIMITY,
- .active_low = 0,
- .can_disable = 1,
- }
+#ifdef CONFIG_REGULATOR_FIXED_VOLTAGE
+static struct platform_device snowball_gpio_wlan_vbat_regulator_device = {
+ .name = "reg-fixed-voltage",
+ .id = 0,
+ .dev = {
+ .platform_data = &snowball_gpio_wlan_vbat_data,
+ },
};
-static struct regulator *prox_regulator;
-static int mop500_prox_activate(struct device *dev);
-static void mop500_prox_deactivate(struct device *dev);
+static struct platform_device snowball_gpio_en_3v3_regulator_device = {
+ .name = "reg-fixed-voltage",
+ .id = 1,
+ .dev = {
+ .platform_data = &snowball_gpio_en_3v3_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_LEDS_PWM
+static struct led_pwm pwm_leds_data[] = {
+ [0] = {
+ .name = "lcd-backlight",
+ .pwm_id = 1,
+ .max_brightness = 255,
+ .lth_brightness = 90,
+ .pwm_period_ns = 1023,
+ .dutycycle_steps = 16,
+ .period_steps = 4,
+ },
+ [1] = {
+ .name = "sec-lcd-backlight",
+ .pwm_id = 2,
+ .max_brightness = 255,
+ .lth_brightness = 90,
+ .pwm_period_ns = 1023,
+ },
+};
-static struct gpio_keys_platform_data mop500_gpio_keys_data = {
- .buttons = mop500_gpio_keys,
- .nbuttons = ARRAY_SIZE(mop500_gpio_keys),
- .enable = mop500_prox_activate,
- .disable = mop500_prox_deactivate,
+static struct led_pwm_platform_data u8500_leds_data = {
+ .num_leds = 1,
+ .leds = pwm_leds_data,
};
-static struct platform_device mop500_gpio_keys_device = {
- .name = "gpio-keys",
+static struct platform_device ux500_leds_device = {
+ .name = "leds_pwm",
+ .dev = {
+ .platform_data = &u8500_leds_data,
+ },
+};
+#endif
+
+#ifdef CONFIG_BACKLIGHT_PWM
+static struct platform_pwm_backlight_data u8500_backlight_data[] = {
+ [0] = {
+ .pwm_id = 1,
+ .max_brightness = 255,
+ .dft_brightness = 200,
+ .lth_brightness = 90,
+ .pwm_period_ns = 1023,
+ },
+ [1] = {
+ .pwm_id = 2,
+ .max_brightness = 255,
+ .dft_brightness = 200,
+ .lth_brightness = 90,
+ .pwm_period_ns = 1023,
+ },
+};
+
+static struct platform_device ux500_backlight_device[] = {
+ [0] = {
+ .name = "pwm-backlight",
+ .id = 0,
+ .dev = {
+ .platform_data = &u8500_backlight_data[0],
+ },
+ },
+ [1] = {
+ .name = "pwm-backlight",
+ .id = 1,
+ .dev = {
+ .platform_data = &u8500_backlight_data[1],
+ },
+ },
+};
+#endif
+
+/* Force feedback vibrator device */
+static struct platform_device ste_ff_vibra_device = {
+ .name = "ste_ff_vibra"
+};
+
+#ifdef CONFIG_HSI
+static struct hsi_board_info __initdata u8500_hsi_devices[] = {
+ {
+ .name = "hsi_char",
+ .hsi_id = 0,
+ .port = 0,
+ .tx_cfg = {
+ .mode = HSI_MODE_FRAME,
+ .channels = 1,
+ .speed = 200000,
+ .ch_prio = {},
+ {.arb_mode = HSI_ARB_RR},
+ },
+ .rx_cfg = {
+ .mode = HSI_MODE_FRAME,
+ .channels = 1,
+ .speed = 200000,
+ .ch_prio = {},
+ {.flow = HSI_FLOW_SYNC},
+ },
+ },
+ {
+ .name = "hsi_test",
+ .hsi_id = 0,
+ .port = 0,
+ .tx_cfg = {
+ .mode = HSI_MODE_FRAME,
+ .channels = 2,
+ .speed = 100000,
+ .ch_prio = {},
+ {.arb_mode = HSI_ARB_RR},
+ },
+ .rx_cfg = {
+ .mode = HSI_MODE_FRAME,
+ .channels = 2,
+ .speed = 200000,
+ .ch_prio = {},
+ {.flow = HSI_FLOW_SYNC},
+ },
+ },
+ {
+ .name = "cfhsi_v3_driver",
+ .hsi_id = 0,
+ .port = 0,
+ .tx_cfg = {
+ .mode = HSI_MODE_STREAM,
+ .channels = 2,
+ .speed = 20000,
+ .ch_prio = {},
+ {.arb_mode = HSI_ARB_RR},
+ },
+ .rx_cfg = {
+ .mode = HSI_MODE_STREAM,
+ .channels = 2,
+ .speed = 200000,
+ .ch_prio = {},
+ {.flow = HSI_FLOW_SYNC},
+ },
+ },
+};
+#endif
+
+#ifdef CONFIG_U8500_SIM_DETECT
+static struct sim_detect_platform_data sim_detect_pdata = {
+ .irq_num = MOP500_AB8500_VIR_GPIO_IRQ(6),
+};
+struct platform_device u8500_sim_detect_device = {
+ .name = "sim-detect",
.id = 0,
.dev = {
- .platform_data = &mop500_gpio_keys_data,
+ .platform_data = &sim_detect_pdata,
},
};
+#endif
-static int mop500_prox_activate(struct device *dev)
-{
- prox_regulator = regulator_get(&mop500_gpio_keys_device.dev,
- "vcc");
- if (IS_ERR(prox_regulator)) {
- dev_err(&mop500_gpio_keys_device.dev,
- "no regulator\n");
- return PTR_ERR(prox_regulator);
+#ifdef CONFIG_CRYPTO_DEV_UX500
+static struct cryp_platform_data u8500_cryp1_platform_data = {
+ .mem_to_engine = {
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB8500_DMA_DEV48_CAC1_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .mode = STEDMA40_MODE_LOGICAL,
+ .src_info.psize = STEDMA40_PSIZE_LOG_4,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_4,
+ },
+ .engine_to_mem = {
+ .dir = STEDMA40_PERIPH_TO_MEM,
+ .src_dev_type = DB8500_DMA_DEV48_CAC1_RX,
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .mode = STEDMA40_MODE_LOGICAL,
+ .src_info.psize = STEDMA40_PSIZE_LOG_4,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_4,
}
- regulator_enable(prox_regulator);
- return 0;
-}
+};
-static void mop500_prox_deactivate(struct device *dev)
-{
- regulator_disable(prox_regulator);
- regulator_put(prox_regulator);
-}
+static struct stedma40_chan_cfg u8500_hash_dma_cfg_tx = {
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB8500_DMA_DEV50_HAC1_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .mode = STEDMA40_MODE_LOGICAL,
+ .src_info.psize = STEDMA40_PSIZE_LOG_16,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_16,
+};
+
+static struct hash_platform_data u8500_hash1_platform_data = {
+ .mem_to_engine = &u8500_hash_dma_cfg_tx,
+ .dma_filter = stedma40_filter,
+};
+#endif
/* add any platform devices here - TODO */
static struct platform_device *mop500_platform_devs[] __initdata = {
- &mop500_gpio_keys_device,
- &ab8500_device,
+#ifdef CONFIG_U8500_SIM_DETECT
+ &u8500_sim_detect_device,
+#endif
+ &ste_ff_vibra_device,
+#ifdef CONFIG_U8500_MMIO
+ &ux500_mmio_device,
+#endif
+ &ux500_hwmem_device,
+#ifdef CONFIG_FB_MCDE
+ &u8500_mcde_device,
+#endif
+#ifdef CONFIG_FB_B2R2
+ &u8500_b2r2_device,
+ &u8500_b2r2_blt_device,
+#endif
+ &u8500_thsens_device,
+#ifdef CONFIG_LEDS_PWM
+ &ux500_leds_device,
+#endif
+#ifdef CONFIG_BACKLIGHT_PWM
+ &ux500_backlight_device[0],
+ &ux500_backlight_device[1],
+#endif
+#ifdef CONFIG_DB8500_MLOADER
+ &mloader_fw_device,
+#endif
+#ifdef CONFIG_HSI
+ &u8500_hsi_device,
+#endif
};
+#ifdef CONFIG_STM_MSP_SPI
+/*
+ * MSP-SPI
+ */
+
+#define NUM_MSP_CLIENTS 10
+
+static struct stm_msp_controller mop500_msp2_spi_data = {
+ .id = 2,
+ .num_chipselect = NUM_MSP_CLIENTS,
+ .base_addr = U8500_MSP2_BASE,
+ .device_name = "msp2",
+};
+
+/*
+ * SSP
+ */
+
+#define NUM_SSP_CLIENTS 10
+
#ifdef CONFIG_STE_DMA40
static struct stedma40_chan_cfg ssp0_dma_cfg_rx = {
.mode = STEDMA40_MODE_LOGICAL,
@@ -444,27 +1002,32 @@ static struct stedma40_chan_cfg ssp0_dma_cfg_tx = {
#endif
static struct pl022_ssp_controller ssp0_plat = {
- .bus_id = 0,
+ .bus_id = 4,
#ifdef CONFIG_STE_DMA40
.enable_dma = 1,
.dma_filter = stedma40_filter,
.dma_rx_param = &ssp0_dma_cfg_rx,
.dma_tx_param = &ssp0_dma_cfg_tx,
-#else
- .enable_dma = 0,
#endif
/* on this platform, gpio 31,142,144,214 &
* 224 are connected as chip selects
*/
- .num_chipselect = 5,
+ .num_chipselect = NUM_SSP_CLIENTS,
};
static void __init mop500_spi_init(struct device *parent)
{
db8500_add_ssp0(parent, &ssp0_plat);
+ if (!machine_is_snowball())
+ db8500_add_msp2_spi(parent, &mop500_msp2_spi_data);
+}
+#else
+static void __init mop500_spi_init(struct device *parent)
+{
}
+#endif /* CONFIG_STM_MSP_SPI */
-#ifdef CONFIG_STE_DMA40
+#ifdef CONFIG_STE_DMA40_REMOVE
static struct stedma40_chan_cfg uart0_dma_cfg_rx = {
.mode = STEDMA40_MODE_LOGICAL,
.dir = STEDMA40_PERIPH_TO_MEM,
@@ -528,26 +1091,6 @@ static pin_cfg_t mop500_pins_uart0[] = {
GPIO3_U0_TXD | PIN_OUTPUT_HIGH,
};
-#define PRCC_K_SOFTRST_SET 0x18
-#define PRCC_K_SOFTRST_CLEAR 0x1C
-static void ux500_uart0_reset(void)
-{
- void __iomem *prcc_rst_set, *prcc_rst_clr;
-
- prcc_rst_set = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE +
- PRCC_K_SOFTRST_SET);
- prcc_rst_clr = (void __iomem *)IO_ADDRESS(U8500_CLKRST1_BASE +
- PRCC_K_SOFTRST_CLEAR);
-
- /* Activate soft reset PRCC_K_SOFTRST_CLEAR */
- writel((readl(prcc_rst_clr) | 0x1), prcc_rst_clr);
- udelay(1);
-
- /* Release soft reset PRCC_K_SOFTRST_SET */
- writel((readl(prcc_rst_set) | 0x1), prcc_rst_set);
- udelay(1);
-}
-
static void ux500_uart0_init(void)
{
int ret;
@@ -568,31 +1111,51 @@ static void ux500_uart0_exit(void)
pr_err("pl011: uart pins_disable failed\n");
}
+static void u8500_uart0_reset(void)
+{
+ /* UART0 lies in PER1 */
+ return u8500_reset_ip(1, PRCC_K_SOFTRST_UART0_MASK);
+}
+
+static void u8500_uart1_reset(void)
+{
+ /* UART1 lies in PER1 */
+ return u8500_reset_ip(1, PRCC_K_SOFTRST_UART1_MASK);
+}
+
+static void u8500_uart2_reset(void)
+{
+ /* UART2 lies in PER3 */
+ return u8500_reset_ip(3, PRCC_K_SOFTRST_UART2_MASK);
+}
+
static struct amba_pl011_data uart0_plat = {
-#ifdef CONFIG_STE_DMA40
+#ifdef CONFIG_STE_DMA40_REMOVE
.dma_filter = stedma40_filter,
.dma_rx_param = &uart0_dma_cfg_rx,
.dma_tx_param = &uart0_dma_cfg_tx,
#endif
.init = ux500_uart0_init,
.exit = ux500_uart0_exit,
- .reset = ux500_uart0_reset,
+ .reset = u8500_uart0_reset,
};
static struct amba_pl011_data uart1_plat = {
-#ifdef CONFIG_STE_DMA40
+#ifdef CONFIG_STE_DMA40_REMOVE
.dma_filter = stedma40_filter,
.dma_rx_param = &uart1_dma_cfg_rx,
.dma_tx_param = &uart1_dma_cfg_tx,
#endif
+ .reset = u8500_uart1_reset,
};
static struct amba_pl011_data uart2_plat = {
-#ifdef CONFIG_STE_DMA40
+#ifdef CONFIG_STE_DMA40_REMOVE
.dma_filter = stedma40_filter,
.dma_rx_param = &uart2_dma_cfg_rx,
.dma_tx_param = &uart2_dma_cfg_tx,
#endif
+ .reset = u8500_uart2_reset,
};
static void __init mop500_uart_init(struct device *parent)
@@ -602,25 +1165,55 @@ static void __init mop500_uart_init(struct device *parent)
db8500_add_uart2(parent, &uart2_plat);
}
+static void __init u8500_cryp1_hash1_init(void)
+{
+#ifdef CONFIG_CRYPTO_DEV_UX500
+ db8500_add_cryp1(&u8500_cryp1_platform_data);
+ db8500_add_hash1(&u8500_hash1_platform_data);
+#endif
+}
+
static struct platform_device *snowball_platform_devs[] __initdata = {
+ &ux500_hwmem_device,
&snowball_led_dev,
&snowball_key_dev,
+#ifdef CONFIG_REGULATOR_FIXED_VOLTAGE
+ &snowball_gpio_en_3v3_regulator_device,
+ &snowball_gpio_wlan_vbat_regulator_device,
+#endif
&snowball_sbnet_dev,
- &ab8500_device,
+#ifdef CONFIG_FB_MCDE
+ &u8500_mcde_device,
+#endif
+#ifdef CONFIG_FB_B2R2
+ &u8500_b2r2_device,
+ &u8500_b2r2_blt_device,
+#endif
};
+static void fixup_ab8505_gpio(void)
+{
+ ab8500_gpio_pdata = ab8505_gpio_pdata;
+}
+
static void __init mop500_init_machine(void)
{
struct device *parent = NULL;
- int i2c0_devs;
int i;
- mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
-
parent = u8500_init_devices();
mop500_pins_init();
+ mop500_regulator_init();
+
+ u8500_cryp1_hash1_init();
+
+#ifdef CONFIG_HSI
+ hsi_register_board_info(u8500_hsi_devices,
+ ARRAY_SIZE(u8500_hsi_devices));
+#endif
+
/* FIXME: parent of ab8500 should be prcmu */
for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++)
mop500_platform_devs[i]->dev.parent = parent;
@@ -628,16 +1221,38 @@ static void __init mop500_init_machine(void)
platform_add_devices(mop500_platform_devs,
ARRAY_SIZE(mop500_platform_devs));
+ platform_device_register(&u8500_shrm_device);
+#ifdef CONFIG_STE_TRACE_MODEM
+ platform_device_register(&u8500_trace_modem);
+#endif
+#ifdef CONFIG_MODEM_U8500
+ platform_device_register(&u8500_modem_dev);
+#endif
mop500_i2c_init(parent);
mop500_sdi_init(parent);
mop500_spi_init(parent);
mop500_uart_init(parent);
+#ifdef CONFIG_STM_MSP_SPI
+ mop500_msp_init(parent);
+#endif
+#if defined(CONFIG_CW1200) || defined(CONFIG_CW1200_MODULE)
+ mop500_wlan_init(parent);
+#endif
- i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
+#ifdef CONFIG_KEYBOARD_NOMADIK_SKE
+ db8500_add_ske_keypad(parent, &mop500_ske_keypad_data,
+ sizeof(mop500_ske_keypad_data));
+#endif
- i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
+#ifdef CONFIG_ANDROID_STE_TIMED_VIBRA
+ mop500_vibra_init();
+#endif
+ platform_device_register(&ab8500_device);
+
+ i2c_register_board_info(0, mop500_i2c0_devices,
+ ARRAY_SIZE(mop500_i2c0_devices));
i2c_register_board_info(2, mop500_i2c2_devices,
- ARRAY_SIZE(mop500_i2c2_devices));
+ ARRAY_SIZE(mop500_i2c2_devices));
/* This board has full regulator constraints */
regulator_has_full_constraints();
@@ -646,13 +1261,21 @@ static void __init mop500_init_machine(void)
static void __init snowball_init_machine(void)
{
struct device *parent = NULL;
- int i2c0_devs;
int i;
parent = u8500_init_devices();
snowball_pins_init();
+ mop500_regulator_init();
+
+ u8500_cryp1_hash1_init();
+
+#ifdef CONFIG_HSI
+ hsi_register_board_info(u8500_hsi_devices,
+ ARRAY_SIZE(u8500_hsi_devices));
+#endif
+
for (i = 0; i < ARRAY_SIZE(snowball_platform_devs); i++)
snowball_platform_devs[i]->dev.parent = parent;
@@ -663,11 +1286,17 @@ static void __init snowball_init_machine(void)
snowball_sdi_init(parent);
mop500_spi_init(parent);
mop500_uart_init(parent);
+#ifdef CONFIG_STM_MSP_SPI
+ mop500_msp_init(parent);
+#endif
+#if defined(CONFIG_CW1200) || defined(CONFIG_CW1200_MODULE)
+ mop500_wlan_init(parent);
+#endif
- i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
- i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
- i2c_register_board_info(2, mop500_i2c2_devices,
- ARRAY_SIZE(mop500_i2c2_devices));
+ platform_device_register(&ab8500_device);
+
+ i2c_register_board_info(0, snowball_i2c0_devices,
+ ARRAY_SIZE(snowball_i2c0_devices));
/* This board has full regulator constraints */
regulator_has_full_constraints();
@@ -676,7 +1305,6 @@ static void __init snowball_init_machine(void)
static void __init hrefv60_init_machine(void)
{
struct device *parent = NULL;
- int i2c0_devs;
int i;
/*
@@ -684,30 +1312,92 @@ static void __init hrefv60_init_machine(void)
* all these GPIO pins to the internal GPIO controller
* instead.
*/
- mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
+
+#ifdef CONFIG_INPUT_AB8500_ACCDET
+ /*
+ * On boards hrefpv60 and later, the accessory insertion/removal,
+ * button press/release are inverted.
+ */
+ ab8500_accdet_pdata.is_detection_inverted = true;
+
+ if (machine_is_u8520()) {
+ ab8500_accdet_pdata.video_ctrl_gpio = AB8500_PIN_GPIO10;
+ ab8500_accdet_pdata.mic_ctrl = AB8500_PIN_GPIO34;
+ }
+#endif
parent = u8500_init_devices();
hrefv60_pins_init();
+ mop500_regulator_init();
+
+ u8500_cryp1_hash1_init();
+
+#ifdef CONFIG_HSI
+ hsi_register_board_info(u8500_hsi_devices,
+ ARRAY_SIZE(u8500_hsi_devices));
+#endif
+#ifdef CONFIG_LEDS_PWM
+ if (uib_is_stuib())
+ u8500_leds_data.num_leds = 2;
+#endif
+
for (i = 0; i < ARRAY_SIZE(mop500_platform_devs); i++)
mop500_platform_devs[i]->dev.parent = parent;
platform_add_devices(mop500_platform_devs,
ARRAY_SIZE(mop500_platform_devs));
+ if (!cpu_is_u9500()) {
+ platform_device_register(&u8500_shrm_device);
+#ifdef CONFIG_STE_TRACE_MODEM
+ platform_device_register(&u8500_trace_modem);
+#endif
+#ifdef CONFIG_MODEM_U8500
+ platform_device_register(&u8500_modem_dev);
+#endif
+ }
+
mop500_i2c_init(parent);
- hrefv60_sdi_init(parent);
+ if (machine_is_u8520())
+ mach_u8520_sdi_init(parent);
+ else
+ hrefv60_sdi_init(parent);
mop500_spi_init(parent);
mop500_uart_init(parent);
+#ifdef CONFIG_STM_MSP_SPI
+ mop500_msp_init(parent);
+#endif
+#if defined(CONFIG_CW1200) || defined(CONFIG_CW1200_MODULE)
+ mop500_wlan_init(parent);
+#endif
- i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
+#ifdef CONFIG_KEYBOARD_NOMADIK_SKE
+ /*
+ * If a hw debugger is detected, do not load the ske driver
+ * since the gpio usage collides.
+ */
+ if (!(prcmu_read(PRCM_DEBUG_NOPWRDOWN_VAL) &
+ ARM_DEBUG_NOPOWER_DOWN_REQ))
+ db8500_add_ske_keypad(parent, &mop500_ske_keypad_data,
+ sizeof(mop500_ske_keypad_data));
+#endif
- i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
+#ifdef CONFIG_ANDROID_STE_TIMED_VIBRA
+ mop500_vibra_init();
+#endif
+ if (machine_is_u8520()) {
+ fixup_ab8505_gpio();
+ platform_device_register(&ab8505_device);
+ }
+ else
+ platform_device_register(&ab8500_device);
- i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
+ i2c_register_board_info(0, mop500_i2c0_devices,
+ ARRAY_SIZE(mop500_i2c0_devices));
i2c_register_board_info(2, mop500_i2c2_devices,
- ARRAY_SIZE(mop500_i2c2_devices));
+ ARRAY_SIZE(mop500_i2c2_devices));
/* This board has full regulator constraints */
regulator_has_full_constraints();
@@ -722,6 +1412,21 @@ MACHINE_START(U8500, "ST-Ericsson MOP500 platform")
.timer = &ux500_timer,
.handle_irq = gic_handle_irq,
.init_machine = mop500_init_machine,
+ .restart = ux500_restart,
+MACHINE_END
+
+/*
+ * NOTE! 8520 machine reports as a HREFV60 until user space updates has been
+ * done for 8520.
+ */
+MACHINE_START(U8520, "ST-Ericsson U8500 Platform HREFv60+")
+ .atag_offset = 0x100,
+ .map_io = u8500_map_io,
+ .init_irq = ux500_init_irq,
+ .timer = &ux500_timer,
+ .handle_irq = gic_handle_irq,
+ .init_machine = hrefv60_init_machine,
+ .restart = ux500_restart,
MACHINE_END
MACHINE_START(HREFV60, "ST-Ericsson U8500 Platform HREFv60+")
@@ -731,9 +1436,10 @@ MACHINE_START(HREFV60, "ST-Ericsson U8500 Platform HREFv60+")
.timer = &ux500_timer,
.handle_irq = gic_handle_irq,
.init_machine = hrefv60_init_machine,
+ .restart = ux500_restart,
MACHINE_END
-MACHINE_START(SNOWBALL, "Calao Systems Snowball platform")
+MACHINE_START(SNOWBALL, "ST-Ericsson Snowball platform")
.atag_offset = 0x100,
.map_io = u8500_map_io,
.init_irq = ux500_init_irq,
@@ -741,6 +1447,18 @@ MACHINE_START(SNOWBALL, "Calao Systems Snowball platform")
.timer = &ux500_timer,
.handle_irq = gic_handle_irq,
.init_machine = snowball_init_machine,
+ .restart = ux500_restart,
+MACHINE_END
+
+/* Temporary reuse hrefv60 machine */
+MACHINE_START(U9540, "ST-Ericsson 9540 platform")
+ .atag_offset = 0x100,
+ .map_io = u8500_map_io,
+ .init_irq = ux500_init_irq,
+ .timer = &ux500_timer,
+ .handle_irq = gic_handle_irq,
+ .init_machine = hrefv60_init_machine,
+ .restart = ux500_restart,
MACHINE_END
#ifdef CONFIG_MACH_UX500_DT
@@ -749,7 +1467,9 @@ struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("arm,pl011", 0x80120000, "uart0", &uart0_plat),
OF_DEV_AUXDATA("arm,pl011", 0x80121000, "uart1", &uart1_plat),
OF_DEV_AUXDATA("arm,pl011", 0x80007000, "uart2", &uart2_plat),
+#ifdef CONFIG_STM_MSP_SPI
OF_DEV_AUXDATA("arm,pl022", 0x80002000, "ssp0", &ssp0_plat),
+#endif
{},
};
@@ -777,7 +1497,6 @@ static void __init u8500_init_machine(void)
of_platform_populate(NULL, u8500_soc_node, u8500_auxdata_lookup, parent);
if (of_machine_is_compatible("st-ericsson,mop500")) {
- mop500_gpio_keys[0].gpio = GPIO_PROX_SENSOR;
mop500_pins_init();
platform_add_devices(mop500_platform_devs,
@@ -796,7 +1515,6 @@ static void __init u8500_init_machine(void)
* all these GPIO pins to the internal GPIO controller
* instead.
*/
- mop500_gpio_keys[0].gpio = HREFV60_PROX_SENSE_GPIO;
i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
hrefv60_pins_init();
platform_add_devices(mop500_platform_devs,
diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h
index fdcfa8721bb..fd6d4aad95c 100644
--- a/arch/arm/mach-ux500/board-mop500.h
+++ b/arch/arm/mach-ux500/board-mop500.h
@@ -7,6 +7,10 @@
#ifndef __BOARD_MOP500_H
#define __BOARD_MOP500_H
+/* This defines the NOMADIK_NR_GPIO */
+#include <linux/mfd/abx500/ab8500-gpio.h>
+#include <mach/gpio.h>
+
/* Snowball specific GPIO assignments, this board has no GPIO expander */
#define SNOWBALL_ACCEL_INT1_GPIO 163
#define SNOWBALL_ACCEL_INT2_GPIO 164
@@ -40,8 +44,12 @@
#define MOP500_HDMI_RST_GPIO 196
#define CYPRESS_SLAVE_SELECT_GPIO 216
+/* U8520-specific GPIO assignments */
+#define U8520_SDMMC_EN_GPIO 78
+#define U8520_SDMMC_1V8_3V_GPIO 5
+#define U8520_SDMMC_CD_GPIO 95
+
/* GPIOs on the TC35892 expander */
-#define MOP500_EGPIO(x) (NOMADIK_NR_GPIO + (x))
#define GPIO_MAGNET_DRDY MOP500_EGPIO(1)
#define GPIO_SDMMC_CD MOP500_EGPIO(3)
#define GPIO_CAMERA_FLASH_ENABLE MOP500_EGPIO(4)
@@ -55,7 +63,6 @@
#define MOP500_DISP1_RST_GPIO MOP500_EGPIO(15)
#define GPIO_SDMMC_EN MOP500_EGPIO(17)
#define GPIO_SDMMC_1V8_3V_SEL MOP500_EGPIO(18)
-#define MOP500_EGPIO_END MOP500_EGPIO(24)
/*
* GPIOs on the AB8500 mixed-signals circuit
@@ -78,14 +85,25 @@ struct i2c_board_info;
extern void mop500_sdi_init(struct device *parent);
extern void snowball_sdi_init(struct device *parent);
extern void hrefv60_sdi_init(struct device *parent);
+extern void mach_u8520_sdi_init(struct device *parent);
extern void mop500_sdi_tc35892_init(struct device *parent);
void __init mop500_u8500uib_init(void);
void __init mop500_stuib_init(void);
+void __init mop500_msp_init(struct device *parent);
void __init mop500_pins_init(void);
+void __init mop500_vibra_init(void);
void __init hrefv60_pins_init(void);
void __init snowball_pins_init(void);
+void __init mop500_u8500uib_r3_init(void);
-void mop500_uib_i2c_add(int busnum, struct i2c_board_info *info,
+void mop500_uib_i2c_add(int busnum, struct i2c_board_info const *info,
unsigned n);
+int msp13_i2s_init(void);
+int msp13_i2s_exit(void);
+
+int uib_is_stuib(void);
+int uib_is_u8500uib(void);
+int uib_is_u8500uibr3(void);
+
#endif
diff --git a/arch/arm/mach-ux500/board-pins-sleep-force.c b/arch/arm/mach-ux500/board-pins-sleep-force.c
new file mode 100644
index 00000000000..91eb1cefc22
--- /dev/null
+++ b/arch/arm/mach-ux500/board-pins-sleep-force.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/string.h>
+
+#include <linux/gpio/nomadik.h>
+#include <mach/hardware.h>
+
+#include "board-pins-sleep-force.h"
+#include "pins-db8500.h"
+#include "pins.h"
+
+static u32 u8500_gpio_banks[] = {U8500_GPIOBANK0_BASE,
+ U8500_GPIOBANK1_BASE,
+ U8500_GPIOBANK2_BASE,
+ U8500_GPIOBANK3_BASE,
+ U8500_GPIOBANK4_BASE,
+ U8500_GPIOBANK5_BASE,
+ U8500_GPIOBANK6_BASE,
+ U8500_GPIOBANK7_BASE,
+ U8500_GPIOBANK8_BASE};
+
+/*
+ * This function is called to force gpio power save
+ * settings during suspend.
+ */
+void sleep_pins_config_pm(pin_cfg_t *cfgs, int num)
+{
+ int i = 0;
+ int gpio = 0;
+ u32 w_imsc = 0;
+ u32 imsc = 0;
+ u32 offset;
+ u32 bitmask = 1;
+ u32 dirs_register = 0;
+ u32 dirc_register = 0;
+ u32 dats_register = 0;
+ u32 datc_register = 0;
+ u32 pdis_register_disable = 0;
+ u32 pdis_register_enabled = 0;
+ u32 slpm_register_disabled = 0;
+ u32 slpm_register_enabled = 0;
+ u32 bankaddr = 0;
+
+ gpio = PIN_NUM(cfgs[i]);
+
+ /* Get the bank number the pin is mapped to */
+ bankaddr = IO_ADDRESS(u8500_gpio_banks[(gpio >> GPIO_BLOCK_SHIFT)]);
+
+ w_imsc = readl(bankaddr + NMK_GPIO_RWIMSC) |
+ readl(bankaddr + NMK_GPIO_FWIMSC);
+
+ imsc = readl(bankaddr + NMK_GPIO_RIMSC) |
+ readl(bankaddr + NMK_GPIO_FIMSC);
+
+ for (i = 0; i < num; i++) {
+ /* Get the pin number */
+ gpio = PIN_NUM(cfgs[i]);
+
+ /* get the offest into the register */
+ offset = gpio % NMK_GPIO_PER_CHIP;
+ /* Set the bit to toggle */
+ bitmask = 1 << offset ;
+
+ /* Next we check for direction (INPUT/OUTPUT) */
+ switch (PIN_SLPM_DIR(cfgs[i])) {
+ case GPIO_IS_INPUT:
+ /* GPIO is set to input */
+ dirc_register |= bitmask;
+
+ /*
+ * Next check for pull (PULLUP/PULLDOWN)
+ * and configure accordingly.
+ */
+ switch (PIN_SLPM_PULL(cfgs[i])) {
+ case GPIO_PULL_UPDOWN_DISABLED:
+ pdis_register_disable |= bitmask;
+ break;
+
+ case GPIO_IS_PULLUP:
+ dats_register |= bitmask;
+ pdis_register_enabled |= bitmask;
+ break;
+
+ case GPIO_IS_PULLDOWN:
+ datc_register |= bitmask;
+ pdis_register_enabled |= bitmask;
+ break;
+
+ case GPIO_PULL_NO_CHANGE:
+ break;
+
+ default:
+ BUG();
+ break;
+
+ }
+ break;
+
+ case GPIO_IS_OUTPUT:
+ /* GPIO is set to output */
+ dirs_register |= bitmask;
+
+ /*
+ * Since its output there should not
+ * be a need to disable PULL UP/DOWN
+ * but better safe than sorry.
+ */
+ pdis_register_disable |= bitmask;
+ /* Next we check for setting GPIO HIGH/LOW */
+ switch (PIN_SLPM_VAL(cfgs[i])) {
+ case GPIO_IS_OUTPUT_LOW:
+ /* GPIO is set to LOW */
+ datc_register |= bitmask;
+ break;
+
+ case GPIO_IS_OUTPUT_HIGH:
+ /* GPIO is set to high */
+ dats_register |= bitmask;
+ break;
+
+ case GPIO_IS_NO_CHANGE:
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ break;
+ case GPIO_IS_NOT_CHANGED:
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ /* Next check for Sleep Power Managment (SLPM) */
+ switch (PIN_SLPM(cfgs[i])) {
+ case GPIO_WAKEUP_IS_ENABLED:
+ slpm_register_enabled |= bitmask;
+ break;
+
+ case GPIO_WAKEUP_IS_DISBLED:
+ slpm_register_disabled |= bitmask;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ /* Next check for Sleep Power Managment (SLPM) */
+ switch (PIN_SLPM_PDIS(cfgs[i])) {
+ case GPIO_PDIS_NO_CHANGE:
+ break;
+
+ case GPIO_PDIS_DISABLED:
+ pdis_register_disable |= bitmask;
+ break;
+
+ case GPIO_PDIS_ENABLED:
+ pdis_register_enabled |= bitmask;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ }
+
+ /* Write the register settings GPIO direction */
+ writel(dirs_register & ~w_imsc, bankaddr + NMK_GPIO_DIRS);
+ writel(dirc_register, bankaddr + NMK_GPIO_DIRC);
+
+ writel(datc_register & ~w_imsc, bankaddr + NMK_GPIO_DATC);
+ writel(dats_register & ~w_imsc, bankaddr + NMK_GPIO_DATS);
+
+ /* Write the PDIS enable/disable */
+ writel(readl(bankaddr + NMK_GPIO_PDIS)
+ | (pdis_register_disable & ~w_imsc & ~imsc),
+ bankaddr + NMK_GPIO_PDIS);
+ writel(readl(bankaddr + NMK_GPIO_PDIS)
+ & (~pdis_register_enabled & ~w_imsc & ~imsc),
+ bankaddr + NMK_GPIO_PDIS);
+
+ /* Write the SLPM enable/disable */
+ writel(readl(bankaddr + NMK_GPIO_SLPC) | slpm_register_disabled,
+ bankaddr + NMK_GPIO_SLPC);
+ writel(readl(bankaddr + NMK_GPIO_SLPC) & ~slpm_register_enabled,
+ bankaddr + NMK_GPIO_SLPC);
+}
+
+void sleep_pins_config_pm_mux(pin_cfg_t *cfgs, int num)
+{
+ int i = 0;
+ int gpio = 0;
+ u32 offset;
+ u32 bitmask = 1;
+ u32 gpio_afsla_register_set = 0;
+ u32 gpio_afslb_register_set = 0;
+ u32 gpio_afsla_register_clear = 0;
+ u32 gpio_afslb_register_clear = 0;
+ u32 bankaddr = 0;
+
+ gpio = PIN_NUM(cfgs[i]);
+
+ /* Get the bank number the pin is mapped to */
+ bankaddr = IO_ADDRESS(u8500_gpio_banks[(gpio >> GPIO_BLOCK_SHIFT)]);
+
+ for (i = 0; i < num; i++) {
+ /* Get the pin number */
+ gpio = PIN_NUM(cfgs[i]);
+
+ /* get the offset into the register */
+ offset = gpio % NMK_GPIO_PER_CHIP;
+ /* Set the bit to toggle */
+ bitmask = 1 << offset ;
+
+ /* First check for ALT pin configuration */
+ switch (PIN_ALT(cfgs[i])) {
+ case NMK_GPIO_ALT_GPIO:
+ /* Set bit to configured as GPIO */
+ gpio_afsla_register_clear |= bitmask;
+ gpio_afslb_register_clear |= bitmask;
+ break;
+
+ case NMK_GPIO_ALT_A:
+ /* ALT A setting so set corresponding bit */
+ gpio_afsla_register_set |= bitmask;
+ break;
+
+ case NMK_GPIO_ALT_B:
+ /* ALT B setting so set corresponding bit */
+ gpio_afslb_register_set |= bitmask;
+ break;
+
+ case NMK_GPIO_ALT_C:
+ /* ALT C setting so set corresponding bits */
+ gpio_afsla_register_set |= bitmask;
+ gpio_afslb_register_set |= bitmask;
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+ }
+ /* Set bits that configures GPIO */
+ writel(readl(bankaddr + NMK_GPIO_AFSLA)
+ & ~gpio_afsla_register_clear, bankaddr + NMK_GPIO_AFSLA);
+ writel(readl(bankaddr + NMK_GPIO_AFSLB)
+ & ~gpio_afslb_register_clear, bankaddr + NMK_GPIO_AFSLB);
+
+ /* Set bits that configures ALT_X */
+ writel(readl(bankaddr + NMK_GPIO_AFSLA)
+ | gpio_afsla_register_set, bankaddr + NMK_GPIO_AFSLA);
+ writel(readl(bankaddr + NMK_GPIO_AFSLB)
+ | gpio_afslb_register_set, bankaddr + NMK_GPIO_AFSLB);
+}
diff --git a/arch/arm/mach-ux500/board-pins-sleep-force.h b/arch/arm/mach-ux500/board-pins-sleep-force.h
new file mode 100644
index 00000000000..0949c9bfcda
--- /dev/null
+++ b/arch/arm/mach-ux500/board-pins-sleep-force.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __BOARD_PINS_SLEEP_FORCE_H
+#define __BOARD_PINS_SLEEP_FORCE_H
+
+#include <plat/pincfg.h>
+
+#define NMK_GPIO_PER_CHIP 32
+#define GPIO_BLOCK_SHIFT 5
+
+#define GPIO_IS_NOT_CHANGED 0
+#define GPIO_IS_INPUT 1
+#define GPIO_IS_OUTPUT 2
+
+#define GPIO_WAKEUP_IS_ENABLED 0
+#define GPIO_WAKEUP_IS_DISBLED 1
+
+#define GPIO_IS_NO_CHANGE 0
+#define GPIO_IS_OUTPUT_LOW 1
+#define GPIO_IS_OUTPUT_HIGH 2
+
+#define GPIO_PULL_NO_CHANGE 0
+#define GPIO_PULL_UPDOWN_DISABLED 1
+#define GPIO_IS_PULLUP 2
+#define GPIO_IS_PULLDOWN 3
+
+#define GPIO_PDIS_NO_CHANGE 0
+#define GPIO_PDIS_DISABLED 1
+#define GPIO_PDIS_ENABLED 2
+
+void sleep_pins_config_pm_mux(pin_cfg_t *cfgs, int num);
+void sleep_pins_config_pm(pin_cfg_t *cfgs, int num);
+
+#endif
diff --git a/arch/arm/mach-ux500/board-u5500-bm.c b/arch/arm/mach-ux500/board-u5500-bm.c
new file mode 100644
index 00000000000..333f2cb8563
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500-bm.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * U5500 board specific charger and battery initialization parameters.
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#include <linux/power_supply.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500-bm.h>
+#include "board-u5500-bm.h"
+
+#ifdef CONFIG_AB5500_BATTERY_THERM_ON_BATCTRL
+/*
+ * These are the defined batteries that uses a NTC and ID resistor placed
+ * inside of the battery pack.
+ * Note that the abx500_res_to_temp table must be strictly sorted by falling
+ * resistance values to work.
+ */
+static struct abx500_res_to_temp temp_tbl_type1[] = {
+ {-20, 67400},
+ { 0, 49200},
+ { 5, 44200},
+ { 10, 39400},
+ { 15, 35000},
+ { 20, 31000},
+ { 25, 27400},
+ { 30, 24300},
+ { 35, 21700},
+ { 40, 19400},
+ { 45, 17500},
+ { 50, 15900},
+ { 55, 14600},
+ { 60, 13500},
+ { 65, 12500},
+ { 70, 11800},
+ {100, 9200},
+};
+
+static struct abx500_res_to_temp temp_tbl_type2[] = {
+ {-20, 180700},
+ { 0, 160000},
+ { 5, 152700},
+ { 10, 144900},
+ { 15, 136800},
+ { 20, 128700},
+ { 25, 121000},
+ { 30, 113800},
+ { 35, 107300},
+ { 40, 101500},
+ { 45, 96500},
+ { 50, 92200},
+ { 55, 88600},
+ { 60, 85600},
+ { 65, 83000},
+ { 70, 80900},
+ {100, 73900},
+};
+
+static struct abx500_res_to_temp temp_tbl_A[] = {
+ {-5, 53407},
+ { 0, 48594},
+ { 5, 43804},
+ {10, 39188},
+ {15, 34870},
+ {20, 30933},
+ {25, 27422},
+ {30, 24347},
+ {35, 21694},
+ {40, 19431},
+ {45, 17517},
+ {50, 15908},
+ {55, 14561},
+ {60, 13437},
+ {65, 12500},
+};
+
+static struct abx500_res_to_temp temp_tbl_B[] = {
+ {-5, 165418},
+ { 0, 159024},
+ { 5, 151921},
+ {10, 144300},
+ {15, 136424},
+ {20, 128565},
+ {25, 120978},
+ {30, 113875},
+ {35, 107397},
+ {40, 101629},
+ {45, 96592},
+ {50, 92253},
+ {55, 88569},
+ {60, 85461},
+ {65, 82869},
+};
+
+static struct abx500_v_to_cap cap_tbl_type1[] = {
+ {4171, 100},
+ {4114, 95},
+ {4009, 83},
+ {3947, 74},
+ {3907, 67},
+ {3863, 59},
+ {3830, 56},
+ {3813, 53},
+ {3791, 46},
+ {3771, 33},
+ {3754, 25},
+ {3735, 20},
+ {3717, 17},
+ {3681, 13},
+ {3664, 8},
+ {3651, 6},
+ {3635, 5},
+ {3560, 3},
+ {3408, 1},
+ {3247, 0},
+};
+
+static struct abx500_v_to_cap cap_tbl_A[] = {
+ {4171, 100},
+ {4114, 95},
+ {4009, 83},
+ {3947, 74},
+ {3907, 67},
+ {3863, 59},
+ {3830, 56},
+ {3813, 53},
+ {3791, 46},
+ {3771, 33},
+ {3754, 25},
+ {3735, 20},
+ {3717, 17},
+ {3681, 13},
+ {3664, 8},
+ {3651, 6},
+ {3635, 5},
+ {3560, 3},
+ {3408, 1},
+ {3247, 0},
+};
+static struct abx500_v_to_cap cap_tbl_B[] = {
+ {4161, 100},
+ {4124, 98},
+ {4044, 90},
+ {4003, 85},
+ {3966, 80},
+ {3933, 75},
+ {3888, 67},
+ {3849, 60},
+ {3813, 55},
+ {3787, 47},
+ {3772, 30},
+ {3751, 25},
+ {3718, 20},
+ {3681, 16},
+ {3660, 14},
+ {3589, 10},
+ {3546, 7},
+ {3495, 4},
+ {3404, 2},
+ {3250, 0},
+};
+#endif
+static struct abx500_v_to_cap cap_tbl[] = {
+ {4186, 100},
+ {4163, 99},
+ {4114, 95},
+ {4068, 90},
+ {3990, 80},
+ {3926, 70},
+ {3898, 65},
+ {3866, 60},
+ {3833, 55},
+ {3812, 50},
+ {3787, 40},
+ {3768, 30},
+ {3747, 25},
+ {3730, 20},
+ {3705, 15},
+ {3699, 14},
+ {3684, 12},
+ {3672, 9},
+ {3657, 7},
+ {3638, 6},
+ {3556, 4},
+ {3424, 2},
+ {3317, 1},
+ {3094, 0},
+};
+
+/*
+ * Note that the abx500_res_to_temp table must be strictly sorted by falling
+ * resistance values to work.
+ */
+static struct abx500_res_to_temp temp_tbl[] = {
+ {-5, 214834},
+ { 0, 162943},
+ { 5, 124820},
+ {10, 96520},
+ {15, 75306},
+ {20, 59254},
+ {25, 47000},
+ {30, 37566},
+ {35, 30245},
+ {40, 24520},
+ {45, 20010},
+ {50, 16432},
+ {55, 13576},
+ {60, 11280},
+ {65, 9425},
+};
+
+static const struct abx500_battery_type bat_type[] = {
+ [BATTERY_UNKNOWN] = {
+ /* First element always represent the UNKNOWN battery */
+ .name = POWER_SUPPLY_TECHNOLOGY_UNKNOWN,
+ .resis_high = 0,
+ .resis_low = 0,
+ .battery_resistance = 300,
+ .charge_full_design = 612,
+ .nominal_voltage = 3700,
+ .termination_vol = 4050,
+ .termination_curr = 200,
+ .recharge_vol = 3990,
+ .normal_cur_lvl = 400,
+ .normal_vol_lvl = 4100,
+ .maint_a_cur_lvl = 400,
+ .maint_a_vol_lvl = 4050,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 400,
+ .maint_b_vol_lvl = 4025,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ },
+
+#ifdef CONFIG_AB5500_BATTERY_THERM_ON_BATCTRL
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 70000,
+ .resis_low = 8200,
+ .battery_resistance = 300,
+ .charge_full_design = 1500,
+ .nominal_voltage = 3600,
+ .termination_vol = 4150,
+ .termination_curr = 80,
+ .recharge_vol = 4025,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4025,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_type1),
+ .r_to_t_tbl = temp_tbl_type1,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_type1),
+ .v_to_cap_tbl = cap_tbl_type1,
+
+ },
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 165418,
+ .resis_low = 82869,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3600,
+ .termination_vol = 4150,
+ .termination_curr = 80,
+ .recharge_vol = 4025,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4025,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl_B),
+ .r_to_t_tbl = temp_tbl_B,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl_B),
+ .v_to_cap_tbl = cap_tbl_B,
+ },
+#else
+/*
+ * These are the batteries that doesn't have an internal NTC resistor to measure
+ * its temperature. The temperature in this case is measure with a NTC placed
+ * near the battery but on the PCB.
+ */
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LIPO,
+ .resis_high = 76000,
+ .resis_low = 53000,
+ .battery_resistance = 300,
+ .charge_full_design = 900,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4025,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4025,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ },
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LION,
+ .resis_high = 30000,
+ .resis_low = 10000,
+ .battery_resistance = 300,
+ .charge_full_design = 950,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4025,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4025,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ },
+ {
+ .name = POWER_SUPPLY_TECHNOLOGY_LION,
+ .resis_high = 95000,
+ .resis_low = 76001,
+ .battery_resistance = 300,
+ .charge_full_design = 950,
+ .nominal_voltage = 3700,
+ .termination_vol = 4150,
+ .termination_curr = 100,
+ .recharge_vol = 4025,
+ .normal_cur_lvl = 700,
+ .normal_vol_lvl = 4200,
+ .maint_a_cur_lvl = 600,
+ .maint_a_vol_lvl = 4150,
+ .maint_a_chg_timer_h = 60,
+ .maint_b_cur_lvl = 600,
+ .maint_b_vol_lvl = 4025,
+ .maint_b_chg_timer_h = 200,
+ .low_high_cur_lvl = 300,
+ .low_high_vol_lvl = 4000,
+ .n_temp_tbl_elements = ARRAY_SIZE(temp_tbl),
+ .r_to_t_tbl = temp_tbl,
+ .n_v_cap_tbl_elements = ARRAY_SIZE(cap_tbl),
+ .v_to_cap_tbl = cap_tbl,
+ },
+#endif
+};
+
+static char *ab5500_charger_supplied_to[] = {
+ "abx500_chargalg",
+ "ab5500_fg",
+ "ab5500_btemp",
+};
+
+static char *ab5500_btemp_supplied_to[] = {
+ "abx500_chargalg",
+ "ab5500_fg",
+};
+
+static char *ab5500_fg_supplied_to[] = {
+ "abx500_chargalg",
+};
+
+static char *abx500_chargalg_supplied_to[] = {
+ "ab5500_fg",
+};
+
+struct abx500_charger_platform_data ab5500_charger_plat_data = {
+ .supplied_to = ab5500_charger_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab5500_charger_supplied_to),
+};
+
+struct abx500_btemp_platform_data ab5500_btemp_plat_data = {
+ .supplied_to = ab5500_btemp_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab5500_btemp_supplied_to),
+};
+
+struct abx500_fg_platform_data ab5500_fg_plat_data = {
+ .supplied_to = ab5500_fg_supplied_to,
+ .num_supplicants = ARRAY_SIZE(ab5500_fg_supplied_to),
+};
+
+struct abx500_chargalg_platform_data abx500_chargalg_plat_data = {
+ .supplied_to = abx500_chargalg_supplied_to,
+ .num_supplicants = ARRAY_SIZE(abx500_chargalg_supplied_to),
+};
+
+static const struct abx500_bm_capacity_levels cap_levels = {
+ .critical = 2,
+ .low = 10,
+ .normal = 70,
+ .high = 95,
+ .full = 100,
+};
+
+static const struct abx500_fg_parameters fg = {
+ .recovery_sleep_timer = 10,
+ .recovery_total_time = 100,
+ .init_timer = 1,
+ .init_discard_time = 5,
+ .init_total_time = 40,
+ .high_curr_time = 60,
+ .accu_charging = 30,
+ .accu_high_curr = 30,
+ .high_curr_threshold = 50,
+ .lowbat_threshold = 3560,
+ .overbat_threshold = 4400,
+};
+
+static const struct abx500_maxim_parameters maxi_params = {
+ .ena_maxi = true,
+ .chg_curr = 910,
+ .wait_cycles = 10,
+ .charger_curr_step = 100,
+};
+
+static const struct abx500_bm_charger_parameters chg = {
+ .usb_volt_max = 5500,
+ .usb_curr_max = 1500,
+ .ac_volt_max = 7500,
+ .ac_curr_max = 1500,
+};
+
+struct abx500_bm_data ab5500_bm_data = {
+ .temp_under = 3,
+ .temp_low = 8,
+ /* TODO: Need to verify the temp values */
+ .temp_high = 155,
+ .temp_over = 160,
+ .main_safety_tmr_h = 4,
+ .usb_safety_tmr_h = 4,
+ .bkup_bat_v = 0x00,
+ .bkup_bat_i = 0x00,
+ .no_maintenance = true,
+#ifdef CONFIG_AB5500_BATTERY_THERM_ON_BATCTRL
+ .adc_therm = ABx500_ADC_THERM_BATCTRL,
+#else
+ .adc_therm = ABx500_ADC_THERM_BATTEMP,
+#endif
+ .chg_unknown_bat = false,
+ .enable_overshoot = false,
+ .auto_trig = true,
+ .fg_res = 200,
+ .cap_levels = &cap_levels,
+ .bat_type = bat_type,
+ .n_btypes = ARRAY_SIZE(bat_type),
+ .batt_id = 0,
+ .interval_charging = 5,
+ .interval_not_charging = 120,
+ .temp_hysteresis = 3,
+ .maxi = &maxi_params,
+ .chg_params = &chg,
+ .fg_params = &fg,
+};
+
+/* ab5500 energy management platform data */
+struct abx500_bm_plat_data abx500_bm_pt_data = {
+ .battery = &ab5500_bm_data,
+ .charger = &ab5500_charger_plat_data,
+ .btemp = &ab5500_btemp_plat_data,
+ .fg = &ab5500_fg_plat_data,
+ .chargalg = &abx500_chargalg_plat_data,
+};
diff --git a/arch/arm/mach-ux500/board-u5500-bm.h b/arch/arm/mach-ux500/board-u5500-bm.h
new file mode 100644
index 00000000000..a6346905911
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500-bm.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * U5500 board specific charger and battery initialization parameters.
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#ifndef __BOARD_U5500_BM_H
+#define __BOARD_U5500_BM_H
+
+#include <linux/mfd/abx500/ab5500-bm.h>
+
+extern struct abx500_charger_platform_data ab5500_charger_plat_data;
+extern struct abx500_btemp_platform_data ab5500_btemp_plat_data;
+extern struct abx500_fg_platform_data ab5500_fg_plat_data;
+extern struct abx500_chargalg_platform_data abx500_chargalg_plat_data;
+extern struct abx500_bm_data ab5500_bm_data;
+extern struct abx500_bm_plat_data abx500_bm_pt_data;
+
+#endif
diff --git a/arch/arm/mach-ux500/board-u5500-cyttsp.c b/arch/arm/mach-ux500/board-u5500-cyttsp.c
new file mode 100644
index 00000000000..47eaa36c030
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500-cyttsp.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Author: Avinash A <avinash.a@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/cyttsp.h>
+#include <linux/delay.h>
+#include <linux/amba/pl022.h>
+#include <plat/pincfg.h>
+#include <mach/hardware.h>
+
+#include "pins-db5500.h"
+#include "board-u5500.h"
+
+/* cyttsp_gpio_board_init : configures the touch panel. */
+static int cyttsp_plat_init(int on)
+{
+ int ret;
+
+ ret = gpio_direction_output(CYPRESS_SLAVE_SELECT_GPIO, 1);
+ if (ret < 0) {
+ pr_err("slave select gpio direction failed\n");
+ gpio_free(CYPRESS_SLAVE_SELECT_GPIO);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cyttsp_wakeup(void)
+{
+ int ret;
+
+ ret = gpio_request(CYPRESS_TOUCH_INT_PIN, "Wakeup_pin");
+ if (ret < 0) {
+ pr_err("touch gpio failed\n");
+ return ret;
+ }
+ ret = gpio_direction_output(CYPRESS_TOUCH_INT_PIN, 1);
+ if (ret < 0) {
+ pr_err("touch gpio direction failed\n");
+ goto out;
+ }
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 0);
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 1);
+ /*
+ * To wake up the controller from sleep
+ * state the interrupt pin needs to be
+ * pulsed twice with a delay greater
+ * than 2 micro seconds.
+ */
+ udelay(3);
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 0);
+ gpio_set_value(CYPRESS_TOUCH_INT_PIN, 1);
+ ret = gpio_direction_input(CYPRESS_TOUCH_INT_PIN);
+ if (ret < 0) {
+ pr_err("touch gpio direction IN config failed\n");
+ goto out;
+ }
+out:
+ gpio_free(CYPRESS_TOUCH_INT_PIN);
+ return 0;
+}
+static struct cyttsp_platform_data cyttsp_spi_platdata = {
+ .maxx = 480,
+ .maxy = 854,
+ .flags = 0,
+ .gen = CY_GEN3,
+ .use_st = 0,
+ .use_mt = 1,
+ .use_trk_id = 0,
+ .use_hndshk = 0,
+ .use_sleep = 1,
+ .use_gestures = 0,
+ .use_load_file = 0,
+ .use_force_fw_update = 0,
+ .use_virtual_keys = 0,
+ /* activate up to 4 groups and set active distance */
+ .gest_set = CY_GEST_GRP_NONE | CY_ACT_DIST,
+ /* change scn_type to enable finger and/or stylus detection */
+ .scn_typ = 0xA5, /* autodetect finger+stylus; balanced mutual scan */
+ .act_intrvl = CY_ACT_INTRVL_DFLT, /* Active refresh interval; ms */
+ .tch_tmout = CY_TCH_TMOUT_DFLT, /* Active touch timeout; ms */
+ .lp_intrvl = CY_LP_INTRVL_DFLT, /* Low power refresh interval; ms */
+ .init = cyttsp_plat_init,
+ .mt_sync = input_mt_sync,
+ .wakeup = cyttsp_wakeup,
+ .name = CY_SPI_NAME,
+ .irq_gpio = CYPRESS_TOUCH_INT_PIN,
+ .rst_gpio = CYPRESS_TOUCH_RST_GPIO,
+};
+
+static void cyttsp_spi_cs_control(u32 command)
+{
+ if (command == SSP_CHIP_SELECT)
+ gpio_set_value(CYPRESS_SLAVE_SELECT_GPIO, 0);
+ else if (command == SSP_CHIP_DESELECT)
+ gpio_set_value(CYPRESS_SLAVE_SELECT_GPIO, 1);
+}
+
+static struct pl022_config_chip cyttsp_ssp_config_chip = {
+ .com_mode = INTERRUPT_TRANSFER,
+ .iface = SSP_INTERFACE_MOTOROLA_SPI,
+ /* we can act as master only */
+ .hierarchy = SSP_MASTER,
+ .slave_tx_disable = 0,
+ .rx_lev_trig = SSP_RX_1_OR_MORE_ELEM,
+ .tx_lev_trig = SSP_TX_16_OR_MORE_EMPTY_LOC,
+ .ctrl_len = SSP_BITS_16,
+ .wait_state = SSP_MWIRE_WAIT_ZERO,
+ .duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
+ .cs_control = cyttsp_spi_cs_control,
+};
+
+static struct spi_board_info cypress_spi_devices[] = {
+ {
+ .modalias = CY_SPI_NAME,
+ .controller_data = &cyttsp_ssp_config_chip,
+ .platform_data = &cyttsp_spi_platdata,
+ .max_speed_hz = 1000000,
+ .bus_num = 1,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ }
+};
+
+void u5500_cyttsp_init(void)
+{
+ int ret = 0;
+
+ ret = gpio_request(CYPRESS_SLAVE_SELECT_GPIO, "slave_select_gpio");
+ if (ret < 0) {
+ pr_err("slave select gpio failed\n");
+ return;
+ }
+ if (cpu_is_u5500v2())
+ cyttsp_spi_platdata.invert = true;
+ spi_register_board_info(cypress_spi_devices,
+ ARRAY_SIZE(cypress_spi_devices));
+}
diff --git a/arch/arm/mach-ux500/board-u5500-pins.c b/arch/arm/mach-ux500/board-u5500-pins.c
new file mode 100644
index 00000000000..a6b63ca3ead
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500-pins.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <plat/pincfg.h>
+
+#include "pins-db5500.h"
+#include "pins.h"
+
+static pin_cfg_t u5500_pins_default[] = {
+ GPIO77_ACCTX_ON | PIN_SLPM_ALTFUNC,
+ GPIO79_ACCSIM_Clk | PIN_SLPM_ALTFUNC,
+ GPIO80_ACCSIM_Da | PIN_SLPM_ALTFUNC,
+ GPIO81_ACCSIM_Reset | PIN_SLPM_ALTFUNC,
+ GPIO82_ACCSIM_DDir | PIN_SLPM_ALTFUNC,
+
+ /* MSP */
+ GPIO32_MSP0_TCK | PIN_INPUT_PULLDOWN,
+ GPIO33_MSP0_TFS | PIN_INPUT_PULLDOWN,
+ GPIO34_MSP0_TXD | PIN_INPUT_PULLDOWN,
+ GPIO35_MSP0_RXD | PIN_INPUT_PULLDOWN,
+ GPIO96_MSP1_TCK | PIN_INPUT_PULLDOWN,
+ GPIO97_MSP1_TFS | PIN_INPUT_PULLDOWN,
+ GPIO98_MSP1_TXD | PIN_INPUT_PULLDOWN,
+ GPIO99_MSP1_RXD | PIN_INPUT_PULLDOWN,
+ GPIO220_MSP2_TCK | PIN_OUTPUT_LOW,
+ GPIO221_MSP2_TFS | PIN_OUTPUT_LOW,
+ GPIO222_MSP2_TXD | PIN_OUTPUT_LOW,
+
+ /* DISPLAY_ENABLE */
+ GPIO226_GPIO | PIN_OUTPUT_HIGH,
+
+ /* Backlight Enable */
+ GPIO224_GPIO | PIN_OUTPUT_HIGH,
+
+ /* UART0 */
+ GPIO28_U0_TXD | PIN_OUTPUT_HIGH,
+ GPIO29_U0_RXD | PIN_INPUT_PULLUP,
+
+ /* UART3 */
+ GPIO165_U3_RXD | PIN_INPUT_PULLUP,
+ GPIO166_U3_TXD | PIN_OUTPUT_HIGH | PIN_LOWEMI_ENABLED,
+ GPIO167_U3_RTSn | PIN_OUTPUT_HIGH | PIN_LOWEMI_ENABLED,
+ GPIO168_U3_CTSn | PIN_INPUT_PULLUP,
+
+ /* AB5500 */
+ GPIO78_IRQn | PIN_SLPM_NOCHANGE,
+ GPIO100_I2C0_SCL | PIN_INPUT_PULLUP | PIN_SLPM_NOCHANGE,
+ GPIO101_I2C0_SDA | PIN_SLPM_NOCHANGE,
+
+ /* TOUCH_IRQ */
+ GPIO179_GPIO | PIN_INPUT_PULLUP,
+
+ /* SD-CARD detect/levelshifter pins */
+ GPIO180_GPIO | PIN_INPUT_NOPULL, /* SD_CARD_DETn */
+ GPIO227_GPIO | PIN_OUTPUT_LOW, /* SD_CARD_CTRL */
+ GPIO185_GPIO | PIN_OUTPUT_LOW, /* SD_CARD_VSEL */
+
+ /* Display & HDMI HW sync */
+ GPIO204_LCD_VSI1 | PIN_INPUT_PULLUP,
+
+ /* TVOUT (connected, but unused) */
+ GPIO205_GPIO | PIN_OUTPUT_LOW,
+ GPIO206_GPIO | PIN_OUTPUT_LOW,
+ GPIO207_GPIO | PIN_OUTPUT_LOW,
+ GPIO208_GPIO | PIN_OUTPUT_LOW,
+ GPIO209_GPIO | PIN_INPUT_PULLDOWN,
+
+ /* Display (connected to NT35560 / TE, but unused) */
+ GPIO211_GPIO | PIN_INPUT_PULLDOWN,
+
+ /* Camera & MMIO XshutDown*/
+ GPIO1_GPIO | PIN_OUTPUT_LOW,
+ GPIO2_GPIO | PIN_OUTPUT_LOW,
+
+ /* USB chip select */
+ GPIO76_GPIO | PIN_OUTPUT_LOW,
+
+ GPIO202_ACCU0_RXD | PIN_INPUT_PULLUP | PIN_SLPM_NOCHANGE,
+ GPIO203_ACCU0_TXD | PIN_OUTPUT_HIGH | PIN_SLPM_NOCHANGE,
+
+ /* Board Id Identification B5500 or S5500 */
+ GPIO0_GPIO | PIN_INPUT_PULLUP,
+ GPIO214_GPIO | PIN_OUTPUT_LOW, /* SW_CRASH_INDICATOR */
+
+ /* Touchscreen chip select */
+ GPIO186_GPIO | PIN_OUTPUT_HIGH | PIN_LOWEMI_ENABLED,
+
+ GPIO133_GPIO | PIN_OUTPUT_LOW, /* DUALSIMRESETn */
+ GPIO187_GPIO | PIN_OUTPUT_HIGH, /* Dual SIM CS */
+
+ GPIO163_GPIO | PIN_INPUT_PULLUP, /* SERVICEn */
+
+ GPIO223_GPIO | PIN_INPUT_PULLDOWN, /* HDMI_INT */
+ GPIO225_GPIO | PIN_OUTPUT_LOW, /* HDMI_ENABLE */
+};
+
+static UX500_PINS(db5500_kp_pins,
+ /* Keypad */
+ GPIO128_KP_I0 | PIN_INPUT_PULLUP | PIN_SLPM_INPUT_PULLUP,
+ GPIO130_KP_I1 | PIN_INPUT_PULLUP | PIN_SLPM_INPUT_PULLUP,
+ GPIO132_KP_I2 | PIN_INPUT_PULLUP | PIN_SLPM_INPUT_PULLUP,
+ GPIO134_KP_I3 | PIN_INPUT_PULLUP | PIN_SLPM_INPUT_PULLUP,
+ GPIO137_KP_O4 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+ GPIO139_KP_O5 | PIN_INPUT_PULLUP | PIN_SLPM_OUTPUT_LOW,
+);
+
+static UX500_PINS(db5500_pins_sdi0,
+ /* SDI0 (eMMC) */
+ GPIO5_MC0_DAT0 | PIN_INPUT_PULLUP,
+ GPIO6_MC0_DAT1 | PIN_INPUT_PULLUP,
+ GPIO7_MC0_DAT2 | PIN_INPUT_PULLUP,
+ GPIO8_MC0_DAT3 | PIN_INPUT_PULLUP,
+ GPIO9_MC0_DAT4 | PIN_INPUT_PULLUP,
+ GPIO10_MC0_DAT5 | PIN_INPUT_PULLUP,
+ GPIO11_MC0_DAT6 | PIN_INPUT_PULLUP,
+ GPIO12_MC0_DAT7 | PIN_INPUT_PULLUP,
+ GPIO13_MC0_CMD | PIN_INPUT_PULLUP,
+ GPIO14_MC0_CLK | PIN_OUTPUT_LOW,
+);
+
+static UX500_PINS(db5500_pins_sdi1,
+ /* SDI1 (SD-CARD) */
+ GPIO191_MC1_DAT0 | PIN_INPUT_PULLUP,
+ GPIO192_MC1_DAT1 | PIN_INPUT_PULLUP,
+ GPIO193_MC1_DAT2 | PIN_INPUT_PULLUP,
+ GPIO194_MC1_DAT3 | PIN_INPUT_PULLUP,
+ GPIO195_MC1_CLK | PIN_OUTPUT_LOW,
+ GPIO196_MC1_CMD | PIN_INPUT_PULLUP,
+ GPIO197_MC1_CMDDIR | PIN_OUTPUT_HIGH,
+ GPIO198_MC1_FBCLK | PIN_INPUT_PULLDOWN,
+ GPIO199_MC1_DAT0DIR | PIN_OUTPUT_HIGH,
+);
+
+static UX500_PINS(db5500_pins_sdi2,
+ /* SDI2 (eMMC) */
+ GPIO16_MC2_CMD | PIN_INPUT_PULLUP,
+ GPIO17_MC2_CLK | PIN_OUTPUT_LOW,
+ GPIO23_MC2_DAT0 | PIN_INPUT_PULLUP,
+ GPIO19_MC2_DAT1 | PIN_INPUT_PULLUP,
+ GPIO24_MC2_DAT2 | PIN_INPUT_PULLUP,
+ GPIO20_MC2_DAT3 | PIN_INPUT_PULLUP,
+ GPIO25_MC2_DAT4 | PIN_INPUT_PULLUP,
+ GPIO21_MC2_DAT5 | PIN_INPUT_PULLUP,
+ GPIO26_MC2_DAT6 | PIN_INPUT_PULLUP,
+ GPIO22_MC2_DAT7 | PIN_INPUT_PULLUP
+);
+
+static UX500_PINS(db5500_pins_sdi3,
+ /* SDI3 (SDIO) */
+ GPIO171_MC3_DAT0 | PIN_INPUT_PULLUP | PIN_LOWEMI_ENABLED,
+ GPIO172_MC3_DAT1 | PIN_INPUT_PULLUP | PIN_LOWEMI_ENABLED,
+ GPIO173_MC3_DAT2 | PIN_INPUT_PULLUP | PIN_LOWEMI_ENABLED,
+ GPIO174_MC3_DAT3 | PIN_INPUT_PULLUP | PIN_LOWEMI_ENABLED,
+ GPIO175_MC3_CMD | PIN_INPUT_PULLUP | PIN_LOWEMI_ENABLED,
+ GPIO176_MC3_CLK | PIN_OUTPUT_LOW,
+);
+
+static UX500_PINS(u5500_pins_i2c1,
+ GPIO3_I2C1_SCL | PIN_INPUT_NOPULL | PIN_LOWEMI_ENABLED,
+ GPIO4_I2C1_SDA | PIN_INPUT_NOPULL | PIN_LOWEMI_ENABLED,
+);
+
+static UX500_PINS(u5500_pins_i2c2,
+ GPIO218_I2C2_SCL | PIN_INPUT_NOPULL | PIN_LOWEMI_ENABLED,
+ GPIO219_I2C2_SDA | PIN_INPUT_NOPULL | PIN_LOWEMI_ENABLED,
+);
+
+static UX500_PINS(u5500_pins_i2c3,
+ GPIO177_I2C3_SCL | PIN_INPUT_NOPULL | PIN_LOWEMI_ENABLED,
+ GPIO178_I2C3_SDA | PIN_INPUT_NOPULL | PIN_LOWEMI_ENABLED,
+);
+
+static UX500_PINS(u5500_pins_spi3,
+ GPIO188_SPI3_RXD | PIN_INPUT_PULLUP,
+ GPIO189_SPI3_TXD | PIN_OUTPUT_LOW | PIN_LOWEMI_ENABLED,
+ GPIO190_SPI3_CLK | PIN_OUTPUT_LOW | PIN_LOWEMI_ENABLED,
+);
+
+/* USB */
+static UX500_PINS(u5500_pins_usb,
+ GPIO74_USB_NXT | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO72_USB_STP | PIN_OUTPUT_HIGH | PIN_SLPM_OUTPUT_HIGH,
+ GPIO75_USB_XCLK | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO73_USB_DIR | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO71_USB_DAT7 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO70_USB_DAT6 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO69_USB_DAT5 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO68_USB_DAT4 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO67_USB_DAT3 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO66_USB_DAT2 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO65_USB_DAT1 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+ GPIO64_USB_DAT0 | PIN_INPUT_NOPULL | PIN_SLPM_INPUT_PULLDOWN,
+);
+
+static struct ux500_pin_lookup u5500_pins[] = {
+ PIN_LOOKUP("nmk-i2c.1", &u5500_pins_i2c1),
+ PIN_LOOKUP("nmk-i2c.2", &u5500_pins_i2c2),
+ PIN_LOOKUP("nmk-i2c.3", &u5500_pins_i2c3),
+ PIN_LOOKUP("spi3", &u5500_pins_spi3),
+ PIN_LOOKUP("db5500_kp", &db5500_kp_pins),
+ PIN_LOOKUP("ab5500-usb.0", &u5500_pins_usb),
+ PIN_LOOKUP("sdi0", &db5500_pins_sdi0),
+ PIN_LOOKUP("sdi1", &db5500_pins_sdi1),
+ PIN_LOOKUP("sdi2", &db5500_pins_sdi2),
+ PIN_LOOKUP("sdi3", &db5500_pins_sdi3),
+};
+
+void __init u5500_pins_init(void)
+{
+ nmk_config_pins(u5500_pins_default, ARRAY_SIZE(u5500_pins_default));
+ ux500_pins_add(u5500_pins, ARRAY_SIZE(u5500_pins));
+}
+
+/* Stub function to make board-ux500-cg2900.c compile within a U5500 configuration */
+int pins_for_u9500(void)
+{
+ return 0;
+}
diff --git a/arch/arm/mach-ux500/board-u5500-regulators.c b/arch/arm/mach-ux500/board-u5500-regulators.c
new file mode 100644
index 00000000000..9e343259e53
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500-regulators.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/fixed.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab5500.h>
+
+#include "regulator-u5500.h"
+#include "board-u5500.h"
+
+/*
+ * AB5500
+ */
+
+static struct regulator_consumer_supply ab5500_ldo_g_consumers[] = {
+ REGULATOR_SUPPLY("vmmc", "sdi1"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_h_consumers[] = {
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ REGULATOR_SUPPLY("vdd", "1-004b"), /* Synaptics */
+ REGULATOR_SUPPLY("vin", "2-0036"), /* LM3530 */
+ REGULATOR_SUPPLY("vcpin", "spi1.0"),
+ REGULATOR_SUPPLY("v-ana", "mmio_camera"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.0"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.1"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_k_consumers[] = {
+ REGULATOR_SUPPLY("v-mmio-camera", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_h_consumers_pre_r3a[] = {
+ REGULATOR_SUPPLY("vddi", "mcde_disp_sony_acx424akp.0"),
+ REGULATOR_SUPPLY("vdd", "1-004b"), /* Synaptics */
+ REGULATOR_SUPPLY("vin", "2-0036"), /* LM3530 */
+ REGULATOR_SUPPLY("vcpin", "spi1.0"),
+ REGULATOR_SUPPLY("v-ana", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_k_consumers_pre_r3a[] = {
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.0"),
+ REGULATOR_SUPPLY("vdd", "lsm303dlh.1"),
+ REGULATOR_SUPPLY("v-mmio-camera", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_l_consumers[] = {
+ REGULATOR_SUPPLY("vmmc", "sdi0"),
+ REGULATOR_SUPPLY("vmmc", "sdi2"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_vdigmic_consumers[] = {
+ REGULATOR_SUPPLY("vdigmic", "ab5500-codec.0"),
+};
+
+static struct regulator_consumer_supply ab5500_ldo_sim_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.5"),
+};
+
+static struct regulator_consumer_supply ab5500_bias2_consumers[] = {
+ REGULATOR_SUPPLY("v-amic", NULL),
+};
+
+static struct regulator_init_data
+ab5500_regulator_init_data[AB5500_NUM_REGULATORS] = {
+ /* SD Card */
+ [AB5500_LDO_G] = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 2910000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .consumer_supplies = ab5500_ldo_g_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_g_consumers),
+ },
+ /* Display */
+ [AB5500_LDO_H] = {
+ .constraints = {
+ .min_uV = 2790000,
+ .max_uV = 2790000,
+ .apply_uV = 1,
+ .boot_on = 1, /* display on during boot */
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_h_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_h_consumers),
+ },
+ /* Camera */
+ [AB5500_LDO_K] = {
+ .constraints = {
+ .min_uV = 2790000,
+ .max_uV = 2790000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_k_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_k_consumers),
+ },
+ /* External eMMC */
+ [AB5500_LDO_L] = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 2910000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_MODE,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_IDLE,
+ },
+ .consumer_supplies = ab5500_ldo_l_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_l_consumers),
+ },
+ [AB5500_LDO_VDIGMIC] = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_vdigmic_consumers,
+ .num_consumer_supplies =
+ ARRAY_SIZE(ab5500_ldo_vdigmic_consumers),
+ },
+ [AB5500_LDO_SIM] = {
+ .constraints = {
+ .min_uV = 1875000,
+ .max_uV = 2900000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_ldo_sim_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_ldo_sim_consumers),
+ },
+ [AB5500_BIAS2] = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = ab5500_bias2_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(ab5500_bias2_consumers),
+ },
+};
+
+static struct ab5500_regulator_data
+ab5500_regulator_data[AB5500_NUM_REGULATORS] = {
+ [AB5500_LDO_H] = {
+ /*
+ * The sub camera on the dev boards needs both supplies to be
+ * on to avoid high leakage.
+ */
+ .off_is_lowpower = true,
+ },
+};
+
+struct ab5500_regulator_platform_data u5500_ab5500_regulator_data = {
+ .regulator = ab5500_regulator_init_data,
+ .data = ab5500_regulator_data,
+ .num_regulator = ARRAY_SIZE(ab5500_regulator_init_data),
+};
+
+
+static void __init u5500_regulators_init_debug(void)
+{
+ const char data[] = "debug";
+ int i;
+
+ for (i = 0; i < 6; i++)
+ platform_device_register_data(NULL, "reg-virt-consumer", i,
+ data, sizeof(data));
+}
+
+static struct regulator_consumer_supply u5500_vio_consumers[] = {
+ REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"),
+};
+
+static struct regulator_init_data u5500_vio_init_data = {
+ .constraints.always_on = 1,
+ .consumer_supplies = u5500_vio_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(u5500_vio_consumers),
+};
+
+static struct fixed_voltage_config u5500_vio_pdata __initdata = {
+ .supply_name = "vio_1v8",
+ .microvolts = 1800000,
+ .init_data = &u5500_vio_init_data,
+ .gpio = -EINVAL,
+};
+
+void __init u5500_regulators_init(void)
+{
+ if (u5500_board_is_pre_r3a()) {
+ struct regulator_init_data *rid = ab5500_regulator_init_data;
+
+ rid[AB5500_LDO_K].consumer_supplies
+ = ab5500_ldo_k_consumers_pre_r3a;
+ rid[AB5500_LDO_K].num_consumer_supplies
+ = ARRAY_SIZE(ab5500_ldo_k_consumers_pre_r3a);
+
+ rid[AB5500_LDO_H].consumer_supplies
+ = ab5500_ldo_h_consumers_pre_r3a;
+ rid[AB5500_LDO_H].num_consumer_supplies
+ = ARRAY_SIZE(ab5500_ldo_h_consumers_pre_r3a);
+ }
+
+ u5500_regulators_init_debug();
+
+ platform_device_register_data(NULL, "reg-fixed-voltage", -1,
+ &u5500_vio_pdata,
+ sizeof(u5500_vio_pdata));
+
+ regulator_has_full_constraints();
+}
diff --git a/arch/arm/mach-ux500/board-u5500-sdi.c b/arch/arm/mach-ux500/board-u5500-sdi.c
index 836112eedde..25b519b5476 100644
--- a/arch/arm/mach-ux500/board-u5500-sdi.c
+++ b/arch/arm/mach-ux500/board-u5500-sdi.c
@@ -5,34 +5,28 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/amba/bus.h>
#include <linux/amba/mmci.h>
#include <linux/mmc/host.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
-#include <plat/pincfg.h>
-#include <plat/gpio-nomadik.h>
-#include <mach/db5500-regs.h>
+#include <asm/mach-types.h>
#include <plat/ste_dma40.h>
+#include <mach/devices.h>
+#include <mach/hardware.h>
+#include <mach/ste-dma40-db5500.h>
-#include "pins-db5500.h"
#include "devices-db5500.h"
-#include "ste-dma40-db5500.h"
-
-static pin_cfg_t u5500_sdi_pins[] = {
- /* SDI0 (POP eMMC) */
- GPIO5_MC0_DAT0 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO6_MC0_DAT1 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO7_MC0_DAT2 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO8_MC0_DAT3 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO9_MC0_DAT4 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO10_MC0_DAT5 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO11_MC0_DAT6 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO12_MC0_DAT7 | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO13_MC0_CMD | PIN_DIR_INPUT | PIN_PULL_UP,
- GPIO14_MC0_CLK | PIN_DIR_OUTPUT | PIN_VAL_LOW,
-};
+#include "board-u5500.h"
+/*
+ * SDI 0 (eMMC)
+ */
#ifdef CONFIG_STE_DMA40
-struct stedma40_chan_cfg u5500_sdi0_dma_cfg_rx = {
+static struct stedma40_chan_cfg sdi0_dma_cfg_rx = {
.mode = STEDMA40_MODE_LOGICAL,
.dir = STEDMA40_PERIPH_TO_MEM,
.src_dev_type = DB5500_DMA_DEV24_SDMMC0_RX,
@@ -41,7 +35,7 @@ struct stedma40_chan_cfg u5500_sdi0_dma_cfg_rx = {
.dst_info.data_width = STEDMA40_WORD_WIDTH,
};
-static struct stedma40_chan_cfg u5500_sdi0_dma_cfg_tx = {
+static struct stedma40_chan_cfg sdi0_dma_cfg_tx = {
.mode = STEDMA40_MODE_LOGICAL,
.dir = STEDMA40_MEM_TO_PERIPH,
.src_dev_type = STEDMA40_DEV_SRC_MEMORY,
@@ -57,18 +51,206 @@ static struct mmci_platform_data u5500_sdi0_data = {
.capabilities = MMC_CAP_4_BIT_DATA |
MMC_CAP_8_BIT_DATA |
MMC_CAP_MMC_HIGHSPEED,
+ .capabilities2 = MMC_CAP2_NO_SLEEP_CMD,
.gpio_cd = -1,
.gpio_wp = -1,
#ifdef CONFIG_STE_DMA40
.dma_filter = stedma40_filter,
- .dma_rx_param = &u5500_sdi0_dma_cfg_rx,
- .dma_tx_param = &u5500_sdi0_dma_cfg_tx,
+ .dma_rx_param = &sdi0_dma_cfg_rx,
+ .dma_tx_param = &sdi0_dma_cfg_tx,
+#endif
+};
+
+/*
+ * SDI 1 (MicroSD slot)
+ */
+
+static int u5500_sdi1_ios_handler(struct device *dev, struct mmc_ios *ios)
+{
+ static int power_mode = -1;
+
+ if (power_mode == ios->power_mode)
+ return 0;
+
+ switch (ios->power_mode) {
+ case MMC_POWER_UP:
+ break;
+ case MMC_POWER_ON:
+ /*
+ * Level shifter voltage should depend on vdd to when deciding
+ * on either 1.8V or 2.9V. Once the decision has been made the
+ * level shifter must be disabled and re-enabled with a changed
+ * select signal in order to switch the voltage. Since there is
+ * no framework support yet for indicating 1.8V in vdd, use the
+ * default 2.9V.
+ */
+ gpio_set_value_cansleep(GPIO_MMC_CARD_CTRL, 1);
+ udelay(100);
+ break;
+ case MMC_POWER_OFF:
+ gpio_set_value_cansleep(GPIO_MMC_CARD_CTRL, 0);
+ break;
+ }
+
+ power_mode = ios->power_mode;
+ return 0;
+}
+
+#ifdef CONFIG_STE_DMA40
+static struct stedma40_chan_cfg sdi1_dma_cfg_rx = {
+ .mode = STEDMA40_MODE_LOGICAL,
+ .dir = STEDMA40_PERIPH_TO_MEM,
+ .src_dev_type = DB5500_DMA_DEV34_SDMMC1_RX,
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+};
+
+static struct stedma40_chan_cfg sdi1_dma_cfg_tx = {
+ .mode = STEDMA40_MODE_LOGICAL,
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB5500_DMA_DEV34_SDMMC1_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+};
+#endif
+
+static struct mmci_platform_data u5500_sdi1_data = {
+ .ios_handler = u5500_sdi1_ios_handler,
+ .ocr_mask = MMC_VDD_29_30,
+ .f_max = 50000000,
+ .capabilities = MMC_CAP_4_BIT_DATA |
+ MMC_CAP_SD_HIGHSPEED |
+ MMC_CAP_MMC_HIGHSPEED,
+ .gpio_cd = GPIO_SDMMC_CD,
+ .gpio_wp = -1,
+ .cd_invert = true,
+#ifdef CONFIG_STE_DMA40
+ .dma_filter = stedma40_filter,
+ .dma_rx_param = &sdi1_dma_cfg_rx,
+ .dma_tx_param = &sdi1_dma_cfg_tx,
#endif
};
+/*
+ * SDI2 (EMMC2)
+ */
+
+static struct stedma40_chan_cfg sdi2_dma_cfg_rx = {
+ .mode = STEDMA40_MODE_LOGICAL,
+ .dir = STEDMA40_PERIPH_TO_MEM,
+ .src_dev_type = DB5500_DMA_DEV26_SDMMC2_RX,
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+};
+
+static struct stedma40_chan_cfg sdi2_dma_cfg_tx = {
+ .mode = STEDMA40_MODE_LOGICAL,
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB5500_DMA_DEV26_SDMMC2_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+};
+
+static struct mmci_platform_data u5500_sdi2_data = {
+ .ocr_mask = MMC_VDD_165_195,
+ .f_max = 50000000,
+ .capabilities = MMC_CAP_4_BIT_DATA |
+ MMC_CAP_8_BIT_DATA |
+ MMC_CAP_MMC_HIGHSPEED,
+ .capabilities2 = MMC_CAP2_NO_SLEEP_CMD,
+ .gpio_cd = -1,
+ .gpio_wp = -1,
+#ifdef CONFIG_STE_DMA40
+ .dma_filter = stedma40_filter,
+ .dma_rx_param = &sdi2_dma_cfg_rx,
+ .dma_tx_param = &sdi2_dma_cfg_tx,
+#endif
+};
+
+/*
+ * SDI 3 (SDIO WLAN)
+ */
+#ifdef SDIO_DMA_ON
+#ifdef CONFIG_STE_DMA40
+static struct stedma40_chan_cfg sdi3_dma_cfg_rx = {
+ .mode = STEDMA40_MODE_LOGICAL,
+ .dir = STEDMA40_PERIPH_TO_MEM,
+ .src_dev_type = DB5500_DMA_DEV27_SDMMC3_RX,
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+};
+
+static struct stedma40_chan_cfg sdi3_dma_cfg_tx = {
+ .mode = STEDMA40_MODE_LOGICAL,
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB5500_DMA_DEV27_SDMMC3_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+};
+#endif
+#endif
+
+static struct mmci_platform_data u5500_sdi3_data = {
+ .ocr_mask = MMC_VDD_29_30,
+ .f_max = 50000000,
+ .capabilities = MMC_CAP_4_BIT_DATA,
+ .gpio_cd = -1,
+ .gpio_wp = -1,
+#ifdef SDIO_DMA_ON
+#ifdef CONFIG_STE_DMA40
+ .dma_filter = stedma40_filter,
+ .dma_rx_param = &sdi3_dma_cfg_rx,
+ .dma_tx_param = &sdi3_dma_cfg_tx,
+#endif
+#endif
+};
+
+static void sdi1_configure(void)
+{
+ int pin[2];
+ int ret;
+
+ /* Level-shifter GPIOs */
+ pin[0] = GPIO_MMC_CARD_CTRL;
+ pin[1] = GPIO_MMC_CARD_VSEL;
+
+ ret = gpio_request(pin[0], "MMC_CARD_CTRL");
+ if (!ret)
+ ret = gpio_request(pin[1], "MMC_CARD_VSEL");
+
+ if (ret) {
+ pr_warning("unable to config sdi0 gpios for level shifter.\n");
+ return;
+ }
+ /* Select the default 2.9V and eanble level shifter */
+ gpio_direction_output(pin[0], 1);
+ gpio_direction_output(pin[1], 0);
+}
+
void __init u5500_sdi_init(struct device *parent)
{
- nmk_config_pins(u5500_sdi_pins, ARRAY_SIZE(u5500_sdi_pins));
+ u32 periphid = 0x10480180;
+
+ /*
+ * Fix me in 5500 v2.1
+ * Dynamic detection of booting device by reading
+ * ROM debug register from BACKUP RAM and register the
+ * corresponding EMMC.
+ * This is done due to wrong configuration of MMC0 clock
+ * in ROM code for u5500 v2.
+ */
+ if (u5500_get_boot_mmc() == 2)
+ db5500_add_sdi2(parent, &u5500_sdi2_data, periphid);
+ else
+ db5500_add_sdi0(parent, &u5500_sdi0_data, periphid);
- db5500_add_sdi0(parent, &u5500_sdi0_data);
+ sdi1_configure();
+ db5500_add_sdi1(parent, &u5500_sdi1_data, periphid);
+ db5500_add_sdi3(parent, &u5500_sdi3_data, periphid);
}
diff --git a/arch/arm/mach-ux500/board-u5500.c b/arch/arm/mach-ux500/board-u5500.c
index 0ff4be72a80..86222034890 100644
--- a/arch/arm/mach-ux500/board-u5500.c
+++ b/arch/arm/mach-ux500/board-u5500.c
@@ -1,7 +1,6 @@
/*
* Copyright (C) ST-Ericsson SA 2010
*
- * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
*/
@@ -9,8 +8,27 @@
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
#include <linux/irq.h>
+#include <linux/gpio/nomadik.h>
#include <linux/i2c.h>
#include <linux/mfd/abx500/ab5500.h>
+#include <linux/ab5500-vibra.h>
+#include <linux/amba/pl022.h>
+#include <linux/delay.h>
+#include <linux/led-lm3530.h>
+#include <../drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h>
+#include <linux/input/matrix_keypad.h>
+#ifdef CONFIG_SENSORS_LSM303DLH
+#include <linux/lsm303dlh.h>
+#endif
+#include <linux/leds-ab5500.h>
+#ifdef CONFIG_TOUCHSCREEN_CYTTSP_SPI
+#include <linux/cyttsp.h>
+#endif
+#include <linux/input/abx500-accdet.h>
+
+#ifdef CONFIG_AV8100
+#include <video/av8100.h>
+#endif
#include <asm/hardware/gic.h>
#include <asm/mach/arch.h>
@@ -18,36 +36,141 @@
#include <plat/pincfg.h>
#include <plat/i2c.h>
-#include <plat/gpio-nomadik.h>
#include <mach/hardware.h>
+#include <mach/ste-dma40-db5500.h>
+#ifdef CONFIG_UX500_SOC_DBX500
+#include <mach/msp.h>
+#endif
#include <mach/devices.h>
#include <mach/setup.h>
+#include <mach/db5500-keypad.h>
+#include <mach/crypto-ux500.h>
+#include <mach/usb.h>
#include "pins-db5500.h"
+#include "pins.h"
#include "devices-db5500.h"
-#include <linux/led-lm3530.h>
+#include "board-u5500.h"
+#include "board-u5500-bm.h"
+#include "board-u5500-wlan.h"
+#include "board-ux500-usb.h"
+#ifdef CONFIG_SENSORS_LSM303DLH
/*
- * GPIO
+ * LSM303DLH
*/
-static pin_cfg_t u5500_pins[] = {
- /* I2C */
- GPIO218_I2C2_SCL | PIN_INPUT_PULLUP,
- GPIO219_I2C2_SDA | PIN_INPUT_PULLUP,
+static struct lsm303dlh_platform_data __initdata lsm303dlh_pdata = {
+ .name_a = "lsm303dlh.0",
+ .name_m = "lsm303dlh.1",
+ .axis_map_x = 1,
+ .axis_map_y = 0,
+ .axis_map_z = 2,
+/* display is mounted reverse in the hardware */
+ .negative_x = 1,
+ .negative_y = 1,
+ .negative_z = 1,
+};
+#endif
- /* DISPLAY_ENABLE */
- GPIO226_GPIO | PIN_OUTPUT_LOW,
+/*
+ * Touchscreen
+ */
+static struct synaptics_rmi4_platform_data rmi4_i2c_platformdata = {
+ .irq_number = NOMADIK_GPIO_TO_IRQ(179),
+ .irq_type = (IRQF_TRIGGER_FALLING | IRQF_SHARED),
+ .x_flip = false,
+ .y_flip = true,
+ .regulator_en = true,
+};
- /* Backlight Enbale */
- GPIO224_GPIO | PIN_OUTPUT_HIGH,
+static struct av8100_platform_data av8100_plat_data = {
+ .irq = NOMADIK_GPIO_TO_IRQ(223),
+ .reset = 225,
+ .alt_powerupseq = true,
+ .mclk_freq = 1, /* MCLK_RNG_22_27 */
};
+
+/*
+ * leds LM3530
+ */
+static struct lm3530_platform_data u5500_als_platform_data = {
+ .mode = LM3530_BL_MODE_MANUAL,
+ .als_input_mode = LM3530_INPUT_ALS1,
+ .max_current = LM3530_FS_CURR_26mA,
+ .pwm_pol_hi = true,
+ .als_avrg_time = LM3530_ALS_AVRG_TIME_4096ms,
+ .brt_ramp_law = 1, /* Linear */
+ .brt_ramp_fall = LM3530_RAMP_TIME_260ms,
+ .brt_ramp_rise = LM3530_RAMP_TIME_260ms,
+ .als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm,
+ .als2_resistor_sel = LM3530_ALS_IMPD_Z,
+ .als_vmin = 730, /* mV */
+ .als_vmax = 1020, /* mV */
+ .brt_val = 0x7F, /* Max brightness */
+ .hw_en_gpio = LM3530_BL_ENABLE_GPIO,
+};
+
+
+/* leds-ab5500 */
+static struct ab5500_hvleds_platform_data ab5500_hvleds_data = {
+ .hw_fade = false,
+ .leds = {
+ [0] = {
+ .name = "red",
+ .led_on = true,
+ .led_id = 0,
+ .fade_hi = 255,
+ .fade_lo = 0,
+ .max_current = 10, /* wrong value may damage h/w */
+ },
+ [1] = {
+ .name = "green",
+ .led_on = true,
+ .led_id = 1,
+ .fade_hi = 255,
+ .fade_lo = 0,
+ .max_current = 10, /* wrong value may damage h/w */
+ },
+ [2] {
+ .name = "blue",
+ .led_on = true,
+ .led_id = 2,
+ .fade_hi = 255,
+ .fade_lo = 0,
+ .max_current = 10, /* wrong value may damage h/w */
+ },
+ },
+};
+
+static struct ab5500_ponkey_platform_data ab5500_ponkey_data = {
+ /*
+ * Shutdown time in secs. Can be set
+ * to 10sec, 5sec and 0sec(disabled)
+ */
+ .shutdown_secs = 10,
+};
+
+/* ab5500-vibra */
+static struct ab5500_vibra_platform_data ab5500_vibra_data = {
+ .type = AB5500_VIB_ROTARY,
+ .voltage = AB5500_VIB_VOLT_MIN,
+ /*
+ * EOL voltage in millivolts. By default, it is
+ * disabled. Set threshold volatge to enable.
+ */
+ .eol_voltage = 0,
+ .res_freq = AB5500_VIB_RFREQ_150HZ,
+ .magnitude = 0x7F,
+ .pulse = AB5500_VIB_PULSE_130ms,
+};
+
/*
* I2C
*/
-#define U5500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, _sm) \
+#define U5500_I2C_CONTROLLER(id, _slsu, _tft, _rft, clk, t_out, _sm) \
static struct nmk_i2c_controller u5500_i2c##id##_data = { \
/* \
* slave data setup time, which is \
@@ -62,31 +185,35 @@ static struct nmk_i2c_controller u5500_i2c##id##_data = { \
.rft = _rft, \
/* std. mode operation */ \
.clk_freq = clk, \
+ /* Slave response timeout(ms) */\
+ .timeout = t_out, \
.sm = _sm, \
}
+
/*
- * The board uses TODO <3> i2c controllers, initialize all of
+ * The board uses 3 i2c controllers, initialize all of
* them with slave data setup time of 250 ns,
* Tx & Rx FIFO threshold values as 1 and standard
* mode of operation
*/
-U5500_I2C_CONTROLLER(2, 0xe, 1, 1, 400000, I2C_FREQ_MODE_FAST);
+U5500_I2C_CONTROLLER(1, 0xe, 1, 10, 400000, 200, I2C_FREQ_MODE_FAST);
+U5500_I2C_CONTROLLER(2, 0xe, 1, 10, 400000, 200, I2C_FREQ_MODE_FAST);
+U5500_I2C_CONTROLLER(3, 0xe, 1, 10, 400000, 200, I2C_FREQ_MODE_FAST);
-static struct lm3530_platform_data u5500_als_platform_data = {
- .mode = LM3530_BL_MODE_MANUAL,
- .als_input_mode = LM3530_INPUT_ALS1,
- .max_current = LM3530_FS_CURR_26mA,
- .pwm_pol_hi = true,
- .als_avrg_time = LM3530_ALS_AVRG_TIME_512ms,
- .brt_ramp_law = 1, /* Linear */
- .brt_ramp_fall = LM3530_RAMP_TIME_8s,
- .brt_ramp_rise = LM3530_RAMP_TIME_8s,
- .als1_resistor_sel = LM3530_ALS_IMPD_13_53kOhm,
- .als2_resistor_sel = LM3530_ALS_IMPD_Z,
- .als_vmin = 730, /* mV */
- .als_vmax = 1020, /* mV */
- .brt_val = 0x7F, /* Max brightness */
+static struct i2c_board_info __initdata u5500_i2c2_sensor_devices[] = {
+#ifdef CONFIG_SENSORS_LSM303DLH
+ {
+ /* LSM303DLHC Accelerometer */
+ I2C_BOARD_INFO("lsm303dlhc_a", 0x19),
+ .platform_data = &lsm303dlh_pdata,
+ },
+ {
+ /* LSM303DLH Magnetometer */
+ I2C_BOARD_INFO("lsm303dlh_m", 0x1E),
+ .platform_data = &lsm303dlh_pdata,
+ },
+#endif
};
static struct i2c_board_info __initdata u5500_i2c2_devices[] = {
@@ -95,43 +222,561 @@ static struct i2c_board_info __initdata u5500_i2c2_devices[] = {
I2C_BOARD_INFO("lm3530-led", 0x36),
.platform_data = &u5500_als_platform_data,
},
+ {
+ I2C_BOARD_INFO("av8100", 0x70),
+ .platform_data = &av8100_plat_data,
+ },
};
-static void __init u5500_i2c_init(struct device *parent)
+/*
+ * Keypad
+ */
+
+#define ROW_PIN_I0 128
+#define ROW_PIN_I1 130
+#define ROW_PIN_I2 132
+#define ROW_PIN_I3 134
+#define COL_PIN_O4 137
+#define COL_PIN_O5 139
+
+static int db5500_kp_rows[] = {
+ ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3,
+};
+
+static int db5500_kp_cols[] = {
+ COL_PIN_O4, COL_PIN_O5,
+};
+
+static bool db5500_config;
+static int db5500_set_gpio_row(int gpio)
{
- db5500_add_i2c2(parent, &u5500_i2c2_data);
- i2c_register_board_info(2, ARRAY_AND_SIZE(u5500_i2c2_devices));
+ int ret = -1;
+
+
+ if (!db5500_config) {
+ ret = gpio_request(gpio, "db5500_kpd");
+ if (ret < 0) {
+ pr_err("db5500_set_gpio_row: gpio request failed\n");
+ return ret;
+ }
+ }
+
+ ret = gpio_direction_output(gpio, 1);
+ if (ret < 0) {
+ pr_err("db5500_set_gpio_row: gpio direction failed\n");
+ gpio_free(gpio);
+ }
+
+ return ret;
}
+static int db5500_kp_init(void)
+{
+ struct ux500_pins *pins;
+ int ret, i;
+
+ pins = ux500_pins_get("db5500_kp");
+ if (pins)
+ ux500_pins_enable(pins);
+
+ for (i = 0; i < ARRAY_SIZE(db5500_kp_rows); i++) {
+ ret = db5500_set_gpio_row(db5500_kp_rows[i]);
+ if (ret < 0) {
+ pr_err("db5500_kp_init: failed init\n");
+ return ret;
+ }
+ }
+
+ if (!db5500_config)
+ db5500_config = true;
+
+ return 0;
+}
+
+static int db5500_kp_exit(void)
+{
+ struct ux500_pins *pins;
+
+ pins = ux500_pins_get("db5500_kp");
+ if (pins)
+ ux500_pins_disable(pins);
+
+ return 0;
+}
+
+static const unsigned int u5500_keymap[] = {
+ KEY(4, 0, KEY_CAMERA), /* Camera2 */
+ KEY(4, 1, KEY_CAMERA_FOCUS), /* Camera1 */
+ KEY(4, 2, KEY_MENU),
+ KEY(4, 3, KEY_BACK),
+ KEY(5, 2, KEY_SEND),
+ KEY(5, 3, KEY_HOME),
+#ifndef CONFIG_INPUT_AB8500_PONKEY
+ /* AB5500 ONSWa is also hooked up to this key */
+ KEY(8, 0, KEY_END),
+#endif
+ KEY(8, 1, KEY_VOLUMEUP),
+ KEY(8, 2, KEY_VOLUMEDOWN),
+};
+
+static struct matrix_keymap_data u5500_keymap_data = {
+ .keymap = u5500_keymap,
+ .keymap_size = ARRAY_SIZE(u5500_keymap),
+};
+
+static struct db5500_keypad_platform_data u5500_keypad_board = {
+ .init = db5500_kp_init,
+ .exit = db5500_kp_exit,
+ .gpio_input_pins = db5500_kp_rows,
+ .gpio_output_pins = db5500_kp_cols,
+ .keymap_data = &u5500_keymap_data,
+ .no_autorepeat = true,
+ .krow = ARRAY_SIZE(db5500_kp_rows),
+ .kcol = ARRAY_SIZE(db5500_kp_cols),
+ .debounce_ms = 40, /* milliseconds */
+ .switch_delay = 200, /* in jiffies */
+};
+
+#ifdef CONFIG_UX500_SOC_DBX500
+/*
+ * MSP
+ */
+
+#define MSP_DMA(num, eventline) \
+static struct stedma40_chan_cfg msp##num##_dma_rx = { \
+ .high_priority = true, \
+ .dir = STEDMA40_PERIPH_TO_MEM, \
+ .src_dev_type = eventline##_RX, \
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY, \
+ .src_info.psize = STEDMA40_PSIZE_LOG_4, \
+ .dst_info.psize = STEDMA40_PSIZE_LOG_4, \
+}; \
+ \
+static struct stedma40_chan_cfg msp##num##_dma_tx = { \
+ .high_priority = true, \
+ .dir = STEDMA40_MEM_TO_PERIPH, \
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY, \
+ .dst_dev_type = eventline##_TX, \
+ .src_info.psize = STEDMA40_PSIZE_LOG_4, \
+ .dst_info.psize = STEDMA40_PSIZE_LOG_4, \
+}
+
+MSP_DMA(0, DB5500_DMA_DEV9_MSP0);
+MSP_DMA(1, DB5500_DMA_DEV10_MSP1);
+MSP_DMA(2, DB5500_DMA_DEV11_MSP2);
+
+static struct msp_i2s_platform_data u5500_msp0_data = {
+ .id = MSP_0_I2S_CONTROLLER,
+ .msp_i2s_dma_rx = &msp0_dma_rx,
+ .msp_i2s_dma_tx = &msp0_dma_tx,
+};
+
+static struct msp_i2s_platform_data u5500_msp1_data = {
+ .id = MSP_1_I2S_CONTROLLER,
+ .msp_i2s_dma_rx = &msp1_dma_rx,
+ .msp_i2s_dma_tx = &msp1_dma_tx,
+};
+
+static struct msp_i2s_platform_data u5500_msp2_data = {
+ .id = MSP_2_I2S_CONTROLLER,
+ .msp_i2s_dma_rx = &msp2_dma_rx,
+ .msp_i2s_dma_tx = &msp2_dma_tx,
+};
+
+static void __init u5500_msp_init(void)
+{
+ db5500_add_msp0_i2s(&u5500_msp0_data);
+ db5500_add_msp1_i2s(&u5500_msp1_data);
+ db5500_add_msp2_i2s(&u5500_msp2_data);
+}
+#else
+static void __init u5500_msp_init(void)
+{
+}
+#endif
+
+/*
+ * SPI
+ */
+
+static struct pl022_ssp_controller u5500_spi3_data = {
+ .bus_id = 1,
+ .num_chipselect = 4, /* 3 possible CS lines + 1 for tests */
+};
+
+static void __init u5500_spi_init(void)
+{
+ db5500_add_spi3(&u5500_spi3_data);
+}
+
+static struct resource ab5500_resources[] = {
+ [0] = {
+ .start = IRQ_DB5500_PRCMU_ABB,
+ .end = IRQ_DB5500_PRCMU_ABB,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+
+#ifdef CONFIG_INPUT_AB5500_ACCDET
+static struct abx500_accdet_platform_data ab5500_accdet_pdata = {
+ .btn_keycode = KEY_MEDIA,
+ .accdet1_dbth = ACCDET1_TH_300mV | ACCDET1_DB_10ms,
+ .accdet2122_th = ACCDET21_TH_300mV | ACCDET22_TH_300mV,
+ .is_detection_inverted = false,
+ };
+#endif
+
+static struct abx500_init_settings ab5500_init_settings[] = {
+ {
+ .bank = AB5500_BANK_SIM_USBSIM,
+ .reg = 0x17,
+ .setting = 0x0F,
+ },
+ /* SIMOFF_N is not connected and is set to GPIO.
+ Set SIMOFF_N in invert mode */
+ {
+ .bank = AB5500_BANK_SIM_USBSIM,
+ .reg = 0x13,
+ .setting = 0x4,
+ },
+ {
+ .bank = AB5500_BANK_SIM_USBSIM,
+ .reg = 0x18,
+ .setting = 0x10,
+ },
+ /* Set unused PWRCTRL0 & PWRCTRL1 as GPIOs */
+ {
+ .bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .reg = 0x30,
+ .setting = 0x30,
+ },
+ /* Set unused EXTBST1/CLK/SLP,RTCCLK2 as GPIOs */
+ {
+ .bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .reg = 0x31,
+ .setting = 0x74,
+ },
+ /* Set unused AVCTRL,EXTBST2/CLK/SLP,RTCCLK2 as GPIO */
+ {
+ .bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .reg = 0x33,
+ .setting = 0x3C,
+ },
+ /* Set unused EXT32CLK as GPIO */
+ {
+ .bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ .reg = 0x35,
+ .setting = 0x1,
+ },
+ /* Set unused SIMOFF_N as GPIO */
+ {
+ .bank = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+ .reg = 0x31,
+ .setting = 0x80,
+ },
+ /* Set unused YcBcR2 & YcBcR1 as GPIO */
+ {
+ .bank = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+ .reg = 0x33,
+ .setting = 0xC0,
+ },
+ /* Set unused USBUICCPD/SE0/DATA,
+ YcBcR0,YcBcR3 as GPIO */
+ {
+ .bank = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST,
+ .reg = 0x34,
+ .setting = 0x1F,
+ },
+};
+
static struct ab5500_platform_data ab5500_plf_data = {
.irq = {
- .base = 0,
- .count = 0,
+ .base = IRQ_AB5500_BASE,
+ .count = AB5500_NR_IRQS,
},
- .init_settings = NULL,
- .init_settings_sz = 0,
- .pm_power_off = false,
+ .pm_power_off = true,
+ .regulator = &u5500_ab5500_regulator_data,
+#ifdef CONFIG_INPUT_AB5500_ACCDET
+ .dev_data[AB5500_DEVID_ACCDET] = &ab5500_accdet_pdata,
+ .dev_data_sz[AB5500_DEVID_ACCDET] = sizeof(ab5500_accdet_pdata),
+#endif
+ .dev_data[AB5500_DEVID_LEDS] = &ab5500_hvleds_data,
+ .dev_data_sz[AB5500_DEVID_LEDS] = sizeof(ab5500_hvleds_data),
+ .dev_data[AB5500_DEVID_VIBRATOR] = &ab5500_vibra_data,
+ .dev_data_sz[AB5500_DEVID_VIBRATOR] = sizeof(ab5500_vibra_data),
+ .init_settings = &ab5500_init_settings,
+ .init_settings_sz = ARRAY_SIZE(ab5500_init_settings),
+#if defined(CONFIG_AB5500_BM)
+ .dev_data[AB5500_DEVID_CHARGALG] = &abx500_bm_pt_data,
+ .dev_data_sz[AB5500_DEVID_CHARGALG] = sizeof(abx500_bm_pt_data),
+ .dev_data[AB5500_DEVID_CHARGER] = &abx500_bm_pt_data,
+ .dev_data_sz[AB5500_DEVID_CHARGER] = sizeof(abx500_bm_pt_data),
+ .dev_data[AB5500_DEVID_FG] = &abx500_bm_pt_data,
+ .dev_data_sz[AB5500_DEVID_FG] = sizeof(abx500_bm_pt_data),
+ .dev_data[AB5500_DEVID_BTEMP] = &abx500_bm_pt_data,
+ .dev_data_sz[AB5500_DEVID_BTEMP] = sizeof(abx500_bm_pt_data),
+#endif
+ .dev_data[AB5500_DEVID_ONSWA] = &ab5500_ponkey_data,
+ .dev_data_sz[AB5500_DEVID_ONSWA] = sizeof(ab5500_ponkey_data),
+ .dev_data[AB5500_DEVID_USB] = &abx500_usbgpio_plat_data,
+ .dev_data_sz[AB5500_DEVID_USB] = sizeof(abx500_usbgpio_plat_data),
};
-static struct platform_device ab5500_device = {
+static struct platform_device u5500_ab5500_device = {
.name = "ab5500-core",
.id = 0,
.dev = {
.platform_data = &ab5500_plf_data,
},
+ .num_resources = 1,
+ .resource = ab5500_resources,
+};
+
+static struct platform_device u5500_mloader_device = {
+ .name = "db5500_mloader",
+ .id = -1,
.num_resources = 0,
};
+static struct cryp_platform_data u5500_cryp1_platform_data = {
+ .mem_to_engine = {
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB5500_DMA_DEV48_CRYPTO1_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .mode = STEDMA40_MODE_LOGICAL,
+ .src_info.psize = STEDMA40_PSIZE_LOG_4,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_4,
+ },
+ .engine_to_mem = {
+ .dir = STEDMA40_PERIPH_TO_MEM,
+ .src_dev_type = DB5500_DMA_DEV48_CRYPTO1_RX,
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .mode = STEDMA40_MODE_LOGICAL,
+ .src_info.psize = STEDMA40_PSIZE_LOG_4,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_4,
+ }
+};
+
+static struct stedma40_chan_cfg u5500_hash_dma_cfg_tx = {
+ .dir = STEDMA40_MEM_TO_PERIPH,
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY,
+ .dst_dev_type = DB5500_DMA_DEV50_HASH1_TX,
+ .src_info.data_width = STEDMA40_WORD_WIDTH,
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,
+ .mode = STEDMA40_MODE_LOGICAL,
+ .src_info.psize = STEDMA40_PSIZE_LOG_16,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_16,
+};
+
+static struct hash_platform_data u5500_hash1_platform_data = {
+ .mem_to_engine = &u5500_hash_dma_cfg_tx,
+ .dma_filter = stedma40_filter,
+};
+
+/* modem crash dump detection driver data */
+static struct resource mcdd_resources[] = {
+ {
+ .name = "mcdd_intreset_addr",
+ .start = U5500_INTCON_MBOX1_INT_RESET_ADDR,
+ .end = U5500_INTCON_MBOX1_INT_RESET_ADDR,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "mcdd_mbox_irq",
+ .start = MBOX_PAIR1_VIRT_IRQ,
+ .end = MBOX_PAIR1_VIRT_IRQ,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device u5500_mcdd_device = {
+ .name = "u5500-mcdd-modem",
+ .id = 0,
+ .resource = mcdd_resources,
+ .num_resources = ARRAY_SIZE(mcdd_resources),
+};
+
static struct platform_device *u5500_platform_devices[] __initdata = {
- &ab5500_device,
+ &u5500_ab5500_device,
+#ifdef CONFIG_FB_MCDE
+ &u5500_mcde_device,
+#endif
+ &ux500_hwmem_device,
+ &u5500_b2r2_device,
+ &u5500_b2r2_blt_device,
+ &u5500_mloader_device,
+#ifdef CONFIG_U5500_MMIO
+ &u5500_mmio_device,
+#endif
+ &u5500_thsens_device,
+ &u5500_mcdd_device,
+};
+
+#define BACKUPRAM_ROM_DEBUG_ADDR 0xFFC
+#define MMC_BLOCK_ID 0x20
+
+int u5500_get_boot_mmc(void)
+{
+ unsigned int mmcblk;
+
+ mmcblk = readl(__io_address(U5500_BACKUPRAM1_BASE) +
+ BACKUPRAM_ROM_DEBUG_ADDR);
+
+ if (mmcblk & MMC_BLOCK_ID)
+ return 2;
+
+ return 0;
+}
+
+/*
+ * R3A (and presumably, future) S5500 boards have different regulator
+ * assignments from the earlier boards. Since there's no clean way to identify
+ * the board revision from hardware, we use the fact that R2A boots from MMC0
+ * (via peripheral boot) and R3A boots from MMC2 to distinguish them.
+ */
+bool u5500_board_is_pre_r3a(void)
+{
+ if (!cpu_is_u5500v20())
+ return false;
+
+ if (!u5500_board_is_s5500())
+ return true;
+
+ if (u5500_get_boot_mmc() == 2)
+ return false;
+
+ return true;
+}
+
+
+/*
+ * This function check whether it is Small S5500 board
+ * GPIO0 is HIGH for S5500
+ */
+bool u5500_board_is_s5500(void)
+{
+ static bool s5500;
+ static bool once;
+ int err, val;
+
+ if (once)
+ return s5500;
+
+ err = gpio_request(GPIO_BOARD_VERSION, "Board Version");
+ if (err) {
+ pr_err("Error %d while requesting GPIO for Board Version\n",
+ err);
+ return err;
+ }
+
+ err = gpio_direction_input(GPIO_BOARD_VERSION);
+ if (err) {
+ pr_err("Error %d while setting GPIO for Board Version"
+ "output mode\n", err);
+ return err;
+ }
+
+ val = gpio_get_value(GPIO_BOARD_VERSION);
+
+ gpio_free(GPIO_BOARD_VERSION);
+
+ s5500 = val;
+ once = true;
+
+ return val;
+}
+
+static long u5500_panic_blink(int state)
+{
+ gpio_direction_output(GPIO_SW_CRASH_INDICATOR, state);
+ return 0;
+}
+
+#define PRCC_K_SOFTRST_SET 0x18
+#define PRCC_K_SOFTRST_CLEAR 0x1C
+/* pl011 reset */
+static void ux500_uart3_reset(void)
+{
+ void __iomem *prcc_rst_set, *prcc_rst_clr;
+
+ prcc_rst_set = __io_address(U5500_CLKRST5_BASE +
+ PRCC_K_SOFTRST_SET);
+ prcc_rst_clr = __io_address(U5500_CLKRST5_BASE +
+ PRCC_K_SOFTRST_CLEAR);
+
+ /*
+ * Activate soft reset PRCC_K_SOFTRST_CLEAR
+ *
+ * As we are dealing with IP register lockup
+ * so to make double sure that IP gets reset
+ * and reset pulse remains for more than one
+ * clock cycle a delay is added.
+ */
+ writel((readl(prcc_rst_clr) | 0x08), prcc_rst_clr);
+ udelay(1);
+
+ /* Release soft reset PRCC_K_SOFTRST_SET */
+ writel((readl(prcc_rst_set) | 0x08), prcc_rst_set);
+ udelay(1);
+}
+
+static struct amba_pl011_data uart3_plat = {
+ .reset = ux500_uart3_reset,
};
+static void __init u5500_i2c_init(struct device *parent)
+{
+ db5500_add_i2c1(pareent, &u5500_i2c1_data);
+ db5500_add_i2c2(pareent, &u5500_i2c2_data);
+ db5500_add_i2c3(pareent, &u5500_i2c3_data);
+
+ i2c_register_board_info(2, ARRAY_AND_SIZE(u5500_i2c2_devices));
+ i2c_register_board_info(2, ARRAY_AND_SIZE(u5500_i2c2_sensor_devices));
+}
+
static void __init u5500_uart_init(struct device *parent)
{
db5500_add_uart0(parent, NULL);
db5500_add_uart1(parent, NULL);
db5500_add_uart2(parent, NULL);
+ db5500_add_uart3(parent, &uart3_plat);
+}
+
+static void __init u5500_cryp1_hash1_init(void)
+{
+ db5500_add_cryp1(&u5500_cryp1_platform_data);
+ db5500_add_hash1(&u5500_hash1_platform_data);
+}
+
+static int __init u5500_accel_sensor_init(void)
+{
+ int status;
+ union i2c_smbus_data data;
+ struct i2c_adapter *i2c2;
+
+ i2c2 = i2c_get_adapter(2);
+ if (!i2c2) {
+ pr_err("failed to get i2c adapter\n");
+ return -ENODEV;
+ }
+ status = i2c_smbus_xfer(i2c2, 0x19 , 0,
+ I2C_SMBUS_READ, 0x0F ,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (status < 0)
+ lsm303dlh_pdata.chip_id = 0;
+ else
+ lsm303dlh_pdata.chip_id = data.byte;
+
+ i2c_put_adapter(i2c2);
+
+ return status;
}
+module_init(u5500_accel_sensor_init);
static void __init u5500_init_machine(void)
{
@@ -139,17 +784,34 @@ static void __init u5500_init_machine(void)
int i;
parent = u5500_init_devices();
- nmk_config_pins(u5500_pins, ARRAY_SIZE(u5500_pins));
+ u5500_regulators_init();
+ u5500_pins_init();
u5500_i2c_init(parent);
+ u5500_msp_init();
+ u5500_spi_init();
+
u5500_sdi_init(parent);
u5500_uart_init(parent);
for (i = 0; i < ARRAY_SIZE(u5500_platform_devices); i++)
u5500_platform_devices[i]->dev.parent = parent;
+ u5500_wlan_init();
+
+ db5500_add_keypad(&u5500_keypad_board);
+ u5500_cryp1_hash1_init();
+
+#ifdef CONFIG_TOUCHSCREEN_CYTTSP_SPI
+ u5500_cyttsp_init();
+#endif
+
platform_add_devices(u5500_platform_devices,
ARRAY_SIZE(u5500_platform_devices));
+
+ if (!gpio_request_one(GPIO_SW_CRASH_INDICATOR, GPIOF_OUT_INIT_LOW,
+ "SW_CRASH_INDICATOR"))
+ panic_blink = u5500_panic_blink;
}
MACHINE_START(U5500, "ST-Ericsson U5500 Platform")
@@ -159,4 +821,14 @@ MACHINE_START(U5500, "ST-Ericsson U5500 Platform")
.timer = &ux500_timer,
.handle_irq = gic_handle_irq,
.init_machine = u5500_init_machine,
+ .restart = ux500_restart,
+MACHINE_END
+
+MACHINE_START(B5500, "ST-Ericsson U5500 Big Board")
+ .atag_offset = 0x00000100,
+ .map_io = u5500_map_io,
+ .init_irq = ux500_init_irq,
+ .timer = &ux500_timer,
+ .init_machine = u5500_init_machine,
+ .restart = ux500_restart,
MACHINE_END
diff --git a/arch/arm/mach-ux500/board-u5500.h b/arch/arm/mach-ux500/board-u5500.h
new file mode 100644
index 00000000000..a7e4bbbc714
--- /dev/null
+++ b/arch/arm/mach-ux500/board-u5500.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __BOARD_U5500_H
+#define __BOARD_U5500_H
+
+#define GPIO_SDMMC_CD 180
+#define GPIO_MMC_CARD_CTRL 227
+#define GPIO_MMC_CARD_VSEL 185
+#define GPIO_BOARD_VERSION 0
+#define GPIO_PRIMARY_CAM_XSHUTDOWN 1
+#define GPIO_SECONDARY_CAM_XSHUTDOWN 2
+#define GPIO_CAMERA_PMIC_EN 212
+#define GPIO_SW_CRASH_INDICATOR 214
+
+#define CYPRESS_TOUCH_INT_PIN 179
+#define CYPRESS_TOUCH_RST_GPIO 135
+#define CYPRESS_SLAVE_SELECT_GPIO 186
+
+#define LM3530_BL_ENABLE_GPIO 224
+
+struct ab5500_regulator_platform_data;
+extern struct ab5500_regulator_platform_data u5500_ab5500_regulator_data;
+
+extern void u5500_pins_init(void);
+extern void __init u5500_regulators_init(void);
+void u5500_cyttsp_init(void);
+bool u5500_board_is_s5500(void);
+int u5500_get_boot_mmc(void);
+bool u5500_board_is_pre_r3a(void);
+
+#endif
diff --git a/arch/arm/mach-ux500/board-ux500-usb.h b/arch/arm/mach-ux500/board-ux500-usb.h
new file mode 100644
index 00000000000..6b35a181c0a
--- /dev/null
+++ b/arch/arm/mach-ux500/board-ux500-usb.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Saketh Ram Bommisetti <sakethram.bommisetti@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __BOARD_UX500_USB_H
+#define __BOARD_UX500_USB_H
+
+extern struct abx500_usbgpio_platform_data abx500_usbgpio_plat_data;
+
+#endif
diff --git a/arch/arm/mach-ux500/cache-l2x0.c b/arch/arm/mach-ux500/cache-l2x0.c
index 77a75ed0df6..7048fc71aff 100644
--- a/arch/arm/mach-ux500/cache-l2x0.c
+++ b/arch/arm/mach-ux500/cache-l2x0.c
@@ -36,9 +36,11 @@ static int __init ux500_l2x0_unlock(void)
static int __init ux500_l2x0_init(void)
{
+ uint32_t aux_val = 0x3e000000; /* 8 way associativity, force WA */
+
if (cpu_is_u5500())
l2x0_base = __io_address(U5500_L2CC_BASE);
- else if (cpu_is_u8500())
+ else if (cpu_is_u8500() || cpu_is_u9540())
l2x0_base = __io_address(U8500_L2CC_BASE);
else
ux500_unknown_soc();
@@ -46,11 +48,18 @@ static int __init ux500_l2x0_init(void)
/* Unlock before init */
ux500_l2x0_unlock();
- /* 64KB way size, 8 way associativity, force WA */
+ /* u9540's L2 has 128KB way size */
+ if (cpu_is_u9540())
+ aux_val |=
+ (0x4 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); /* 128KB way size */
+ else
+ aux_val |=
+ (0x3 << L2X0_AUX_CTRL_WAY_SIZE_SHIFT); /* 64KB way size */
+
if (of_have_populated_dt())
- l2x0_of_init(0x3e060000, 0xc0000fff);
+ l2x0_of_init(aux_val, 0xc0000fff);
else
- l2x0_init(l2x0_base, 0x3e060000, 0xc0000fff);
+ l2x0_init(l2x0_base, aux_val, 0xc0000fff);
/*
* We can't disable l2 as we are in non secure mode, currently
diff --git a/arch/arm/mach-ux500/clock-db5500.c b/arch/arm/mach-ux500/clock-db5500.c
new file mode 100644
index 00000000000..aa61b1129e6
--- /dev/null
+++ b/arch/arm/mach-ux500/clock-db5500.c
@@ -0,0 +1,745 @@
+/*
+ * Copyright (C) 2009 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * 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 <linux/init.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <plat/pincfg.h>
+
+#include <mach/hardware.h>
+
+#include "clock.h"
+#include "pins-db5500.h"
+
+static DEFINE_MUTEX(sysclk_mutex);
+static DEFINE_MUTEX(pll_mutex);
+
+/* SysClk operations. */
+static int sysclk_enable(struct clk *clk)
+{
+ return prcmu_request_clock(PRCMU_SYSCLK, true);
+}
+
+static void sysclk_disable(struct clk *clk)
+{
+
+ prcmu_request_clock(PRCMU_SYSCLK, false);
+ return;
+}
+
+static struct clkops sysclk_ops = {
+ .enable = sysclk_enable,
+ .disable = sysclk_disable,
+};
+
+static int rtc_clk_enable(struct clk *clk)
+{
+ return ab5500_clock_rtc_enable(clk->cg_sel, true);
+}
+
+static void rtc_clk_disable(struct clk *clk)
+{
+ int ret = ab5500_clock_rtc_enable(clk->cg_sel, false);
+
+ if (ret)
+ pr_err("clock: %s failed to disable: %d\n", clk->name, ret);
+}
+
+static struct clkops rtc_clk_ops = {
+ .enable = rtc_clk_enable,
+ .disable = rtc_clk_disable,
+};
+
+static pin_cfg_t clkout0_pins[] = {
+ GPIO161_CLKOUT_0 | PIN_OUTPUT_LOW,
+};
+
+static pin_cfg_t clkout1_pins[] = {
+ GPIO162_CLKOUT_1 | PIN_OUTPUT_LOW,
+};
+
+static int clkout0_enable(struct clk *clk)
+{
+ return nmk_config_pins(clkout0_pins, ARRAY_SIZE(clkout0_pins));
+}
+
+static void clkout0_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pins_sleep(clkout0_pins, ARRAY_SIZE(clkout0_pins));
+ if (!r)
+ return;
+
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static int clkout1_enable(struct clk *clk)
+{
+ return nmk_config_pins(clkout1_pins, ARRAY_SIZE(clkout0_pins));
+}
+
+static void clkout1_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pins_sleep(clkout1_pins, ARRAY_SIZE(clkout1_pins));
+ if (!r)
+ return;
+
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static struct clkops clkout0_ops = {
+ .enable = clkout0_enable,
+ .disable = clkout0_disable,
+};
+
+static struct clkops clkout1_ops = {
+ .enable = clkout1_enable,
+ .disable = clkout1_disable,
+};
+
+#define PRCM_CLKOCR2 0x58C
+#define PRCM_CLKOCR2_REFCLK (1 << 0)
+#define PRCM_CLKOCR2_STATIC0 (1 << 2)
+
+static int clkout2_enable(struct clk *clk)
+{
+ prcmu_write(PRCM_CLKOCR2, PRCM_CLKOCR2_REFCLK);
+ return 0;
+}
+
+static void clkout2_disable(struct clk *clk)
+{
+ prcmu_write(PRCM_CLKOCR2, PRCM_CLKOCR2_STATIC0);
+}
+
+static struct clkops clkout2_ops = {
+ .enable = clkout2_enable,
+ .disable = clkout2_disable,
+};
+
+#define DEF_PER1_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST1_BASE, _cg_bit, &per1clk)
+#define DEF_PER2_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST2_BASE, _cg_bit, &per2clk)
+#define DEF_PER3_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST3_BASE, _cg_bit, &per3clk)
+#define DEF_PER5_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST5_BASE, _cg_bit, &per5clk)
+#define DEF_PER6_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U5500_CLKRST6_BASE, _cg_bit, &per6clk)
+
+#define DEF_PER1_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST1_BASE, _cg_bit, _parent, &per1clk)
+#define DEF_PER2_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST2_BASE, _cg_bit, _parent, &per2clk)
+#define DEF_PER3_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST3_BASE, _cg_bit, _parent, &per3clk)
+#define DEF_PER5_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST5_BASE, _cg_bit, _parent, &per5clk)
+#define DEF_PER6_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U5500_CLKRST6_BASE, _cg_bit, _parent, &per6clk)
+
+/* Clock sources. */
+
+static struct clk soc0_pll = {
+ .name = "soc0_pll",
+ .ops = &prcmu_clk_ops,
+ .mutex = &pll_mutex,
+ .cg_sel = PRCMU_PLLSOC0,
+};
+
+static struct clk soc1_pll = {
+ .name = "soc1_pll",
+ .ops = &prcmu_clk_ops,
+ .mutex = &pll_mutex,
+ .cg_sel = PRCMU_PLLSOC1,
+};
+
+static struct clk ddr_pll = {
+ .name = "ddr_pll",
+ .ops = &prcmu_clk_ops,
+ .mutex = &pll_mutex,
+ .cg_sel = PRCMU_PLLDDR,
+};
+
+static struct clk sysclk = {
+ .name = "sysclk",
+ .ops = &sysclk_ops,
+ .rate = 26000000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk rtc32k = {
+ .name = "rtc32k",
+ .rate = 32768,
+};
+
+static struct clk kbd32k = {
+ .name = "kbd32k",
+ .rate = 32768,
+};
+
+static struct clk clk_dummy = {
+ .name = "dummy",
+};
+
+static struct clk rtc_clk1 = {
+ .name = "rtc_clk1",
+ .ops = &rtc_clk_ops,
+ .cg_sel = 1,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk clkout0 = {
+ .name = "clkout0",
+ .ops = &clkout0_ops,
+ .parent = &sysclk,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk clkout1 = {
+ .name = "clkout1",
+ .ops = &clkout1_ops,
+ .parent = &sysclk,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk clkout2 = {
+ .name = "clkout2",
+ .ops = &clkout2_ops,
+ .parent = &sysclk,
+ .mutex = &sysclk_mutex,
+};
+
+static DEFINE_MUTEX(parented_prcmu_mutex);
+
+#define DEF_PRCMU_CLK_PARENT(_name, _cg_sel, _rate, _parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ .parent = _parent, \
+ .mutex = &parented_prcmu_mutex, \
+ }
+
+static DEFINE_MUTEX(prcmu_client_mutex);
+
+#define DEF_PRCMU_CLIENT_CLK(_name, _cg_sel, _rate) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ .mutex = &prcmu_client_mutex, \
+ }
+
+static DEF_PRCMU_CLK(dmaclk, PRCMU_DMACLK, 200000000);
+static DEF_PRCMU_CLK(b2r2clk, PRCMU_B2R2CLK, 200000000);
+static DEF_PRCMU_CLK(sgaclk, PRCMU_SGACLK, 199900000);
+static DEF_PRCMU_CLK(uartclk, PRCMU_UARTCLK, 36360000);
+static DEF_PRCMU_CLK(msp02clk, PRCMU_MSP02CLK, 13000000);
+static DEF_PRCMU_CLIENT_CLK(msp1clk, PRCMU_MSP1CLK, 26000000);
+static DEF_PRCMU_CLIENT_CLK(cdclk, PRCMU_CDCLK, 26000000);
+static DEF_PRCMU_CLK(i2cclk, PRCMU_I2CCLK, 24000000);
+static DEF_PRCMU_CLK_PARENT(irdaclk, PRCMU_IRDACLK, 48000000, &soc1_pll);
+static DEF_PRCMU_CLK_PARENT(irrcclk, PRCMU_IRRCCLK, 48000000, &soc1_pll);
+static DEF_PRCMU_CLK(rngclk, PRCMU_RNGCLK, 26000000);
+static DEF_PRCMU_CLK(pwmclk, PRCMU_PWMCLK, 26000000);
+static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 50000000);
+static DEF_PRCMU_CLK(spare1clk, PRCMU_SPARE1CLK, 50000000);
+static DEF_PRCMU_CLK(per1clk, PRCMU_PER1CLK, 133330000);
+static DEF_PRCMU_CLK(per2clk, PRCMU_PER2CLK, 133330000);
+static DEF_PRCMU_CLK(per3clk, PRCMU_PER3CLK, 133330000);
+static DEF_PRCMU_CLK(per5clk, PRCMU_PER5CLK, 133330000);
+static DEF_PRCMU_CLK(per6clk, PRCMU_PER6CLK, 133330000);
+static DEF_PRCMU_CLK(hdmiclk, PRCMU_HDMICLK, 26000000);
+static DEF_PRCMU_CLK(apeatclk, PRCMU_APEATCLK, 200000000);
+static DEF_PRCMU_CLK(apetraceclk, PRCMU_APETRACECLK, 266000000);
+static DEF_PRCMU_CLK(mcdeclk, PRCMU_MCDECLK, 160000000);
+static DEF_PRCMU_CLK(tvclk, PRCMU_TVCLK, 40000000);
+static DEF_PRCMU_CLK(dsialtclk, PRCMU_DSIALTCLK, 400000000);
+static DEF_PRCMU_CLK(timclk, PRCMU_TIMCLK, 3250000);
+static DEF_PRCMU_CLK_PARENT(svaclk, PRCMU_SVACLK, 156000000, &soc1_pll);
+static DEF_PRCMU_CLK(siaclk, PRCMU_SIACLK, 133330000);
+
+/* PRCC PClocks */
+
+static DEF_PER1_PCLK(0, p1_pclk0);
+static DEF_PER1_PCLK(1, p1_pclk1);
+static DEF_PER1_PCLK(2, p1_pclk2);
+static DEF_PER1_PCLK(3, p1_pclk3);
+static DEF_PER1_PCLK(4, p1_pclk4);
+static DEF_PER1_PCLK(5, p1_pclk5);
+static DEF_PER1_PCLK(6, p1_pclk6);
+
+static DEF_PER2_PCLK(0, p2_pclk0);
+static DEF_PER2_PCLK(1, p2_pclk1);
+
+static DEF_PER3_PCLK(0, p3_pclk0);
+static DEF_PER3_PCLK(1, p3_pclk1);
+static DEF_PER3_PCLK(2, p3_pclk2);
+
+static DEF_PER5_PCLK(0, p5_pclk0);
+static DEF_PER5_PCLK(1, p5_pclk1);
+static DEF_PER5_PCLK(2, p5_pclk2);
+static DEF_PER5_PCLK(3, p5_pclk3);
+static DEF_PER5_PCLK(4, p5_pclk4);
+static DEF_PER5_PCLK(5, p5_pclk5);
+static DEF_PER5_PCLK(6, p5_pclk6);
+static DEF_PER5_PCLK(7, p5_pclk7);
+static DEF_PER5_PCLK(8, p5_pclk8);
+static DEF_PER5_PCLK(9, p5_pclk9);
+static DEF_PER5_PCLK(10, p5_pclk10);
+static DEF_PER5_PCLK(11, p5_pclk11);
+static DEF_PER5_PCLK(12, p5_pclk12);
+static DEF_PER5_PCLK(13, p5_pclk13);
+static DEF_PER5_PCLK(14, p5_pclk14);
+static DEF_PER5_PCLK(15, p5_pclk15);
+
+static DEF_PER6_PCLK(0, p6_pclk0);
+static DEF_PER6_PCLK(1, p6_pclk1);
+static DEF_PER6_PCLK(2, p6_pclk2);
+static DEF_PER6_PCLK(3, p6_pclk3);
+static DEF_PER6_PCLK(4, p6_pclk4);
+static DEF_PER6_PCLK(5, p6_pclk5);
+static DEF_PER6_PCLK(6, p6_pclk6);
+static DEF_PER6_PCLK(7, p6_pclk7);
+
+/* MSP0 */
+static DEF_PER1_KCLK(0, p1_msp0_kclk, &msp02clk);
+static DEF_PER_CLK(p1_msp0_clk, &p1_pclk0, &p1_msp0_kclk);
+
+/* SDI0 */
+static DEF_PER1_KCLK(1, p1_sdi0_kclk, &spare1clk); /* &sdmmcclk on v1 */
+static DEF_PER_CLK(p1_sdi0_clk, &p1_pclk1, &p1_sdi0_kclk);
+
+/* SDI2 */
+static DEF_PER1_KCLK(2, p1_sdi2_kclk, &sdmmcclk);
+static DEF_PER_CLK(p1_sdi2_clk, &p1_pclk2, &p1_sdi2_kclk);
+
+/* UART0 */
+static DEF_PER1_KCLK(3, p1_uart0_kclk, &uartclk);
+static DEF_PER_CLK(p1_uart0_clk, &p1_pclk3, &p1_uart0_kclk);
+
+/* I2C1 */
+static DEF_PER1_KCLK(4, p1_i2c1_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c1_clk, &p1_pclk4, &p1_i2c1_kclk);
+
+/* PWM */
+static DEF_PER3_KCLK(0, p3_pwm_kclk, &pwmclk);
+static DEF_PER_CLK(p3_pwm_clk, &p3_pclk1, &p3_pwm_kclk);
+
+/* KEYPAD */
+static DEF_PER3_KCLK(0, p3_keypad_kclk, &kbd32k);
+static DEF_PER_CLK(p3_keypad_clk, &p3_pclk0, &p3_keypad_kclk);
+
+/* MSP2 */
+static DEF_PER5_KCLK(0, p5_msp2_kclk, &msp02clk);
+static DEF_PER_CLK(p5_msp2_clk, &p5_pclk0, &p5_msp2_kclk);
+
+/* UART1 */
+static DEF_PER5_KCLK(1, p5_uart1_kclk, &uartclk);
+static DEF_PER_CLK(p5_uart1_clk, &p5_pclk1, &p5_uart1_kclk);
+
+/* UART2 */
+static DEF_PER5_KCLK(2, p5_uart2_kclk, &uartclk);
+static DEF_PER_CLK(p5_uart2_clk, &p5_pclk2, &p5_uart2_kclk);
+
+/* UART3 */
+static DEF_PER5_KCLK(3, p5_uart3_kclk, &uartclk);
+static DEF_PER_CLK(p5_uart3_clk, &p5_pclk3, &p5_uart3_kclk);
+
+/* SDI1 */
+static DEF_PER5_KCLK(4, p5_sdi1_kclk, &sdmmcclk);
+static DEF_PER_CLK(p5_sdi1_clk, &p5_pclk4, &p5_sdi1_kclk);
+
+/* SDI3 */
+static DEF_PER5_KCLK(5, p5_sdi3_kclk, &sdmmcclk);
+static DEF_PER_CLK(p5_sdi3_clk, &p5_pclk5, &p5_sdi3_kclk);
+
+/* SDI4 */
+static DEF_PER5_KCLK(6, p5_sdi4_kclk, &sdmmcclk);
+static DEF_PER_CLK(p5_sdi4_clk, &p5_pclk6, &p5_sdi4_kclk);
+
+/* I2C2 */
+static DEF_PER5_KCLK(7, p5_i2c2_kclk, &i2cclk);
+static DEF_PER_CLK(p5_i2c2_clk, &p5_pclk7, &p5_i2c2_kclk);
+
+/* I2C3 */
+static DEF_PER5_KCLK(8, p5_i2c3_kclk, &i2cclk);
+static DEF_PER_CLK(p5_i2c3_clk, &p5_pclk8, &p5_i2c3_kclk);
+
+/* IRRC */
+static DEF_PER5_KCLK(9, p5_irrc_kclk, &irrcclk);
+static DEF_PER_CLK(p5_irrc_clk, &p5_pclk9, &p5_irrc_kclk);
+
+/* IRDA */
+static DEF_PER5_KCLK(10, p5_irda_kclk, &irdaclk);
+static DEF_PER_CLK(p5_irda_clk, &p5_pclk10, &p5_irda_kclk);
+
+/* RNG */
+static DEF_PER6_KCLK(0, p6_rng_kclk, &rngclk);
+static DEF_PER_CLK(p6_rng_clk, &p6_pclk0, &p6_rng_kclk);
+
+/* MTU:S */
+
+/* MTU0 */
+static DEF_PER_CLK(p6_mtu0_clk, &p6_pclk6, &timclk);
+
+/* MTU1 */
+static DEF_PER_CLK(p6_mtu1_clk, &p6_pclk7, &timclk);
+
+static struct clk *db5500_dbg_clks[] __initdata = {
+ /* Clock sources */
+ &soc0_pll,
+ &soc1_pll,
+ &ddr_pll,
+ &sysclk,
+ &rtc32k,
+
+ /* PRCMU clocks */
+ &sgaclk,
+ &siaclk,
+ &svaclk,
+ &uartclk,
+ &msp02clk,
+ &msp1clk,
+ &cdclk,
+ &i2cclk,
+ &irdaclk,
+ &irrcclk,
+ &sdmmcclk,
+ &spare1clk,
+ &per1clk,
+ &per2clk,
+ &per3clk,
+ &per5clk,
+ &per6clk,
+ &hdmiclk,
+ &apeatclk,
+ &apetraceclk,
+ &mcdeclk,
+ &dsialtclk,
+ &dmaclk,
+ &b2r2clk,
+ &tvclk,
+ &rngclk,
+ &pwmclk,
+
+ /* PRCC clocks */
+ &p1_pclk0,
+ &p1_pclk1,
+ &p1_pclk2,
+ &p1_pclk3,
+ &p1_pclk4,
+ &p1_pclk5,
+ &p1_pclk6,
+
+ &p2_pclk0,
+ &p2_pclk1,
+
+ &p3_pclk0,
+ &p3_pclk1,
+ &p3_pclk2,
+
+ &p5_pclk0,
+ &p5_pclk1,
+ &p5_pclk2,
+ &p5_pclk3,
+ &p5_pclk4,
+ &p5_pclk5,
+ &p5_pclk6,
+ &p5_pclk7,
+ &p5_pclk8,
+ &p5_pclk9,
+ &p5_pclk10,
+ &p5_pclk11,
+ &p5_pclk12,
+ &p5_pclk13,
+ &p5_pclk14,
+ &p5_pclk15,
+
+ &p6_pclk0,
+ &p6_pclk1,
+ &p6_pclk2,
+ &p6_pclk3,
+ &p6_pclk4,
+ &p6_pclk5,
+ &p6_pclk6,
+ &p6_pclk7,
+
+ /* Clock sources */
+ &clkout0,
+ &clkout1,
+ &clkout2,
+ &rtc_clk1,
+};
+
+static struct clk_lookup u8500_common_clock_sources[] = {
+ CLK_LOOKUP(soc0_pll, NULL, "soc0_pll"),
+ CLK_LOOKUP(soc1_pll, NULL, "soc1_pll"),
+ CLK_LOOKUP(ddr_pll, NULL, "ddr_pll"),
+ CLK_LOOKUP(sysclk, NULL, "sysclk"),
+ CLK_LOOKUP(rtc32k, NULL, "clk32k"),
+};
+
+static struct clk_lookup db5500_prcmu_clocks[] = {
+ CLK_LOOKUP(sgaclk, "mali", NULL),
+ CLK_LOOKUP(siaclk, "mmio_camera", "sia"),
+ CLK_LOOKUP(svaclk, "hva", NULL),
+ CLK_LOOKUP(uartclk, "UART", NULL),
+ CLK_LOOKUP(msp02clk, "MSP02", NULL),
+ CLK_LOOKUP(msp1clk, "ux500-msp-i2s.1", NULL),
+ CLK_LOOKUP(cdclk, "cable_detect.0", NULL),
+ CLK_LOOKUP(i2cclk, "I2C", NULL),
+ CLK_LOOKUP(sdmmcclk, "sdmmc", NULL),
+ CLK_LOOKUP(per1clk, "PERIPH1", NULL),
+ CLK_LOOKUP(per2clk, "PERIPH2", NULL),
+ CLK_LOOKUP(per3clk, "PERIPH3", NULL),
+ CLK_LOOKUP(per5clk, "PERIPH5", NULL),
+ CLK_LOOKUP(per6clk, "PERIPH6", NULL),
+ CLK_LOOKUP(hdmiclk, "mcde", "hdmi"),
+ CLK_LOOKUP(apeatclk, "apeat", NULL),
+ CLK_LOOKUP(apetraceclk, "apetrace", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", "mcde"),
+ CLK_LOOKUP(dmaclk, "dma40.0", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2_bus", NULL),
+ CLK_LOOKUP(b2r2clk, "U8500-B2R2.0", NULL),
+ CLK_LOOKUP(tvclk, "tv", NULL),
+ CLK_LOOKUP(tvclk, "mcde", "tv"),
+};
+
+static struct clk_lookup db5500_prcc_clocks[] = {
+ CLK_LOOKUP(p1_msp0_clk, "ux500-msp-i2s.0", NULL),
+ CLK_LOOKUP(p1_sdi0_clk, "sdi0", NULL),
+ CLK_LOOKUP(p1_sdi2_clk, "sdi2", NULL),
+ CLK_LOOKUP(p1_uart0_clk, "uart0", NULL),
+ CLK_LOOKUP(p1_i2c1_clk, "nmk-i2c.1", NULL),
+ CLK_LOOKUP(p1_pclk5, "gpio.0", NULL),
+ CLK_LOOKUP(p1_pclk5, "gpio.1", NULL),
+ CLK_LOOKUP(p1_pclk6, "fsmc", NULL),
+
+ CLK_LOOKUP(p2_pclk0, "musb-ux500.0", "usb"),
+ CLK_LOOKUP(p2_pclk1, "gpio.2", NULL),
+
+ CLK_LOOKUP(p3_keypad_clk, "db5500-keypad", NULL),
+ CLK_LOOKUP(p3_pwm_clk, "pwm", NULL),
+ CLK_LOOKUP(p3_pclk2, "gpio.4", NULL),
+
+ CLK_LOOKUP(p5_msp2_clk, "ux500-msp-i2s.2", NULL),
+ CLK_LOOKUP(p5_uart1_clk, "uart1", NULL),
+ CLK_LOOKUP(p5_uart2_clk, "uart2", NULL),
+ CLK_LOOKUP(p5_uart3_clk, "uart3", NULL),
+ CLK_LOOKUP(p5_sdi1_clk, "sdi1", NULL),
+ CLK_LOOKUP(p5_sdi3_clk, "sdi3", NULL),
+ CLK_LOOKUP(p5_sdi4_clk, "sdi4", NULL),
+ CLK_LOOKUP(p5_i2c2_clk, "nmk-i2c.2", NULL),
+ CLK_LOOKUP(p5_i2c3_clk, "nmk-i2c.3", NULL),
+ CLK_LOOKUP(p5_irrc_clk, "irrc", NULL),
+ CLK_LOOKUP(p5_irda_clk, "irda", NULL),
+ CLK_LOOKUP(p5_pclk11, "spi0", NULL),
+ CLK_LOOKUP(p5_pclk12, "spi1", NULL),
+ CLK_LOOKUP(p5_pclk13, "spi2", NULL),
+ CLK_LOOKUP(p5_pclk14, "spi3", NULL),
+ CLK_LOOKUP(p5_pclk15, "gpio.5", NULL),
+ CLK_LOOKUP(p5_pclk15, "gpio.6", NULL),
+ CLK_LOOKUP(p5_pclk15, "gpio.7", NULL),
+
+ CLK_LOOKUP(p6_rng_clk, "rng", NULL),
+ CLK_LOOKUP(p6_pclk1, "cryp0", NULL),
+ CLK_LOOKUP(p6_pclk2, "hash0", NULL),
+ CLK_LOOKUP(p6_pclk3, "pka", NULL),
+ CLK_LOOKUP(p6_pclk4, "hash1", NULL),
+ CLK_LOOKUP(p6_pclk1, "cryp1", NULL),
+ CLK_LOOKUP(p6_pclk5, "cfgreg", NULL),
+ CLK_LOOKUP(p6_mtu0_clk, "mtu0", NULL),
+ CLK_LOOKUP(p6_mtu1_clk, "mtu1", NULL),
+
+ /*
+ * Dummy clock sets up the GPIOs.
+ */
+ CLK_LOOKUP(clk_dummy, "gpio.3", NULL),
+ CLK_LOOKUP(rtc32k, "rtc-pl031", NULL),
+};
+
+static struct clk_lookup db5500_clkouts[] = {
+ CLK_LOOKUP(clkout1, "mmio_camera", "primary-cam"),
+ CLK_LOOKUP(clkout1, "mmio_camera", "secondary-cam"),
+ CLK_LOOKUP(clkout2, "ab5500-usb.0", "sysclk"),
+ CLK_LOOKUP(clkout2, "ab5500-codec.0", "sysclk"),
+};
+
+static struct clk_lookup u5500_clocks[] = {
+ CLK_LOOKUP(rtc_clk1, "cg2900-uart.0", "lpoclk"),
+};
+
+static const char *db5500_boot_clk[] __initdata = {
+ "spi0",
+ "spi1",
+ "spi2",
+ "spi3",
+ "uart0",
+ "uart1",
+ "uart2",
+ "uart3",
+ "sdi0",
+ "sdi1",
+ "sdi2",
+ "sdi3",
+ "sdi4",
+};
+
+static struct clk *boot_clks[ARRAY_SIZE(db5500_boot_clk)] __initdata;
+
+static int __init db5500_boot_clk_disable(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(db5500_boot_clk); i++) {
+ clk_disable(boot_clks[i]);
+ clk_put(boot_clks[i]);
+ }
+
+ return 0;
+}
+late_initcall_sync(db5500_boot_clk_disable);
+
+static void __init db5500_boot_clk_enable(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(db5500_boot_clk); i++) {
+ boot_clks[i] = clk_get_sys(db5500_boot_clk[i], NULL);
+ BUG_ON(IS_ERR(boot_clks[i]));
+ clk_enable(boot_clks[i]);
+ }
+}
+
+static void configure_clkouts(void)
+{
+ /* div parameter does not matter for sel0 REF_CLK */
+ WARN_ON(prcmu_config_clkout(DB5500_CLKOUT0,
+ DB5500_CLKOUT_REF_CLK_SEL0, 0));
+ WARN_ON(prcmu_config_clkout(DB5500_CLKOUT1,
+ DB5500_CLKOUT_REF_CLK_SEL0, 0));
+}
+
+static struct clk *db5500_clks_tobe_disabled[] __initdata = {
+ &siaclk,
+ &sgaclk,
+ &sdmmcclk,
+ &p1_pclk0,
+ &p1_pclk6,
+ &p3_keypad_clk,
+ &p3_pclk1,
+ &p5_pclk0,
+ &p5_pclk11,
+ &p5_pclk12,
+ &p5_pclk13,
+ &p5_pclk14,
+ &p6_pclk4,
+ &p6_pclk5,
+ &p6_pclk7,
+ &p5_uart1_clk,
+ &p5_uart2_clk,
+ &p5_uart3_clk,
+ &p5_sdi1_clk,
+ &p5_sdi3_clk,
+ &p5_sdi4_clk,
+ &p5_i2c3_clk,
+ &pwmclk,
+ &svaclk,
+ &cdclk,
+ &clkout2,
+};
+
+static int __init init_clock_states(void)
+{
+ int i = 0;
+ /*
+ * The following clks are shared with secure world.
+ * Currently this leads to a limitation where we need to
+ * enable them at all times.
+ */
+ clk_enable(&p6_pclk1);
+ clk_enable(&p6_pclk2);
+ clk_enable(&p6_pclk3);
+ clk_enable(&p6_rng_clk);
+
+ /*
+ * Disable clocks that are on at boot, but should be off.
+ */
+ for (i = 0; i < ARRAY_SIZE(db5500_clks_tobe_disabled); i++) {
+ if (!clk_enable(db5500_clks_tobe_disabled[i]))
+ clk_disable(db5500_clks_tobe_disabled[i]);
+ }
+ return 0;
+}
+late_initcall(init_clock_states);
+
+int __init db5500_clk_init(void)
+{
+ if (ux500_is_svp()) {
+ prcmu_clk_ops.enable = NULL;
+ prcmu_clk_ops.disable = NULL;
+ prcc_pclk_ops.enable = NULL;
+ prcc_pclk_ops.disable = NULL;
+ prcc_kclk_ops.enable = NULL;
+ prcc_kclk_ops.disable = NULL;
+ }
+ prcmu_clk_ops.get_rate = NULL;
+
+ clkdev_add_table(u8500_common_clock_sources,
+ ARRAY_SIZE(u8500_common_clock_sources));
+
+ clkdev_add_table(db5500_prcmu_clocks, ARRAY_SIZE(db5500_prcmu_clocks));
+ clkdev_add_table(db5500_prcc_clocks, ARRAY_SIZE(db5500_prcc_clocks));
+ clkdev_add_table(db5500_clkouts, ARRAY_SIZE(db5500_clkouts));
+ clkdev_add_table(u5500_clocks, ARRAY_SIZE(u5500_clocks));
+
+ db5500_boot_clk_enable();
+
+ /*
+ * The following clks are shared with secure world.
+ * Currently this leads to a limitation where we need to
+ * enable them at all times.
+ */
+ clk_enable(&p6_pclk1);
+ clk_enable(&p6_pclk2);
+ clk_enable(&p6_pclk3);
+ clk_enable(&p6_rng_clk);
+
+ configure_clkouts();
+
+ return 0;
+}
+
+int __init db5500_clk_debug_init(void)
+{
+ return dbx500_clk_debug_init(db5500_dbg_clks,
+ ARRAY_SIZE(db5500_dbg_clks));
+}
diff --git a/arch/arm/mach-ux500/clock-db8500.c b/arch/arm/mach-ux500/clock-db8500.c
new file mode 100644
index 00000000000..d41a22dd041
--- /dev/null
+++ b/arch/arm/mach-ux500/clock-db8500.c
@@ -0,0 +1,1171 @@
+/*
+ * Copyright (C) 2009-2011 ST-Ericsson SA
+ * Copyright (C) 2009 STMicroelectronics
+ *
+ * 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 <linux/init.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+
+#include <plat/pincfg.h>
+
+#include <mach/hardware.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include "clock.h"
+#include "pins-db8500.h"
+#include "product.h"
+#include "prcc.h"
+
+static DEFINE_MUTEX(soc0_pll_mutex);
+static DEFINE_MUTEX(soc1_pll_mutex);
+static DEFINE_MUTEX(sysclk_mutex);
+static DEFINE_MUTEX(ab_ulpclk_mutex);
+static DEFINE_MUTEX(ab_intclk_mutex);
+static DEFINE_MUTEX(clkout0_mutex);
+static DEFINE_MUTEX(dsi_pll_mutex);
+
+static struct delayed_work sysclk_disable_work;
+
+/* PLL operations. */
+
+static unsigned long pll_get_rate(struct clk *clk)
+{
+ return prcmu_clock_rate(clk->cg_sel);
+}
+
+static struct clkops pll_ops = {
+ .get_rate = pll_get_rate,
+};
+
+/* SysClk operations. */
+
+static int request_sysclk(bool enable)
+{
+ static int requests;
+
+ if ((enable && (requests++ == 0)) || (!enable && (--requests == 0)))
+ return prcmu_request_clock(PRCMU_SYSCLK, enable);
+ return 0;
+}
+
+static int sysclk_enable(struct clk *clk)
+{
+ static bool swat_enable;
+ int r;
+
+ if (!swat_enable) {
+ r = ab8500_sysctrl_set(AB8500_SWATCTRL,
+ AB8500_SWATCTRL_SWATENABLE);
+ if (r)
+ return r;
+
+ swat_enable = true;
+ }
+
+ r = request_sysclk(true);
+ if (r)
+ return r;
+
+ if (clk->cg_sel) {
+ r = ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1, (u8)clk->cg_sel);
+ if (r)
+ (void)request_sysclk(false);
+ }
+ return r;
+}
+
+static void sysclk_disable(struct clk *clk)
+{
+ int r;
+
+ if (clk->cg_sel) {
+ r = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ (u8)clk->cg_sel);
+ if (r)
+ goto disable_failed;
+ }
+ r = request_sysclk(false);
+ if (r)
+ goto disable_failed;
+ return;
+
+disable_failed:
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static struct clkops sysclk_ops = {
+ .enable = sysclk_enable,
+ .disable = sysclk_disable,
+};
+
+/* AB8500 UlpClk operations */
+
+static int ab_ulpclk_enable(struct clk *clk)
+{
+ int err;
+
+ if (clk->regulator == NULL) {
+ struct regulator *reg;
+
+ reg = regulator_get(NULL, "v-intcore");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ clk->regulator = reg;
+ }
+ err = regulator_set_optimum_mode(clk->regulator, 1500);
+ if (unlikely(err < 0))
+ goto regulator_enable_error;
+ err = regulator_enable(clk->regulator);
+ if (unlikely(err))
+ goto regulator_enable_error;
+ err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCONF,
+ AB8500_SYSULPCLKCONF_ULPCLKCONF_MASK);
+ if (unlikely(err))
+ goto enable_error;
+ err = ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_ULPCLKREQ);
+ if (unlikely(err))
+ goto enable_error;
+ /* Unknown/undocumented PLL locking time => wait 1 ms. */
+ mdelay(1);
+ return 0;
+
+enable_error:
+ (void)regulator_disable(clk->regulator);
+regulator_enable_error:
+ return err;
+}
+
+static void ab_ulpclk_disable(struct clk *clk)
+{
+ int err;
+
+ err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_ULPCLKREQ);
+ if (unlikely(regulator_disable(clk->regulator) || err))
+ goto out_err;
+
+ regulator_set_optimum_mode(clk->regulator, 0);
+
+ return;
+
+out_err:
+ pr_err("clock: %s failed to disable %s.\n", __func__, clk->name);
+}
+
+static struct clkops ab_ulpclk_ops = {
+ .enable = ab_ulpclk_enable,
+ .disable = ab_ulpclk_disable,
+};
+
+/* AB8500 intclk operations */
+
+enum ab_intclk_parent {
+ AB_INTCLK_PARENT_SYSCLK,
+ AB_INTCLK_PARENT_ULPCLK,
+ AB_INTCLK_PARENTS_END,
+ NUM_AB_INTCLK_PARENTS
+};
+
+static int ab_intclk_enable(struct clk *clk)
+{
+ if (clk->parent == clk->parents[AB_INTCLK_PARENT_ULPCLK]) {
+ return ab8500_sysctrl_write(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK,
+ (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT));
+ }
+ return 0;
+}
+
+static void ab_intclk_disable(struct clk *clk)
+{
+ if (clk->parent == clk->parents[AB_INTCLK_PARENT_SYSCLK])
+ return;
+
+ if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK)) {
+ pr_err("clock: %s failed to disable %s.\n", __func__,
+ clk->name);
+ }
+}
+
+static int ab_intclk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int err;
+
+ if (!clk->enabled)
+ return 0;
+
+ err = __clk_enable(parent, clk->mutex);
+
+ if (unlikely(err))
+ goto parent_enable_error;
+
+ if (parent == clk->parents[AB_INTCLK_PARENT_ULPCLK]) {
+ err = ab8500_sysctrl_write(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK,
+ (1 << AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_SHIFT));
+ } else {
+ err = ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_SYSULPCLKINTSEL_MASK);
+ }
+ if (unlikely(err))
+ goto config_error;
+
+ __clk_disable(clk->parent, clk->mutex);
+
+ return 0;
+
+config_error:
+ __clk_disable(parent, clk->mutex);
+parent_enable_error:
+ return err;
+}
+
+static struct clkops ab_intclk_ops = {
+ .enable = ab_intclk_enable,
+ .disable = ab_intclk_disable,
+ .set_parent = ab_intclk_set_parent,
+};
+
+/* AB8500 audio clock operations */
+
+static int audioclk_enable(struct clk *clk)
+{
+ return ab8500_sysctrl_set(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_AUDIOCLKENA);
+}
+
+static void audioclk_disable(struct clk *clk)
+{
+ if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ AB8500_SYSULPCLKCTRL1_AUDIOCLKENA)) {
+ pr_err("clock: %s failed to disable %s.\n", __func__,
+ clk->name);
+ }
+}
+
+static struct clkops audioclk_ops = {
+ .enable = audioclk_enable,
+ .disable = audioclk_disable,
+};
+
+/* Primary camera clock operations */
+static int clkout0_enable(struct clk *clk)
+{
+ int r;
+
+ if (clk->regulator == NULL) {
+ struct regulator *reg;
+
+ reg = regulator_get(NULL, "v-ape");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ clk->regulator = reg;
+ }
+ r = regulator_enable(clk->regulator);
+ if (r)
+ goto regulator_failed;
+ r = prcmu_config_clkout(0, PRCMU_CLKSRC_CLK38M, 4);
+ if (r)
+ goto config_failed;
+ r = nmk_config_pin(GPIO227_CLKOUT1, false);
+ if (r)
+ goto gpio_failed;
+ return r;
+
+gpio_failed:
+ (void)prcmu_config_clkout(0, PRCMU_CLKSRC_CLK38M, 0);
+config_failed:
+ (void)regulator_disable(clk->regulator);
+regulator_failed:
+ return r;
+}
+
+static void clkout0_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pin((GPIO227_GPIO | PIN_OUTPUT_LOW), false);
+ if (r)
+ goto disable_failed;
+ (void)prcmu_config_clkout(0, PRCMU_CLKSRC_CLK38M, 0);
+ (void)regulator_disable(clk->regulator);
+ return;
+
+disable_failed:
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+/* Touch screen/secondary camera clock operations. */
+static int clkout1_enable(struct clk *clk)
+{
+ int r;
+
+ if (clk->regulator == NULL) {
+ struct regulator *reg;
+
+ reg = regulator_get(NULL, "v-ape");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ clk->regulator = reg;
+ }
+ r = regulator_enable(clk->regulator);
+ if (r)
+ goto regulator_failed;
+ r = prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 4);
+ if (r)
+ goto config_failed;
+ r = nmk_config_pin(GPIO228_CLKOUT2, false);
+ if (r)
+ goto gpio_failed;
+ return r;
+
+gpio_failed:
+ (void)prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 0);
+config_failed:
+ (void)regulator_disable(clk->regulator);
+regulator_failed:
+ return r;
+}
+
+static void clkout1_disable(struct clk *clk)
+{
+ int r;
+
+ r = nmk_config_pin((GPIO228_GPIO | PIN_OUTPUT_LOW), false);
+ if (r)
+ goto disable_failed;
+ (void)prcmu_config_clkout(1, PRCMU_CLKSRC_SYSCLK, 0);
+ (void)regulator_disable(clk->regulator);
+ return;
+
+disable_failed:
+ pr_err("clock: failed to disable %s.\n", clk->name);
+}
+
+static struct clkops clkout0_ops = {
+ .enable = clkout0_enable,
+ .disable = clkout0_disable,
+};
+
+static struct clkops clkout1_ops = {
+ .enable = clkout1_enable,
+ .disable = clkout1_disable,
+};
+
+#define DEF_PER1_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST1_BASE, _cg_bit, &per1clk)
+#define DEF_PER2_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST2_BASE, _cg_bit, &per2clk)
+#define DEF_PER3_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST3_BASE, _cg_bit, &per3clk)
+#define DEF_PER5_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST5_BASE, _cg_bit, &per5clk)
+#define DEF_PER6_PCLK(_cg_bit, _name) \
+ DEF_PRCC_PCLK(_name, U8500_CLKRST6_BASE, _cg_bit, &per6clk)
+
+#define DEF_PER1_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST1_BASE, _cg_bit, _parent, &per1clk)
+#define DEF_PER2_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST2_BASE, _cg_bit, _parent, &per2clk)
+#define DEF_PER3_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST3_BASE, _cg_bit, _parent, &per3clk)
+#define DEF_PER5_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST5_BASE, _cg_bit, _parent, &per5clk)
+#define DEF_PER6_KCLK(_cg_bit, _name, _parent) \
+ DEF_PRCC_KCLK(_name, U8500_CLKRST6_BASE, _cg_bit, _parent, &per6clk)
+
+/* Clock sources. */
+
+static struct clk soc0_pll = {
+ .name = "soc0_pll",
+ .ops = &prcmu_clk_ops,
+ .cg_sel = PRCMU_PLLSOC0,
+ .mutex = &soc0_pll_mutex,
+};
+
+static struct clk soc1_pll = {
+ .name = "soc1_pll",
+ .ops = &prcmu_clk_ops,
+ .cg_sel = PRCMU_PLLSOC1,
+ .mutex = &soc1_pll_mutex,
+};
+
+static struct clk ddr_pll = {
+ .name = "ddr_pll",
+ .ops = &pll_ops,
+ .cg_sel = PRCMU_PLLDDR,
+};
+
+static struct clk ulp38m4 = {
+ .name = "ulp38m4",
+ .rate = 38400000,
+};
+
+static struct clk sysclk = {
+ .name = "sysclk",
+ .ops = &sysclk_ops,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk sysclk2 = {
+ .name = "sysclk2",
+ .ops = &sysclk_ops,
+ .cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF2REQ,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk sysclk3 = {
+ .name = "sysclk3",
+ .ops = &sysclk_ops,
+ .cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF3REQ,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk sysclk4 = {
+ .name = "sysclk4",
+ .ops = &sysclk_ops,
+ .cg_sel = AB8500_SYSULPCLKCTRL1_SYSCLKBUF4REQ,
+ .rate = 38400000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk rtc32k = {
+ .name = "rtc32k",
+ .rate = 32768,
+};
+
+static struct clk clkout0 = {
+ .name = "clkout0",
+ .ops = &clkout0_ops,
+ .parent = &ulp38m4,
+ .rate = 9600000,
+ .mutex = &clkout0_mutex,
+};
+
+static struct clk clkout1 = {
+ .name = "clkout1",
+ .ops = &clkout1_ops,
+ .parent = &sysclk,
+ .rate = 9600000,
+ .mutex = &sysclk_mutex,
+};
+
+static struct clk ab_ulpclk = {
+ .name = "ab_ulpclk",
+ .ops = &ab_ulpclk_ops,
+ .rate = 38400000,
+ .mutex = &ab_ulpclk_mutex,
+};
+
+static struct clk *ab_intclk_parents[NUM_AB_INTCLK_PARENTS] = {
+ [AB_INTCLK_PARENT_SYSCLK] = &sysclk,
+ [AB_INTCLK_PARENT_ULPCLK] = &ab_ulpclk,
+ [AB_INTCLK_PARENTS_END] = NULL,
+};
+
+static struct clk ab_intclk = {
+ .name = "ab_intclk",
+ .ops = &ab_intclk_ops,
+ .mutex = &ab_intclk_mutex,
+ .parent = &sysclk,
+ .parents = ab_intclk_parents,
+};
+
+static struct clk audioclk = {
+ .name = "audioclk",
+ .ops = &audioclk_ops,
+ .mutex = &ab_intclk_mutex,
+ .parent = &ab_intclk,
+};
+
+static DEF_PRCMU_CLK(sgaclk, PRCMU_SGACLK, 320000000);
+static DEF_PRCMU_CLK(uartclk, PRCMU_UARTCLK, 38400000);
+static DEF_PRCMU_CLK(msp02clk, PRCMU_MSP02CLK, 19200000);
+static DEF_PRCMU_CLK(msp1clk, PRCMU_MSP1CLK, 19200000);
+static DEF_PRCMU_CLK(i2cclk, PRCMU_I2CCLK, 24000000);
+static DEF_PRCMU_CLK(slimclk, PRCMU_SLIMCLK, 19200000);
+static DEF_PRCMU_CLK(per1clk, PRCMU_PER1CLK, 133330000);
+static DEF_PRCMU_CLK(per2clk, PRCMU_PER2CLK, 133330000);
+static DEF_PRCMU_CLK(per3clk, PRCMU_PER3CLK, 133330000);
+static DEF_PRCMU_CLK(per5clk, PRCMU_PER5CLK, 133330000);
+static DEF_PRCMU_CLK(per6clk, PRCMU_PER6CLK, 133330000);
+static DEF_PRCMU_CLK(per7clk, PRCMU_PER7CLK, 100000000);
+static DEF_PRCMU_SCALABLE_CLK(lcdclk, PRCMU_LCDCLK);
+static DEF_PRCMU_OPP100_CLK(bmlclk, PRCMU_BMLCLK, 200000000);
+static DEF_PRCMU_SCALABLE_CLK(hsitxclk, PRCMU_HSITXCLK);
+static DEF_PRCMU_SCALABLE_CLK(hsirxclk, PRCMU_HSIRXCLK);
+static DEF_PRCMU_SCALABLE_CLK(hdmiclk, PRCMU_HDMICLK);
+static DEF_PRCMU_CLK(apeatclk, PRCMU_APEATCLK, 160000000);
+static DEF_PRCMU_CLK(apetraceclk, PRCMU_APETRACECLK, 160000000);
+static DEF_PRCMU_CLK(mcdeclk, PRCMU_MCDECLK, 160000000);
+static DEF_PRCMU_OPP100_CLK(ipi2cclk, PRCMU_IPI2CCLK, 24000000);
+static DEF_PRCMU_CLK(dsialtclk, PRCMU_DSIALTCLK, 384000000);
+static DEF_PRCMU_CLK(dmaclk, PRCMU_DMACLK, 200000000);
+static DEF_PRCMU_CLK(b2r2clk, PRCMU_B2R2CLK, 200000000);
+static DEF_PRCMU_SCALABLE_CLK(tvclk, PRCMU_TVCLK);
+/* TODO: For SSPCLK, the spec says 24MHz, while the old driver says 48MHz. */
+static DEF_PRCMU_CLK(sspclk, PRCMU_SSPCLK, 24000000);
+static DEF_PRCMU_CLK(rngclk, PRCMU_RNGCLK, 19200000);
+static DEF_PRCMU_CLK(uiccclk, PRCMU_UICCCLK, 48000000);
+static DEF_PRCMU_CLK(timclk, PRCMU_TIMCLK, 2400000);
+static DEF_PRCMU_CLK(sdmmcclk, PRCMU_SDMMCCLK, 50000000);
+
+static struct clk dsi_pll = {
+ .name = "dsi_pll",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_PLLDSI,
+ .parent = &hdmiclk,
+ .mutex = &dsi_pll_mutex,
+};
+
+static struct clk dsi0clk = {
+ .name = "dsi0clk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI0CLK,
+ .parent = &dsi_pll,
+ .mutex = &dsi_pll_mutex,
+};
+
+static struct clk dsi1clk = {
+ .name = "dsi1clk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI1CLK,
+ .parent = &dsi_pll,
+ .mutex = &dsi_pll_mutex,
+};
+
+static struct clk dsi0escclk = {
+ .name = "dsi0escclk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI0ESCCLK,
+ .parent = &tvclk,
+};
+
+static struct clk dsi1escclk = {
+ .name = "dsi1escclk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI1ESCCLK,
+ .parent = &tvclk,
+};
+
+static struct clk dsi2escclk = {
+ .name = "dsi2escclk",
+ .ops = &prcmu_scalable_clk_ops,
+ .cg_sel = PRCMU_DSI2ESCCLK,
+ .parent = &tvclk,
+};
+
+/* PRCC PClocks */
+
+static DEF_PER1_PCLK(0, p1_pclk0);
+static DEF_PER1_PCLK(1, p1_pclk1);
+static DEF_PER1_PCLK(2, p1_pclk2);
+static DEF_PER1_PCLK(3, p1_pclk3);
+static DEF_PER1_PCLK(4, p1_pclk4);
+static DEF_PER1_PCLK(5, p1_pclk5);
+static DEF_PER1_PCLK(6, p1_pclk6);
+static DEF_PER1_PCLK(7, p1_pclk7);
+static DEF_PER1_PCLK(8, p1_pclk8);
+static DEF_PER1_PCLK(9, p1_pclk9);
+static DEF_PER1_PCLK(10, p1_pclk10);
+static DEF_PER1_PCLK(11, p1_pclk11);
+
+static DEF_PER2_PCLK(0, p2_pclk0);
+static DEF_PER2_PCLK(1, p2_pclk1);
+static DEF_PER2_PCLK(2, p2_pclk2);
+static DEF_PER2_PCLK(3, p2_pclk3);
+static DEF_PER2_PCLK(4, p2_pclk4);
+static DEF_PER2_PCLK(5, p2_pclk5);
+static DEF_PER2_PCLK(6, p2_pclk6);
+static DEF_PER2_PCLK(7, p2_pclk7);
+static DEF_PER2_PCLK(8, p2_pclk8);
+static DEF_PER2_PCLK(9, p2_pclk9);
+static DEF_PER2_PCLK(10, p2_pclk10);
+static DEF_PER2_PCLK(11, p2_pclk11);
+
+static DEF_PER3_PCLK(0, p3_pclk0);
+static DEF_PER3_PCLK(1, p3_pclk1);
+static DEF_PER3_PCLK(2, p3_pclk2);
+static DEF_PER3_PCLK(3, p3_pclk3);
+static DEF_PER3_PCLK(4, p3_pclk4);
+static DEF_PER3_PCLK(5, p3_pclk5);
+static DEF_PER3_PCLK(6, p3_pclk6);
+static DEF_PER3_PCLK(7, p3_pclk7);
+static DEF_PER3_PCLK(8, p3_pclk8);
+
+static DEF_PER5_PCLK(0, p5_pclk0);
+static DEF_PER5_PCLK(1, p5_pclk1);
+
+static DEF_PER6_PCLK(0, p6_pclk0);
+static DEF_PER6_PCLK(1, p6_pclk1);
+static DEF_PER6_PCLK(2, p6_pclk2);
+static DEF_PER6_PCLK(3, p6_pclk3);
+static DEF_PER6_PCLK(4, p6_pclk4);
+static DEF_PER6_PCLK(5, p6_pclk5);
+static DEF_PER6_PCLK(6, p6_pclk6);
+static DEF_PER6_PCLK(7, p6_pclk7);
+
+/* UART0 */
+static DEF_PER1_KCLK(0, p1_uart0_kclk, &uartclk);
+static DEF_PER_CLK(p1_uart0_clk, &p1_pclk0, &p1_uart0_kclk);
+
+/* UART1 */
+static DEF_PER1_KCLK(1, p1_uart1_kclk, &uartclk);
+static DEF_PER_CLK(p1_uart1_clk, &p1_pclk1, &p1_uart1_kclk);
+
+/* I2C1 */
+static DEF_PER1_KCLK(2, p1_i2c1_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c1_clk, &p1_pclk2, &p1_i2c1_kclk);
+
+/* MSP0 */
+static DEF_PER1_KCLK(3, p1_msp0_kclk, &msp02clk);
+static DEF_PER_CLK(p1_msp0_clk, &p1_pclk3, &p1_msp0_kclk);
+
+/* MSP1 */
+static DEF_PER1_KCLK(4, p1_msp1_kclk, &msp1clk);
+static DEF_PER_CLK(p1_msp1_clk, &p1_pclk4, &p1_msp1_kclk);
+
+/* SDI0 */
+static DEF_PER1_KCLK(5, p1_sdi0_kclk, &sdmmcclk);
+static DEF_PER_CLK(p1_sdi0_clk, &p1_pclk5, &p1_sdi0_kclk);
+
+/* I2C2 */
+static DEF_PER1_KCLK(6, p1_i2c2_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c2_clk, &p1_pclk6, &p1_i2c2_kclk);
+
+/* SLIMBUS0 */
+static DEF_PER1_KCLK(3, p1_slimbus0_kclk, &slimclk);
+static DEF_PER_CLK(p1_slimbus0_clk, &p1_pclk8, &p1_slimbus0_kclk);
+
+/* I2C4 */
+static DEF_PER1_KCLK(9, p1_i2c4_kclk, &i2cclk);
+static DEF_PER_CLK(p1_i2c4_clk, &p1_pclk10, &p1_i2c4_kclk);
+
+/* MSP3 */
+static DEF_PER1_KCLK(10, p1_msp3_kclk, &msp1clk);
+static DEF_PER_CLK(p1_msp3_clk, &p1_pclk11, &p1_msp3_kclk);
+
+/* I2C3 */
+static DEF_PER2_KCLK(0, p2_i2c3_kclk, &i2cclk);
+static DEF_PER_CLK(p2_i2c3_clk, &p2_pclk0, &p2_i2c3_kclk);
+
+/* SDI4 */
+static DEF_PER2_KCLK(2, p2_sdi4_kclk, &sdmmcclk);
+static DEF_PER_CLK(p2_sdi4_clk, &p2_pclk4, &p2_sdi4_kclk);
+
+/* MSP2 */
+static DEF_PER2_KCLK(3, p2_msp2_kclk, &msp02clk);
+static DEF_PER_CLK(p2_msp2_clk, &p2_pclk5, &p2_msp2_kclk);
+
+/* SDI1 */
+static DEF_PER2_KCLK(4, p2_sdi1_kclk, &sdmmcclk);
+static DEF_PER_CLK(p2_sdi1_clk, &p2_pclk6, &p2_sdi1_kclk);
+
+/* SDI3 */
+static DEF_PER2_KCLK(5, p2_sdi3_kclk, &sdmmcclk);
+static DEF_PER_CLK(p2_sdi3_clk, &p2_pclk7, &p2_sdi3_kclk);
+
+/* HSIR */
+static struct clk p2_ssirx_kclk = {
+ .name = "p2_ssirx_kclk",
+ .ops = &prcc_kclk_rec_ops,
+ .io_base = U8500_CLKRST2_BASE,
+ .cg_sel = BIT(6),
+ .parent = &hsirxclk,
+ .clock = &per2clk,
+};
+
+/* HSIT */
+static struct clk p2_ssitx_kclk = {
+ .name = "p2_ssitx_kclk",
+ .ops = &prcc_kclk_rec_ops,
+ .io_base = U8500_CLKRST2_BASE,
+ .cg_sel = BIT(7),
+ .parent = &hsitxclk,
+ .clock = &per2clk,
+};
+
+/* SSP0 */
+static DEF_PER3_KCLK(1, p3_ssp0_kclk, &sspclk);
+static DEF_PER_CLK(p3_ssp0_clk, &p3_pclk1, &p3_ssp0_kclk);
+
+/* SSP1 */
+static DEF_PER3_KCLK(2, p3_ssp1_kclk, &sspclk);
+static DEF_PER_CLK(p3_ssp1_clk, &p3_pclk2, &p3_ssp1_kclk);
+
+/* I2C0 */
+static DEF_PER3_KCLK(3, p3_i2c0_kclk, &i2cclk);
+static DEF_PER_CLK(p3_i2c0_clk, &p3_pclk3, &p3_i2c0_kclk);
+
+/* SDI2 */
+static DEF_PER3_KCLK(4, p3_sdi2_kclk, &sdmmcclk);
+static DEF_PER_CLK(p3_sdi2_clk, &p3_pclk4, &p3_sdi2_kclk);
+
+/* SKE */
+static DEF_PER3_KCLK(5, p3_ske_kclk, &rtc32k);
+static DEF_PER_CLK(p3_ske_clk, &p3_pclk5, &p3_ske_kclk);
+
+/* UART2 */
+static DEF_PER3_KCLK(6, p3_uart2_kclk, &uartclk);
+static DEF_PER_CLK(p3_uart2_clk, &p3_pclk6, &p3_uart2_kclk);
+
+/* SDI5 */
+static DEF_PER3_KCLK(7, p3_sdi5_kclk, &sdmmcclk);
+static DEF_PER_CLK(p3_sdi5_clk, &p3_pclk7, &p3_sdi5_kclk);
+
+/* RNG */
+static DEF_PER6_KCLK(0, p6_rng_kclk, &rngclk);
+static DEF_PER_CLK(p6_rng_clk, &p6_pclk0, &p6_rng_kclk);
+
+/* MTU:S */
+
+/* MTU0 */
+static DEF_PER_CLK(p6_mtu0_clk, &p6_pclk6, &timclk);
+
+/* MTU1 */
+static DEF_PER_CLK(p6_mtu1_clk, &p6_pclk7, &timclk);
+
+/*
+ * TODO: Ensure names match with devices and then remove unnecessary entries
+ * when all drivers use the clk API.
+ */
+
+static struct clk_lookup u8500_clocks[] = {
+ CLK_LOOKUP(soc0_pll, NULL, "soc0_pll"),
+ CLK_LOOKUP(soc1_pll, NULL, "soc1_pll"),
+ CLK_LOOKUP(ddr_pll, NULL, "ddr_pll"),
+ CLK_LOOKUP(ulp38m4, NULL, "ulp38m4"),
+ CLK_LOOKUP(sysclk, NULL, "sysclk"),
+ CLK_LOOKUP(rtc32k, NULL, "clk32k"),
+ CLK_LOOKUP(sysclk, "ab8500-usb.0", "sysclk"),
+ CLK_LOOKUP(sysclk, "ab8500-codec.0", "sysclk"),
+ CLK_LOOKUP(ab_ulpclk, "ab8500-codec.0", "ulpclk"),
+ CLK_LOOKUP(ab_intclk, "ab8500-codec.0", "intclk"),
+ CLK_LOOKUP(audioclk, "ab8500-codec.0", "audioclk"),
+ CLK_LOOKUP(ab_intclk, "ab8500-pwm.1", NULL),
+
+ CLK_LOOKUP(clkout0, "pri-cam", NULL),
+ CLK_LOOKUP(clkout1, "3-005c", NULL),
+ CLK_LOOKUP(clkout1, "3-005d", NULL),
+ CLK_LOOKUP(clkout1, "sec-cam", NULL),
+
+ /* prcmu */
+ CLK_LOOKUP(sgaclk, "mali", NULL),
+ CLK_LOOKUP(uartclk, "UART", NULL),
+ CLK_LOOKUP(msp02clk, "MSP02", NULL),
+ CLK_LOOKUP(i2cclk, "I2C", NULL),
+ CLK_LOOKUP(sdmmcclk, "sdmmc", NULL),
+ CLK_LOOKUP(slimclk, "slim", NULL),
+ CLK_LOOKUP(per1clk, "PERIPH1", NULL),
+ CLK_LOOKUP(per2clk, "PERIPH2", NULL),
+ CLK_LOOKUP(per3clk, "PERIPH3", NULL),
+ CLK_LOOKUP(per5clk, "PERIPH5", NULL),
+ CLK_LOOKUP(per6clk, "PERIPH6", NULL),
+ CLK_LOOKUP(per7clk, "PERIPH7", NULL),
+ CLK_LOOKUP(lcdclk, "lcd", NULL),
+ CLK_LOOKUP(bmlclk, "bml", NULL),
+ CLK_LOOKUP(p2_ssitx_kclk, "ste_hsi.0", "hsit_hsitxclk"),
+ CLK_LOOKUP(p2_ssirx_kclk, "ste_hsi.0", "hsir_hsirxclk"),
+ CLK_LOOKUP(lcdclk, "mcde", "lcd"),
+ CLK_LOOKUP(hdmiclk, "hdmi", NULL),
+ CLK_LOOKUP(hdmiclk, "mcde", "hdmi"),
+ CLK_LOOKUP(apeatclk, "apeat", NULL),
+ CLK_LOOKUP(apetraceclk, "apetrace", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", NULL),
+ CLK_LOOKUP(mcdeclk, "mcde", "mcde"),
+ CLK_LOOKUP(ipi2cclk, "ipi2", NULL),
+ CLK_LOOKUP(dmaclk, "dma40.0", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2", NULL),
+ CLK_LOOKUP(b2r2clk, "b2r2_core", NULL),
+ CLK_LOOKUP(b2r2clk, "U8500-B2R2.0", NULL),
+ CLK_LOOKUP(tvclk, "tv", NULL),
+ CLK_LOOKUP(tvclk, "mcde", "tv"),
+ CLK_LOOKUP(msp1clk, "MSP1", NULL),
+ CLK_LOOKUP(dsialtclk, "dsialt", NULL),
+ CLK_LOOKUP(sspclk, "SSP", NULL),
+ CLK_LOOKUP(rngclk, "rngclk", NULL),
+ CLK_LOOKUP(uiccclk, "uicc", NULL),
+ CLK_LOOKUP(dsi0clk, "mcde", "dsihs0"),
+ CLK_LOOKUP(dsi1clk, "mcde", "dsihs1"),
+ CLK_LOOKUP(dsi_pll, "mcde", "dsihs2"),
+ CLK_LOOKUP(dsi0escclk, "mcde", "dsilp0"),
+ CLK_LOOKUP(dsi1escclk, "mcde", "dsilp1"),
+ CLK_LOOKUP(dsi2escclk, "mcde", "dsilp2"),
+
+ /* PERIPH 1 */
+ CLK_LOOKUP(p1_msp3_clk, "msp3", NULL),
+ CLK_LOOKUP(p1_msp3_clk, "ux500-msp-i2s.3", NULL),
+ CLK_LOOKUP(p1_msp3_kclk, "ab8500-codec.0", "msp3-kernel"),
+ CLK_LOOKUP(p1_pclk11, "ab8500-codec.0", "msp3-bus"),
+ CLK_LOOKUP(p1_uart0_clk, "uart0", NULL),
+ CLK_LOOKUP(p1_uart1_clk, "uart1", NULL),
+ CLK_LOOKUP(p1_i2c1_clk, "nmk-i2c.1", NULL),
+ CLK_LOOKUP(p1_msp0_clk, "msp0", NULL),
+ CLK_LOOKUP(p1_msp0_clk, "ux500-msp-i2s.0", NULL),
+ CLK_LOOKUP(p1_sdi0_clk, "sdi0", NULL),
+ CLK_LOOKUP(p1_i2c2_clk, "nmk-i2c.2", NULL),
+ CLK_LOOKUP(p1_slimbus0_clk, "slimbus0", NULL),
+ CLK_LOOKUP(p1_pclk9, "gpio.0", NULL),
+ CLK_LOOKUP(p1_pclk9, "gpio.1", NULL),
+ CLK_LOOKUP(p1_pclk9, "gpioblock0", NULL),
+ CLK_LOOKUP(p1_msp1_clk, "msp1", NULL),
+ CLK_LOOKUP(p1_msp1_clk, "ux500-msp-i2s.1", NULL),
+ CLK_LOOKUP(p1_msp1_kclk, "ab8500-codec.0", "msp1-kernel"),
+ CLK_LOOKUP(p1_pclk4, "ab8500-codec.0", "msp1-bus"),
+ CLK_LOOKUP(p1_pclk7, "spi3", NULL),
+ CLK_LOOKUP(p1_i2c4_clk, "nmk-i2c.4", NULL),
+
+ /* PERIPH 2 */
+ CLK_LOOKUP(p2_i2c3_clk, "nmk-i2c.3", NULL),
+ CLK_LOOKUP(p2_pclk1, "spi2", NULL),
+ CLK_LOOKUP(p2_pclk2, "spi1", NULL),
+ CLK_LOOKUP(p2_pclk3, "pwl", NULL),
+ CLK_LOOKUP(p2_sdi4_clk, "sdi4", NULL),
+ CLK_LOOKUP(p2_msp2_clk, "msp2", NULL),
+ CLK_LOOKUP(p2_msp2_clk, "ux500-msp-i2s.2", NULL),
+ CLK_LOOKUP(p2_sdi1_clk, "sdi1", NULL),
+ CLK_LOOKUP(p2_sdi3_clk, "sdi3", NULL),
+ CLK_LOOKUP(p2_pclk8, "spi0", NULL),
+ CLK_LOOKUP(p2_pclk9, "ste_hsi.0", "hsir_hclk"),
+ CLK_LOOKUP(p2_pclk10, "ste_hsi.0", "hsit_hclk"),
+ CLK_LOOKUP(p2_pclk11, "gpio.6", NULL),
+ CLK_LOOKUP(p2_pclk11, "gpio.7", NULL),
+ CLK_LOOKUP(p2_pclk11, "gpioblock1", NULL),
+
+ /* PERIPH 3 */
+ CLK_LOOKUP(p3_pclk0, NULL, "fsmc"),
+ CLK_LOOKUP(p3_i2c0_clk, "nmk-i2c.0", NULL),
+ CLK_LOOKUP(p3_sdi2_clk, "sdi2", NULL),
+ CLK_LOOKUP(p3_ske_clk, "ske", NULL),
+ CLK_LOOKUP(p3_ske_clk, "nmk-ske-keypad", NULL),
+ CLK_LOOKUP(p3_uart2_clk, "uart2", NULL),
+ CLK_LOOKUP(p3_sdi5_clk, "sdi5", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.2", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.3", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.4", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpio.5", NULL),
+ CLK_LOOKUP(p3_pclk8, "gpioblock2", NULL),
+ CLK_LOOKUP(p3_ssp0_clk, "ssp0", NULL),
+ CLK_LOOKUP(p3_ssp1_clk, "ssp1", NULL),
+
+ /* PERIPH 5 */
+ CLK_LOOKUP(p5_pclk1, "gpio.8", NULL),
+ CLK_LOOKUP(p5_pclk1, "gpioblock3", NULL),
+ CLK_LOOKUP(p5_pclk0, "musb-ux500.0", "usb"),
+
+ /* PERIPH 6 */
+ CLK_LOOKUP(p6_pclk1, "cryp0", NULL),
+ CLK_LOOKUP(p6_pclk2, "hash0", NULL),
+ CLK_LOOKUP(p6_pclk3, "pka", NULL),
+ CLK_LOOKUP(p6_pclk5, "cfgreg", NULL),
+ CLK_LOOKUP(p6_mtu0_clk, "mtu0", NULL),
+ CLK_LOOKUP(p6_mtu1_clk, "mtu1", NULL),
+ CLK_LOOKUP(p6_pclk4, "hash1", NULL),
+ CLK_LOOKUP(p6_pclk1, "cryp1", NULL),
+ CLK_LOOKUP(p6_rng_clk, "rng", NULL),
+
+};
+
+static struct clk_lookup u8500_v2_sysclks[] = {
+ CLK_LOOKUP(sysclk2, NULL, "sysclk2"),
+ CLK_LOOKUP(sysclk3, NULL, "sysclk3"),
+ CLK_LOOKUP(sysclk4, NULL, "sysclk4"),
+};
+
+static void sysclk_init_disable(struct work_struct *not_used)
+{
+ int i;
+
+ mutex_lock(&sysclk_mutex);
+
+ /* Enable SWAT */
+ if (ab8500_sysctrl_set(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE))
+ goto err_swat;
+
+ for (i = 0; i < ARRAY_SIZE(u8500_v2_sysclks); i++) {
+ struct clk *clk = u8500_v2_sysclks[i].clk;
+
+ /* Disable sysclks */
+ if (!clk->enabled && clk->cg_sel) {
+ if (ab8500_sysctrl_clear(AB8500_SYSULPCLKCTRL1,
+ (u8)clk->cg_sel))
+ goto err_sysclk;
+ }
+ }
+ goto unlock_and_exit;
+
+err_sysclk:
+ pr_err("clock: Disable %s failed", u8500_v2_sysclks[i].clk->name);
+ ab8500_sysctrl_clear(AB8500_SWATCTRL, AB8500_SWATCTRL_SWATENABLE);
+ goto unlock_and_exit;
+
+err_swat:
+ pr_err("clock: Enable SWAT failed");
+
+unlock_and_exit:
+ mutex_unlock(&sysclk_mutex);
+}
+
+static struct clk *db8500_dbg_clks[] __initdata = {
+ /* Clock sources */
+ &soc0_pll,
+ &soc1_pll,
+ &ddr_pll,
+ &ulp38m4,
+ &sysclk,
+ &rtc32k,
+ /* PRCMU clocks */
+ &sgaclk,
+ &uartclk,
+ &msp02clk,
+ &msp1clk,
+ &i2cclk,
+ &sdmmcclk,
+ &slimclk,
+ &per1clk,
+ &per2clk,
+ &per3clk,
+ &per5clk,
+ &per6clk,
+ &per7clk,
+ &lcdclk,
+ &bmlclk,
+ &hsitxclk,
+ &hsirxclk,
+ &hdmiclk,
+ &apeatclk,
+ &apetraceclk,
+ &mcdeclk,
+ &ipi2cclk,
+ &dsialtclk,
+ &dsi_pll,
+ &dsi0clk,
+ &dsi1clk,
+ &dsi0escclk,
+ &dsi1escclk,
+ &dsi2escclk,
+ &dmaclk,
+ &b2r2clk,
+ &tvclk,
+ &sspclk,
+ &rngclk,
+ &uiccclk,
+ &sysclk2,
+ &clkout0,
+ &clkout1,
+ &p1_pclk0,
+ &p1_pclk1,
+ &p1_pclk2,
+ &p1_pclk3,
+ &p1_pclk4,
+ &p1_pclk5,
+ &p1_pclk6,
+ &p1_pclk7,
+ &p1_pclk8,
+ &p1_pclk9,
+ &p1_pclk10,
+ &p1_pclk11,
+ &p2_pclk0,
+ &p2_pclk1,
+ &p2_pclk2,
+ &p2_pclk3,
+ &p2_pclk4,
+ &p2_pclk5,
+ &p2_pclk6,
+ &p2_pclk7,
+ &p2_pclk8,
+ &p2_pclk9,
+ &p2_pclk10,
+ &p2_pclk11,
+ &p3_pclk0,
+ &p3_pclk1,
+ &p3_pclk2,
+ &p3_pclk3,
+ &p3_pclk4,
+ &p3_pclk5,
+ &p3_pclk6,
+ &p3_pclk7,
+ &p3_pclk8,
+ &p5_pclk0,
+ &p5_pclk1,
+ &p6_pclk0,
+ &p6_pclk1,
+ &p6_pclk2,
+ &p6_pclk3,
+ &p6_pclk4,
+ &p6_pclk5,
+ &p6_pclk6,
+ &p6_pclk7,
+};
+
+/* List of clocks which might be enabled from the bootloader */
+
+/*
+ * SOC settings enable bus + kernel clocks of all periphs without
+ * properly configuring the parents of the kernel clocks for all units.
+ * Enable and Disable them all to get them into a known and working state.
+ */
+static struct clk *loader_enabled_clk[] __initdata = {
+ /* periph 1 */
+ &p1_uart0_clk,
+ &p1_uart1_clk,
+ &p1_i2c1_clk,
+ &p1_msp0_clk,
+ &p1_msp1_clk,
+ &p1_sdi0_clk,
+ &p1_i2c2_clk,
+ &p1_pclk7, /* spi3 */
+ &p1_pclk9, /* gpioctrl */
+ &p1_i2c4_clk,
+
+ /* periph 2 */
+ &p2_i2c3_clk,
+ &p2_pclk1, /* spi2 */
+ &p2_pclk2, /* spi1 */
+ /* pwl has an unknown kclk parent, ignore it */
+ &p2_sdi4_clk,
+ &p2_msp2_clk,
+ &p2_sdi1_clk,
+ &p2_sdi3_clk,
+ &p2_pclk8, /* spi0 */
+ &p2_ssirx_kclk, /* hsir kernel */
+ &p2_ssitx_kclk, /* hsit kernel */
+ &p2_pclk9, /* hsir bus */
+ &p2_pclk10, /* hsit bus */
+ &p2_pclk11, /* gpioctrl */
+ /* periph 3 */
+ &p3_pclk0, /* fsmc */
+ &p3_ssp0_clk,
+ &p3_ssp1_clk,
+ &p3_i2c0_clk,
+ &p3_sdi2_clk,
+ &p3_ske_clk,
+ &p3_uart2_clk,
+ &p3_sdi5_clk,
+ &p3_pclk8, /* gpio */
+ /* periph 5 */
+ &p5_pclk0, /* usb */
+ &p5_pclk1, /* gpio */
+ /* periph 6 */
+ /* Leave out rng, cryp0, hash0 and pka */
+ &p6_pclk4, /* hash1 */
+ &p6_pclk5, /* cr */
+ &p6_mtu0_clk,
+ &p6_mtu1_clk,
+ /* periph 7 */
+ &per7clk, /* PERIPH7 */
+
+ &bmlclk, /* BML */
+ &dsialtclk, /* dsialt */
+ &hsirxclk, /* hsirx */
+ &hsitxclk, /* hsitx */
+ &ipi2cclk, /* ipi2 */
+ &lcdclk, /* mcde */
+ &b2r2clk, /* b2r2_bus */
+};
+
+static int __init init_clock_states(void)
+{
+ unsigned int i;
+ /*
+ * Disable peripheral clocks enabled by bootloader/default
+ * but without drivers
+ */
+ for (i = 0; i < ARRAY_SIZE(loader_enabled_clk); i++)
+ if (!clk_enable(loader_enabled_clk[i]))
+ clk_disable(loader_enabled_clk[i]);
+
+ /*
+ * APEATCLK and APETRACECLK are enabled at boot and needed
+ * in order to debug with Lauterbach
+ */
+ if (!clk_enable(&apeatclk)) {
+ if (!ux500_jtag_enabled())
+ clk_disable(&apeatclk);
+ }
+ if (!clk_enable(&apetraceclk)) {
+ if (!ux500_jtag_enabled())
+ clk_disable(&apetraceclk);
+ }
+
+ INIT_DELAYED_WORK(&sysclk_disable_work, sysclk_init_disable);
+ schedule_delayed_work(&sysclk_disable_work, 10 * HZ);
+
+ return 0;
+}
+late_initcall(init_clock_states);
+
+static void __init configure_c2_clocks(void)
+{
+ sgaclk.parent = &soc0_pll;
+ sgaclk.mutex = &soc0_pll_mutex;
+}
+
+int __init db8500_clk_init(void)
+{
+ struct prcmu_fw_version *fw_version;
+
+ /*
+ * Disable pwl's and slimbus' bus and kernel clocks without touching
+ * any parents. Because for slimbus, the prcmu fw has not correctly
+ * configured the clocks at boot and for pwl the kclk parent
+ * is unknown.
+ */
+
+ /* slimbus' bus and kernel clocks */
+ writel(1 << 8, __io_address(U8500_CLKRST1_BASE) + PRCC_PCKDIS);
+ writel(1 << 8, __io_address(U8500_CLKRST1_BASE) + PRCC_KCKDIS);
+ /* pwl's bus and kernel clocks */
+ writel(1 << 3, __io_address(U8500_CLKRST2_BASE) + PRCC_PCKDIS);
+ writel(1 << 1, __io_address(U8500_CLKRST2_BASE) + PRCC_KCKDIS);
+
+ fw_version = prcmu_get_fw_version();
+ if (fw_version != NULL)
+ switch (fw_version->project) {
+ case PRCMU_FW_PROJECT_U8500_C2:
+ case PRCMU_FW_PROJECT_U9500_C2:
+ case PRCMU_FW_PROJECT_U8520:
+ configure_c2_clocks();
+ break;
+ default:
+ break;
+ }
+ clkdev_add_table(u8500_v2_sysclks,
+ ARRAY_SIZE(u8500_v2_sysclks));
+ clkdev_add_table(u8500_clocks,
+ ARRAY_SIZE(u8500_clocks));
+#ifdef CONFIG_DEBUG_FS
+ clk_debugfs_add_table(u8500_v2_sysclks, ARRAY_SIZE(u8500_v2_sysclks));
+ clk_debugfs_add_table(u8500_clocks, ARRAY_SIZE(u8500_clocks));
+#endif
+ return 0;
+}
+
+int __init db8500_clk_debug_init(void)
+{
+ return dbx500_clk_debug_init(db8500_dbg_clks,
+ ARRAY_SIZE(db8500_dbg_clks));
+}
diff --git a/arch/arm/mach-ux500/clock-debug.c b/arch/arm/mach-ux500/clock-debug.c
new file mode 100644
index 00000000000..1ebc69fe061
--- /dev/null
+++ b/arch/arm/mach-ux500/clock-debug.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/clk.h>
+#include <mach/hardware.h>
+
+#include "clock.h"
+
+struct clk_debug_info {
+ struct clk *clk;
+ struct dentry *dir;
+ struct dentry *enable;
+ struct dentry *requests;
+ int enabled;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *clk_dir;
+static struct dentry *clk_show;
+static struct dentry *clk_show_enabled_only;
+
+static struct clk_debug_info *cdi;
+static int num_clks;
+
+static int clk_show_print(struct seq_file *s, void *p)
+{
+ int i;
+ int enabled_only = (int)s->private;
+
+ seq_printf(s, "\n%-20s %10s %s\n", "name", "rate",
+ "enabled (kernel + debug)");
+ for (i = 0; i < num_clks; i++) {
+ if (enabled_only && !cdi[i].clk->enabled)
+ continue;
+ seq_printf(s,
+ "%-20s %10lu %5d + %d\n",
+ cdi[i].clk->name,
+ clk_get_rate(cdi[i].clk),
+ cdi[i].clk->enabled - cdi[i].enabled,
+ cdi[i].enabled);
+ }
+
+ return 0;
+}
+
+static int clk_show_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_show_print, inode->i_private);
+}
+
+static int clk_enable_print(struct seq_file *s, void *p)
+{
+ struct clk_debug_info *cdi = s->private;
+
+ return seq_printf(s, "%d\n", cdi->enabled);
+}
+
+static int clk_enable_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_enable_print, inode->i_private);
+}
+
+static ssize_t clk_enable_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct clk_debug_info *cdi;
+ long user_val;
+ int err;
+
+ cdi = ((struct seq_file *)(file->private_data))->private;
+
+ err = kstrtol_from_user(user_buf, count, 0, &user_val);
+
+ if (err)
+ return err;
+
+ if ((user_val > 0) && (!cdi->enabled)) {
+ err = clk_enable(cdi->clk);
+ if (err) {
+ pr_err("clock: clk_enable(%s) failed.\n",
+ cdi->clk->name);
+ return -EFAULT;
+ }
+ cdi->enabled = 1;
+ } else if ((user_val <= 0) && (cdi->enabled)) {
+ clk_disable(cdi->clk);
+ cdi->enabled = 0;
+ }
+ return count;
+}
+
+static int clk_requests_print(struct seq_file *s, void *p)
+{
+ struct clk_debug_info *cdi = s->private;
+
+ return seq_printf(s, "%d\n", cdi->clk->enabled);
+}
+
+static int clk_requests_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, clk_requests_print, inode->i_private);
+}
+
+static const struct file_operations clk_enable_fops = {
+ .open = clk_enable_open,
+ .write = clk_enable_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations clk_requests_fops = {
+ .open = clk_requests_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations clk_show_fops = {
+ .open = clk_show_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int create_clk_dirs(struct clk_debug_info *cdi, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ cdi[i].dir = debugfs_create_dir(cdi[i].clk->name, clk_dir);
+ if (!cdi[i].dir)
+ goto no_dir;
+ }
+
+ for (i = 0; i < size; i++) {
+ cdi[i].enable = debugfs_create_file("enable",
+ (S_IRUGO | S_IWUGO),
+ cdi[i].dir, &cdi[i],
+ &clk_enable_fops);
+ if (!cdi[i].enable)
+ goto no_enable;
+ }
+ for (i = 0; i < size; i++) {
+ cdi[i].requests = debugfs_create_file("requests", S_IRUGO,
+ cdi[i].dir, &cdi[i],
+ &clk_requests_fops);
+ if (!cdi[i].requests)
+ goto no_requests;
+ }
+ return 0;
+
+no_requests:
+ while (i--)
+ debugfs_remove(cdi[i].requests);
+ i = size;
+no_enable:
+ while (i--)
+ debugfs_remove(cdi[i].enable);
+ i = size;
+no_dir:
+ while (i--)
+ debugfs_remove(cdi[i].dir);
+
+ return -ENOMEM;
+}
+
+int __init dbx500_clk_debug_init(struct clk **clks, int num)
+{
+ int i;
+
+ cdi = kcalloc(sizeof(struct clk_debug_info), num, GFP_KERNEL);
+ if (!cdi)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++)
+ cdi[i].clk = clks[i];
+
+ num_clks = num;
+
+ clk_dir = debugfs_create_dir("clk", NULL);
+ if (!clk_dir)
+ goto no_dir;
+
+ clk_show = debugfs_create_file("show", S_IRUGO, clk_dir, (void *)0,
+ &clk_show_fops);
+ if (!clk_show)
+ goto no_show;
+
+ clk_show_enabled_only = debugfs_create_file("show-enabled-only",
+ S_IRUGO, clk_dir, (void *)1,
+ &clk_show_fops);
+ if (!clk_show_enabled_only)
+ goto no_enabled_only;
+
+ if (create_clk_dirs(cdi, num))
+ goto no_clks;
+
+ return 0;
+
+no_clks:
+ debugfs_remove(clk_show_enabled_only);
+no_enabled_only:
+ debugfs_remove(clk_show);
+no_show:
+ debugfs_remove(clk_dir);
+no_dir:
+ kfree(cdi);
+ return -ENOMEM;
+}
+
+static int __init clk_debug_init(void)
+{
+ if (cpu_is_u8500())
+ db8500_clk_debug_init();
+ else if (cpu_is_u5500())
+ db5500_clk_debug_init();
+
+ return 0;
+}
+module_init(clk_debug_init);
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c
index ec35f0aa566..b8bac2d6966 100644
--- a/arch/arm/mach-ux500/clock.c
+++ b/arch/arm/mach-ux500/clock.c
@@ -7,18 +7,11 @@
* published by the Free Software Foundation.
*/
#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/clkdev.h>
-#include <linux/cpufreq.h>
-
-#include <plat/mtu.h>
-#include <mach/hardware.h>
-#include "clock.h"
+#include <linux/spinlock.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/dbx500-prcmu.h>
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
@@ -26,477 +19,495 @@
static LIST_HEAD(clk_list);
#endif
-#define PRCC_PCKEN 0x00
-#define PRCC_PCKDIS 0x04
-#define PRCC_KCKEN 0x08
-#define PRCC_KCKDIS 0x0C
-
-#define PRCM_YYCLKEN0_MGT_SET 0x510
-#define PRCM_YYCLKEN1_MGT_SET 0x514
-#define PRCM_YYCLKEN0_MGT_CLR 0x518
-#define PRCM_YYCLKEN1_MGT_CLR 0x51C
-#define PRCM_YYCLKEN0_MGT_VAL 0x520
-#define PRCM_YYCLKEN1_MGT_VAL 0x524
-
-#define PRCM_SVAMMDSPCLK_MGT 0x008
-#define PRCM_SIAMMDSPCLK_MGT 0x00C
-#define PRCM_SGACLK_MGT 0x014
-#define PRCM_UARTCLK_MGT 0x018
-#define PRCM_MSP02CLK_MGT 0x01C
-#define PRCM_MSP1CLK_MGT 0x288
-#define PRCM_I2CCLK_MGT 0x020
-#define PRCM_SDMMCCLK_MGT 0x024
-#define PRCM_SLIMCLK_MGT 0x028
-#define PRCM_PER1CLK_MGT 0x02C
-#define PRCM_PER2CLK_MGT 0x030
-#define PRCM_PER3CLK_MGT 0x034
-#define PRCM_PER5CLK_MGT 0x038
-#define PRCM_PER6CLK_MGT 0x03C
-#define PRCM_PER7CLK_MGT 0x040
-#define PRCM_LCDCLK_MGT 0x044
-#define PRCM_BMLCLK_MGT 0x04C
-#define PRCM_HSITXCLK_MGT 0x050
-#define PRCM_HSIRXCLK_MGT 0x054
-#define PRCM_HDMICLK_MGT 0x058
-#define PRCM_APEATCLK_MGT 0x05C
-#define PRCM_APETRACECLK_MGT 0x060
-#define PRCM_MCDECLK_MGT 0x064
-#define PRCM_IPI2CCLK_MGT 0x068
-#define PRCM_DSIALTCLK_MGT 0x06C
-#define PRCM_DMACLK_MGT 0x074
-#define PRCM_B2R2CLK_MGT 0x078
-#define PRCM_TVCLK_MGT 0x07C
-#define PRCM_TCR 0x1C8
-#define PRCM_TCR_STOPPED (1 << 16)
-#define PRCM_TCR_DOZE_MODE (1 << 17)
-#define PRCM_UNIPROCLK_MGT 0x278
-#define PRCM_SSPCLK_MGT 0x280
-#define PRCM_RNGCLK_MGT 0x284
-#define PRCM_UICCCLK_MGT 0x27C
-
-#define PRCM_MGT_ENABLE (1 << 8)
-
-static DEFINE_SPINLOCK(clocks_lock);
-
-static void __clk_enable(struct clk *clk)
-{
- if (clk->enabled++ == 0) {
- if (clk->parent_cluster)
- __clk_enable(clk->parent_cluster);
-
- if (clk->parent_periph)
- __clk_enable(clk->parent_periph);
-
- if (clk->ops && clk->ops->enable)
- clk->ops->enable(clk);
+#include "clock.h"
+#include "prcc.h"
+
+DEFINE_MUTEX(clk_opp100_mutex);
+static DEFINE_SPINLOCK(clk_spin_lock);
+#define NO_LOCK &clk_spin_lock
+
+static void __iomem *prcmu_base;
+
+static void __clk_lock(struct clk *clk, void *last_lock, unsigned long *flags)
+{
+ if (clk->mutex != last_lock) {
+ if (clk->mutex == NULL)
+ spin_lock_irqsave(&clk_spin_lock, *flags);
+ else
+ mutex_lock(clk->mutex);
}
}
-int clk_enable(struct clk *clk)
+static void __clk_unlock(struct clk *clk, void *last_lock, unsigned long flags)
+{
+ if (clk->mutex != last_lock) {
+ if (clk->mutex == NULL)
+ spin_unlock_irqrestore(&clk_spin_lock, flags);
+ else
+ mutex_unlock(clk->mutex);
+ }
+}
+
+void __clk_disable(struct clk *clk, void *current_lock)
{
unsigned long flags;
- spin_lock_irqsave(&clocks_lock, flags);
- __clk_enable(clk);
- spin_unlock_irqrestore(&clocks_lock, flags);
+ if (clk == NULL)
+ return;
- return 0;
+ __clk_lock(clk, current_lock, &flags);
+
+ if (clk->enabled && (--clk->enabled == 0)) {
+ if ((clk->ops != NULL) && (clk->ops->disable != NULL))
+ clk->ops->disable(clk);
+ __clk_disable(clk->parent, clk->mutex);
+ __clk_disable(clk->bus_parent, clk->mutex);
+ }
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return;
}
-EXPORT_SYMBOL(clk_enable);
-static void __clk_disable(struct clk *clk)
+int __clk_enable(struct clk *clk, void *current_lock)
{
- if (--clk->enabled == 0) {
- if (clk->ops && clk->ops->disable)
- clk->ops->disable(clk);
+ int err;
+ unsigned long flags;
- if (clk->parent_periph)
- __clk_disable(clk->parent_periph);
+ if (clk == NULL)
+ return 0;
- if (clk->parent_cluster)
- __clk_disable(clk->parent_cluster);
+ __clk_lock(clk, current_lock, &flags);
+
+ if (!clk->enabled) {
+ err = __clk_enable(clk->bus_parent, clk->mutex);
+ if (unlikely(err))
+ goto bus_parent_error;
+
+ err = __clk_enable(clk->parent, clk->mutex);
+ if (unlikely(err))
+ goto parent_error;
+
+ if ((clk->ops != NULL) && (clk->ops->enable != NULL)) {
+ err = clk->ops->enable(clk);
+ if (unlikely(err))
+ goto enable_error;
+ }
}
+ clk->enabled++;
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return 0;
+
+enable_error:
+ __clk_disable(clk->parent, clk->mutex);
+parent_error:
+ __clk_disable(clk->bus_parent, clk->mutex);
+bus_parent_error:
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return err;
}
-void clk_disable(struct clk *clk)
+unsigned long __clk_get_rate(struct clk *clk, void *current_lock)
{
+ unsigned long rate;
unsigned long flags;
- WARN_ON(!clk->enabled);
+ if (clk == NULL)
+ return 0;
+
+ __clk_lock(clk, current_lock, &flags);
- spin_lock_irqsave(&clocks_lock, flags);
- __clk_disable(clk);
- spin_unlock_irqrestore(&clocks_lock, flags);
+ if ((clk->ops != NULL) && (clk->ops->get_rate != NULL))
+ rate = clk->ops->get_rate(clk);
+ else if (clk->rate)
+ rate = clk->rate;
+ else
+ rate = __clk_get_rate(clk->parent, clk->mutex);
+
+ __clk_unlock(clk, current_lock, flags);
+
+ return rate;
}
-EXPORT_SYMBOL(clk_disable);
-/*
- * The MTU has a separate, rather complex muxing setup
- * with alternative parents (peripheral cluster or
- * ULP or fixed 32768 Hz) depending on settings
- */
-static unsigned long clk_mtu_get_rate(struct clk *clk)
+static long __clk_round_rate(struct clk *clk, unsigned long rate)
{
- void __iomem *addr;
- u32 tcr;
- int mtu = (int) clk->data;
- /*
- * One of these is selected eventually
- * TODO: Replace the constant with a reference
- * to the ULP source once this is modeled.
- */
- unsigned long clk32k = 32768;
- unsigned long mturate;
- unsigned long retclk;
-
- if (cpu_is_u5500())
- addr = __io_address(U5500_PRCMU_BASE);
- else if (cpu_is_u8500())
- addr = __io_address(U8500_PRCMU_BASE);
- else
- ux500_unknown_soc();
+ if ((clk->ops != NULL) && (clk->ops->round_rate != NULL))
+ return clk->ops->round_rate(clk, rate);
- /*
- * On a startup, always conifgure the TCR to the doze mode;
- * bootloaders do it for us. Do this in the kernel too.
- */
- writel(PRCM_TCR_DOZE_MODE, addr + PRCM_TCR);
+ return -ENOSYS;
+}
- tcr = readl(addr + PRCM_TCR);
+static int __clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if ((clk->ops != NULL) && (clk->ops->set_rate != NULL))
+ return clk->ops->set_rate(clk, rate);
- /* Get the rate from the parent as a default */
- if (clk->parent_periph)
- mturate = clk_get_rate(clk->parent_periph);
- else if (clk->parent_cluster)
- mturate = clk_get_rate(clk->parent_cluster);
- else
- /* We need to be connected SOMEWHERE */
- BUG();
+ return -ENOSYS;
+}
- /* Return the clock selected for this MTU */
- if (tcr & (1 << mtu))
- retclk = clk32k;
- else
- retclk = mturate;
+int clk_enable(struct clk *clk)
+{
+ if (clk == NULL)
+ return -EINVAL;
- pr_info("MTU%d clock rate: %lu Hz\n", mtu, retclk);
- return retclk;
+ return __clk_enable(clk, NO_LOCK);
}
+EXPORT_SYMBOL(clk_enable);
-unsigned long clk_get_rate(struct clk *clk)
+void clk_disable(struct clk *clk)
{
- unsigned long rate;
- /*
- * If there is a custom getrate callback for this clock,
- * it will take precedence.
- */
- if (clk->get_rate)
- return clk->get_rate(clk);
-
- if (clk->ops && clk->ops->get_rate)
- return clk->ops->get_rate(clk);
-
- rate = clk->rate;
- if (!rate) {
- if (clk->parent_periph)
- rate = clk_get_rate(clk->parent_periph);
- else if (clk->parent_cluster)
- rate = clk_get_rate(clk->parent_cluster);
- }
+ if (clk == NULL)
+ return;
- return rate;
+ WARN_ON(!clk->enabled);
+ __clk_disable(clk, NO_LOCK);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (clk == NULL)
+ return 0;
+
+ return __clk_get_rate(clk, NO_LOCK);
}
EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
- /*TODO*/
- return rate;
+ long rounded_rate;
+ unsigned long flags;
+
+ if (clk == NULL)
+ return -EINVAL;
+
+ __clk_lock(clk, NO_LOCK, &flags);
+
+ rounded_rate = __clk_round_rate(clk, rate);
+
+ __clk_unlock(clk, NO_LOCK, flags);
+
+ return rounded_rate;
}
EXPORT_SYMBOL(clk_round_rate);
+long clk_round_rate_rec(struct clk *clk, unsigned long rate)
+{
+ long rounded_rate;
+ unsigned long flags;
+
+ if ((clk == NULL) || (clk->parent == NULL))
+ return -EINVAL;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ rounded_rate = __clk_round_rate(clk->parent, rate);
+
+ __clk_unlock(clk->parent, clk->mutex, flags);
+
+ return rounded_rate;
+}
+
+static void lock_parent_rate(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (clk->parent == NULL)
+ return;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ lock_parent_rate(clk->parent);
+ clk->parent->rate_locked++;
+
+ __clk_unlock(clk->parent, clk->mutex, flags);
+}
+
+static void unlock_parent_rate(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (clk->parent == NULL)
+ return;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ unlock_parent_rate(clk->parent);
+ clk->parent->rate_locked--;
+
+ __clk_unlock(clk->parent, clk->mutex, flags);
+}
+
int clk_set_rate(struct clk *clk, unsigned long rate)
{
- clk->rate = rate;
- return 0;
+ int err;
+ unsigned long flags;
+
+ if (clk == NULL)
+ return -EINVAL;
+
+ __clk_lock(clk, NO_LOCK, &flags);
+
+ if (clk->enabled) {
+ err = -EBUSY;
+ goto unlock_and_return;
+ }
+ if (clk->rate_locked) {
+ err = -EAGAIN;
+ goto unlock_and_return;
+ }
+
+ lock_parent_rate(clk);
+ err = __clk_set_rate(clk, rate);
+ unlock_parent_rate(clk);
+
+unlock_and_return:
+ __clk_unlock(clk, NO_LOCK, flags);
+
+ return err;
}
EXPORT_SYMBOL(clk_set_rate);
+int clk_set_rate_rec(struct clk *clk, unsigned long rate)
+{
+ int err;
+ unsigned long flags;
+
+ if ((clk == NULL) || (clk->parent == NULL))
+ return -EINVAL;
+
+ __clk_lock(clk->parent, clk->mutex, &flags);
+
+ if (clk->parent->enabled) {
+ err = -EBUSY;
+ goto unlock_and_return;
+ }
+ if (clk->parent->rate_locked != 1) {
+ err = -EAGAIN;
+ goto unlock_and_return;
+ }
+ err = __clk_set_rate(clk->parent, rate);
+
+unlock_and_return:
+ __clk_unlock(clk->parent, clk->mutex, flags);
+
+ return err;
+}
+
int clk_set_parent(struct clk *clk, struct clk *parent)
{
- /*TODO*/
- return -ENOSYS;
+ int err = 0;
+ unsigned long flags;
+ struct clk **p;
+
+ if ((clk == NULL) || (clk->parents == NULL))
+ return -EINVAL;
+ for (p = clk->parents; *p != parent; p++) {
+ if (*p == NULL) /* invalid parent */
+ return -EINVAL;
+ }
+
+ __clk_lock(clk, NO_LOCK, &flags);
+
+ if ((clk->ops != NULL) && (clk->ops->set_parent != NULL)) {
+ err = clk->ops->set_parent(clk, parent);
+ if (err)
+ goto unlock_and_return;
+ } else if (clk->enabled) {
+ err = __clk_enable(parent, clk->mutex);
+ if (err)
+ goto unlock_and_return;
+ __clk_disable(clk->parent, clk->mutex);
+ }
+
+ clk->parent = parent;
+
+unlock_and_return:
+ __clk_unlock(clk, NO_LOCK, flags);
+
+ return err;
}
-EXPORT_SYMBOL(clk_set_parent);
-static void clk_prcmu_enable(struct clk *clk)
+/* PRCMU clock operations. */
+
+static int prcmu_clk_enable(struct clk *clk)
{
- void __iomem *cg_set_reg = __io_address(U8500_PRCMU_BASE)
- + PRCM_YYCLKEN0_MGT_SET + clk->prcmu_cg_off;
+ return prcmu_request_clock(clk->cg_sel, true);
+}
+
+static void prcmu_clk_disable(struct clk *clk)
+{
+ if (prcmu_request_clock(clk->cg_sel, false)) {
+ pr_err("clock: %s failed to disable %s.\n", __func__,
+ clk->name);
+ }
+}
+
+static int request_ape_opp100(bool enable)
+{
+ static unsigned int requests;
+
+ if (enable) {
+ if (0 == requests++) {
+ return prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ "clock", 100);
+ }
+ } else if (1 == requests--) {
+ prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, "clock");
+ }
+ return 0;
+}
+
+static int prcmu_opp100_clk_enable(struct clk *clk)
+{
+ int r;
- writel(1 << clk->prcmu_cg_bit, cg_set_reg);
+ r = request_ape_opp100(true);
+ if (r) {
+ pr_err("clock: %s failed to request APE OPP 100%% for %s.\n",
+ __func__, clk->name);
+ return r;
+ }
+ return prcmu_request_clock(clk->cg_sel, true);
+}
+
+static void prcmu_opp100_clk_disable(struct clk *clk)
+{
+ if (prcmu_request_clock(clk->cg_sel, false))
+ goto out_error;
+ if (request_ape_opp100(false))
+ goto out_error;
+ return;
+
+out_error:
+ pr_err("clock: %s failed to disable %s.\n", __func__, clk->name);
+}
+
+static unsigned long prcmu_clk_get_rate(struct clk *clk)
+{
+ return prcmu_clock_rate(clk->cg_sel);
}
-static void clk_prcmu_disable(struct clk *clk)
+static long prcmu_clk_round_rate(struct clk *clk, unsigned long rate)
{
- void __iomem *cg_clr_reg = __io_address(U8500_PRCMU_BASE)
- + PRCM_YYCLKEN0_MGT_CLR + clk->prcmu_cg_off;
+ return prcmu_round_clock_rate(clk->cg_sel, rate);
+}
- writel(1 << clk->prcmu_cg_bit, cg_clr_reg);
+static int prcmu_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ return prcmu_set_clock_rate(clk->cg_sel, rate);
}
-static struct clkops clk_prcmu_ops = {
- .enable = clk_prcmu_enable,
- .disable = clk_prcmu_disable,
+struct clkops prcmu_clk_ops = {
+ .enable = prcmu_clk_enable,
+ .disable = prcmu_clk_disable,
+ .get_rate = prcmu_clk_get_rate,
+};
+
+struct clkops prcmu_scalable_clk_ops = {
+ .enable = prcmu_clk_enable,
+ .disable = prcmu_clk_disable,
+ .get_rate = prcmu_clk_get_rate,
+ .round_rate = prcmu_clk_round_rate,
+ .set_rate = prcmu_clk_set_rate,
};
-static unsigned int clkrst_base[] = {
- [1] = U8500_CLKRST1_BASE,
- [2] = U8500_CLKRST2_BASE,
- [3] = U8500_CLKRST3_BASE,
- [5] = U8500_CLKRST5_BASE,
- [6] = U8500_CLKRST6_BASE,
+struct clkops prcmu_opp100_clk_ops = {
+ .enable = prcmu_opp100_clk_enable,
+ .disable = prcmu_opp100_clk_disable,
+ .get_rate = prcmu_clk_get_rate,
};
-static void clk_prcc_enable(struct clk *clk)
+/* PRCC clock operations. */
+
+static int prcc_pclk_enable(struct clk *clk)
{
- void __iomem *addr = __io_address(clkrst_base[clk->cluster]);
+ void __iomem *io_base = __io_address(clk->io_base);
+
+ writel(clk->cg_sel, (io_base + PRCC_PCKEN));
+ while (!(readl(io_base + PRCC_PCKSR) & clk->cg_sel))
+ cpu_relax();
+ return 0;
+}
- if (clk->prcc_kernel != -1)
- writel(1 << clk->prcc_kernel, addr + PRCC_KCKEN);
+static void prcc_pclk_disable(struct clk *clk)
+{
+ void __iomem *io_base = __io_address(clk->io_base);
- if (clk->prcc_bus != -1)
- writel(1 << clk->prcc_bus, addr + PRCC_PCKEN);
+ writel(clk->cg_sel, (io_base + PRCC_PCKDIS));
}
-static void clk_prcc_disable(struct clk *clk)
+struct clkops prcc_pclk_ops = {
+ .enable = prcc_pclk_enable,
+ .disable = prcc_pclk_disable,
+};
+
+static int prcc_kclk_enable(struct clk *clk)
{
- void __iomem *addr = __io_address(clkrst_base[clk->cluster]);
+ int err;
+ void __iomem *io_base = __io_address(clk->io_base);
+
+ err = __clk_enable(clk->clock, clk->mutex);
+ if (err)
+ return err;
- if (clk->prcc_bus != -1)
- writel(1 << clk->prcc_bus, addr + PRCC_PCKDIS);
+ writel(clk->cg_sel, (io_base + PRCC_KCKEN));
+ while (!(readl(io_base + PRCC_KCKSR) & clk->cg_sel))
+ cpu_relax();
- if (clk->prcc_kernel != -1)
- writel(1 << clk->prcc_kernel, addr + PRCC_KCKDIS);
+ __clk_disable(clk->clock, clk->mutex);
+
+ return 0;
}
-static struct clkops clk_prcc_ops = {
- .enable = clk_prcc_enable,
- .disable = clk_prcc_disable,
+static void prcc_kclk_disable(struct clk *clk)
+{
+ void __iomem *io_base = __io_address(clk->io_base);
+
+ (void)__clk_enable(clk->clock, clk->mutex);
+ writel(clk->cg_sel, (io_base + PRCC_KCKDIS));
+ __clk_disable(clk->clock, clk->mutex);
+}
+
+struct clkops prcc_kclk_ops = {
+ .enable = prcc_kclk_enable,
+ .disable = prcc_kclk_disable,
};
-static struct clk clk_32khz = {
- .name = "clk_32khz",
- .rate = 32000,
+struct clkops prcc_kclk_rec_ops = {
+ .enable = prcc_kclk_enable,
+ .disable = prcc_kclk_disable,
+ .round_rate = clk_round_rate_rec,
+ .set_rate = clk_set_rate_rec,
};
-/*
- * PRCMU level clock gating
- */
+#ifdef CONFIG_CPU_FREQ
+extern unsigned long dbx500_cpufreq_getfreq(void);
-/* Bank 0 */
-static DEFINE_PRCMU_CLK(svaclk, 0x0, 2, SVAMMDSPCLK);
-static DEFINE_PRCMU_CLK(siaclk, 0x0, 3, SIAMMDSPCLK);
-static DEFINE_PRCMU_CLK(sgaclk, 0x0, 4, SGACLK);
-static DEFINE_PRCMU_CLK_RATE(uartclk, 0x0, 5, UARTCLK, 38400000);
-static DEFINE_PRCMU_CLK(msp02clk, 0x0, 6, MSP02CLK);
-static DEFINE_PRCMU_CLK(msp1clk, 0x0, 7, MSP1CLK); /* v1 */
-static DEFINE_PRCMU_CLK_RATE(i2cclk, 0x0, 8, I2CCLK, 48000000);
-static DEFINE_PRCMU_CLK_RATE(sdmmcclk, 0x0, 9, SDMMCCLK, 100000000);
-static DEFINE_PRCMU_CLK(slimclk, 0x0, 10, SLIMCLK);
-static DEFINE_PRCMU_CLK(per1clk, 0x0, 11, PER1CLK);
-static DEFINE_PRCMU_CLK(per2clk, 0x0, 12, PER2CLK);
-static DEFINE_PRCMU_CLK(per3clk, 0x0, 13, PER3CLK);
-static DEFINE_PRCMU_CLK(per5clk, 0x0, 14, PER5CLK);
-static DEFINE_PRCMU_CLK_RATE(per6clk, 0x0, 15, PER6CLK, 133330000);
-static DEFINE_PRCMU_CLK(lcdclk, 0x0, 17, LCDCLK);
-static DEFINE_PRCMU_CLK(bmlclk, 0x0, 18, BMLCLK);
-static DEFINE_PRCMU_CLK(hsitxclk, 0x0, 19, HSITXCLK);
-static DEFINE_PRCMU_CLK(hsirxclk, 0x0, 20, HSIRXCLK);
-static DEFINE_PRCMU_CLK(hdmiclk, 0x0, 21, HDMICLK);
-static DEFINE_PRCMU_CLK(apeatclk, 0x0, 22, APEATCLK);
-static DEFINE_PRCMU_CLK(apetraceclk, 0x0, 23, APETRACECLK);
-static DEFINE_PRCMU_CLK(mcdeclk, 0x0, 24, MCDECLK);
-static DEFINE_PRCMU_CLK(ipi2clk, 0x0, 25, IPI2CCLK);
-static DEFINE_PRCMU_CLK(dsialtclk, 0x0, 26, DSIALTCLK); /* v1 */
-static DEFINE_PRCMU_CLK(dmaclk, 0x0, 27, DMACLK);
-static DEFINE_PRCMU_CLK(b2r2clk, 0x0, 28, B2R2CLK);
-static DEFINE_PRCMU_CLK(tvclk, 0x0, 29, TVCLK);
-static DEFINE_PRCMU_CLK(uniproclk, 0x0, 30, UNIPROCLK); /* v1 */
-static DEFINE_PRCMU_CLK_RATE(sspclk, 0x0, 31, SSPCLK, 48000000); /* v1 */
-
-/* Bank 1 */
-static DEFINE_PRCMU_CLK(rngclk, 0x4, 0, RNGCLK); /* v1 */
-static DEFINE_PRCMU_CLK(uiccclk, 0x4, 1, UICCCLK); /* v1 */
+unsigned long clk_smp_twd_get_rate(struct clk *clk)
+{
+ return dbx500_cpufreq_getfreq() / 2;
+}
-/*
- * PRCC level clock gating
- * Format: per#, clk, PCKEN bit, KCKEN bit, parent
- */
+static struct clkops clk_smp_twd_ops = {
+ .get_rate = clk_smp_twd_get_rate,
+};
-/* Peripheral Cluster #1 */
-static DEFINE_PRCC_CLK(1, i2c4, 10, 9, &clk_i2cclk);
-static DEFINE_PRCC_CLK(1, gpio0, 9, -1, NULL);
-static DEFINE_PRCC_CLK(1, slimbus0, 8, 8, &clk_slimclk);
-static DEFINE_PRCC_CLK(1, spi3, 7, -1, NULL);
-static DEFINE_PRCC_CLK(1, i2c2, 6, 6, &clk_i2cclk);
-static DEFINE_PRCC_CLK(1, sdi0, 5, 5, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(1, msp1, 4, 4, &clk_msp1clk);
-static DEFINE_PRCC_CLK(1, msp0, 3, 3, &clk_msp02clk);
-static DEFINE_PRCC_CLK(1, i2c1, 2, 2, &clk_i2cclk);
-static DEFINE_PRCC_CLK(1, uart1, 1, 1, &clk_uartclk);
-static DEFINE_PRCC_CLK(1, uart0, 0, 0, &clk_uartclk);
-
-/* Peripheral Cluster #2 */
-static DEFINE_PRCC_CLK(2, gpio1, 11, -1, NULL);
-static DEFINE_PRCC_CLK(2, ssitx, 10, 7, NULL);
-static DEFINE_PRCC_CLK(2, ssirx, 9, 6, NULL);
-static DEFINE_PRCC_CLK(2, spi0, 8, -1, NULL);
-static DEFINE_PRCC_CLK(2, sdi3, 7, 5, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(2, sdi1, 6, 4, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(2, msp2, 5, 3, &clk_msp02clk);
-static DEFINE_PRCC_CLK(2, sdi4, 4, 2, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(2, pwl, 3, 1, NULL);
-static DEFINE_PRCC_CLK(2, spi1, 2, -1, NULL);
-static DEFINE_PRCC_CLK(2, spi2, 1, -1, NULL);
-static DEFINE_PRCC_CLK(2, i2c3, 0, 0, &clk_i2cclk);
-
-/* Peripheral Cluster #3 */
-static DEFINE_PRCC_CLK(3, gpio2, 8, -1, NULL);
-static DEFINE_PRCC_CLK(3, sdi5, 7, 7, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(3, uart2, 6, 6, &clk_uartclk);
-static DEFINE_PRCC_CLK(3, ske, 5, 5, &clk_32khz);
-static DEFINE_PRCC_CLK(3, sdi2, 4, 4, &clk_sdmmcclk);
-static DEFINE_PRCC_CLK(3, i2c0, 3, 3, &clk_i2cclk);
-static DEFINE_PRCC_CLK(3, ssp1, 2, 2, &clk_sspclk);
-static DEFINE_PRCC_CLK(3, ssp0, 1, 1, &clk_sspclk);
-static DEFINE_PRCC_CLK(3, fsmc, 0, -1, NULL);
-
-/* Peripheral Cluster #4 is in the always on domain */
-
-/* Peripheral Cluster #5 */
-static DEFINE_PRCC_CLK(5, gpio3, 1, -1, NULL);
-static DEFINE_PRCC_CLK(5, usb, 0, 0, NULL);
-
-/* Peripheral Cluster #6 */
-
-/* MTU ID in data */
-static DEFINE_PRCC_CLK_CUSTOM(6, mtu1, 8, -1, NULL, clk_mtu_get_rate, 1);
-static DEFINE_PRCC_CLK_CUSTOM(6, mtu0, 7, -1, NULL, clk_mtu_get_rate, 0);
-static DEFINE_PRCC_CLK(6, cfgreg, 6, 6, NULL);
-static DEFINE_PRCC_CLK(6, hash1, 5, -1, NULL);
-static DEFINE_PRCC_CLK(6, unipro, 4, 1, &clk_uniproclk);
-static DEFINE_PRCC_CLK(6, pka, 3, -1, NULL);
-static DEFINE_PRCC_CLK(6, hash0, 2, -1, NULL);
-static DEFINE_PRCC_CLK(6, cryp0, 1, -1, NULL);
-static DEFINE_PRCC_CLK(6, rng, 0, 0, &clk_rngclk);
-
-static struct clk clk_dummy_apb_pclk = {
- .name = "apb_pclk",
+static struct clk clk_smp_twd = {
+ .name = "smp_twd",
+ .ops = &clk_smp_twd_ops,
};
-static struct clk_lookup u8500_clks[] = {
- CLK(dummy_apb_pclk, NULL, "apb_pclk"),
-
- /* Peripheral Cluster #1 */
- CLK(gpio0, "gpio.0", NULL),
- CLK(gpio0, "gpio.1", NULL),
- CLK(slimbus0, "slimbus0", NULL),
- CLK(i2c2, "nmk-i2c.2", NULL),
- CLK(sdi0, "sdi0", NULL),
- CLK(msp0, "msp0", NULL),
- CLK(i2c1, "nmk-i2c.1", NULL),
- CLK(uart1, "uart1", NULL),
- CLK(uart0, "uart0", NULL),
-
- /* Peripheral Cluster #3 */
- CLK(gpio2, "gpio.2", NULL),
- CLK(gpio2, "gpio.3", NULL),
- CLK(gpio2, "gpio.4", NULL),
- CLK(gpio2, "gpio.5", NULL),
- CLK(sdi5, "sdi5", NULL),
- CLK(uart2, "uart2", NULL),
- CLK(ske, "ske", NULL),
- CLK(ske, "nmk-ske-keypad", NULL),
- CLK(sdi2, "sdi2", NULL),
- CLK(i2c0, "nmk-i2c.0", NULL),
- CLK(fsmc, "fsmc", NULL),
-
- /* Peripheral Cluster #5 */
- CLK(gpio3, "gpio.8", NULL),
-
- /* Peripheral Cluster #6 */
- CLK(hash1, "hash1", NULL),
- CLK(pka, "pka", NULL),
- CLK(hash0, "hash0", NULL),
- CLK(cryp0, "cryp0", NULL),
-
- /* PRCMU level clock gating */
-
- /* Bank 0 */
- CLK(svaclk, "sva", NULL),
- CLK(siaclk, "sia", NULL),
- CLK(sgaclk, "sga", NULL),
- CLK(slimclk, "slim", NULL),
- CLK(lcdclk, "lcd", NULL),
- CLK(bmlclk, "bml", NULL),
- CLK(hsitxclk, "stm-hsi.0", NULL),
- CLK(hsirxclk, "stm-hsi.1", NULL),
- CLK(hdmiclk, "hdmi", NULL),
- CLK(apeatclk, "apeat", NULL),
- CLK(apetraceclk, "apetrace", NULL),
- CLK(mcdeclk, "mcde", NULL),
- CLK(ipi2clk, "ipi2", NULL),
- CLK(dmaclk, "dma40.0", NULL),
- CLK(b2r2clk, "b2r2", NULL),
- CLK(tvclk, "tv", NULL),
-
- /* Peripheral Cluster #1 */
- CLK(i2c4, "nmk-i2c.4", NULL),
- CLK(spi3, "spi3", NULL),
- CLK(msp1, "msp1", NULL),
-
- /* Peripheral Cluster #2 */
- CLK(gpio1, "gpio.6", NULL),
- CLK(gpio1, "gpio.7", NULL),
- CLK(ssitx, "ssitx", NULL),
- CLK(ssirx, "ssirx", NULL),
- CLK(spi0, "spi0", NULL),
- CLK(sdi3, "sdi3", NULL),
- CLK(sdi1, "sdi1", NULL),
- CLK(msp2, "msp2", NULL),
- CLK(sdi4, "sdi4", NULL),
- CLK(pwl, "pwl", NULL),
- CLK(spi1, "spi1", NULL),
- CLK(spi2, "spi2", NULL),
- CLK(i2c3, "nmk-i2c.3", NULL),
-
- /* Peripheral Cluster #3 */
- CLK(ssp1, "ssp1", NULL),
- CLK(ssp0, "ssp0", NULL),
-
- /* Peripheral Cluster #5 */
- CLK(usb, "musb-ux500.0", "usb"),
-
- /* Peripheral Cluster #6 */
- CLK(mtu1, "mtu1", NULL),
- CLK(mtu0, "mtu0", NULL),
- CLK(cfgreg, "cfgreg", NULL),
- CLK(hash1, "hash1", NULL),
- CLK(unipro, "unipro", NULL),
- CLK(rng, "rng", NULL),
-
- /* PRCMU level clock gating */
-
- /* Bank 0 */
- CLK(uniproclk, "uniproclk", NULL),
- CLK(dsialtclk, "dsialt", NULL),
-
- /* Bank 1 */
- CLK(rngclk, "rng", NULL),
- CLK(uiccclk, "uicc", NULL),
+static struct clk_lookup clk_smp_twd_lookup = {
+ .clk = &clk_smp_twd,
+ .dev_id = "smp_twd",
};
+#endif
#ifdef CONFIG_DEBUG_FS
/*
@@ -585,8 +596,8 @@ err_out:
static int clk_debugfs_register_one(struct clk *c)
{
- struct clk *pa = c->parent_periph;
- struct clk *bpa = c->parent_cluster;
+ struct clk *pa = c->parent;
+ struct clk *bpa = c->bus_parent;
if (!(bpa && !pa)) {
c->dent = clk_debugfs_register_dir(c,
@@ -610,8 +621,8 @@ static int clk_debugfs_register_one(struct clk *c)
static int clk_debugfs_register(struct clk *c)
{
int err;
- struct clk *pa = c->parent_periph;
- struct clk *bpa = c->parent_cluster;
+ struct clk *pa = c->parent;
+ struct clk *bpa = c->bus_parent;
if (pa && (!pa->dent && !pa->dent_bus)) {
err = clk_debugfs_register(pa);
@@ -658,66 +669,25 @@ err_out:
late_initcall(clk_debugfs_init);
#endif /* defined(CONFIG_DEBUG_FS) */
-unsigned long clk_smp_twd_rate = 500000000;
-
-unsigned long clk_smp_twd_get_rate(struct clk *clk)
-{
- return clk_smp_twd_rate;
-}
-
-static struct clk clk_smp_twd = {
- .get_rate = clk_smp_twd_get_rate,
- .name = "smp_twd",
-};
-
-static struct clk_lookup clk_smp_twd_lookup = {
- .dev_id = "smp_twd",
- .clk = &clk_smp_twd,
-};
-
-#ifdef CONFIG_CPU_FREQ
-
-static int clk_twd_cpufreq_transition(struct notifier_block *nb,
- unsigned long state, void *data)
-{
- struct cpufreq_freqs *f = data;
-
- if (state == CPUFREQ_PRECHANGE) {
- /* Save frequency in simple Hz */
- clk_smp_twd_rate = (f->new * 1000) / 2;
- }
-
- return NOTIFY_OK;
-}
-
-static struct notifier_block clk_twd_cpufreq_nb = {
- .notifier_call = clk_twd_cpufreq_transition,
-};
-
-static int clk_init_smp_twd_cpufreq(void)
-{
- return cpufreq_register_notifier(&clk_twd_cpufreq_nb,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-late_initcall(clk_init_smp_twd_cpufreq);
-
-#endif
-
int __init clk_init(void)
{
- if (cpu_is_u5500()) {
- /* Clock tree for U5500 not implemented yet */
- clk_prcc_ops.enable = clk_prcc_ops.disable = NULL;
- clk_prcmu_ops.enable = clk_prcmu_ops.disable = NULL;
- clk_uartclk.rate = 36360000;
- clk_sdmmcclk.rate = 99900000;
+ if (cpu_is_u8500()) {
+ prcmu_base = __io_address(U8500_PRCMU_BASE);
+ } else if (cpu_is_u5500()) {
+ prcmu_base = __io_address(U5500_PRCMU_BASE);
+ } else {
+ pr_err("clock: Unknown DB Asic.\n");
+ return -EIO;
}
- clkdev_add_table(u8500_clks, ARRAY_SIZE(u8500_clks));
- clkdev_add(&clk_smp_twd_lookup);
+ if (cpu_is_u8500())
+ db8500_clk_init();
+ else if (cpu_is_u5500())
+ db5500_clk_init();
-#ifdef CONFIG_DEBUG_FS
- clk_debugfs_add_table(u8500_clks, ARRAY_SIZE(u8500_clks));
+#ifdef CONFIG_CPU_FREQ
+ clkdev_add(&clk_smp_twd_lookup);
#endif
+
return 0;
}
diff --git a/arch/arm/mach-ux500/clock.h b/arch/arm/mach-ux500/clock.h
index d776ada08db..2403e51dc7f 100644
--- a/arch/arm/mach-ux500/clock.h
+++ b/arch/arm/mach-ux500/clock.h
@@ -1,11 +1,61 @@
/*
- * Copyright (C) 2010 ST-Ericsson
+ * Copyright (C) 2010 ST-Ericsson SA
* Copyright (C) 2009 STMicroelectronics
*
* 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 UX500_CLOCK_H
+#define UX500_CLOCK_H
+
+#include <linux/clkdev.h>
+
+/**
+ * struct clk
+ * @ops: The hardware specific operations defined for the clock.
+ * @name: The name of the clock.
+ * @mutex: The mutex to lock when operating on the clock. %NULL means that
+ * the common clock spinlock will be used.
+ * @enabled: A reference counter of the enable requests for the clock.
+ * @rate_locked: A rate lock counter used by clk_set_rate().
+ * @opp100: A flag saying whether the clock is requested to run at the
+ * OPP 100%% frequency.
+ * @rate: The frequency of the clock. For scalable and scaling clocks,
+ * this is the OPP 100%% frequency.
+ * @io_base: An IO memory base address, meaningful only when considered
+ * together with the defined @ops.
+ * @cg_sel: Clock gate selector, meaningful only when considered together
+ * with the specified @ops.
+ * @parent: The current (or only) parent clock of the clock.
+ * @bus_parent: The (optional) auxiliary bus clock "parent" of the clock.
+ * @parents: A list of the possible parents the clock can have. This should
+ * be a %NULL-terminated &struct_clk array. Present if and only
+ * if clk_set_parent() is implemented for the clock.
+ * @regulator: The regulator needed to have the clock functional, if any.
+ * @clock: The clock needed to control the clock, if any.
+ */
+struct clk {
+ const struct clkops *ops;
+ const char *name;
+ struct mutex *mutex;
+ unsigned int enabled;
+ unsigned int rate_locked;
+ bool opp100;
+ unsigned long rate;
+ unsigned int io_base;
+ u32 cg_sel;
+ struct clk *parent;
+ struct clk *bus_parent;
+ struct clk **parents;
+ struct regulator *regulator;
+ struct clk *clock;
+ struct list_head list;
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *dent; /* For visible tree hierarchy */
+ struct dentry *dent_bus; /* For visible tree hierarchy */
+#endif
+};
/**
* struct clkops - ux500 clock operations
@@ -18,135 +68,120 @@
* NULL, the rate in the struct clk will be used.
*/
struct clkops {
- void (*enable) (struct clk *);
- void (*disable) (struct clk *);
- unsigned long (*get_rate) (struct clk *);
+ int (*enable)(struct clk *);
+ void (*disable)(struct clk *);
+ unsigned long (*get_rate)(struct clk *);
+ int (*set_rate)(struct clk *, unsigned long);
+ long (*round_rate)(struct clk *, unsigned long);
int (*set_parent)(struct clk *, struct clk *);
};
-/**
- * struct clk - ux500 clock structure
- * @ops: pointer to clkops struct used to control this clock
- * @name: name, for debugging
- * @enabled: refcount. positive if enabled, zero if disabled
- * @get_rate: custom callback for getting the clock rate
- * @data: custom per-clock data for example for the get_rate
- * callback
- * @rate: fixed rate for clocks which don't implement
- * ops->getrate
- * @prcmu_cg_off: address offset of the combined enable/disable register
- * (used on u8500v1)
- * @prcmu_cg_bit: bit in the combined enable/disable register (used on
- * u8500v1)
- * @prcmu_cg_mgt: address of the enable/disable register (used on
- * u8500ed)
- * @cluster: peripheral cluster number
- * @prcc_bus: bit for the bus clock in the peripheral's CLKRST
- * @prcc_kernel: bit for the kernel clock in the peripheral's CLKRST.
- * -1 if no kernel clock exists.
- * @parent_cluster: pointer to parent's cluster clk struct
- * @parent_periph: pointer to parent's peripheral clk struct
- *
- * Peripherals are organised into clusters, and each cluster has an associated
- * bus clock. Some peripherals also have a parent peripheral clock.
- *
- * In order to enable a clock for a peripheral, we need to enable:
- * (1) the parent cluster (bus) clock at the PRCMU level
- * (2) the parent peripheral clock (if any) at the PRCMU level
- * (3) the peripheral's bus & kernel clock at the PRCC level
- *
- * (1) and (2) are handled by defining clk structs (DEFINE_PRCMU_CLK) for each
- * of the cluster and peripheral clocks, and hooking these as the parents of
- * the individual peripheral clocks.
- *
- * (3) is handled by specifying the bits in the PRCC control registers required
- * to enable these clocks and modifying them in the ->enable and
- * ->disable callbacks of the peripheral clocks (DEFINE_PRCC_CLK).
- *
- * This structure describes both the PRCMU-level clocks and PRCC-level clocks.
- * The prcmu_* fields are only used for the PRCMU clocks, and the cluster,
- * prcc, and parent pointers are only used for the PRCC-level clocks.
- */
-struct clk {
- const struct clkops *ops;
- const char *name;
- unsigned int enabled;
- unsigned long (*get_rate)(struct clk *);
- void *data;
-
- unsigned long rate;
- struct list_head list;
+extern struct clkops prcmu_clk_ops;
+extern struct clkops prcmu_scalable_clk_ops;
+extern struct clkops prcmu_opp100_clk_ops;
+extern struct mutex clk_opp100_mutex;
+extern struct clkops prcc_pclk_ops;
+extern struct clkops prcc_kclk_ops;
+extern struct clkops prcc_kclk_rec_ops;
+extern struct clkops sga_clk_ops;
- /* These three are only for PRCMU clks */
+#define CLK_LOOKUP(_clk, _dev_id, _con_id) \
+ { .dev_id = _dev_id, .con_id = _con_id, .clk = &_clk }
- unsigned int prcmu_cg_off;
- unsigned int prcmu_cg_bit;
- unsigned int prcmu_cg_mgt;
-
- /* The rest are only for PRCC clks */
-
- int cluster;
- unsigned int prcc_bus;
- unsigned int prcc_kernel;
-
- struct clk *parent_cluster;
- struct clk *parent_periph;
-#if defined(CONFIG_DEBUG_FS)
- struct dentry *dent; /* For visible tree hierarchy */
- struct dentry *dent_bus; /* For visible tree hierarchy */
-#endif
-};
+/* Define PRCMU Clock */
+#define DEF_PRCMU_CLK(_name, _cg_sel, _rate) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ }
-#define DEFINE_PRCMU_CLK(_name, _cg_off, _cg_bit, _reg) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcmu_ops, \
- .prcmu_cg_off = _cg_off, \
- .prcmu_cg_bit = _cg_bit, \
- .prcmu_cg_mgt = PRCM_##_reg##_MGT \
+#define DEF_PRCMU_SCALABLE_CLK(_name, _cg_sel) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_scalable_clk_ops, \
+ .cg_sel = _cg_sel, \
}
-#define DEFINE_PRCMU_CLK_RATE(_name, _cg_off, _cg_bit, _reg, _rate) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcmu_ops, \
- .prcmu_cg_off = _cg_off, \
- .prcmu_cg_bit = _cg_bit, \
- .rate = _rate, \
- .prcmu_cg_mgt = PRCM_##_reg##_MGT \
+/* Use this for clocks that are only defined at OPP 100%. */
+#define DEF_PRCMU_OPP100_CLK(_name, _cg_sel, _rate) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcmu_opp100_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .rate = _rate, \
+ .mutex = &clk_opp100_mutex, \
}
-#define DEFINE_PRCC_CLK(_pclust, _name, _bus_en, _kernel_en, _kernclk) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcc_ops, \
- .cluster = _pclust, \
- .prcc_bus = _bus_en, \
- .prcc_kernel = _kernel_en, \
- .parent_cluster = &clk_per##_pclust##clk, \
- .parent_periph = _kernclk \
+/* Define PRCC clock */
+#define DEF_PRCC_PCLK(_name, _io_base, _cg_bit, _parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcc_pclk_ops, \
+ .io_base = _io_base, \
+ .cg_sel = BIT(_cg_bit), \
+ .parent = _parent, \
}
-#define DEFINE_PRCC_CLK_CUSTOM(_pclust, _name, _bus_en, _kernel_en, _kernclk, _callback, _data) \
-struct clk clk_##_name = { \
- .name = #_name, \
- .ops = &clk_prcc_ops, \
- .cluster = _pclust, \
- .prcc_bus = _bus_en, \
- .prcc_kernel = _kernel_en, \
- .parent_cluster = &clk_per##_pclust##clk, \
- .parent_periph = _kernclk, \
- .get_rate = _callback, \
- .data = (void *) _data \
+#define DEF_PRCC_KCLK(_name, _io_base, _cg_bit, _parent, _clock) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &prcc_kclk_ops, \
+ .io_base = _io_base, \
+ .cg_sel = BIT(_cg_bit), \
+ .parent = _parent, \
+ .clock = _clock, \
}
+#define DEF_PER_CLK(_name, _bus_parent, _parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .parent = _parent, \
+ .bus_parent = _bus_parent, \
+ }
-#define CLK(_clk, _devname, _conname) \
- { \
- .clk = &clk_##_clk, \
- .dev_id = _devname, \
- .con_id = _conname, \
+#define DEF_MTU_CLK(_cg_sel, _name, _bus_parent) \
+ struct clk _name = { \
+ .name = #_name, \
+ .ops = &mtu_clk_ops, \
+ .cg_sel = _cg_sel, \
+ .bus_parent = _bus_parent, \
}
-int __init clk_db8500_ed_fixup(void);
+/* Functions defined in clock.c */
int __init clk_init(void);
+void clks_register(struct clk_lookup *clks, size_t num);
+int __clk_enable(struct clk *clk, void *current_lock);
+void __clk_disable(struct clk *clk, void *current_lock);
+unsigned long __clk_get_rate(struct clk *clk, void *current_lock);
+long clk_round_rate_rec(struct clk *clk, unsigned long rate);
+int clk_set_rate_rec(struct clk *clk, unsigned long rate);
+
+#ifdef CONFIG_DEBUG_FS
+int dbx500_clk_debug_init(struct clk **clks, int num);
+void clk_debugfs_add_table(struct clk_lookup *cl, size_t num);
+#else
+static inline int dbx500_clk_debug_init(struct clk **clks, int num)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_UX500_SOC_DB8500
+int __init db8500_clk_init(void);
+int __init db8500_clk_debug_init(void);
+#else
+static inline int db8500_clk_init(void) { return 0; }
+static inline int db8500_clk_debug_init(void) { return 0; }
+#endif
+
+#ifdef CONFIG_UX500_SOC_DB5500
+int __init db5500_clk_init(void);
+int __init db5500_clk_debug_init(void);
+#else
+static inline int db5500_clk_init(void) { return 0; }
+static inline int db5500_clk_debug_init(void) { return 0; }
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/cpu-db5500.c b/arch/arm/mach-ux500/cpu-db5500.c
index bca47f32082..ab50ca6865f 100644
--- a/arch/arm/mach-ux500/cpu-db5500.c
+++ b/arch/arm/mach-ux500/cpu-db5500.c
@@ -9,20 +9,23 @@
#include <linux/amba/bus.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/gpio/nomadik.h>
-#include <asm/mach/map.h>
#include <asm/pmu.h>
+#include <asm/mach/map.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
-#include <plat/gpio-nomadik.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <mach/devices.h>
#include <mach/setup.h>
#include <mach/irqs.h>
#include <mach/usb.h>
+#include <mach/ste-dma40-db5500.h>
#include "devices-db5500.h"
-#include "ste-dma40-db5500.h"
static struct map_desc u5500_uart_io_desc[] __initdata = {
__IO_DEV_DESC(U5500_UART0_BASE, SZ_4K),
@@ -35,8 +38,16 @@ static struct map_desc u5500_io_desc[] __initdata = {
__IO_DEV_DESC(U5500_GIC_DIST_BASE, SZ_4K),
__IO_DEV_DESC(U5500_L2CC_BASE, SZ_4K),
__IO_DEV_DESC(U5500_MTU0_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_MTU1_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_RTC_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_MTIMER_BASE, SZ_4K),
__IO_DEV_DESC(U5500_BACKUPRAM0_BASE, SZ_8K),
+ /* Map U5500_PUBLIC_BOOT_ROM_BASE (base+18000) only
+ * for TEE security driver
+ * and avoid overlap with asic ID at base+1D000 */
+ __MEM_DEV_DESC(U5500_BOOT_ROM_BASE+0x18000, 6*SZ_4K),
+
__IO_DEV_DESC(U5500_GPIO0_BASE, SZ_4K),
__IO_DEV_DESC(U5500_GPIO1_BASE, SZ_4K),
__IO_DEV_DESC(U5500_GPIO2_BASE, SZ_4K),
@@ -44,6 +55,11 @@ static struct map_desc u5500_io_desc[] __initdata = {
__IO_DEV_DESC(U5500_GPIO4_BASE, SZ_4K),
__IO_DEV_DESC(U5500_PRCMU_BASE, SZ_4K),
__IO_DEV_DESC(U5500_PRCMU_TCDM_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_CLKRST1_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_CLKRST2_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_CLKRST3_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_CLKRST5_BASE, SZ_4K),
+ __IO_DEV_DESC(U5500_CLKRST6_BASE, SZ_4K),
};
static struct resource mbox0_resources[] = {
@@ -130,31 +146,58 @@ static struct platform_device mbox2_device = {
.num_resources = ARRAY_SIZE(mbox2_resources),
};
+static struct platform_device db5500_prcmu_device = {
+ .name = "db5500-prcmu",
+};
+
static struct platform_device *db5500_platform_devs[] __initdata = {
+ &u5500_gpio_devs[0],
+ &u5500_gpio_devs[1],
+ &u5500_gpio_devs[2],
+ &u5500_gpio_devs[3],
+ &u5500_gpio_devs[4],
+ &u5500_gpio_devs[5],
+ &u5500_gpio_devs[6],
+ &u5500_gpio_devs[7],
&mbox0_device,
&mbox1_device,
&mbox2_device,
+ &db5500_prcmu_device,
+ &u5500_wdt_device,
};
-static resource_size_t __initdata db5500_gpio_base[] = {
- U5500_GPIOBANK0_BASE,
- U5500_GPIOBANK1_BASE,
- U5500_GPIOBANK2_BASE,
- U5500_GPIOBANK3_BASE,
- U5500_GPIOBANK4_BASE,
- U5500_GPIOBANK5_BASE,
- U5500_GPIOBANK6_BASE,
- U5500_GPIOBANK7_BASE,
-};
+static u8 db5500_revision;
-static void __init db5500_add_gpios(struct device *parent)
+bool cpu_is_u5500v1()
{
- struct nmk_gpio_platform_data pdata = {
- /* No custom data yet */
- };
+ return db5500_revision == 0xA0;
+}
- dbx500_add_gpios(parent, ARRAY_AND_SIZE(db5500_gpio_base),
- IRQ_DB5500_GPIO0, &pdata);
+bool cpu_is_u5500v2()
+{
+ return (db5500_revision & 0xf0) == 0xB0;
+}
+
+bool cpu_is_u5500v20()
+{
+ return db5500_revision == 0xB0;
+}
+
+bool cpu_is_u5500v21()
+{
+ return db5500_revision == 0xB1;
+}
+
+static void db5500_rev_init(void)
+{
+ unsigned int asicid;
+
+ /* As in devicemaps_init() */
+ local_flush_tlb_all();
+ flush_cache_all();
+
+ asicid = readl_relaxed(__io_address(U5500_ASIC_ID_ADDRESS));
+ db5500_revision = asicid & 0xff;
}
void __init u5500_map_io(void)
@@ -169,6 +212,8 @@ void __init u5500_map_io(void)
iotable_init(u5500_io_desc, ARRAY_SIZE(u5500_io_desc));
_PRCMU_BASE = __io_address(U5500_PRCMU_BASE);
+
+ db5500_rev_init();
}
static void __init db5500_pmu_init(void)
@@ -229,9 +274,12 @@ struct device * __init u5500_init_devices(void)
struct device *parent;
int i;
+#ifdef CONFIG_STM_TRACE
+ /* Early init for STM tracing */
+ /* platform_device_register(&u5500_stm_device); */
+#endif
parent = db5500_soc_device_init();
- db5500_add_gpios(parent);
db5500_pmu_init();
db5500_dma_init(parent);
db5500_add_rtc(parent);
diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c
index 9bd8163896c..ac7c4df4996 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -14,20 +14,23 @@
#include <linux/amba/bus.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/gpio/nomadik.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
-#include <asm/mach/map.h>
#include <asm/pmu.h>
-#include <plat/gpio-nomadik.h>
+#include <asm/mach/map.h>
#include <mach/hardware.h>
#include <mach/setup.h>
#include <mach/devices.h>
#include <mach/usb.h>
#include <mach/db8500-regs.h>
+#include <mach/ste-dma40-db8500.h>
#include "devices-db8500.h"
-#include "ste-dma40-db8500.h"
+#include "prcc.h"
/* minimum static i/o mapping required to boot U8500 platforms */
static struct map_desc u8500_uart_io_desc[] __initdata = {
@@ -35,28 +38,45 @@ static struct map_desc u8500_uart_io_desc[] __initdata = {
__IO_DEV_DESC(U8500_UART2_BASE, SZ_4K),
};
-static struct map_desc u8500_io_desc[] __initdata = {
+/* U8500 and U9540 common io_desc */
+static struct map_desc u8500_common_io_desc[] __initdata = {
/* SCU base also covers GIC CPU BASE and TWD with its 4K page */
__IO_DEV_DESC(U8500_SCU_BASE, SZ_4K),
__IO_DEV_DESC(U8500_GIC_DIST_BASE, SZ_4K),
__IO_DEV_DESC(U8500_L2CC_BASE, SZ_4K),
__IO_DEV_DESC(U8500_MTU0_BASE, SZ_4K),
+ __IO_DEV_DESC(U8500_MTU1_BASE, SZ_4K),
+ __IO_DEV_DESC(U8500_RTC_BASE, SZ_4K),
__IO_DEV_DESC(U8500_BACKUPRAM0_BASE, SZ_8K),
-
__IO_DEV_DESC(U8500_CLKRST1_BASE, SZ_4K),
__IO_DEV_DESC(U8500_CLKRST2_BASE, SZ_4K),
__IO_DEV_DESC(U8500_CLKRST3_BASE, SZ_4K),
__IO_DEV_DESC(U8500_CLKRST5_BASE, SZ_4K),
__IO_DEV_DESC(U8500_CLKRST6_BASE, SZ_4K),
- __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K),
__IO_DEV_DESC(U8500_GPIO0_BASE, SZ_4K),
__IO_DEV_DESC(U8500_GPIO1_BASE, SZ_4K),
__IO_DEV_DESC(U8500_GPIO2_BASE, SZ_4K),
__IO_DEV_DESC(U8500_GPIO3_BASE, SZ_4K),
+};
+
+/* U8500 IO map specific description */
+static struct map_desc u8500_io_desc[] __initdata = {
+ __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K),
+ /* Map U8500_PUBLIC_BOOT_ROM_BASE (base+17000) only
+ * for TEE security driver
+ * and avoid overlap with asic ID at base+1D000 */
+ __MEM_DEV_DESC(U8500_BOOT_ROM_BASE+0x17000, 6*SZ_4K),
__IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K),
};
+/* U9540 IO map specific description */
+static struct map_desc u9540_io_desc[] __initdata = {
+ __IO_DEV_DESC(U8500_PRCMU_BASE, SZ_4K + SZ_8K),
+ __MEM_DEV_DESC_DB9540_ROM(U9540_BOOT_ROM_BASE, SZ_1M),
+ __IO_DEV_DESC(U8500_PRCMU_TCDM_BASE, SZ_4K + SZ_8K),
+};
+
void __init u8500_map_io(void)
{
/*
@@ -64,9 +84,22 @@ void __init u8500_map_io(void)
*/
iotable_init(u8500_uart_io_desc, ARRAY_SIZE(u8500_uart_io_desc));
+ /*
+ * STE NMF CM driver only used on the U8500 allocate using
+ * dma_alloc_coherent:
+ * 8M for SIA and SVA data + 2M for SIA code + 2M for SVA code
+ * Can't be higher than 14M with VMALLOC_END at 0xFF000000
+ */
+ init_consistent_dma_size(14*SZ_1M);
+
ux500_map_io();
- iotable_init(u8500_io_desc, ARRAY_SIZE(u8500_io_desc));
+ iotable_init(u8500_common_io_desc, ARRAY_SIZE(u8500_common_io_desc));
+
+ if (cpu_is_u9540())
+ iotable_init(u9540_io_desc, ARRAY_SIZE(u9540_io_desc));
+ else
+ iotable_init(u8500_io_desc, ARRAY_SIZE(u8500_io_desc));
_PRCMU_BASE = __io_address(U8500_PRCMU_BASE);
}
@@ -115,34 +148,50 @@ static struct platform_device db8500_prcmu_device = {
.name = "db8500-prcmu",
};
-static struct platform_device *platform_devs[] __initdata = {
- &u8500_dma40_device,
- &db8500_pmu_device,
- &db8500_prcmu_device,
+static unsigned int per_clkrst_base[7] = {
+ 0,
+ U8500_CLKRST1_BASE,
+ U8500_CLKRST2_BASE,
+ U8500_CLKRST3_BASE,
+ 0,
+ 0,
+ U8500_CLKRST6_BASE,
};
-static resource_size_t __initdata db8500_gpio_base[] = {
- U8500_GPIOBANK0_BASE,
- U8500_GPIOBANK1_BASE,
- U8500_GPIOBANK2_BASE,
- U8500_GPIOBANK3_BASE,
- U8500_GPIOBANK4_BASE,
- U8500_GPIOBANK5_BASE,
- U8500_GPIOBANK6_BASE,
- U8500_GPIOBANK7_BASE,
- U8500_GPIOBANK8_BASE,
-};
-
-static void __init db8500_add_gpios(struct device *parent)
+void u8500_reset_ip(unsigned char per, unsigned int ip_mask)
{
- struct nmk_gpio_platform_data pdata = {
- .supports_sleepmode = true,
- };
+ void __iomem *prcc_rst_set, *prcc_rst_clr;
+
+ if (per == 0 || per == 4 || per == 5 || per > 6)
+ return;
- dbx500_add_gpios(parent, ARRAY_AND_SIZE(db8500_gpio_base),
- IRQ_DB8500_GPIO0, &pdata);
+ prcc_rst_set = __io_address(per_clkrst_base[per] + PRCC_K_SOFTRST_SET);
+ prcc_rst_clr = __io_address(per_clkrst_base[per] + PRCC_K_SOFTRST_CLR);
+
+ /* Activate soft reset PRCC_K_SOFTRST_CLR */
+ writel(ip_mask, prcc_rst_clr);
+ udelay(1);
+
+ /* Release soft reset PRCC_K_SOFTRST_SET */
+ writel(ip_mask, prcc_rst_set);
+ udelay(1);
}
+static struct platform_device *platform_devs[] __initdata = {
+ &u8500_gpio_devs[0],
+ &u8500_gpio_devs[1],
+ &u8500_gpio_devs[2],
+ &u8500_gpio_devs[3],
+ &u8500_gpio_devs[4],
+ &u8500_gpio_devs[5],
+ &u8500_gpio_devs[6],
+ &u8500_gpio_devs[7],
+ &u8500_gpio_devs[8],
+ &db8500_pmu_device,
+ &db8500_prcmu_device,
+ &u8500_wdt_device,
+};
+
static int usb_db8500_rx_dma_cfg[] = {
DB8500_DMA_DEV38_USB_OTG_IEP_1_9,
DB8500_DMA_DEV37_USB_OTG_IEP_2_10,
@@ -190,10 +239,14 @@ struct device * __init u8500_init_devices(void)
struct device *parent;
int i;
+#ifdef CONFIG_STM_TRACE
+ /* Early init for STM tracing */
+ platform_device_register(&u8500_stm_device);
+#endif
parent = db8500_soc_device_init();
+ db8500_dma_init(parent);
db8500_add_rtc(parent);
- db8500_add_gpios(parent);
db8500_add_usb(parent, usb_db8500_rx_dma_cfg, usb_db8500_tx_dma_cfg);
platform_device_register_data(parent,
diff --git a/arch/arm/mach-ux500/cpu-db8500.h b/arch/arm/mach-ux500/cpu-db8500.h
new file mode 100644
index 00000000000..80766f49d60
--- /dev/null
+++ b/arch/arm/mach-ux500/cpu-db8500.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * CPU specific routines.
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ */
+
+#ifndef __CPU_DB8500_H__
+#define __CPU_DB8500_H__
+
+/* PER1 IP's softreset masks */
+#define PRCC_K_SOFTRST_UART0_MASK (1 << 0)
+#define PRCC_K_SOFTRST_UART1_MASK (1 << 1)
+#define PRCC_K_SOFTRST_I2C1_MASK (1 << 2)
+#define PRCC_K_SOFTRST_MSP0_MASK (1 << 3)
+#define PRCC_K_SOFTRST_MSP1_MASK (1 << 4)
+#define PRCC_K_SOFTRST_SDI0_MASK (1 << 5)
+#define PRCC_K_SOFTRST_I2C2_MASK (1 << 6)
+#define PRCC_K_SOFTRST_SP13_MASK (1 << 7)
+#define PRCC_K_SOFTRST_SLIMBUS0_MASK (1 << 8)
+#define PRCC_K_SOFTRST_I2C4_MASK (1 << 9)
+#define PRCC_K_SOFTRST_MSP3_MASK (1 << 10)
+#define PRCC_K_SOFTRST_PER_MSP3_MASK (1 << 11)
+#define PRCC_K_SOFTRST_PER_MSP1_MASK (1 << 12)
+#define PRCC_K_SOFTRST_PER_MSP0_MASK (1 << 13)
+#define PRCC_K_SOFTRST_PER_SLIMBUS_MASK (1 << 14)
+
+/* PER2 IP's softreset masks */
+#define PRCC_K_SOFTRST_I2C3_MASK (1 << 0)
+#define PRCC_K_SOFTRST_PWL_MASK (1 << 1)
+#define PRCC_K_SOFTRST_SDI4_MASK (1 << 2)
+#define PRCC_K_SOFTRST_MSP2_MASK (1 << 3)
+#define PRCC_K_SOFTRST_SDI1_MASK (1 << 4)
+#define PRCC_K_SOFTRST_SDI3_MASK (1 << 5)
+#define PRCC_K_SOFTRST_HSIRX_MASK (1 << 6)
+#define PRCC_K_SOFTRST_HSITX_MASK (1 << 7)
+#define PRCC_K_SOFTRST_PER_MSP2_MASK (1 << 8)
+
+/* PER3 IP's softreset masks */
+#define PRCC_K_SOFTRST_SSP0_MASK (1 << 1)
+#define PRCC_K_SOFTRST_SSP1_MASK (1 << 2)
+#define PRCC_K_SOFTRST_I2C0_MASK (1 << 3)
+#define PRCC_K_SOFTRST_SDI2_MASK (1 << 4)
+#define PRCC_K_SOFTRST_SKE_MASK (1 << 5)
+#define PRCC_K_SOFTRST_UART2_MASK (1 << 6)
+#define PRCC_K_SOFTRST_SDI5_MASK (1 << 7)
+
+/* PER6 IP's softreset masks */
+#define PRCC_K_SOFTRST_RNG_MASK (1 << 0)
+
+void u8500_reset_ip(unsigned char per, unsigned int ip_mask);
+
+#endif
diff --git a/arch/arm/mach-ux500/cpu-db9500.c b/arch/arm/mach-ux500/cpu-db9500.c
new file mode 100644
index 00000000000..4d900cbe537
--- /dev/null
+++ b/arch/arm/mach-ux500/cpu-db9500.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Pawel SZYSZUK <pawel.szyszuk@stericsson.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 <linux/types.h>
+#include <linux/init.h>
+
+#include <mach/id.h>
+
+#include "pins.h"
+
+/*
+ * U9500 is currently using U8500v2 HW. Therefore, the platform detection
+ * is based on the kernel cmd line setting (early_param "pinsfor").
+ */
+bool cpu_is_u9500()
+{
+ if (pins_for_u9500())
+ return true;
+ else
+ return false;
+}
diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c
index d11f3892a27..935d5006261 100644
--- a/arch/arm/mach-ux500/cpu.c
+++ b/arch/arm/mach-ux500/cpu.c
@@ -9,6 +9,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/mfd/dbx500-prcmu.h>
#include <linux/mfd/db8500-prcmu.h>
#include <linux/mfd/db5500-prcmu.h>
#include <linux/clksrc-dbx500-prcmu.h>
@@ -18,13 +19,17 @@
#include <linux/stat.h>
#include <linux/of.h>
#include <linux/of_irq.h>
+#include <linux/delay.h>
#include <asm/hardware/gic.h>
#include <asm/mach/map.h>
+#include <asm/system_misc.h>
#include <mach/hardware.h>
#include <mach/setup.h>
#include <mach/devices.h>
+#include <mach/reboot_reasons.h>
+#include <mach/pm.h>
#include "clock.h"
@@ -35,6 +40,52 @@ static const struct of_device_id ux500_dt_irq_match[] = {
{},
};
+/*
+ * The reboot reason string can be 255 characters long and the memory
+ * in which we save the sw reset reason is 2 bytes. Therefore we need to
+ * convert the string into a 16 bit pattern.
+ *
+ * See file reboot_reasons.h for conversion.
+ */
+static unsigned short map_cmd_to_code(const char *cmd)
+{
+ int i;
+
+ if (cmd == NULL)
+ /* normal reboot w/o argument */
+ return SW_RESET_NO_ARGUMENT;
+
+ /* Search through reboot reason list */
+ for (i = 0; i < reboot_reasons_size; i++) {
+ if (!strcmp(reboot_reasons[i].reason, cmd))
+ return reboot_reasons[i].code;
+ }
+
+ /* No valid Reboot Reason found */
+ return SW_RESET_CRASH;
+}
+
+void ux500_restart(char mode, const char *cmd)
+{
+ unsigned short reset_code;
+
+ reset_code = map_cmd_to_code(cmd);
+ prcmu_system_reset(reset_code);
+
+ mdelay(1000);
+
+ /*
+ * On 5500, the PRCMU firmware waits for up to 2 seconds for the modem
+ * to respond.
+ */
+ if (cpu_is_u5500())
+ mdelay(2000);
+
+ printk(KERN_ERR "Reboot via PRCMU failed -- System halted\n");
+ while (1)
+ ;
+}
+
void __init ux500_init_irq(void)
{
void __iomem *dist_base;
@@ -43,7 +94,7 @@ void __init ux500_init_irq(void)
if (cpu_is_u5500()) {
dist_base = __io_address(U5500_GIC_DIST_BASE);
cpu_base = __io_address(U5500_GIC_CPU_BASE);
- } else if (cpu_is_u8500()) {
+ } else if (cpu_is_u8500() || cpu_is_u9540()) {
dist_base = __io_address(U8500_GIC_DIST_BASE);
cpu_base = __io_address(U8500_GIC_CPU_BASE);
} else
@@ -57,13 +108,21 @@ void __init ux500_init_irq(void)
gic_init(0, 29, dist_base, cpu_base);
/*
+ * On WD reboot gic is in some cases decoupled.
+ * This will make sure that the GIC is correctly configured.
+ */
+ ux500_pm_gic_recouple();
+
+ /*
* Init clocks here so that they are available for system timer
* initialization.
*/
if (cpu_is_u5500())
db5500_prcmu_early_init();
- if (cpu_is_u8500())
+ if (cpu_is_u8500() || cpu_is_u9540())
db8500_prcmu_early_init();
+
+ arm_pm_restart = ux500_restart;
clk_init();
}
diff --git a/arch/arm/mach-ux500/cpuidle.c b/arch/arm/mach-ux500/cpuidle.c
new file mode 100644
index 00000000000..b54884bd254
--- /dev/null
+++ b/arch/arm/mach-ux500/cpuidle.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2012 Linaro : Daniel Lezcano <daniel.lezcano@linaro.org> (IBM)
+ *
+ * Based on the work of Rickard Andersson <rickard.andersson@stericsson.com>
+ * and Jonas Aaberg <jonas.aberg@stericsson.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 <linux/module.h>
+#include <linux/cpuidle.h>
+#include <linux/clockchips.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+#include <linux/smp.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <asm/cpuidle.h>
+#include <asm/proc-fns.h>
+
+static atomic_t master = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(master_lock);
+static DEFINE_PER_CPU(struct cpuidle_device, ux500_cpuidle_device);
+
+static inline int ux500_enter_idle(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int index)
+{
+ int this_cpu = smp_processor_id();
+ bool recouple = false;
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &this_cpu);
+
+ if (atomic_inc_return(&master) == num_online_cpus()) {
+
+ /* With this lock, we prevent the other cpu to exit and enter
+ * this function again and become the master */
+ if (!spin_trylock(&master_lock))
+ goto wfi;
+
+ /* decouple the gic from the A9 cores */
+ if (prcmu_gic_decouple())
+ goto out;
+
+ /* If an error occur, we will have to recouple the gic
+ * manually */
+ recouple = true;
+
+ /* At this state, as the gic is decoupled, if the other
+ * cpu is in WFI, we have the guarantee it won't be wake
+ * up, so we can safely go to retention */
+ if (!prcmu_is_cpu_in_wfi(this_cpu ? 0 : 1))
+ goto out;
+
+ /* The prcmu will be in charge of watching the interrupts
+ * and wake up the cpus */
+ if (prcmu_copy_gic_settings())
+ goto out;
+
+ /* Check in the meantime an interrupt did
+ * not occur on the gic ... */
+ if (prcmu_gic_pending_irq())
+ goto out;
+
+ /* ... and the prcmu */
+ if (prcmu_pending_irq())
+ goto out;
+
+ /* Go to the retention state, the prcmu will wait for the
+ * cpu to go WFI and this is what happens after exiting this
+ * 'master' critical section */
+ if (prcmu_set_power_state(PRCMU_AP_IDLE, true, true))
+ goto out;
+
+ /* When we switch to retention, the prcmu is in charge
+ * of recoupling the gic automatically */
+ recouple = false;
+
+ spin_unlock(&master_lock);
+ }
+wfi:
+ cpu_do_idle();
+out:
+ atomic_dec(&master);
+
+ if (recouple) {
+ prcmu_gic_recouple();
+ spin_unlock(&master_lock);
+ }
+
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &this_cpu);
+
+ return index;
+}
+
+static struct cpuidle_driver ux500_idle_driver = {
+ .name = "ux500_idle",
+ .owner = THIS_MODULE,
+ .en_core_tk_irqen = 1,
+ .states = {
+ ARM_CPUIDLE_WFI_STATE,
+ {
+ .enter = ux500_enter_idle,
+ .exit_latency = 70,
+ .target_residency = 260,
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .name = "ApIdle",
+ .desc = "ARM Retention",
+ },
+ },
+ .safe_state_index = 0,
+ .state_count = 2,
+};
+
+/*
+ * For each cpu, setup the broadcast timer because we will
+ * need to migrate the timers for the states >= ApIdle.
+ */
+static void ux500_setup_broadcast_timer(void *arg)
+{
+ int cpu = smp_processor_id();
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu);
+}
+
+int __init ux500_idle_init(void)
+{
+ int ret, cpu;
+ struct cpuidle_device *device;
+
+ /* Configure wake up reasons */
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB));
+
+ /*
+ * Configure the timer broadcast for each cpu, that must
+ * be done from the cpu context, so we use a smp cross
+ * call with 'on_each_cpu'.
+ */
+ on_each_cpu(ux500_setup_broadcast_timer, NULL, 1);
+
+ ret = cpuidle_register_driver(&ux500_idle_driver);
+ if (ret) {
+ printk(KERN_ERR "failed to register ux500 idle driver\n");
+ return ret;
+ }
+
+ for_each_online_cpu(cpu) {
+ device = &per_cpu(ux500_cpuidle_device, cpu);
+ device->cpu = cpu;
+ ret = cpuidle_register_device(device);
+ if (ret) {
+ printk(KERN_ERR "Failed to register cpuidle "
+ "device for cpu%d\n", cpu);
+ goto out_unregister;
+ }
+ }
+out:
+ return ret;
+
+out_unregister:
+ for_each_online_cpu(cpu) {
+ device = &per_cpu(ux500_cpuidle_device, cpu);
+ cpuidle_unregister_device(device);
+ }
+
+ cpuidle_unregister_driver(&ux500_idle_driver);
+ goto out;
+}
+
+device_initcall(ux500_idle_init);
diff --git a/arch/arm/mach-ux500/dbx500_dump.c b/arch/arm/mach-ux500/dbx500_dump.c
new file mode 100644
index 00000000000..89470216590
--- /dev/null
+++ b/arch/arm/mach-ux500/dbx500_dump.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Johan Bjornstedt <johan.bjornstedt@stericsson.com>
+ *
+ * Save DBx500 registers in case of kernel crash
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/kdebug.h>
+
+#include <mach/hardware.h>
+#include <mach/db8500-regs.h>
+#include <mach/db5500-regs.h>
+
+struct dbx500_dump_info {
+ char *name;
+ int *data;
+ int *io_addr;
+ int phy_addr;
+ int size;
+};
+
+static struct dbx500_dump_info db8500_dump[] = {
+ {
+ .name = "prcmu_tcdm",
+ .phy_addr = U8500_PRCMU_TCDM_BASE,
+ .size = 0x1000,
+ },
+ {
+ .name = "prcmu_non_sec_1",
+ .phy_addr = U8500_PRCMU_BASE,
+ .size = 0x340,
+ },
+ {
+ .name = "prcmu_pmb",
+ .phy_addr = (U8500_PRCMU_BASE + 0x344),
+ .size = 0xC,
+ },
+ {
+ .name = "prcmu_thermal",
+ .phy_addr = (U8500_PRCMU_BASE + 0x3C0),
+ .size = 0x40,
+ },
+ {
+ .name = "prcmu_non_sec_2",
+ .phy_addr = (U8500_PRCMU_BASE + 0x404),
+ .size = 0x1FC,
+ },
+ {
+ .name = "prcmu_icn_pmu",
+ .phy_addr = (U8500_PRCMU_BASE + 0xE00),
+ .size = 0x90,
+ },
+ {
+ .name = "db8500_fuses",
+ .phy_addr = (U8500_BACKUPRAM1_BASE + 0xF70),
+ .size = 0xc,
+ },
+};
+
+static struct dbx500_dump_info db9540_dump[] = {
+ {
+ .name = "prcmu_tcdm",
+ .phy_addr = U9540_PRCMU_TCDM_BASE,
+ .size = 0x1000,
+ },
+ {
+ .name = "prcmu_non_sec_1",
+ .phy_addr = U8500_PRCMU_BASE,
+ .size = 0x340,
+ },
+ {
+ .name = "prcmu_pmb",
+ .phy_addr = (U8500_PRCMU_BASE + 0x344),
+ .size = 0xC,
+ },
+ {
+ .name = "prcmu_thermal",
+ .phy_addr = (U8500_PRCMU_BASE + 0x3C0),
+ .size = 0x40,
+ },
+ {
+ .name = "prcmu_non_sec_2",
+ .phy_addr = (U8500_PRCMU_BASE + 0x404),
+ .size = 0x1FC,
+ },
+ {
+ .name = "prcmu_icn_pmu",
+ .phy_addr = (U8500_PRCMU_BASE + 0xE00),
+ .size = 0x118,
+ },
+};
+
+static struct dbx500_dump_info db5500_dump[] = {
+ {
+ .name = "prcmu_tcdm",
+ .phy_addr = U5500_PRCMU_TCDM_BASE,
+ .size = 0x5000,
+ },
+ {
+ .name = "prcmu_gpio",
+ .phy_addr = U5500_GPIO2_BASE,
+ .size = 0x1000,
+ },
+ {
+ .name = "prcmu_msp1",
+ .phy_addr = U5500_MSP1_BASE,
+ .size = 0x1000,
+ },
+ {
+ .name = "prcmu_sec",
+ .phy_addr = (U5500_PRCMU_BASE + 0x1000),
+ .size = 0x1000,
+ },
+ {
+ .name = "prcmu_unsec",
+ .phy_addr = U5500_PRCMU_BASE,
+ .size = 0x1000,
+ },
+};
+
+static struct dbx500_dump_info *dbx500_dump;
+static int dbx500_dump_size;
+static bool dbx500_dump_done;
+
+static DEFINE_SPINLOCK(dbx500_dump_lock);
+
+static int crash_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ int i;
+ unsigned long flags;
+
+ /*
+ * Since there are two ways into this function (die and panic) we have
+ * to make sure we only dump the DB registers once
+ */
+ spin_lock_irqsave(&dbx500_dump_lock, flags);
+ if (dbx500_dump_done)
+ return NOTIFY_DONE;
+
+ pr_info("dbx500_dump notified of crash\n");
+
+ for (i = 0; i < dbx500_dump_size; i++) {
+ memcpy_fromio(dbx500_dump[i].data, dbx500_dump[i].io_addr,
+ dbx500_dump[i].size);
+ }
+
+ dbx500_dump_done = true;
+ spin_unlock_irqrestore(&dbx500_dump_lock, flags);
+
+ return NOTIFY_DONE;
+}
+
+static void __init init_io_addresses(void)
+{
+ int i;
+
+ for (i = 0; i < dbx500_dump_size; i++)
+ dbx500_dump[i].io_addr = ioremap(dbx500_dump[i].phy_addr,
+ dbx500_dump[i].size);
+}
+
+static struct notifier_block die_notifier = {
+ .notifier_call = crash_notifier,
+};
+
+static struct notifier_block panic_notifier = {
+ .notifier_call = crash_notifier,
+};
+
+int __init dbx500_dump_init(void)
+{
+ int err, i;
+
+ if (cpu_is_u5500()) {
+ dbx500_dump = db5500_dump;
+ dbx500_dump_size = ARRAY_SIZE(db5500_dump);
+ } else if (cpu_is_u8500()) {
+ dbx500_dump = db8500_dump;
+ dbx500_dump_size = ARRAY_SIZE(db8500_dump);
+ } else if (cpu_is_u9540()) {
+ dbx500_dump = db9540_dump;
+ dbx500_dump_size = ARRAY_SIZE(db9540_dump);
+ } else {
+ ux500_unknown_soc();
+ }
+
+ for (i = 0; i < dbx500_dump_size; i++) {
+ dbx500_dump[i].data = kmalloc(dbx500_dump[i].size, GFP_KERNEL);
+ if (!dbx500_dump[i].data) {
+ pr_err("dbx500_dump: Could not allocate memory for "
+ "%s\n", dbx500_dump[i].name);
+ err = -ENOMEM;
+ goto free_mem;
+ }
+ }
+
+ init_io_addresses();
+
+ err = atomic_notifier_chain_register(&panic_notifier_list,
+ &panic_notifier);
+ if (err != 0) {
+ pr_err("dbx500_dump: Unable to register a panic notifier %d\n",
+ err);
+ goto free_mem;
+ }
+
+ err = register_die_notifier(&die_notifier);
+ if (err != 0) {
+ pr_err("dbx500_dump: Unable to register a die notifier %d\n",
+ err);
+ goto free_panic_notifier;
+ }
+ pr_info("dbx500_dump: driver initialized\n");
+ return err;
+
+free_panic_notifier:
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &panic_notifier);
+
+free_mem:
+ for (i = i - 1; i >= 0; i--)
+ kfree(dbx500_dump[i].data);
+
+ return err;
+}
+arch_initcall(dbx500_dump_init);
diff --git a/arch/arm/mach-ux500/devices-common.c b/arch/arm/mach-ux500/devices-common.c
index c5312a4b49f..04f27d7b9c9 100644
--- a/arch/arm/mach-ux500/devices-common.c
+++ b/arch/arm/mach-ux500/devices-common.c
@@ -6,16 +6,19 @@
*/
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/amba/bus.h>
-
-#include <plat/gpio-nomadik.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
#include <mach/hardware.h>
+#include <mach/pm.h>
#include "devices-common.h"
@@ -33,6 +36,7 @@ dbx500_add_amba_device(struct device *parent, const char *name,
dev->dma_mask = DMA_BIT_MASK(32);
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ dev->dev.pm_domain = &ux500_amba_dev_power_domain;
dev->irq[0] = irq;
@@ -52,6 +56,76 @@ dbx500_add_amba_device(struct device *parent, const char *name,
}
static struct platform_device *
+dbx500_add_platform_device(const char *name, int id, void *pdata,
+ struct resource *res, int resnum)
+{
+ struct platform_device *dev;
+ int ret;
+
+ dev = platform_device_alloc(name, id);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
+ dev->dev.pm_domain = &ux500_dev_power_domain;
+
+ ret = platform_device_add_resources(dev, res, resnum);
+ if (ret)
+ goto out_free;
+
+ dev->dev.platform_data = pdata;
+
+ ret = platform_device_add(dev);
+ if (ret)
+ goto out_free;
+
+ return dev;
+
+out_free:
+ platform_device_put(dev);
+ return ERR_PTR(ret);
+}
+
+struct platform_device *
+dbx500_add_platform_device_4k1irq(const char *name, int id,
+ resource_size_t base,
+ int irq, void *pdata)
+{
+ struct resource resources[] = {
+ [0] = {
+ .start = base,
+ .end = base + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = irq,
+ .end = irq,
+ .flags = IORESOURCE_IRQ,
+ }
+ };
+
+ return dbx500_add_platform_device(name, id, pdata, resources,
+ ARRAY_SIZE(resources));
+}
+
+struct platform_device *
+dbx500_add_platform_device_noirq(const char *name, int id,
+ resource_size_t base, void *pdata)
+{
+ struct resource resources[] = {
+ [0] = {
+ .start = base,
+ .end = base + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }
+ };
+
+ return dbx500_add_platform_device(name, id, pdata, resources,
+ ARRAY_SIZE(resources));
+}
+
+static struct platform_device *
dbx500_add_gpio(struct device *parent, int id, resource_size_t addr, int irq,
struct nmk_gpio_platform_data *pdata)
{
diff --git a/arch/arm/mach-ux500/devices-common.h b/arch/arm/mach-ux500/devices-common.h
index 39c74ec82ad..23d65b07795 100644
--- a/arch/arm/mach-ux500/devices-common.h
+++ b/arch/arm/mach-ux500/devices-common.h
@@ -11,6 +11,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/sys_soc.h>
+#include <linux/amba/serial.h>
#include <plat/i2c.h>
extern struct amba_device *
@@ -18,20 +19,31 @@ dbx500_add_amba_device(struct device *parent, const char *name,
resource_size_t base, int irq, void *pdata,
unsigned int periphid);
-struct spi_master_cntlr;
+extern struct platform_device *
+dbx500_add_platform_device_4k1irq(const char *name, int id,
+ resource_size_t base,
+ int irq, void *pdata);
+
+extern struct platform_device *
+dbx500_add_platform_device_noirq(const char *name, int id,
+ resource_size_t base, void *pdata);
+
+struct stm_msp_controller;
static inline struct amba_device *
dbx500_add_msp_spi(struct device *parent, const char *name,
resource_size_t base, int irq,
- struct spi_master_cntlr *pdata)
+ struct stm_msp_controller *pdata)
{
return dbx500_add_amba_device(parent, name, base, irq,
pdata, 0);
}
+struct pl022_ssp_controller;
+
static inline struct amba_device *
dbx500_add_spi(struct device *parent, const char *name, resource_size_t base,
- int irq, struct spi_master_cntlr *pdata,
+ int irq, struct pl022_ssp_controller *pdata,
u32 periphid)
{
return dbx500_add_amba_device(parent, name, base, irq,
@@ -82,12 +94,42 @@ dbx500_add_i2c(struct device *parent, int id, resource_size_t base, int irq,
return platform_device_register_full(&pdevinfo);
}
+struct msp_i2s_platform_data;
+
+static inline struct platform_device *
+dbx500_add_msp_i2s(struct device *parent, int id, resource_size_t base, int irq,
+ struct msp_i2s_platform_data *pdata)
+{
+ /* FIXME parent parameter ignored */
+ return dbx500_add_platform_device_4k1irq("ux500-msp-i2s", id, base, irq,
+ pdata);
+}
+
static inline struct amba_device *
dbx500_add_rtc(struct device *parent, resource_size_t base, int irq)
{
return dbx500_add_amba_device(parent, "rtc-pl031", base, irq, NULL, 0);
}
+struct cryp_platform_data;
+
+static inline struct platform_device *
+dbx500_add_cryp1(int id, resource_size_t base, int irq,
+ struct cryp_platform_data *pdata)
+{
+ return dbx500_add_platform_device_4k1irq("cryp1", id, base, irq,
+ pdata);
+}
+
+struct hash_platform_data;
+
+static inline struct platform_device *
+dbx500_add_hash1(int id, resource_size_t base,
+ struct hash_platform_data *pdata)
+{
+ return dbx500_add_platform_device_noirq("hash1", id, base, pdata);
+}
+
struct nmk_gpio_platform_data;
void dbx500_add_gpios(struct device *parent, resource_size_t *base, int num,
diff --git a/arch/arm/mach-ux500/devices-db5500.c b/arch/arm/mach-ux500/devices-db5500.c
new file mode 100644
index 00000000000..ec7c6378d0e
--- /dev/null
+++ b/arch/arm/mach-ux500/devices-db5500.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ *
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * for the System Trace Module part.
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/nomadik.h>
+
+#include <mach/hardware.h>
+#include <mach/devices.h>
+
+#ifdef CONFIG_FB_MCDE
+#include <video/mcde.h>
+#endif
+#include <mach/db5500-regs.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+#include <mach/pm.h>
+
+#define GPIO_DATA(_name, first, num) \
+ { \
+ .name = _name, \
+ .first_gpio = first, \
+ .first_irq = NOMADIK_GPIO_TO_IRQ(first), \
+ .num_gpio = num, \
+ .get_secondary_status = ux500_pm_gpio_read_wake_up_status, \
+ .set_ioforce = ux500_pm_prcmu_set_ioforce, \
+ }
+
+#define GPIO_RESOURCE(block) \
+ { \
+ .start = U5500_GPIOBANK##block##_BASE, \
+ .end = U5500_GPIOBANK##block##_BASE + 127, \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ { \
+ .start = IRQ_DB5500_GPIO##block, \
+ .end = IRQ_DB5500_GPIO##block, \
+ .flags = IORESOURCE_IRQ, \
+ }, \
+ { \
+ .start = IRQ_DB5500_PRCMU_GPIO##block, \
+ .end = IRQ_DB5500_PRCMU_GPIO##block, \
+ .flags = IORESOURCE_IRQ, \
+ }
+
+#define GPIO_DEVICE(block) \
+ { \
+ .name = "gpio", \
+ .id = block, \
+ .num_resources = 3, \
+ .resource = &u5500_gpio_resources[block * 3], \
+ .dev = { \
+ .platform_data = &u5500_gpio_data[block], \
+ }, \
+ }
+
+static struct nmk_gpio_platform_data u5500_gpio_data[] = {
+ GPIO_DATA("GPIO-0-31", 0, 32),
+ GPIO_DATA("GPIO-32-63", 32, 4), /* 36..63 not routed to pin */
+ GPIO_DATA("GPIO-64-95", 64, 19), /* 83..95 not routed to pin */
+ GPIO_DATA("GPIO-96-127", 96, 6), /* 102..127 not routed to pin */
+ GPIO_DATA("GPIO-128-159", 128, 21), /* 149..159 not routed to pin */
+ GPIO_DATA("GPIO-160-191", 160, 32),
+ GPIO_DATA("GPIO-192-223", 192, 32),
+ GPIO_DATA("GPIO-224-255", 224, 4), /* 228..255 not routed to pin */
+};
+
+static struct resource u5500_gpio_resources[] = {
+ GPIO_RESOURCE(0),
+ GPIO_RESOURCE(1),
+ GPIO_RESOURCE(2),
+ GPIO_RESOURCE(3),
+ GPIO_RESOURCE(4),
+ GPIO_RESOURCE(5),
+ GPIO_RESOURCE(6),
+ GPIO_RESOURCE(7),
+};
+
+struct platform_device u5500_gpio_devs[] = {
+ GPIO_DEVICE(0),
+ GPIO_DEVICE(1),
+ GPIO_DEVICE(2),
+ GPIO_DEVICE(3),
+ GPIO_DEVICE(4),
+ GPIO_DEVICE(5),
+ GPIO_DEVICE(6),
+ GPIO_DEVICE(7),
+};
+
+#define U5500_PWM_SIZE 0x20
+static struct resource u5500_pwm0_resource[] = {
+ {
+ .name = "PWM_BASE",
+ .start = U5500_PWM_BASE,
+ .end = U5500_PWM_BASE + U5500_PWM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource u5500_pwm1_resource[] = {
+ {
+ .name = "PWM_BASE",
+ .start = U5500_PWM_BASE + U5500_PWM_SIZE,
+ .end = U5500_PWM_BASE + U5500_PWM_SIZE * 2 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource u5500_pwm2_resource[] = {
+ {
+ .name = "PWM_BASE",
+ .start = U5500_PWM_BASE + U5500_PWM_SIZE * 2,
+ .end = U5500_PWM_BASE + U5500_PWM_SIZE * 3 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource u5500_pwm3_resource[] = {
+ {
+ .name = "PWM_BASE",
+ .start = U5500_PWM_BASE + U5500_PWM_SIZE * 3,
+ .end = U5500_PWM_BASE + U5500_PWM_SIZE * 4 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device u5500_pwm0_device = {
+ .id = 0,
+ .name = "pwm",
+ .resource = u5500_pwm0_resource,
+ .num_resources = ARRAY_SIZE(u5500_pwm0_resource),
+};
+
+struct platform_device u5500_pwm1_device = {
+ .id = 1,
+ .name = "pwm",
+ .resource = u5500_pwm1_resource,
+ .num_resources = ARRAY_SIZE(u5500_pwm1_resource),
+};
+
+struct platform_device u5500_pwm2_device = {
+ .id = 2,
+ .name = "pwm",
+ .resource = u5500_pwm2_resource,
+ .num_resources = ARRAY_SIZE(u5500_pwm2_resource),
+};
+
+struct platform_device u5500_pwm3_device = {
+ .id = 3,
+ .name = "pwm",
+ .resource = u5500_pwm3_resource,
+ .num_resources = ARRAY_SIZE(u5500_pwm3_resource),
+};
+
+#ifdef CONFIG_FB_MCDE
+static struct resource mcde_resources[] = {
+ [0] = {
+ .name = MCDE_IO_AREA,
+ .start = U5500_MCDE_BASE,
+ .end = U5500_MCDE_BASE + U5500_MCDE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = MCDE_IO_AREA,
+ .start = U5500_DSI_LINK1_BASE,
+ .end = U5500_DSI_LINK1_BASE + U5500_DSI_LINK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .name = MCDE_IO_AREA,
+ .start = U5500_DSI_LINK2_BASE,
+ .end = U5500_DSI_LINK2_BASE + U5500_DSI_LINK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [3] = {
+ .name = MCDE_IRQ,
+ .start = IRQ_DB5500_DISP,
+ .end = IRQ_DB5500_DISP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int mcde_platform_enable_dsipll(void)
+{
+ return prcmu_enable_dsipll();
+}
+
+static int mcde_platform_disable_dsipll(void)
+{
+ return prcmu_disable_dsipll();
+}
+
+static int mcde_platform_set_display_clocks(void)
+{
+ return prcmu_set_display_clocks();
+}
+
+static struct mcde_platform_data mcde_pdata = {
+ .syncmux = 0x01,
+ .regulator_mcde_epod_id = "vsupply",
+ .regulator_esram_epod_id = "v-esram12",
+#ifdef CONFIG_MCDE_DISPLAY_DSI
+ .clock_dsi_id = "hdmi",
+ .clock_dsi_lp_id = "tv",
+#endif
+ .clock_mcde_id = "mcde",
+ .platform_set_clocks = mcde_platform_set_display_clocks,
+ .platform_enable_dsipll = mcde_platform_enable_dsipll,
+ .platform_disable_dsipll = mcde_platform_disable_dsipll,
+ /* TODO: Remove rotation buffers once ESRAM driver is completed */
+ .rotbuf1 = U8500_ESRAM_BASE + 0x20000 * 4 + 0x2000,
+ .rotbuf2 = U8500_ESRAM_BASE + 0x20000 * 4 + 0x11000,
+ .rotbufsize = 0xF000,
+};
+
+struct platform_device u5500_mcde_device = {
+ .name = "mcde",
+ .id = -1,
+ .dev = {
+ .platform_data = &mcde_pdata,
+ },
+ .num_resources = ARRAY_SIZE(mcde_resources),
+ .resource = mcde_resources,
+};
+#endif
+
+struct platform_device u5500_b2r2_blt_device = {
+ .name = "b2r2_blt",
+ .id = 0,
+ .dev = {
+ .init_name = "b2r2_blt_init",
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static struct b2r2_platform_data b2r2_platform_data = {
+ .regulator_id = "vsupply",
+ .clock_id = "b2r2",
+};
+
+static struct resource b2r2_resources[] = {
+ [0] = {
+ .start = U5500_B2R2_BASE,
+ .end = U5500_B2R2_BASE + ((4*1024)-1),
+ .name = "b2r2_base",
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "B2R2_IRQ",
+ .start = IRQ_DB5500_B2R2,
+ .end = IRQ_DB5500_B2R2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device u5500_b2r2_device = {
+ .name = "b2r2",
+ .id = 0,
+ .dev = {
+ .init_name = "b2r2_bus",
+ .platform_data = &b2r2_platform_data,
+ .coherent_dma_mask = ~0,
+ },
+ .num_resources = ARRAY_SIZE(b2r2_resources),
+ .resource = b2r2_resources,
+};
+
+static struct resource u5500_thsens_resources[] = {
+ [0] = {
+ .name = "IRQ_HOTMON_LOW",
+ .start = IRQ_DB5500_PRCMU_TEMP_SENSOR_LOW,
+ .end = IRQ_DB5500_PRCMU_TEMP_SENSOR_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .name = "IRQ_HOTMON_HIGH",
+ .start = IRQ_DB5500_PRCMU_TEMP_SENSOR_HIGH,
+ .end = IRQ_DB5500_PRCMU_TEMP_SENSOR_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device u5500_thsens_device = {
+ .name = "db5500_temp",
+ .resource = u5500_thsens_resources,
+ .num_resources = ARRAY_SIZE(u5500_thsens_resources),
+};
+
+struct platform_device u5500_wdt_device = {
+ .name = "ux500_wdt",
+ .id = -1,
+};
diff --git a/arch/arm/mach-ux500/devices-db5500.h b/arch/arm/mach-ux500/devices-db5500.h
index e70955502c3..22d6086d4dc 100644
--- a/arch/arm/mach-ux500/devices-db5500.h
+++ b/arch/arm/mach-ux500/devices-db5500.h
@@ -17,6 +17,16 @@
#define db5500_add_i2c3(parent, pdata) \
dbx500_add_i2c(parent, 3, U5500_I2C3_BASE, IRQ_DB5500_I2C3, pdata)
+struct db5500_keypad_platform_data;
+
+static inline struct platform_device *
+db5500_add_keypad(struct db5500_keypad_platform_data *pdata)
+{
+ return dbx500_add_platform_device_4k1irq("db5500-keypad", -1,
+ U5500_KEYPAD_BASE,
+ IRQ_DB5500_KBD, pdata);
+}
+
#define db5500_add_msp0_spi(parent, pdata) \
dbx500_add_msp_spi(parent, "msp0", U5500_MSP0_BASE, \
IRQ_DB5500_MSP0, pdata)
@@ -44,26 +54,26 @@
ux500_add_usb(parent, U5500_USBOTG_BASE, \
IRQ_DB5500_USBOTG, rx_cfg, tx_cfg)
-#define db5500_add_sdi0(parent, pdata) \
+#define db5500_add_sdi0(parent, pdata, pid) \
dbx500_add_sdi(parent, "sdi0", U5500_SDI0_BASE, \
IRQ_DB5500_SDMMC0, pdata, \
- 0x10480180)
-#define db5500_add_sdi1(parent, pdata) \
+ pid)
+#define db5500_add_sdi1(parent, pdata, pid) \
dbx500_add_sdi(parent, "sdi1", U5500_SDI1_BASE, \
IRQ_DB5500_SDMMC1, pdata, \
- 0x10480180)
-#define db5500_add_sdi2(parent, pdata) \
+ pid)
+#define db5500_add_sdi2(parent, pdata, pid) \
dbx500_add_sdi(parent, "sdi2", U5500_SDI2_BASE, \
IRQ_DB5500_SDMMC2, pdata \
- 0x10480180)
-#define db5500_add_sdi3(parent, pdata) \
+ pid)
+#define db5500_add_sdi3(parent, pdata, pid) \
dbx500_add_sdi(parent, "sdi3", U5500_SDI3_BASE, \
- IRQ_DB5500_SDMMC3, pdata \
- 0x10480180)
-#define db5500_add_sdi4(parent, pdata) \
+ IRQ_DB5500_SDMMC3, pdata, \
+ pid)
+#define db5500_add_sdi4(parent, pdata, pid) \
dbx500_add_sdi(parent, "sdi4", U5500_SDI4_BASE, \
- IRQ_DB5500_SDMMC4, pdata \
- 0x10480180)
+ IRQ_DB5500_SDMMC4, pdata, \
+ pid)
/* This one has a bad peripheral ID in the U5500 silicon */
#define db5500_add_spi0(parent, pdata) \
@@ -76,11 +86,11 @@
0x10080023)
#define db5500_add_spi2(parent, pdata) \
dbx500_add_spi(parent, "spi2", U5500_SPI2_BASE, \
- IRQ_DB5500_SPI2, pdata \
+ IRQ_DB5500_SPI2, pdata, \
0x10080023)
#define db5500_add_spi3(parent, pdata) \
dbx500_add_spi(parent, "spi3", U5500_SPI3_BASE, \
- IRQ_DB5500_SPI3, pdata \
+ IRQ_DB5500_SPI3, pdata, \
0x10080023)
#define db5500_add_uart0(parent, plat) \
@@ -96,4 +106,9 @@
dbx500_add_uart(parent, "uart3", U5500_UART3_BASE, \
IRQ_DB5500_UART3, plat)
+#define db5500_add_cryp1(pdata) \
+ dbx500_add_cryp1(-1, U5500_CRYP1_BASE, IRQ_DB5500_CRYP1, pdata)
+#define db5500_add_hash1(pdata) \
+ dbx500_add_hash1(-1, U5500_HASH1_BASE, pdata)
+
#endif
diff --git a/arch/arm/mach-ux500/devices-db8500.c b/arch/arm/mach-ux500/devices-db8500.c
index 6e66d3777ed..28f6da58e1e 100644
--- a/arch/arm/mach-ux500/devices-db8500.c
+++ b/arch/arm/mach-ux500/devices-db8500.c
@@ -10,166 +10,459 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl022.h>
+#include <plat/pincfg.h>
#include <plat/ste_dma40.h>
+#include <mach/devices.h>
#include <mach/hardware.h>
#include <mach/setup.h>
+#include <mach/pm.h>
+#ifdef CONFIG_FB_MCDE
+#include <video/mcde.h>
+#endif
+#include <linux/mfd/dbx500-prcmu.h>
+#ifdef CONFIG_HSI
+#include <mach/hsi.h>
+#endif
+#include <mach/ste-dma40-db8500.h>
+#ifdef CONFIG_FB_B2R2
+#include <video/b2r2_blt.h>
+#endif
-#include "ste-dma40-db8500.h"
+#include "pins-db8500.h"
-static struct resource dma40_resources[] = {
+#define GPIO_DATA(_name, first, num) \
+ { \
+ .name = _name, \
+ .first_gpio = first, \
+ .first_irq = NOMADIK_GPIO_TO_IRQ(first), \
+ .num_gpio = num, \
+ .get_secondary_status = ux500_pm_gpio_read_wake_up_status, \
+ .set_ioforce = ux500_pm_prcmu_set_ioforce, \
+ .supports_sleepmode = true, \
+ }
+
+#define GPIO_RESOURCE(block) \
+ { \
+ .start = U8500_GPIOBANK##block##_BASE, \
+ .end = U8500_GPIOBANK##block##_BASE + 127, \
+ .flags = IORESOURCE_MEM, \
+ }, \
+ { \
+ .start = IRQ_DB8500_GPIO##block, \
+ .end = IRQ_DB8500_GPIO##block, \
+ .flags = IORESOURCE_IRQ, \
+ }, \
+ { \
+ .start = IRQ_PRCMU_GPIO##block, \
+ .end = IRQ_PRCMU_GPIO##block, \
+ .flags = IORESOURCE_IRQ, \
+ }
+
+#define GPIO_DEVICE(block) \
+ { \
+ .name = "gpio", \
+ .id = block, \
+ .num_resources = 3, \
+ .resource = &u8500_gpio_resources[block * 3], \
+ .dev = { \
+ .platform_data = &u8500_gpio_data[block], \
+ }, \
+ }
+
+static struct nmk_gpio_platform_data u8500_gpio_data[] = {
+ GPIO_DATA("GPIO-0-31", 0, 32),
+ GPIO_DATA("GPIO-32-63", 32, 5), /* 37..63 not routed to pin */
+ GPIO_DATA("GPIO-64-95", 64, 32),
+ GPIO_DATA("GPIO-96-127", 96, 2), /* 98..127 not routed to pin */
+ GPIO_DATA("GPIO-128-159", 128, 32),
+ GPIO_DATA("GPIO-160-191", 160, 12), /* 172..191 not routed to pin */
+ GPIO_DATA("GPIO-192-223", 192, 32),
+ GPIO_DATA("GPIO-224-255", 224, 7), /* 231..255 not routed to pin */
+ GPIO_DATA("GPIO-256-288", 256, 12), /* 268..288 not routed to pin */
+};
+
+static struct resource u8500_gpio_resources[] = {
+ GPIO_RESOURCE(0),
+ GPIO_RESOURCE(1),
+ GPIO_RESOURCE(2),
+ GPIO_RESOURCE(3),
+ GPIO_RESOURCE(4),
+ GPIO_RESOURCE(5),
+ GPIO_RESOURCE(6),
+ GPIO_RESOURCE(7),
+ GPIO_RESOURCE(8),
+};
+
+struct platform_device u8500_gpio_devs[] = {
+ GPIO_DEVICE(0),
+ GPIO_DEVICE(1),
+ GPIO_DEVICE(2),
+ GPIO_DEVICE(3),
+ GPIO_DEVICE(4),
+ GPIO_DEVICE(5),
+ GPIO_DEVICE(6),
+ GPIO_DEVICE(7),
+ GPIO_DEVICE(8),
+};
+
+static struct resource u8500_shrm_resources[] = {
[0] = {
- .start = U8500_DMA_BASE,
- .end = U8500_DMA_BASE + SZ_4K - 1,
+ .start = U8500_SHRM_GOP_INTERRUPT_BASE,
+ .end = U8500_SHRM_GOP_INTERRUPT_BASE + ((4*4)-1),
+ .name = "shrm_gop_register_base",
.flags = IORESOURCE_MEM,
- .name = "base",
},
[1] = {
- .start = U8500_DMA_LCPA_BASE,
- .end = U8500_DMA_LCPA_BASE + 2 * SZ_1K - 1,
- .flags = IORESOURCE_MEM,
- .name = "lcpa",
+ .start = IRQ_CA_WAKE_REQ_V1,
+ .end = IRQ_CA_WAKE_REQ_V1,
+ .name = "ca_irq_wake_req",
+ .flags = IORESOURCE_IRQ,
},
[2] = {
- .start = IRQ_DB8500_DMA,
- .end = IRQ_DB8500_DMA,
+ .start = IRQ_AC_READ_NOTIFICATION_0_V1,
+ .end = IRQ_AC_READ_NOTIFICATION_0_V1,
+ .name = "ac_read_notification_0_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+ [3] = {
+ .start = IRQ_AC_READ_NOTIFICATION_1_V1,
+ .end = IRQ_AC_READ_NOTIFICATION_1_V1,
+ .name = "ac_read_notification_1_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+ [4] = {
+ .start = IRQ_CA_MSG_PEND_NOTIFICATION_0_V1,
+ .end = IRQ_CA_MSG_PEND_NOTIFICATION_0_V1,
+ .name = "ca_msg_pending_notification_0_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+ [5] = {
+ .start = IRQ_CA_MSG_PEND_NOTIFICATION_1_V1,
+ .end = IRQ_CA_MSG_PEND_NOTIFICATION_1_V1,
+ .name = "ca_msg_pending_notification_1_irq",
.flags = IORESOURCE_IRQ,
}
};
-/* Default configuration for physcial memcpy */
-struct stedma40_chan_cfg dma40_memcpy_conf_phy = {
- .mode = STEDMA40_MODE_PHYSICAL,
- .dir = STEDMA40_MEM_TO_MEM,
+struct platform_device u8500_shrm_device = {
+ .name = "u8500_shrm",
+ .id = 0,
+ .dev = {
+ .init_name = "shrm_bus",
+ .coherent_dma_mask = ~0,
+ },
+
+ .num_resources = ARRAY_SIZE(u8500_shrm_resources),
+ .resource = u8500_shrm_resources
+};
+
+#ifdef CONFIG_FB_MCDE
+static struct resource mcde_resources[] = {
+ [0] = {
+ .name = MCDE_IO_AREA,
+ .start = U8500_MCDE_BASE,
+ .end = U8500_MCDE_BASE + U8500_MCDE_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = MCDE_IO_AREA,
+ .start = U8500_DSI_LINK1_BASE,
+ .end = U8500_DSI_LINK1_BASE + U8500_DSI_LINK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .name = MCDE_IO_AREA,
+ .start = U8500_DSI_LINK2_BASE,
+ .end = U8500_DSI_LINK2_BASE + U8500_DSI_LINK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [3] = {
+ .name = MCDE_IO_AREA,
+ .start = U8500_DSI_LINK3_BASE,
+ .end = U8500_DSI_LINK3_BASE + U8500_DSI_LINK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [4] = {
+ .name = MCDE_IRQ,
+ .start = IRQ_DB8500_DISP,
+ .end = IRQ_DB8500_DISP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int mcde_platform_enable_dsipll(void)
+{
+ return prcmu_enable_dsipll();
+}
+
+static int mcde_platform_disable_dsipll(void)
+{
+ return prcmu_disable_dsipll();
+}
+
+static int mcde_platform_set_display_clocks(void)
+{
+ return prcmu_set_display_clocks();
+}
+
+static struct mcde_platform_data mcde_u8500_pdata = {
+ /*
+ * [0] = 3: 24 bits DPI: connect LSB Ch B to D[0:7]
+ * [3] = 4: 24 bits DPI: connect MID Ch B to D[24:31]
+ * [4] = 5: 24 bits DPI: connect MSB Ch B to D[32:39]
+ *
+ * [1] = 3: TV out : connect LSB Ch B to D[8:15]
+ */
+#define DONT_CARE 0
+ .outmux = { 3, 3, DONT_CARE, 4, 5 },
+#undef DONT_CARE
+ .syncmux = 0x00, /* DPI channel A and B on output pins A and B resp */
+#ifdef CONFIG_MCDE_DISPLAY_DSI
+ .regulator_vana_id = "vdddsi1v2",
+#endif
+ .regulator_mcde_epod_id = "vsupply",
+ .regulator_esram_epod_id = "v-esram34",
+#ifdef CONFIG_MCDE_DISPLAY_DSI
+ .clock_dsi_id = "hdmi",
+ .clock_dsi_lp_id = "tv",
+#endif
+ .clock_dpi_id = "lcd",
+ .clock_mcde_id = "mcde",
+ .platform_set_clocks = mcde_platform_set_display_clocks,
+ .platform_enable_dsipll = mcde_platform_enable_dsipll,
+ .platform_disable_dsipll = mcde_platform_disable_dsipll,
+ /* TODO: Remove rotation buffers once ESRAM driver is completed */
+ .rotbuf1 = U8500_ESRAM_BASE + 0x20000 * 4 + 0x2000,
+ .rotbuf2 = U8500_ESRAM_BASE + 0x20000 * 4 + 0x11000,
+ .rotbufsize = 0xF000,
+};
+
+struct platform_device u8500_mcde_device = {
+ .name = "mcde",
+ .id = -1,
+ .dev = {
+ .platform_data = &mcde_u8500_pdata,
+ },
+ .num_resources = ARRAY_SIZE(mcde_resources),
+ .resource = mcde_resources,
+};
+#endif /* CONFIG_FB_MCDE */
+
+#ifdef CONFIG_FB_B2R2
+struct platform_device u8500_b2r2_blt_device = {
+ .name = "b2r2_blt",
+ .id = 0,
+ .dev = {
+ .init_name = "b2r2_blt_init",
+ .coherent_dma_mask = ~0,
+ },
+};
+
+static struct b2r2_platform_data b2r2_platform_data = {
+ .regulator_id = "vsupply",
+ .clock_id = "b2r2",
+};
- .src_info.data_width = STEDMA40_BYTE_WIDTH,
- .src_info.psize = STEDMA40_PSIZE_PHY_1,
- .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+static struct resource b2r2_resources[] = {
+ [0] = {
+ .start = U8500_B2R2_BASE,
+ .end = U8500_B2R2_BASE + ((4*1024)-1),
+ .name = "b2r2_base",
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "B2R2_IRQ",
+ .start = IRQ_DB8500_B2R2,
+ .end = IRQ_DB8500_B2R2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
- .dst_info.data_width = STEDMA40_BYTE_WIDTH,
- .dst_info.psize = STEDMA40_PSIZE_PHY_1,
- .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+struct platform_device u8500_b2r2_device = {
+ .name = "b2r2",
+ .id = 0,
+ .dev = {
+ .init_name = "b2r2_core",
+ .platform_data = &b2r2_platform_data,
+ .coherent_dma_mask = ~0,
+ },
+ .num_resources = ARRAY_SIZE(b2r2_resources),
+ .resource = b2r2_resources,
};
-/* Default configuration for logical memcpy */
-struct stedma40_chan_cfg dma40_memcpy_conf_log = {
- .dir = STEDMA40_MEM_TO_MEM,
+#endif /* CONFIG_FB_B2R2 */
- .src_info.data_width = STEDMA40_BYTE_WIDTH,
- .src_info.psize = STEDMA40_PSIZE_LOG_1,
- .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+/*
+ * WATCHDOG
+ */
- .dst_info.data_width = STEDMA40_BYTE_WIDTH,
- .dst_info.psize = STEDMA40_PSIZE_LOG_1,
- .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+struct platform_device u8500_wdt_device = {
+ .name = "ux500_wdt",
+ .id = -1,
};
+#ifdef CONFIG_HSI
/*
- * Mapping between destination event lines and physical device address.
- * The event line is tied to a device and therefore the address is constant.
- * When the address comes from a primecell it will be configured in runtime
- * and we set the address to -1 as a placeholder.
+ * HSI
*/
-static const dma_addr_t dma40_tx_map[DB8500_DMA_NR_DEV] = {
- /* MUSB - these will be runtime-reconfigured */
- [DB8500_DMA_DEV39_USB_OTG_OEP_8] = -1,
- [DB8500_DMA_DEV16_USB_OTG_OEP_7_15] = -1,
- [DB8500_DMA_DEV17_USB_OTG_OEP_6_14] = -1,
- [DB8500_DMA_DEV18_USB_OTG_OEP_5_13] = -1,
- [DB8500_DMA_DEV19_USB_OTG_OEP_4_12] = -1,
- [DB8500_DMA_DEV36_USB_OTG_OEP_3_11] = -1,
- [DB8500_DMA_DEV37_USB_OTG_OEP_2_10] = -1,
- [DB8500_DMA_DEV38_USB_OTG_OEP_1_9] = -1,
- /* PrimeCells - run-time configured */
- [DB8500_DMA_DEV0_SPI0_TX] = -1,
- [DB8500_DMA_DEV1_SD_MMC0_TX] = -1,
- [DB8500_DMA_DEV2_SD_MMC1_TX] = -1,
- [DB8500_DMA_DEV3_SD_MMC2_TX] = -1,
- [DB8500_DMA_DEV8_SSP0_TX] = -1,
- [DB8500_DMA_DEV9_SSP1_TX] = -1,
- [DB8500_DMA_DEV11_UART2_TX] = -1,
- [DB8500_DMA_DEV12_UART1_TX] = -1,
- [DB8500_DMA_DEV13_UART0_TX] = -1,
- [DB8500_DMA_DEV28_SD_MM2_TX] = -1,
- [DB8500_DMA_DEV29_SD_MM0_TX] = -1,
- [DB8500_DMA_DEV32_SD_MM1_TX] = -1,
- [DB8500_DMA_DEV33_SPI2_TX] = -1,
- [DB8500_DMA_DEV35_SPI1_TX] = -1,
- [DB8500_DMA_DEV40_SPI3_TX] = -1,
- [DB8500_DMA_DEV41_SD_MM3_TX] = -1,
- [DB8500_DMA_DEV42_SD_MM4_TX] = -1,
- [DB8500_DMA_DEV43_SD_MM5_TX] = -1,
- [DB8500_DMA_DEV14_MSP2_TX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
- [DB8500_DMA_DEV30_MSP1_TX] = U8500_MSP1_BASE + MSP_TX_RX_REG_OFFSET,
- [DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
-};
-
-/* Mapping between source event lines and physical device address */
-static const dma_addr_t dma40_rx_map[DB8500_DMA_NR_DEV] = {
- /* MUSB - these will be runtime-reconfigured */
- [DB8500_DMA_DEV39_USB_OTG_IEP_8] = -1,
- [DB8500_DMA_DEV16_USB_OTG_IEP_7_15] = -1,
- [DB8500_DMA_DEV17_USB_OTG_IEP_6_14] = -1,
- [DB8500_DMA_DEV18_USB_OTG_IEP_5_13] = -1,
- [DB8500_DMA_DEV19_USB_OTG_IEP_4_12] = -1,
- [DB8500_DMA_DEV36_USB_OTG_IEP_3_11] = -1,
- [DB8500_DMA_DEV37_USB_OTG_IEP_2_10] = -1,
- [DB8500_DMA_DEV38_USB_OTG_IEP_1_9] = -1,
- /* PrimeCells */
- [DB8500_DMA_DEV0_SPI0_RX] = -1,
- [DB8500_DMA_DEV1_SD_MMC0_RX] = -1,
- [DB8500_DMA_DEV2_SD_MMC1_RX] = -1,
- [DB8500_DMA_DEV3_SD_MMC2_RX] = -1,
- [DB8500_DMA_DEV8_SSP0_RX] = -1,
- [DB8500_DMA_DEV9_SSP1_RX] = -1,
- [DB8500_DMA_DEV11_UART2_RX] = -1,
- [DB8500_DMA_DEV12_UART1_RX] = -1,
- [DB8500_DMA_DEV13_UART0_RX] = -1,
- [DB8500_DMA_DEV28_SD_MM2_RX] = -1,
- [DB8500_DMA_DEV29_SD_MM0_RX] = -1,
- [DB8500_DMA_DEV32_SD_MM1_RX] = -1,
- [DB8500_DMA_DEV33_SPI2_RX] = -1,
- [DB8500_DMA_DEV35_SPI1_RX] = -1,
- [DB8500_DMA_DEV40_SPI3_RX] = -1,
- [DB8500_DMA_DEV41_SD_MM3_RX] = -1,
- [DB8500_DMA_DEV42_SD_MM4_RX] = -1,
- [DB8500_DMA_DEV43_SD_MM5_RX] = -1,
- [DB8500_DMA_DEV14_MSP2_RX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
- [DB8500_DMA_DEV30_MSP3_RX] = U8500_MSP3_BASE + MSP_TX_RX_REG_OFFSET,
- [DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
-};
-
-/* Reserved event lines for memcpy only */
-static int dma40_memcpy_event[] = {
- DB8500_DMA_MEMCPY_TX_0,
- DB8500_DMA_MEMCPY_TX_1,
- DB8500_DMA_MEMCPY_TX_2,
- DB8500_DMA_MEMCPY_TX_3,
- DB8500_DMA_MEMCPY_TX_4,
- DB8500_DMA_MEMCPY_TX_5,
-};
-
-static struct stedma40_platform_data dma40_plat_data = {
- .dev_len = DB8500_DMA_NR_DEV,
- .dev_rx = dma40_rx_map,
- .dev_tx = dma40_tx_map,
- .memcpy = dma40_memcpy_event,
- .memcpy_len = ARRAY_SIZE(dma40_memcpy_event),
- .memcpy_conf_phy = &dma40_memcpy_conf_phy,
- .memcpy_conf_log = &dma40_memcpy_conf_log,
- .disabled_channels = {-1},
-};
-
-struct platform_device u8500_dma40_device = {
+#define HSI0_CAWAKE { \
+ .start = IRQ_PRCMU_HSI0, \
+ .end = IRQ_PRCMU_HSI0, \
+ .flags = IORESOURCE_IRQ, \
+ .name = "hsi0_cawake" \
+}
+
+#define HSI0_ACWAKE { \
+ .start = 226, \
+ .end = 226, \
+ .flags = IORESOURCE_IO, \
+ .name = "hsi0_acwake" \
+}
+
+#define HSIR_OVERRUN(num) { \
+ .start = IRQ_DB8500_HSIR_CH##num##_OVRRUN, \
+ .end = IRQ_DB8500_HSIR_CH##num##_OVRRUN, \
+ .flags = IORESOURCE_IRQ, \
+ .name = "hsi_rx_overrun_ch"#num \
+}
+
+#define STE_HSI_PORT0_TX_CHANNEL_CFG(n) { \
+ .dir = STEDMA40_MEM_TO_PERIPH, \
+ .high_priority = true, \
+ .mode = STEDMA40_MODE_LOGICAL, \
+ .mode_opt = STEDMA40_LCHAN_SRC_LOG_DST_LOG, \
+ .src_dev_type = STEDMA40_DEV_SRC_MEMORY, \
+ .dst_dev_type = n,\
+ .src_info.big_endian = false,\
+ .src_info.data_width = STEDMA40_WORD_WIDTH,\
+ .dst_info.big_endian = false,\
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,\
+},
+
+#define STE_HSI_PORT0_RX_CHANNEL_CFG(n) { \
+ .dir = STEDMA40_PERIPH_TO_MEM, \
+ .high_priority = true, \
+ .mode = STEDMA40_MODE_LOGICAL, \
+ .mode_opt = STEDMA40_LCHAN_SRC_LOG_DST_LOG, \
+ .src_dev_type = n,\
+ .dst_dev_type = STEDMA40_DEV_DST_MEMORY, \
+ .src_info.big_endian = false,\
+ .src_info.data_width = STEDMA40_WORD_WIDTH,\
+ .dst_info.big_endian = false,\
+ .dst_info.data_width = STEDMA40_WORD_WIDTH,\
+},
+
+static struct resource u8500_hsi_resources[] = {
+ {
+ .start = U8500_HSIR_BASE,
+ .end = U8500_HSIR_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "hsi_rx_base"
+ },
+ {
+ .start = U8500_HSIT_BASE,
+ .end = U8500_HSIT_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "hsi_tx_base"
+ },
+ {
+ .start = IRQ_DB8500_HSIRD0,
+ .end = IRQ_DB8500_HSIRD0,
+ .flags = IORESOURCE_IRQ,
+ .name = "hsi_rx_irq0"
+ },
+ {
+ .start = IRQ_DB8500_HSITD0,
+ .end = IRQ_DB8500_HSITD0,
+ .flags = IORESOURCE_IRQ,
+ .name = "hsi_tx_irq0"
+ },
+ {
+ .start = IRQ_DB8500_HSIR_EXCEP,
+ .end = IRQ_DB8500_HSIR_EXCEP,
+ .flags = IORESOURCE_IRQ,
+ .name = "hsi_rx_excep0"
+ },
+ HSIR_OVERRUN(0),
+ HSIR_OVERRUN(1),
+ HSIR_OVERRUN(2),
+ HSIR_OVERRUN(3),
+ HSIR_OVERRUN(4),
+ HSIR_OVERRUN(5),
+ HSIR_OVERRUN(6),
+ HSIR_OVERRUN(7),
+ HSI0_CAWAKE,
+ HSI0_ACWAKE,
+};
+
+#ifdef CONFIG_STE_DMA40
+static struct stedma40_chan_cfg ste_hsi_port0_dma_tx_cfg[] = {
+ STE_HSI_PORT0_TX_CHANNEL_CFG(DB8500_DMA_DEV20_SLIM0_CH0_TX_HSI_TX_CH0)
+ STE_HSI_PORT0_TX_CHANNEL_CFG(DB8500_DMA_DEV21_SLIM0_CH1_TX_HSI_TX_CH1)
+ STE_HSI_PORT0_TX_CHANNEL_CFG(DB8500_DMA_DEV22_SLIM0_CH2_TX_HSI_TX_CH2)
+ STE_HSI_PORT0_TX_CHANNEL_CFG(DB8500_DMA_DEV23_SLIM0_CH3_TX_HSI_TX_CH3)
+};
+
+static struct stedma40_chan_cfg ste_hsi_port0_dma_rx_cfg[] = {
+ STE_HSI_PORT0_RX_CHANNEL_CFG(DB8500_DMA_DEV20_SLIM0_CH0_RX_HSI_RX_CH0)
+ STE_HSI_PORT0_RX_CHANNEL_CFG(DB8500_DMA_DEV21_SLIM0_CH1_RX_HSI_RX_CH1)
+ STE_HSI_PORT0_RX_CHANNEL_CFG(DB8500_DMA_DEV22_SLIM0_CH2_RX_HSI_RX_CH2)
+ STE_HSI_PORT0_RX_CHANNEL_CFG(DB8500_DMA_DEV23_SLIM0_CH3_RX_HSI_RX_CH3)
+};
+#endif
+
+static struct ste_hsi_port_cfg ste_hsi_port0_cfg = {
+#ifdef CONFIG_STE_DMA40
+ .dma_filter = stedma40_filter,
+ .dma_tx_cfg = ste_hsi_port0_dma_tx_cfg,
+ .dma_rx_cfg = ste_hsi_port0_dma_rx_cfg
+#endif
+};
+
+struct ste_hsi_platform_data u8500_hsi_platform_data = {
+ .num_ports = 1,
+ .use_dma = 1,
+ .port_cfg = &ste_hsi_port0_cfg,
+};
+
+struct platform_device u8500_hsi_device = {
.dev = {
- .platform_data = &dma40_plat_data,
+ .platform_data = &u8500_hsi_platform_data,
},
- .name = "dma40",
+ .name = "ste_hsi",
.id = 0,
- .num_resources = ARRAY_SIZE(dma40_resources),
- .resource = dma40_resources
+ .resource = u8500_hsi_resources,
+ .num_resources = ARRAY_SIZE(u8500_hsi_resources)
+};
+#endif /* CONFIG_HSI */
+
+/*
+ * Thermal Sensor
+ */
+
+static struct resource u8500_thsens_resources[] = {
+ {
+ .name = "IRQ_HOTMON_LOW",
+ .start = IRQ_PRCMU_HOTMON_LOW,
+ .end = IRQ_PRCMU_HOTMON_LOW,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "IRQ_HOTMON_HIGH",
+ .start = IRQ_PRCMU_HOTMON_HIGH,
+ .end = IRQ_PRCMU_HOTMON_HIGH,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device u8500_thsens_device = {
+ .name = "dbx500_temp",
+ .resource = u8500_thsens_resources,
+ .num_resources = ARRAY_SIZE(u8500_thsens_resources),
};
struct resource keypad_resources[] = {
diff --git a/arch/arm/mach-ux500/devices-db8500.h b/arch/arm/mach-ux500/devices-db8500.h
index 9fd93e9da52..9b20e971cc8 100644
--- a/arch/arm/mach-ux500/devices-db8500.h
+++ b/arch/arm/mach-ux500/devices-db8500.h
@@ -124,4 +124,9 @@ db8500_add_ssp(struct device *parent, const char *name, resource_size_t base,
dbx500_add_uart(parent, "uart2", U8500_UART2_BASE, \
IRQ_DB8500_UART2, pdata)
+#define db8500_add_cryp1(pdata) \
+ dbx500_add_cryp1(-1, U8500_CRYP1_BASE, IRQ_DB8500_CRYP1, pdata)
+#define db8500_add_hash1(pdata) \
+ dbx500_add_hash1(-1, U8500_HASH1_BASE, pdata)
+
#endif
diff --git a/arch/arm/mach-ux500/devices.c b/arch/arm/mach-ux500/devices.c
index ea0a2f92ca7..77d8d088460 100644
--- a/arch/arm/mach-ux500/devices.c
+++ b/arch/arm/mach-ux500/devices.c
@@ -14,6 +14,51 @@
#include <mach/hardware.h>
#include <mach/setup.h>
+#ifdef CONFIG_STE_TRACE_MODEM
+#include <linux/db8500-modem-trace.h>
+#endif
+
+#ifdef CONFIG_STE_TRACE_MODEM
+static struct resource trace_resource = {
+ .start = 0,
+ .end = 0,
+ .name = "db8500-trace-area",
+ .flags = IORESOURCE_MEM
+};
+
+static struct db8500_trace_platform_data trace_pdata = {
+ .ape_base = U8500_APE_BASE,
+ .modem_base = U8500_MODEM_BASE,
+};
+
+struct platform_device u8500_trace_modem = {
+ .name = "db8500-modem-trace",
+ .id = 0,
+ .dev = {
+ .init_name = "db8500-modem-trace",
+ .platform_data = &trace_pdata,
+ },
+ .num_resources = 1,
+ .resource = &trace_resource,
+};
+
+static int __init early_trace_modem(char *p)
+{
+ struct resource *data = &trace_resource;
+ u32 size = memparse(p, &p);
+ if (*p == '@')
+ data->start = memparse(p + 1, &p);
+ data->end = data->start + size - 1;
+ return 0;
+}
+
+early_param("mem_mtrace", early_trace_modem);
+#endif
+
+struct platform_device ux500_hwmem_device = {
+ .name = "hwmem",
+};
+
void __init amba_add_devices(struct amba_device *devs[], int num)
{
int i;
diff --git a/arch/arm/mach-ux500/dma-db5500.c b/arch/arm/mach-ux500/dma-db5500.c
index 41e9470fa0e..3a56ea8b095 100644
--- a/arch/arm/mach-ux500/dma-db5500.c
+++ b/arch/arm/mach-ux500/dma-db5500.c
@@ -14,8 +14,8 @@
#include <plat/ste_dma40.h>
#include <mach/setup.h>
#include <mach/hardware.h>
-
-#include "ste-dma40-db5500.h"
+#include <mach/pm.h>
+#include <mach/ste-dma40-db5500.h>
static struct resource dma40_resources[] = {
[0] = {
@@ -72,28 +72,128 @@ static struct stedma40_chan_cfg dma40_memcpy_conf_log = {
* now.
*/
static const dma_addr_t dma40_rx_map[DB5500_DMA_NR_DEV] = {
- [DB5500_DMA_DEV24_SDMMC0_RX] = -1,
- [DB5500_DMA_DEV38_USB_OTG_IEP_8] = -1,
- [DB5500_DMA_DEV23_USB_OTG_IEP_7_15] = -1,
- [DB5500_DMA_DEV22_USB_OTG_IEP_6_14] = -1,
- [DB5500_DMA_DEV21_USB_OTG_IEP_5_13] = -1,
- [DB5500_DMA_DEV20_USB_OTG_IEP_4_12] = -1,
- [DB5500_DMA_DEV6_USB_OTG_IEP_3_11] = -1,
- [DB5500_DMA_DEV5_USB_OTG_IEP_2_10] = -1,
- [DB5500_DMA_DEV4_USB_OTG_IEP_1_9] = -1,
+ [DB5500_DMA_DEV0_SPI0_RX] = 0,
+ [DB5500_DMA_DEV1_SPI1_RX] = 0,
+ [DB5500_DMA_DEV2_SPI2_RX] = 0,
+ [DB5500_DMA_DEV3_SPI3_RX] = 0,
+ [DB5500_DMA_DEV4_USB_OTG_IEP_1_9] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV5_USB_OTG_IEP_2_10] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV6_USB_OTG_IEP_3_11] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV7_IRDA_RFS] = 0,
+ [DB5500_DMA_DEV8_IRDA_FIFO_RX] = 0,
+ [DB5500_DMA_DEV9_MSP0_RX] = U5500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV10_MSP1_RX] = U5500_MSP1_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV11_MSP2_RX] = U5500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV12_UART0_RX] = 0,
+ [DB5500_DMA_DEV13_UART1_RX] = 0,
+ [DB5500_DMA_DEV14_UART2_RX] = 0,
+ [DB5500_DMA_DEV15_UART3_RX] = 0,
+ [DB5500_DMA_DEV16_USB_OTG_IEP_8] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV17_USB_OTG_IEP_1_9] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV18_USB_OTG_IEP_2_10] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV19_USB_OTG_IEP_3_11] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV20_USB_OTG_IEP_4_12] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV21_USB_OTG_IEP_5_13] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV22_USB_OTG_IEP_6_14] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV23_USB_OTG_IEP_7_15] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV24_SDMMC0_RX] = U5500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV25_SDMMC1_RX] = U5500_SDI1_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV26_SDMMC2_RX] = U5500_SDI2_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV27_SDMMC3_RX] = U5500_SDI3_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV28_SDMMC4_RX] = U5500_SDI4_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ /* 29, 30 not used */
+ [DB5500_DMA_DEV31_CRYPTO1_RX] = 0, /* v2 */
+ /* 32 not used */
+ [DB5500_DMA_DEV33_SDMMC0_RX] = U5500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV34_SDMMC1_RX] = U5500_SDI1_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV35_SDMMC2_RX] = U5500_SDI2_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV36_SDMMC3_RX] = U5500_SDI3_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV37_SDMMC4_RX] = U5500_SDI4_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV38_USB_OTG_IEP_8] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV39_USB_OTG_IEP_1_9] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV40_USB_OTG_IEP_2_10] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV41_USB_OTG_IEP_3_11] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV42_USB_OTG_IEP_4_12] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV43_USB_OTG_IEP_5_13] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV44_USB_OTG_IEP_6_14] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV45_USB_OTG_IEP_7_15] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV46_CRYPTO1_RX] = 0, /* v2 */
+ [DB5500_DMA_DEV47_MCDE_RX] = 0,
+ [DB5500_DMA_DEV48_CRYPTO1_RX] = U5500_CRYP1_BASE + CRYP1_RX_REG_OFFSET,
+ /* 49, 50 not used */
+ [DB5500_DMA_DEV49_I2C1_RX] = 0,
+ [DB5500_DMA_DEV50_I2C3_RX] = 0,
+ [DB5500_DMA_DEV51_I2C2_RX] = 0,
+ /* 54 - 60 not used */
+ [DB5500_DMA_DEV61_CRYPTO0_RX] = 0,
+ /* 62, 63 not used */
};
/* Mapping between destination event lines and physical device address */
static const dma_addr_t dma40_tx_map[DB5500_DMA_NR_DEV] = {
- [DB5500_DMA_DEV24_SDMMC0_TX] = -1,
- [DB5500_DMA_DEV38_USB_OTG_OEP_8] = -1,
- [DB5500_DMA_DEV23_USB_OTG_OEP_7_15] = -1,
- [DB5500_DMA_DEV22_USB_OTG_OEP_6_14] = -1,
- [DB5500_DMA_DEV21_USB_OTG_OEP_5_13] = -1,
- [DB5500_DMA_DEV20_USB_OTG_OEP_4_12] = -1,
- [DB5500_DMA_DEV6_USB_OTG_OEP_3_11] = -1,
- [DB5500_DMA_DEV5_USB_OTG_OEP_2_10] = -1,
- [DB5500_DMA_DEV4_USB_OTG_OEP_1_9] = -1,
+ [DB5500_DMA_DEV0_SPI0_TX] = 0,
+ [DB5500_DMA_DEV1_SPI1_TX] = 0,
+ [DB5500_DMA_DEV2_SPI2_TX] = 0,
+ [DB5500_DMA_DEV3_SPI3_TX] = 0,
+ [DB5500_DMA_DEV4_USB_OTG_OEP_1_9] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV5_USB_OTG_OEP_2_10] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV6_USB_OTG_OEP_3_11] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV7_IRRC_TX] = 0,
+ [DB5500_DMA_DEV8_IRDA_FIFO_TX] = 0,
+ [DB5500_DMA_DEV9_MSP0_TX] = U5500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV10_MSP1_TX] = U5500_MSP1_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV11_MSP2_TX] = U5500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV12_UART0_TX] = 0,
+ [DB5500_DMA_DEV13_UART1_TX] = 0,
+ [DB5500_DMA_DEV14_UART2_TX] = 0,
+ [DB5500_DMA_DEV15_UART3_TX] = 0,
+ [DB5500_DMA_DEV16_USB_OTG_OEP_8] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV17_USB_OTG_OEP_1_9] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV18_USB_OTG_OEP_2_10] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV19_USB_OTG_OEP_3_11] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV20_USB_OTG_OEP_4_12] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV21_USB_OTG_OEP_5_13] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV22_USB_OTG_OEP_6_14] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV23_USB_OTG_OEP_7_15] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV24_SDMMC0_TX] = U5500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV25_SDMMC1_TX] = U5500_SDI1_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV26_SDMMC2_TX] = U5500_SDI2_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV27_SDMMC3_TX] = U5500_SDI3_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV28_SDMMC4_TX] = U5500_SDI4_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ /* 29 not used */
+ [DB5500_DMA_DEV30_HASH1_TX] = 0, /* v2 */
+ [DB5500_DMA_DEV31_CRYPTO1_TX] = 0, /* v2 */
+ [DB5500_DMA_DEV32_FSMC_TX] = 0,
+ [DB5500_DMA_DEV33_SDMMC0_TX] = U5500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV34_SDMMC1_TX] = U5500_SDI1_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV35_SDMMC2_TX] = U5500_SDI2_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV36_SDMMC3_TX] = U5500_SDI3_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV37_SDMMC4_TX] = U5500_SDI4_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB5500_DMA_DEV38_USB_OTG_OEP_8] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV39_USB_OTG_OEP_1_9] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV40_USB_OTG_OEP_2_10] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV41_USB_OTG_OEP_3_11] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV42_USB_OTG_OEP_4_12] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV43_USB_OTG_OEP_5_13] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV44_USB_OTG_OEP_6_14] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV45_USB_OTG_OEP_7_15] = U5500_USBOTG_BASE,
+ [DB5500_DMA_DEV46_CRYPTO1_TX] = 0, /* v2 */
+ [DB5500_DMA_DEV47_STM_TX] = 0,
+ [DB5500_DMA_DEV48_CRYPTO1_TX] = U5500_CRYP1_BASE + CRYP1_TX_REG_OFFSET,
+ [DB5500_DMA_DEV49_CRYPTO1_TX_HASH1_TX] = 0,
+ [DB5500_DMA_DEV50_HASH1_TX] = U5500_HASH1_BASE + HASH1_TX_REG_OFFSET,
+ [DB5500_DMA_DEV51_I2C1_TX] = 0,
+ [DB5500_DMA_DEV52_I2C3_TX] = 0,
+ [DB5500_DMA_DEV53_I2C2_TX] = 0,
+ /* 54, 55 not used */
+ [DB5500_DMA_MEMCPY_TX_1] = 0,
+ [DB5500_DMA_MEMCPY_TX_2] = 0,
+ [DB5500_DMA_MEMCPY_TX_3] = 0,
+ [DB5500_DMA_MEMCPY_TX_4] = 0,
+ [DB5500_DMA_MEMCPY_TX_5] = 0,
+ [DB5500_DMA_DEV61_CRYPTO0_TX] = 0,
+ [DB5500_DMA_DEV62_CRYPTO0_TX_HASH0_TX] = 0,
+ [DB5500_DMA_DEV63_HASH0_TX] = 0,
};
static int dma40_memcpy_event[] = {
@@ -118,6 +218,9 @@ static struct stedma40_platform_data dma40_plat_data = {
static struct platform_device dma40_device = {
.dev = {
.platform_data = &dma40_plat_data,
+#ifdef CONFIG_PM
+ .pm_domain = &ux500_dev_power_domain,
+#endif
},
.name = "dma40",
.id = 0,
@@ -132,6 +235,6 @@ void __init db5500_dma_init(struct device *parent)
dma40_device.dev.parent = parent;
ret = platform_device_register(&dma40_device);
if (ret)
- dev_err(&dma40_device.dev, "unable to register device: %d\n", ret);
-
+ dev_err(&dma40_device.dev, "unable to register device: %d\n",
+ ret);
}
diff --git a/arch/arm/mach-ux500/dma-db8500.c b/arch/arm/mach-ux500/dma-db8500.c
new file mode 100644
index 00000000000..d4363e2efb1
--- /dev/null
+++ b/arch/arm/mach-ux500/dma-db8500.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2007-2010
+ *
+ * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <plat/ste_dma40.h>
+
+#ifdef CONFIG_HSI
+#include <mach/hsi.h>
+#endif
+#include <mach/setup.h>
+#include <mach/ste-dma40-db8500.h>
+#include <mach/pm.h>
+#include <mach/context.h>
+
+
+
+static struct resource dma40_resources[] = {
+ [0] = {
+ .start = U8500_DMA_BASE,
+ .end = U8500_DMA_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "base",
+ },
+ [1] = {
+ .start = U8500_DMA_LCPA_BASE,
+ .end = U8500_DMA_LCPA_BASE + 2 * SZ_1K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "lcpa",
+ },
+ [2] = {
+ .start = IRQ_DB8500_DMA,
+ .end = IRQ_DB8500_DMA,
+ .flags = IORESOURCE_IRQ
+ },
+ [3] = {
+ .start = U8500_DMA_LCLA_BASE,
+ .end = U8500_DMA_LCLA_BASE + SZ_8K - 1,
+ .flags = IORESOURCE_MEM,
+ .name = "lcla_esram",
+ }
+};
+
+/* Default configuration for physcial memcpy */
+static struct stedma40_chan_cfg dma40_memcpy_conf_phy = {
+ .mode = STEDMA40_MODE_PHYSICAL,
+ .dir = STEDMA40_MEM_TO_MEM,
+
+ .src_info.data_width = STEDMA40_BYTE_WIDTH,
+ .src_info.psize = STEDMA40_PSIZE_PHY_1,
+ .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+
+ .dst_info.data_width = STEDMA40_BYTE_WIDTH,
+ .dst_info.psize = STEDMA40_PSIZE_PHY_1,
+ .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+
+};
+
+/* Default configuration for logical memcpy */
+static struct stedma40_chan_cfg dma40_memcpy_conf_log = {
+ .dir = STEDMA40_MEM_TO_MEM,
+
+ .src_info.data_width = STEDMA40_BYTE_WIDTH,
+ .src_info.psize = STEDMA40_PSIZE_LOG_1,
+ .src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+
+ .dst_info.data_width = STEDMA40_BYTE_WIDTH,
+ .dst_info.psize = STEDMA40_PSIZE_LOG_1,
+ .dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL,
+
+};
+
+/*
+ * Mapping between soruce event lines and physical device address
+ * This was created assuming that the event line is tied to a device and
+ * therefore the address is constant, however this is not true for at least
+ * USB, and the values are just placeholders for USB. This table is preserved
+ * and used for now.
+ */
+static dma_addr_t dma40_rx_map[DB8500_DMA_NR_DEV] = {
+ [DB8500_DMA_DEV0_SPI0_RX] = 0,
+ [DB8500_DMA_DEV1_SD_MMC0_RX] = U8500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV2_SD_MMC1_RX] = 0,
+ [DB8500_DMA_DEV3_SD_MMC2_RX] = 0,
+ [DB8500_DMA_DEV4_I2C1_RX] = 0,
+ [DB8500_DMA_DEV5_I2C3_RX] = 0,
+ [DB8500_DMA_DEV6_I2C2_RX] = 0,
+ [DB8500_DMA_DEV7_I2C4_RX] = 0,
+ [DB8500_DMA_DEV8_SSP0_RX] = U8500_SSP0_BASE + SSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV9_SSP1_RX] = 0,
+ [DB8500_DMA_DEV10_MCDE_RX] = 0,
+ [DB8500_DMA_DEV11_UART2_RX] = 0,
+ [DB8500_DMA_DEV12_UART1_RX] = 0,
+ [DB8500_DMA_DEV13_UART0_RX] = 0,
+ [DB8500_DMA_DEV14_MSP2_RX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV15_I2C0_RX] = 0,
+ [DB8500_DMA_DEV16_USB_OTG_IEP_7_15] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV17_USB_OTG_IEP_6_14] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV18_USB_OTG_IEP_5_13] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV19_USB_OTG_IEP_4_12] = U8500_USBOTG_BASE,
+#ifdef CONFIG_HSI
+ [DB8500_DMA_DEV20_SLIM0_CH0_RX_HSI_RX_CH0] = U8500_HSIR_BASE + 0x0 + STE_HSI_RX_BUFFERX,
+ [DB8500_DMA_DEV21_SLIM0_CH1_RX_HSI_RX_CH1] = U8500_HSIR_BASE + 0x4 + STE_HSI_RX_BUFFERX,
+ [DB8500_DMA_DEV22_SLIM0_CH2_RX_HSI_RX_CH2] = U8500_HSIR_BASE + 0x8 + STE_HSI_RX_BUFFERX,
+ [DB8500_DMA_DEV23_SLIM0_CH3_RX_HSI_RX_CH3] = U8500_HSIR_BASE + 0xC + STE_HSI_RX_BUFFERX,
+#endif
+ [DB8500_DMA_DEV24_SRC_SXA0_RX_TX] = 0,
+ [DB8500_DMA_DEV25_SRC_SXA1_RX_TX] = 0,
+ [DB8500_DMA_DEV26_SRC_SXA2_RX_TX] = 0,
+ [DB8500_DMA_DEV27_SRC_SXA3_RX_TX] = 0,
+ [DB8500_DMA_DEV28_SD_MM2_RX] = U8500_SDI2_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV29_SD_MM0_RX] = U8500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV30_MSP3_RX] = U8500_MSP3_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV32_SD_MM1_RX] = U8500_SDI1_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV33_SPI2_RX] = 0,
+ [DB8500_DMA_DEV34_I2C3_RX2] = 0,
+ [DB8500_DMA_DEV35_SPI1_RX] = 0,
+ [DB8500_DMA_DEV36_USB_OTG_IEP_3_11] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV37_USB_OTG_IEP_2_10] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV38_USB_OTG_IEP_1_9] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV39_USB_OTG_IEP_8] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV40_SPI3_RX] = 0,
+ [DB8500_DMA_DEV41_SD_MM3_RX] = 0,
+ [DB8500_DMA_DEV42_SD_MM4_RX] = U8500_SDI4_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV43_SD_MM5_RX] = 0,
+ [DB8500_DMA_DEV44_SRC_SXA4_RX_TX] = 0,
+ [DB8500_DMA_DEV45_SRC_SXA5_RX_TX] = 0,
+ [DB8500_DMA_DEV46_SLIM0_CH8_RX_SRC_SXA6_RX_TX] = 0,
+ [DB8500_DMA_DEV47_SLIM0_CH9_RX_SRC_SXA7_RX_TX] = 0,
+ [DB8500_DMA_DEV48_CAC1_RX] = U8500_CRYP1_BASE + CRYP1_RX_REG_OFFSET,
+ /* 49, 50 and 51 are not used */
+ [DB8500_DMA_DEV52_SLIM0_CH4_RX_HSI_RX_CH4] = 0,
+ [DB8500_DMA_DEV53_SLIM0_CH5_RX_HSI_RX_CH5] = 0,
+ [DB8500_DMA_DEV54_SLIM0_CH6_RX_HSI_RX_CH6] = 0,
+ [DB8500_DMA_DEV55_SLIM0_CH7_RX_HSI_RX_CH7] = 0,
+ /* 56, 57, 58, 59 and 60 are not used */
+ [DB8500_DMA_DEV61_CAC0_RX] = 0,
+ /* 62 and 63 are not used */
+};
+
+/* Mapping between destination event lines and physical device address */
+static const dma_addr_t dma40_tx_map[DB8500_DMA_NR_DEV] = {
+ [DB8500_DMA_DEV0_SPI0_TX] = 0,
+ [DB8500_DMA_DEV1_SD_MMC0_TX] = U8500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV2_SD_MMC1_TX] = 0,
+ [DB8500_DMA_DEV3_SD_MMC2_TX] = 0,
+ [DB8500_DMA_DEV4_I2C1_TX] = 0,
+ [DB8500_DMA_DEV5_I2C3_TX] = 0,
+ [DB8500_DMA_DEV6_I2C2_TX] = 0,
+ [DB8500_DMA_DEV7_I2C4_TX] = 0,
+ [DB8500_DMA_DEV8_SSP0_TX] = U8500_SSP0_BASE + SSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV9_SSP1_TX] = 0,
+ /* 10 is not used*/
+ [DB8500_DMA_DEV11_UART2_TX] = 0,
+ [DB8500_DMA_DEV12_UART1_TX] = 0,
+ [DB8500_DMA_DEV13_UART0_TX] = 0,
+ [DB8500_DMA_DEV14_MSP2_TX] = U8500_MSP2_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV15_I2C0_TX] = 0,
+ [DB8500_DMA_DEV16_USB_OTG_OEP_7_15] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV17_USB_OTG_OEP_6_14] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV18_USB_OTG_OEP_5_13] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV19_USB_OTG_OEP_4_12] = U8500_USBOTG_BASE,
+#ifdef CONFIG_HSI
+ [DB8500_DMA_DEV20_SLIM0_CH0_TX_HSI_TX_CH0] = U8500_HSIT_BASE + 0x0 + STE_HSI_TX_BUFFERX,
+ [DB8500_DMA_DEV21_SLIM0_CH1_TX_HSI_TX_CH1] = U8500_HSIT_BASE + 0x4 + STE_HSI_TX_BUFFERX,
+ [DB8500_DMA_DEV22_SLIM0_CH2_TX_HSI_TX_CH2] = U8500_HSIT_BASE + 0x8 + STE_HSI_TX_BUFFERX,
+ [DB8500_DMA_DEV23_SLIM0_CH3_TX_HSI_TX_CH3] = U8500_HSIT_BASE + 0xC + STE_HSI_TX_BUFFERX,
+#endif
+ [DB8500_DMA_DEV24_DST_SXA0_RX_TX] = 0,
+ [DB8500_DMA_DEV25_DST_SXA1_RX_TX] = 0,
+ [DB8500_DMA_DEV26_DST_SXA2_RX_TX] = 0,
+ [DB8500_DMA_DEV27_DST_SXA3_RX_TX] = 0,
+ [DB8500_DMA_DEV28_SD_MM2_TX] = U8500_SDI2_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV29_SD_MM0_TX] = U8500_SDI0_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV30_MSP1_TX] = U8500_MSP1_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV31_MSP0_TX_SLIM0_CH0_TX] = U8500_MSP0_BASE + MSP_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV32_SD_MM1_TX] = U8500_SDI1_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV33_SPI2_TX] = 0,
+ [DB8500_DMA_DEV34_I2C3_TX2] = 0,
+ [DB8500_DMA_DEV35_SPI1_TX] = 0,
+ [DB8500_DMA_DEV36_USB_OTG_OEP_3_11] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV37_USB_OTG_OEP_2_10] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV38_USB_OTG_OEP_1_9] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV39_USB_OTG_OEP_8] = U8500_USBOTG_BASE,
+ [DB8500_DMA_DEV40_SPI3_TX] = 0,
+ [DB8500_DMA_DEV41_SD_MM3_TX] = 0,
+ [DB8500_DMA_DEV42_SD_MM4_TX] = U8500_SDI4_BASE + SD_MMC_TX_RX_REG_OFFSET,
+ [DB8500_DMA_DEV43_SD_MM5_TX] = 0,
+ [DB8500_DMA_DEV44_DST_SXA4_RX_TX] = 0,
+ [DB8500_DMA_DEV45_DST_SXA5_RX_TX] = 0,
+ [DB8500_DMA_DEV46_SLIM0_CH8_TX_DST_SXA6_RX_TX] = 0,
+ [DB8500_DMA_DEV47_SLIM0_CH9_TX_DST_SXA7_RX_TX] = 0,
+ [DB8500_DMA_DEV48_CAC1_TX] = U8500_CRYP1_BASE + CRYP1_TX_REG_OFFSET,
+ [DB8500_DMA_DEV49_CAC1_TX_HAC1_TX] = 0,
+ [DB8500_DMA_DEV50_HAC1_TX] = U8500_HASH1_BASE + HASH1_TX_REG_OFFSET,
+ [DB8500_DMA_MEMCPY_TX_0] = 0,
+ [DB8500_DMA_DEV52_SLIM1_CH4_TX_HSI_TX_CH4] = 0,
+ [DB8500_DMA_DEV53_SLIM1_CH5_TX_HSI_TX_CH5] = 0,
+ [DB8500_DMA_DEV54_SLIM1_CH6_TX_HSI_TX_CH6] = 0,
+ [DB8500_DMA_DEV55_SLIM1_CH7_TX_HSI_TX_CH7] = 0,
+ [DB8500_DMA_MEMCPY_TX_1] = 0,
+ [DB8500_DMA_MEMCPY_TX_2] = 0,
+ [DB8500_DMA_MEMCPY_TX_3] = 0,
+ [DB8500_DMA_MEMCPY_TX_4] = 0,
+ [DB8500_DMA_MEMCPY_TX_5] = 0,
+ [DB8500_DMA_DEV61_CAC0_TX] = 0,
+ [DB8500_DMA_DEV62_CAC0_TX_HAC0_TX] = 0,
+ [DB8500_DMA_DEV63_HAC0_TX] = 0,
+};
+
+/* Reserved event lines for memcpy only */
+static int dma40_memcpy_event[] = {
+ DB8500_DMA_MEMCPY_TX_0,
+ DB8500_DMA_MEMCPY_TX_1,
+ DB8500_DMA_MEMCPY_TX_2,
+ DB8500_DMA_MEMCPY_TX_3,
+ DB8500_DMA_MEMCPY_TX_4,
+ DB8500_DMA_MEMCPY_TX_5,
+};
+
+static struct stedma40_platform_data dma40_plat_data = {
+ .dev_len = ARRAY_SIZE(dma40_rx_map),
+ .dev_rx = dma40_rx_map,
+ .dev_tx = dma40_tx_map,
+ .memcpy = dma40_memcpy_event,
+ .memcpy_len = ARRAY_SIZE(dma40_memcpy_event),
+ .memcpy_conf_phy = &dma40_memcpy_conf_phy,
+ .memcpy_conf_log = &dma40_memcpy_conf_log,
+ /* Audio is using physical channel 2 from MMDSP */
+ .disabled_channels = {2, -1},
+ .use_esram_lcla = true,
+};
+
+#ifdef CONFIG_UX500_CONTEXT
+#define D40_DREG_GCC 0x000
+#define D40_DREG_LCPA 0x020
+#define D40_DREG_LCLA 0x024
+
+static void __iomem *base;
+
+static int dma_context_notifier_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ static unsigned long lcpa;
+ static unsigned long lcla;
+ static unsigned long gcc;
+
+ switch (event) {
+ case CONTEXT_APE_SAVE:
+ lcla = readl(base + D40_DREG_LCLA);
+ lcpa = readl(base + D40_DREG_LCPA);
+ gcc = readl(base + D40_DREG_GCC);
+ break;
+
+ case CONTEXT_APE_RESTORE:
+ writel(gcc, base + D40_DREG_GCC);
+ writel(lcpa, base + D40_DREG_LCPA);
+ writel(lcla, base + D40_DREG_LCLA);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dma_context_notifier = {
+ .notifier_call = dma_context_notifier_call,
+};
+
+static void dma_context_notifier_init(void)
+{
+ base = ioremap(dma40_resources[0].start,
+ resource_size(&dma40_resources[0]));
+ if (WARN_ON(!base))
+ return;
+
+ WARN_ON(context_ape_notifier_register(&dma_context_notifier));
+}
+#else
+static void dma_context_notifier_init(void)
+{
+}
+#endif
+
+static struct platform_device dma40_device = {
+ .dev = {
+ .platform_data = &dma40_plat_data,
+#ifdef CONFIG_PM
+ .pm_domain = &ux500_dev_power_domain,
+#endif
+ },
+ .name = "dma40",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(dma40_resources),
+ .resource = dma40_resources
+};
+
+void __init db8500_dma_init(struct device *parent)
+{
+ int ret;
+
+ dma40_device.dev.parent = parent;
+ ret = platform_device_register(&dma40_device);
+ if (ret)
+ dev_err(&dma40_device.dev, "unable to register device: %d\n",
+ ret);
+
+ dma_context_notifier_init();
+}
diff --git a/arch/arm/mach-ux500/hotplug.c b/arch/arm/mach-ux500/hotplug.c
index c76f0f456f0..bf7e81705d2 100644
--- a/arch/arm/mach-ux500/hotplug.c
+++ b/arch/arm/mach-ux500/hotplug.c
@@ -11,20 +11,31 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>
+#include <linux/completion.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
+#include <mach/context.h>
+
extern volatile int pen_release;
+static DECLARE_COMPLETION(cpu_killed);
+
static inline void platform_do_lowpower(unsigned int cpu)
{
flush_cache_all();
- /* we put the platform to just WFI */
for (;;) {
- __asm__ __volatile__("dsb\n\t" "wfi\n\t"
- : : : "memory");
+
+ context_varm_save_core();
+ context_save_cpu_registers();
+
+ context_save_to_sram_and_wfi(false);
+
+ context_restore_cpu_registers();
+ context_varm_restore_core();
+
if (pen_release == cpu_logical_map(cpu)) {
/*
* OK, proper wakeup, we're done
@@ -36,7 +47,7 @@ static inline void platform_do_lowpower(unsigned int cpu)
int platform_cpu_kill(unsigned int cpu)
{
- return 1;
+ return wait_for_completion_timeout(&cpu_killed, 5000);
}
/*
@@ -46,6 +57,19 @@ int platform_cpu_kill(unsigned int cpu)
*/
void platform_cpu_die(unsigned int cpu)
{
+#ifdef DEBUG
+ unsigned int this_cpu = hard_smp_processor_id();
+
+ if (cpu != this_cpu) {
+ printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
+ this_cpu, cpu);
+ BUG();
+ }
+#endif
+
+ printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
+ complete(&cpu_killed);
+
/* directly enter low power state, skipping secure registers */
platform_do_lowpower(cpu);
}
diff --git a/arch/arm/mach-ux500/hwreg.c b/arch/arm/mach-ux500/hwreg.c
new file mode 100644
index 00000000000..585f2a2b7be
--- /dev/null
+++ b/arch/arm/mach-ux500/hwreg.c
@@ -0,0 +1,736 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Etienne CARRIERE <etienne.carriere@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * HWREG: debug purpose module to map declared IOs and read/write
+ * access from debugfs entries.
+ *
+ * HWREG 32bit DB8500 v2.0 register access
+ * =======================================
+ *
+ * 32bit read:
+ * # echo <addr> > <debugfs>/mem/reg-addr
+ * # cat <debugfs>/mem/reg-val
+ *
+ * 32bit write:
+ * # echo <addr> > <debugfs>/mem/reg-addr
+ * # echo <value> > <debugfs>/mem/reg-val
+ *
+ * <addr> 0x-prefixed hexadecimal
+ * <value> decimal or 0x-prefixed hexadecimal
+ *
+ * HWREG DB8500 formated read/write access
+ * =======================================
+ *
+ * Read: read data, data>>SHIFT, data&=MASK, output data
+ * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
+ * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
+ * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
+ *
+ * Usage:
+ * # echo "CMD [OPTIONS] ADRESS [VALUE]" > $debugfs/mem/hwreg
+ *
+ * CMD read read access
+ * write write access
+ *
+ * ADDRESS target reg physical addr (0x-hexa)
+ *
+ * VALUE (write) value to be updated
+ *
+ * OPTIONS
+ * -d|-dec (read) output in decimal
+ * -h|-hexa (read) output in 0x-hexa (default)
+ * -l|-w|-b 32bit (default), 16bit or 8bit reg access
+ * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
+ * -s|-shift SHIFT bit shift value (read:left, write:right)
+ * -o|-offset OFFSET address offset to add to ADDRESS value
+ *
+ * Warning: bit shift operation is applied to bit-mask.
+ * Warning: bit shift direction depends on read or right command.
+ *
+ * Examples:
+ *
+ * before: [*ADDRESS = 0xABCDEF98]
+ * # echo read -h -mask 0xFFF -shift 12 ADDRESS > hwreg
+ * # cat hwreg-shift
+ * 0x0000CDE
+ * # echo write -h -mask 0xFFF -shift 12 ADDRESS 0x123 > hwreg
+ * # cat hwreg-shift
+ * 0x0000123
+ * after [*ADDRESS = 0xAB123F98]
+ *
+ * before: [*ADDRESS = 0xABCDEF98]
+ * # echo read -h -mask 0x00F0F000 ADDRESS 0x12345678 > hwreg
+ * # cat hwreg-shift
+ * 0x00C0E000
+ * # echo write -h -mask 0x00F0F000 ADDRESS 0x12345678 > hwreg
+ * # cat hwreg-shift
+ * 0xAB3D5F98
+ * after [*ADDRESS = 0xAB123F98]
+ *
+ * Read DB8500 version (full ID, chip version ID, chip version ID):
+ *
+ * echo read 0x9001DBF4 > hwreg
+ * cat hwreg
+ * echo read -m 0xFFFF -s 8 0x9001DBF4 > hwreg
+ * cat hwreg
+ * echo read -m 0xFF -s 0 0x9001DBF4 > hwreg
+ * cat hwreg
+ *
+ * Read and Enable/Disable I2C PRCMU clock:
+ *
+ * printf "I2CCLK = " && echo read -m 1 -s 8 0x80157520 > hwreg
+ * cat /sys/kernel/debug/db8500/hwreg
+ * printf "I2CCLK off" && echo write -m 1 -s 8 0x80157518 1 > hwreg
+ * printf "I2CCLK on" && echo write -m 1 -s 8 0x80157510 1 > hwreg
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+
+/*
+ * temporary definitions
+ * The following declarations are to be removed
+ * when kernel/arch/arm/mach-ux8500/include/mach/db8500-regs.h is up-to-date
+ */
+
+/* DDR-SDRAM chip-select 0 (0x0000 0000 : 0x1FFF FFFF) */
+#ifndef U8500_SCU_CD_R4_BASE
+#define U8500_SCU_CD_R4_BASE 0x17c40000
+#endif
+
+#ifndef U8500_SCU_AD_R4_BASE
+#define U8500_SCU_AD_R4_BASE 0x17d40000
+#endif
+
+#ifndef U8500_HSI2CMODEMR4_BASE
+#define U8500_HSI2CMODEMR4_BASE 0x17e02000
+#endif
+/* End of temporary definitions */
+
+static struct dentry *hwreg_debugfs_dir;
+
+/* 32bit read/write ressources */
+static u32 debug_address; /* shared: single read/write access */
+
+/* hwreg entry ressources */
+struct hwreg_cfg {
+ uint addr; /* target physical addr to access */
+ uint fmt; /* format */
+ uint mask; /* read/write mask, applied before any bit shift */
+ int shift; /* bit shift (read:right shift, write:left shift */
+};
+#define REG_FMT_DEC(c) ((c)->fmt & 0x1) /* bit 0: 0=hexa, 1=dec */
+#define REG_FMT_HEX(c) (!REG_FMT_DEC(c)) /* bit 0: 0=hexa, 1=dec */
+#define REG_FMT_32B(c) (((c)->fmt & 0x6) == 0x0) /* bit[2:1]=0 => 32b access */
+#define REG_FMT_16B(c) (((c)->fmt & 0x6) == 0x2) /* bit[2:1]=1 => 16b access */
+#define REG_FMT_8B(c) (((c)->fmt & 0x6) == 0x4) /* bit[2:1]=2 => 8b access */
+
+static struct hwreg_cfg hwreg_cfg = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+};
+
+/* HWREG guts: mapping table */
+
+struct hwreg_io_range {
+ u32 base;
+ u32 size;
+ u8 *addr;
+};
+
+static struct hwreg_io_range *hwreg_io_current_map;
+
+/*
+ * HWREG guts: mapping table
+ */
+static struct hwreg_io_range hwreg_u8500_io_map[] = {
+ /* Periph1 Peripherals */
+ {.base = U8500_PER1_BASE, .size = 0x10000},
+ /* Periph2 Peripherals */
+ {.base = U8500_PER2_BASE, .size = 0x10000},
+ /* Periph3 Peripherals */
+ {.base = U8500_PER3_BASE, .size = 0x10000},
+ /* Periph4 Peripherals */
+ {.base = U8500_PER4_BASE, .size = 0x70000},
+ /* Periph5 Periphals */
+ {.base = U8500_PER5_BASE, .size = 0x20000},
+ /* Periph6 Peripherals */
+ {.base = U8500_PER6_BASE, .size = 0x10000},
+ /*
+ * Snoop Control Unit, A9 Private interrupt IF,
+ * A9 private peripherals, Level-2 Cache Configuration registers,
+ * and some reserved area
+ */
+ {.base = U8500_SCU_BASE, .size = 0x4000},
+
+ /* DISPLAY Ctrl. configuration registers */
+ {.base = U8500_MCDE_BASE, .size = SZ_4K},
+
+ /* DSI1 link registers */
+ {.base = U8500_DSI_LINK1_BASE, .size = SZ_4K},
+
+ /* DSI2 link registers */
+ {.base = U8500_DSI_LINK2_BASE, .size = SZ_4K},
+
+ /* DSI3 link registers */
+ {.base = U8500_DSI_LINK3_BASE, .size = SZ_4K},
+
+ /* DMA Ctrl. configuration registers (base address changed in V1) */
+ {.base = U8500_DMA_BASE, .size = SZ_4K},
+
+ /* 0xB7A00000 -> 0xB7E04000: Modem I2C */
+ {.base = U8500_MODEM_I2C, .size = 0x404000},
+
+ /* 0xA0390000 -> 0xA039FFFF: SBAG configuration registers */
+ {.base = U8500_SBAG_BASE, .size = SZ_4K},
+
+ /* 0xA0300000 -> 0xA031FFFF: SGA configuration registers */
+ {.base = U8500_SGA_BASE, .size = 0x10000},
+
+ /* 0xA0200000 -> 0xA02FFFFF: Smart Imaging Acc. Data Memory space (SIA) */
+ {.base = U8500_SIA_BASE, .size = 0x60000},
+
+ /* 0xA0100000 -> 0xA01FFFFF: Smart Video Acc. Data Memory space (SVA) */
+ {.base = U8500_SVA_BASE, .size = 0x60000},
+
+ /* 0x81000000 -> 0x8103FFFF: Main ICN Crossbar configuration registers */
+ {.base = U8500_ICN_BASE, .size = 0x2000},
+
+ /* 0x80140000 -> 0x8014FFFF: HSEM (Semaphores) configuration */
+ {.base = U8500_HSEM_BASE, .size = SZ_4K},
+
+ /* 0x80130000 -> 0x8013FFFF: B2R2 configuration registers */
+ {.base = U8500_B2R2_BASE, .size = SZ_4K},
+
+ /* 0x80100000 -> 0x8010FFFF: STM */
+ {.base = U8500_STM_BASE, .size = 0x10000},
+
+ /* High part of embedded boot ROM */
+ {.base = U8500_ASIC_ID_BASE, .size = SZ_4K},
+
+ /* 0x17C4 0000 : 0x17C4 007C */
+ {.base = U8500_SCU_CD_R4_BASE, .size = SZ_4K},
+
+ /* 0x17D4 0000 : 0x17D4 041C */
+ {.base = U8500_SCU_AD_R4_BASE, .size = SZ_4K},
+
+ /* 0x17E0 2000 : 0x17E0 2FFC */
+ {.base = U8500_HSI2CMODEMR4_BASE, .size = SZ_4K},
+
+ {.base = 0, .size = 0, },
+
+};
+
+static struct hwreg_io_range hwreg_u9540_io_map[] = {
+ /* Periph1 Peripherals */
+ {.base = U8500_PER1_BASE, .size = 0x10000},
+ /* Periph2 Peripherals */
+ {.base = U8500_PER2_BASE, .size = 0x10000},
+ /* Periph3 Peripherals */
+ {.base = U8500_PER3_BASE, .size = 0x10000},
+ /* Periph4 Peripherals */
+ {.base = U8500_PER4_BASE, .size = 0x70000},
+ /* Periph5 Periphals */
+ {.base = U8500_PER5_BASE, .size = 0x20000},
+ /* Periph6 Peripherals */
+ {.base = U8500_PER6_BASE, .size = 0x10000},
+ /*
+ * Snoop Control Unit, A9 Private interrupt IF,
+ * A9 private peripherals, Level-2 Cache Configuration registers,
+ * and some reserved area
+ */
+ {.base = U8500_SCU_BASE, .size = 0x4000},
+
+ /* DISPLAY Ctrl. configuration registers */
+ {.base = U8500_MCDE_BASE, .size = SZ_4K},
+
+ /* DSI1 link registers */
+ {.base = U8500_DSI_LINK1_BASE, .size = SZ_4K},
+
+ /* DSI2 link registers */
+ {.base = U8500_DSI_LINK2_BASE, .size = SZ_4K},
+
+ /* DSI3 link registers */
+ {.base = U8500_DSI_LINK3_BASE, .size = SZ_4K},
+
+ /* DMA Ctrl. configuration registers (base address changed in V1) */
+ {.base = U8500_DMA_BASE, .size = SZ_4K},
+
+ /* 0xB7A00000 -> 0xB7E04000: Modem I2C */
+ {.base = U8500_MODEM_I2C, .size = 0x404000},
+
+ /* 0xA0390000 -> 0xA039FFFF: SBAG configuration registers */
+ {.base = U8500_SBAG_BASE, .size = SZ_4K},
+
+ /* 0xA0300000 -> 0xA031FFFF: SGA configuration registers */
+ {.base = U8500_SGA_BASE, .size = 0x10000},
+
+ /* 0xA0200000 -> 0xA02FFFFF: Smart Imaging Acc. Data Memory space (SIA)
+ */
+ {.base = U8500_SIA_BASE, .size = 0x60000},
+
+ /* 0xA0100000 -> 0xA01FFFFF: Smart Video Acc. Data Memory space (SVA) */
+ {.base = U8500_SVA_BASE, .size = 0x60000},
+
+ /* 0x81000000 -> 0x8103FFFF: Main ICN Crossbar configuration registers
+ */
+ {.base = U8500_ICN_BASE, .size = 0x2000},
+
+ /* 0x80140000 -> 0x8014FFFF: HSEM (Semaphores) configuration */
+ {.base = U8500_HSEM_BASE, .size = SZ_4K},
+
+ /* 0x80130000 -> 0x8013FFFF: B2R2 configuration registers */
+ {.base = U8500_B2R2_BASE, .size = SZ_4K},
+
+ /* 0x80100000 -> 0x8010FFFF: STM */
+ {.base = U8500_STM_BASE, .size = 0x10000},
+
+ /* High part of embedded boot ROM */
+ {.base = U9540_ASIC_ID_BASE, .size = SZ_4K},
+
+ {.base = 0, .size = 0, },
+
+};
+
+static void hwreg_io_init(void)
+{
+ int i;
+
+ for (i = 0; hwreg_io_current_map[i].base; ++i) {
+ hwreg_io_current_map[i].addr =
+ ioremap(hwreg_io_current_map[i].base,
+ hwreg_io_current_map[i].size);
+ if (!hwreg_io_current_map[i].addr)
+ printk(KERN_WARNING
+ "%s: ioremap for %d (%08x) failed\n",
+ __func__, i, hwreg_io_current_map[i].base);
+ }
+}
+
+static void hwreg_io_exit(void)
+{
+ int i;
+
+ for (i = 0; hwreg_io_current_map[i].base; ++i)
+ if (hwreg_io_current_map[i].addr)
+ iounmap(hwreg_io_current_map[i].addr);
+}
+
+static void *hwreg_io_ptov(u32 phys)
+{
+ int i;
+
+ for (i = 0; hwreg_io_current_map[i].base; ++i) {
+ u32 base = hwreg_io_current_map[i].base;
+ u32 size = hwreg_io_current_map[i].size;
+ u8 *addr = hwreg_io_current_map[i].addr;
+
+ if (phys < base || phys >= base + size)
+ continue;
+
+ if (addr)
+ return addr + phys - base;
+
+ break;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * HWREG 32bit DB8500 register read/write access debugfs part
+ */
+
+static int hwreg_address_print(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "0x%08X\n", debug_address);
+}
+
+static int hwreg_address_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_address_print, inode->i_private);
+}
+
+static ssize_t hwreg_address_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long user_address;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_address);
+
+ if (err)
+ return err;
+
+ if (hwreg_io_ptov(user_address) == NULL)
+ return -EADDRNOTAVAIL;
+
+ debug_address = user_address;
+ return count;
+}
+
+static int hwreg_value_print(struct seq_file *s, void *p)
+{
+ void *ptr;
+
+ ptr = hwreg_io_ptov(debug_address);
+ if (ptr == NULL)
+ return -EADDRNOTAVAIL;
+ seq_printf(s, "0x%X\n", readl(ptr));
+ return 0;
+}
+
+static int hwreg_value_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_value_print, inode->i_private);
+}
+
+static ssize_t hwreg_value_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err;
+ unsigned long user_val;
+ void *ptr;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &user_val);
+
+ if (err)
+ return err;
+
+ if ((ptr = hwreg_io_ptov(debug_address)) == NULL)
+ return -EFAULT;
+ writel(user_val, ptr);
+ return count;
+}
+
+static const struct file_operations hwreg_address_fops = {
+ .open = hwreg_address_open,
+ .write = hwreg_address_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+static const struct file_operations hwreg_value_fops = {
+ .open = hwreg_value_open,
+ .write = hwreg_value_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/* 'map' read entry: display current HWREG IO mapping table */
+static int hwreg_map_print(struct seq_file *s, void *p)
+{
+ int err, i;
+
+ for (i = 0; hwreg_io_current_map[i].base; ++i) {
+ err = seq_printf(s, "%d: 0x%08X => 0x%08X\n",
+ i, hwreg_io_current_map[i].base,
+ hwreg_io_current_map[i].base +
+ hwreg_io_current_map[i].size);
+ if (err < 0)
+ return -ENOMEM;
+ }
+ return 0;
+}
+static int hwreg_map_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_map_print, inode->i_private);
+}
+
+static const struct file_operations hwreg_map_fops = {
+ .open = hwreg_map_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * HWREG DB8500 formated routines
+ */
+
+static int hwreg_print(struct seq_file *s, void *d)
+{
+ struct hwreg_cfg *c = (struct hwreg_cfg *) s->private;
+ void *p;
+ uint v;
+
+ if ((c == NULL) || ((p = hwreg_io_ptov(c->addr)) == NULL))
+ return -EADDRNOTAVAIL;
+
+ v = (uint) (REG_FMT_32B(c) ? readl(p) : REG_FMT_16B(c) ? readw(p) : readb(p));
+ v = (c->shift >= 0) ? v >> c->shift : v << (-c->shift);
+ v = v & c->mask;
+
+ if (REG_FMT_DEC(c))
+ seq_printf(s, "%d\n", v);
+ else if (REG_FMT_32B(c))
+ seq_printf(s, "0x%08X\n", v);
+ else if (REG_FMT_32B(c))
+ seq_printf(s, "0x%04X\n", v);
+ else
+ seq_printf(s, "0x%02X\n", v);
+ return 0;
+}
+
+static int hwreg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hwreg_print, inode->i_private);
+}
+
+/*
+ * return length of an ASCII numerical value, 0 is string is not a numerical
+ * value. string shall start at value 1st char.
+ * string can be tailed with \0 or space or newline chars only.
+ * value can be decimal or hexadecimal (prefixed 0x or 0X).
+ */
+static int strval_len(char *b)
+{
+ char *s = b;
+ if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
+ s += 2;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isxdigit(*s))
+ return 0;
+ }
+ } else {
+ if (*s == '-')
+ s++;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isdigit(*s))
+ return 0;
+ }
+ }
+ return (int) (s-b);
+}
+
+/*
+ * parse hwreg input data.
+ * update global hwreg_cfg only if input data syntax is ok.
+ */
+static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg)
+{
+ uint write, val = 0, offset = 0;
+ struct hwreg_cfg loc = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+ };
+
+ /* read or write ? */
+ if (!strncmp(b, "read ", 5)) {
+ write = 0;
+ b += 5;
+ } else if (!strncmp(b, "write ", 6)) {
+ write = 1;
+ b += 6;
+ } else {
+ return -EINVAL;
+ }
+
+ /* OPTIONS -l|-w|-b -s -m -o */
+ while ((*b == ' ') || (*b == '-')) {
+ if (*(b-1) != ' ') {
+ b++;
+ continue;
+ }
+ if ((!strncmp(b, "-d ", 3)) || (!strncmp(b, "-dec ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt |= (1<<0);
+ } else if ((!strncmp(b, "-h ", 3)) || (!strncmp(b, "-hex ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt &= ~(1<<0);
+ } else if ((!strncmp(b, "-m ", 3)) || (!strncmp(b, "-mask ", 6))) {
+ b += (*(b+2) == ' ') ? 3 : 6;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.mask = simple_strtoul(b, &b, 0);
+ } else if ((!strncmp(b, "-s ", 3)) || (!strncmp(b, "-shift ", 7))) {
+ b += (*(b+2) == ' ') ? 3 : 7;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.shift = simple_strtol(b, &b, 0);
+
+ } else if ((!strncmp(b, "-o ", 3)) || (!strncmp(b, "-offset ", 8))) {
+ b += (*(b+2) == ' ') ? 3 : 8;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ offset = simple_strtol(b, &b, 0);
+ } else if (!strncmp(b, "-l ", 3)) {
+ b += 3;
+ loc.fmt = (loc.fmt & ~(3<<1)) | (0<<1);
+ } else if (!strncmp(b, "-w ", 3)) {
+ b += 3;
+ loc.fmt = (loc.fmt & ~(3<<1)) | (1<<1);
+ } else if (!strncmp(b, "-b ", 3)) {
+ b += 3;
+ loc.fmt = (loc.fmt & ~(3<<1)) | (2<<1);
+ } else {
+ return -EINVAL;
+ }
+ }
+ /* get arg ADDRESS */
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.addr = simple_strtoul(b, &b, 0);
+ loc.addr += offset;
+ if (hwreg_io_ptov(loc.addr) == NULL)
+ return -EINVAL;
+
+ if (write) {
+ while (*b == ' ')
+ b++; /* skip spaces up to arg VALUE */
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ val = simple_strtoul(b, &b, 0);
+ }
+
+ /* args are ok, update target cfg (mainly for read) */
+ *cfg = loc;
+
+#ifdef DEBUG
+ printk(KERN_INFO "HWREG request: %s %d-bit reg, %s, addr=0x%08X, "
+ "mask=0x%X, shift=%d value=0x%X\n",
+ (write) ? "write" : "read",
+ REG_FMT_32B(cfg) ? 32 : REG_FMT_16B(cfg) ? 16 : 8,
+ REG_FMT_DEC(cfg) ? "decimal" : "hexa",
+ cfg->addr, cfg->mask, cfg->shift, val);
+#endif
+
+ if (write) {
+ void *p = hwreg_io_ptov(cfg->addr);
+ uint d = (uint) (REG_FMT_32B(cfg)) ? readl(p) :
+ (REG_FMT_16B(cfg)) ? readw(p) : readb(p);
+
+ if (cfg->shift>=0) {
+ d &= ~(cfg->mask << (cfg->shift));
+ val = (val & cfg->mask) << (cfg->shift);
+ } else {
+ d &= ~(cfg->mask >> (-cfg->shift));
+ val = (val & cfg->mask) >> (-cfg->shift);
+ }
+ val = val | d;
+
+ /* read reg, reset mask field and update value bit-field */
+ if (REG_FMT_32B(cfg))
+ writel(val, p);
+ else if (REG_FMT_16B(cfg))
+ writew(val, p);
+ else
+ writeb(val, p);
+ }
+ return 0;
+}
+
+static ssize_t hwreg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[128];
+ int buf_size, ret;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* get args and process */
+ ret = hwreg_common_write(buf, &hwreg_cfg);
+ return (ret) ? ret : buf_size;
+}
+
+static const struct file_operations hwreg_fops = {
+ .open = hwreg_open,
+ .write = hwreg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * hwreg module init/cleanup
+ */
+static int __init hwreg_initialize(void)
+{
+ static struct dentry *file;
+
+ if (cpu_is_u9540()) {
+ printk(KERN_INFO "hwreg: cpu is U9540\n");
+ hwreg_io_current_map = hwreg_u9540_io_map;
+ } else {
+ printk(KERN_INFO "hwreg: cpu is U8500\n");
+ hwreg_io_current_map = hwreg_u8500_io_map;
+ }
+
+ hwreg_io_init();
+
+ hwreg_debugfs_dir = debugfs_create_dir("mem", NULL);
+ if (!hwreg_debugfs_dir)
+ goto debugfs_err;
+
+ file = debugfs_create_file("reg-addr",
+ (S_IRUGO | S_IWUGO), hwreg_debugfs_dir,
+ NULL, &hwreg_address_fops);
+ if (!file)
+ goto debugfs_err;
+ file = debugfs_create_file("reg-val",
+ (S_IRUGO | S_IWUGO), hwreg_debugfs_dir,
+ NULL, &hwreg_value_fops);
+ if (!file)
+ goto debugfs_err;
+ file = debugfs_create_file("reg-map",
+ (S_IRUGO),
+ hwreg_debugfs_dir, NULL, &hwreg_map_fops);
+ if (!file)
+ goto debugfs_err;
+ file = debugfs_create_file("hwreg",
+ (S_IRUGO),
+ hwreg_debugfs_dir, &hwreg_cfg, &hwreg_fops);
+ if (!file)
+ goto debugfs_err;
+ return 0;
+
+debugfs_err:
+ if (hwreg_debugfs_dir)
+ debugfs_remove_recursive(hwreg_debugfs_dir);
+ printk(KERN_ERR "hwreg: failed to register debugfs entries.\n");
+ return -1;
+}
+
+static void __exit hwreg_finalize(void)
+{
+ debugfs_remove_recursive(hwreg_debugfs_dir);
+ hwreg_io_exit();
+}
+
+module_init(hwreg_initialize);
+module_exit(hwreg_finalize);
+
+MODULE_AUTHOR("ST-Ericsson");
+MODULE_DESCRIPTION("DB8500 HW registers access through debugfs");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-ux500/id.c b/arch/arm/mach-ux500/id.c
index 15a0f63b2e2..4d12f826386 100644
--- a/arch/arm/mach-ux500/id.c
+++ b/arch/arm/mach-ux500/id.c
@@ -8,6 +8,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/export.h>
#include <asm/cputype.h>
#include <asm/tlbflush.h>
@@ -18,6 +19,7 @@
#include <mach/setup.h>
struct dbx500_asic_id dbx500_id;
+EXPORT_SYMBOL(dbx500_id);
static unsigned int ux500_read_asicid(phys_addr_t addr)
{
@@ -38,6 +40,25 @@ static unsigned int ux500_read_asicid(phys_addr_t addr)
return readl(__io_address(addr));
}
+static unsigned int u9540_read_asicid(phys_addr_t addr)
+{
+ phys_addr_t base = addr & ~0xfff;
+ struct map_desc desc = {
+ .virtual = IO_ADDRESS_DB9540_ROM(base),
+ .pfn = __phys_to_pfn(base),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ };
+
+ iotable_init(&desc, 1);
+
+ /* As in devicemaps_init() */
+ local_flush_tlb_all();
+ flush_cache_all();
+
+ return readl(__io_address_db9540_rom(addr));
+}
+
static void ux500_print_soc_info(unsigned int asicid)
{
unsigned int rev = dbx500_revision();
@@ -66,7 +87,9 @@ static unsigned int partnumber(unsigned int asicid)
* DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1
* DB8500v2 0x412fc091 0x9001DBF4 0x008500B0
* DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2
+ * AP9500 0x412fc091 0x9001DBF4 0x008500B2
* DB5500v1 0x412fc091 0x9001FFF4 0x005500A0
+ * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx
*/
void __init ux500_map_io(void)
@@ -81,7 +104,7 @@ void __init ux500_map_io(void)
addr = 0x9001FFF4;
break;
- case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */
+ case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 / AP9500 */
asicid = ux500_read_asicid(0x9001DBF4);
if (partnumber(asicid) == 0x8500 ||
partnumber(asicid) == 0x8520)
@@ -91,6 +114,12 @@ void __init ux500_map_io(void)
/* DB5500v1 */
addr = 0x9001FFF4;
break;
+
+ case 0x413fc090: /* DB9540 */
+ addr = 0xFFFFDBF4;
+ asicid = u9540_read_asicid(addr);
+ addr = 0;
+ break;
}
if (addr)
diff --git a/arch/arm/mach-ux500/include/mach/context.h b/arch/arm/mach-ux500/include/mach/context.h
new file mode 100644
index 00000000000..a3490121b67
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/context.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include <linux/notifier.h>
+
+#ifdef CONFIG_UX500_CONTEXT
+
+/* Defines to be with
+ * context_ape_notifier_register
+ */
+#define CONTEXT_APE_SAVE 0 /* APE save */
+#define CONTEXT_APE_RESTORE 1 /* APE restore */
+
+/* Defines to be with
+ * context_arm_notifier_register
+ */
+#define CONTEXT_ARM_CORE_SAVE 0 /* Called for each ARM core */
+#define CONTEXT_ARM_CORE_RESTORE 1 /* Called for each ARM core */
+#define CONTEXT_ARM_COMMON_SAVE 2 /* Called when ARM common is saved */
+#define CONTEXT_ARM_COMMON_RESTORE 3 /* Called when ARM common is restored */
+
+int context_ape_notifier_register(struct notifier_block *nb);
+int context_ape_notifier_unregister(struct notifier_block *nb);
+
+int context_arm_notifier_register(struct notifier_block *nb);
+int context_arm_notifier_unregister(struct notifier_block *nb);
+
+void context_vape_save(void);
+void context_vape_restore(void);
+
+void context_fsmc_save(void);
+void context_fsmc_restore(void);
+
+void context_gpio_save(void);
+void context_gpio_restore(void);
+void context_gpio_restore_mux(void);
+void context_gpio_mux_safe_switch(bool begin);
+
+void context_gic_dist_disable_unneeded_irqs(void);
+
+void context_varm_save_common(void);
+void context_varm_restore_common(void);
+
+void context_varm_save_core(void);
+void context_varm_restore_core(void);
+
+void context_save_cpu_registers(void);
+void context_restore_cpu_registers(void);
+
+void context_save_to_sram_and_wfi(bool cleanL2cache);
+
+void context_clean_l1_cache_all(void);
+void context_save_arm_registers(u32 **backup_stack);
+void context_restore_arm_registers(u32 **backup_stack);
+
+void context_save_cp15_registers(u32 **backup_stack);
+void context_restore_cp15_registers(u32 **backup_stack);
+
+void context_save_to_sram_and_wfi_internal(u32 backup_sram_storage,
+ bool cleanL2cache);
+
+/* DB specific functions in either context-db8500 or context-db5500 */
+void u8500_context_save_icn(void);
+void u8500_context_restore_icn(void);
+void u8500_context_init(void);
+
+void u5500_context_save_icn(void);
+void u5500_context_restore_icn(void);
+void u5500_context_init(void);
+
+void u9540_context_save_icn(void);
+void u9540_context_restore_icn(void);
+void u9540_context_init(void);
+#else
+
+static inline void context_varm_save_core(void) {}
+static inline void context_save_cpu_registers(void) {}
+static inline void context_save_to_sram_and_wfi(bool cleanL2cache) {}
+static inline void context_restore_cpu_registers(void) {}
+static inline void context_varm_restore_core(void) {}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/db5500-regs.h b/arch/arm/mach-ux500/include/mach/db5500-regs.h
index 8e714bcb099..187163bd63d 100644
--- a/arch/arm/mach-ux500/include/mach/db5500-regs.h
+++ b/arch/arm/mach-ux500/include/mach/db5500-regs.h
@@ -34,6 +34,7 @@
#define U5500_ICN_BASE 0xA0040000
#define U5500_B2R2_BASE 0xa0200000
#define U5500_BOOT_ROM_BASE 0x90000000
+#define U5500_ASIC_ID_ADDRESS (U5500_BOOT_ROM_BASE + 0x1FFF4)
#define U5500_FSMC_BASE (U5500_PER1_BASE + 0x0000)
#define U5500_SDI0_BASE (U5500_PER1_BASE + 0x1000)
diff --git a/arch/arm/mach-ux500/include/mach/db8500-regs.h b/arch/arm/mach-ux500/include/mach/db8500-regs.h
index 9ec20b96d8f..d2cc53d3557 100644
--- a/arch/arm/mach-ux500/include/mach/db8500-regs.h
+++ b/arch/arm/mach-ux500/include/mach/db8500-regs.h
@@ -41,6 +41,10 @@
/* ASIC ID is at 0xbf4 offset within this region */
#define U8500_ASIC_ID_BASE 0x9001D000
+#define U9540_BOOT_ROM_BASE 0xFFFE0000
+/* ASIC ID is at 0xbf4 offset within this region */
+#define U9540_ASIC_ID_BASE 0xFFFFD000
+
#define U8500_PER6_BASE 0xa03c0000
#define U8500_PER7_BASE 0xa03d0000
#define U8500_PER5_BASE 0xa03e0000
@@ -96,7 +100,10 @@
#define U8500_SCR_BASE (U8500_PER4_BASE + 0x05000)
#define U8500_DMC_BASE (U8500_PER4_BASE + 0x06000)
#define U8500_PRCMU_BASE (U8500_PER4_BASE + 0x07000)
+#define U8500_PRCMU_SEC_BASE (U8500_PER4_BASE + 0x08000)
+#define U9540_DMC1_BASE (U8500_PER4_BASE + 0x0A000)
#define U8500_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x68000)
+#define U9540_PRCMU_TCDM_BASE (U8500_PER4_BASE + 0x6A000)
#define U8500_PRCMU_TCPM_BASE (U8500_PER4_BASE + 0x60000)
#define U8500_PRCMU_TIMER_3_BASE (U8500_PER4_BASE + 0x07338)
#define U8500_PRCMU_TIMER_4_BASE (U8500_PER4_BASE + 0x07450)
diff --git a/arch/arm/mach-ux500/include/mach/devices.h b/arch/arm/mach-ux500/include/mach/devices.h
index 5f6cb71fc62..6d176e93653 100644
--- a/arch/arm/mach-ux500/include/mach/devices.h
+++ b/arch/arm/mach-ux500/include/mach/devices.h
@@ -13,9 +13,27 @@ struct amba_device;
extern struct platform_device u5500_gpio_devs[];
extern struct platform_device u8500_gpio_devs[];
+extern struct platform_device u8500_mcde_device;
+extern struct platform_device u5500_mcde_device;
+extern struct platform_device u8500_shrm_device;
+extern struct platform_device u8500_b2r2_blt_device;
+extern struct platform_device u5500_b2r2_blt_device;
+extern struct platform_device u8500_b2r2_device;
+extern struct platform_device u5500_b2r2_device;
+extern struct platform_device u8500_trace_modem;
+extern struct platform_device ux500_hwmem_device;
+extern struct platform_device u8500_stm_device;
extern struct amba_device ux500_pl031_device;
-
-extern struct platform_device u8500_dma40_device;
+extern struct platform_device ux500_hash1_device;
+extern struct platform_device ux500_cryp1_device;
+extern struct platform_device mloader_fw_device;
+extern struct platform_device u5500_thsens_device;
+extern struct platform_device u8500_thsens_device;
extern struct platform_device ux500_ske_keypad_device;
+extern struct platform_device u8500_wdt_device;
+extern struct platform_device u5500_wdt_device;
+extern struct platform_device u8500_hsi_device;
+extern struct platform_device ux500_mmio_device;
+extern struct platform_device u5500_mmio_device;
#endif
diff --git a/arch/arm/mach-ux500/include/mach/gpio.h b/arch/arm/mach-ux500/include/mach/gpio.h
index c01ef66537f..e0d903af605 100644
--- a/arch/arm/mach-ux500/include/mach/gpio.h
+++ b/arch/arm/mach-ux500/include/mach/gpio.h
@@ -1,5 +1,29 @@
#ifndef __ASM_ARCH_GPIO_H
#define __ASM_ARCH_GPIO_H
+/*
+ * 288 (#267 is the highest one actually hooked up) onchip GPIOs, plus enough
+ * room for a couple of GPIO expanders.
+ */
+
+#if CONFIG_ARCH_NR_GPIO > 0
+#define ARCH_NR_GPIOS CONFIG_ARCH_NR_GPIO
+#else
+#define ARCH_NR_GPIOS 366
+#endif
+
+#define NOMADIK_NR_GPIO 288
+
+#include <asm-generic/gpio.h>
+
+/* Invoke gpiolibs gpio_chip abstraction */
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+#define MOP500_EGPIO(x) (NOMADIK_NR_GPIO + (x))
+#define MOP500_EGPIO_END MOP500_EGPIO(24)
+#define AB8500_GPIO_BASE MOP500_EGPIO_END
#endif /* __ASM_ARCH_GPIO_H */
diff --git a/arch/arm/mach-ux500/include/mach/hardware.h b/arch/arm/mach-ux500/include/mach/hardware.h
index f84698936d3..88b105d8d89 100644
--- a/arch/arm/mach-ux500/include/mach/hardware.h
+++ b/arch/arm/mach-ux500/include/mach/hardware.h
@@ -22,15 +22,42 @@
#define IO_ADDRESS(x) \
(((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + U8500_IO_VIRTUAL)
+/*
+ * For 9540, ROM code is at address 0xFFFE0000
+ * The previous macro cannot be used
+ * Or else its virtual address would be above 0xFFFFFFFF
+ */
+#define IO_ADDRESS_DB9540_ROM(x) \
+ (((x) & 0x0001ffff) + U8500_IO_VIRTUAL + 0x0B000000)
+
/* typesafe io address */
#define __io_address(n) IOMEM(IO_ADDRESS(n))
+
+#define __io_address_db9540_rom(n) __io(IO_ADDRESS_DB9540_ROM(n))
/* Used by some plat-nomadik code */
#define io_p2v(n) __io_address(n)
#include <mach/db8500-regs.h>
#include <mach/db5500-regs.h>
+/*
+ * FIFO offsets for IPs
+ */
#define MSP_TX_RX_REG_OFFSET 0
+#define HASH1_TX_REG_OFFSET 0x4
+#define SSP_TX_RX_REG_OFFSET 0x8
+#define SPI_TX_RX_REG_OFFSET 0x8
+#define SD_MMC_TX_RX_REG_OFFSET 0x80
+#define CRYP1_RX_REG_OFFSET 0x10
+#define CRYP1_TX_REG_OFFSET 0x8
+
+#define SSP_0_CONTROLLER 4
+#define SSP_1_CONTROLLER 5
+
+#define SPI023_0_CONTROLLER 6
+#define SPI023_1_CONTROLLER 7
+#define SPI023_2_CONTROLLER 8
+#define SPI023_3_CONTROLLER 9
#ifndef __ASSEMBLY__
diff --git a/arch/arm/mach-ux500/include/mach/id.h b/arch/arm/mach-ux500/include/mach/id.h
index 833d6a6edc9..240534b19f3 100644
--- a/arch/arm/mach-ux500/include/mach/id.h
+++ b/arch/arm/mach-ux500/include/mach/id.h
@@ -38,12 +38,47 @@ static inline unsigned int __attribute_const__ dbx500_revision(void)
static inline bool __attribute_const__ cpu_is_u8500(void)
{
- return dbx500_partnumber() == 0x8500;
+#ifdef CONFIG_UX500_SOC_DB8500
+ /* partnumber 8520 also comes under 8500 */
+ return ((dbx500_partnumber() >> 8) & 0xff) == 0x85;
+#else
+ return false;
+#endif
+}
+
+static inline bool __attribute_const__ cpu_is_u8520(void)
+{
+#ifdef CONFIG_UX500_SOC_DB8500
+ return dbx500_partnumber() == 0x8520;
+#else
+ return false;
+#endif
}
static inline bool __attribute_const__ cpu_is_u5500(void)
{
+#ifdef CONFIG_UX500_SOC_DB5500
return dbx500_partnumber() == 0x5500;
+#else
+ return false;
+#endif
+}
+
+#ifdef CONFIG_UX500_SOC_DB8500
+bool cpu_is_u9500(void);
+#else
+static inline bool cpu_is_u9500(void)
+{
+ return false;
+}
+#endif
+static inline bool __attribute_const__ cpu_is_u9540(void)
+{
+#ifdef CONFIG_UX500_SOC_DB8500
+ return dbx500_partnumber() == 0x9540;
+#else
+ return false;
+#endif
}
/*
@@ -74,26 +109,6 @@ static inline bool __attribute_const__ cpu_is_u5500v21(void)
* 8500 revisions
*/
-static inline bool __attribute_const__ cpu_is_u8500ed(void)
-{
- return cpu_is_u8500() && dbx500_revision() == 0x00;
-}
-
-static inline bool __attribute_const__ cpu_is_u8500v1(void)
-{
- return cpu_is_u8500() && (dbx500_revision() & 0xf0) == 0xA0;
-}
-
-static inline bool __attribute_const__ cpu_is_u8500v10(void)
-{
- return cpu_is_u8500() && dbx500_revision() == 0xA0;
-}
-
-static inline bool __attribute_const__ cpu_is_u8500v11(void)
-{
- return cpu_is_u8500() && dbx500_revision() == 0xA1;
-}
-
static inline bool __attribute_const__ cpu_is_u8500v2(void)
{
return cpu_is_u8500() && ((dbx500_revision() & 0xf0) == 0xB0);
@@ -109,9 +124,15 @@ static inline bool cpu_is_u8500v21(void)
return cpu_is_u8500() && (dbx500_revision() == 0xB1);
}
+static inline bool cpu_is_u8500v22(void)
+{
+ return cpu_is_u8500() && (dbx500_revision() == 0xB2);
+}
+
static inline bool cpu_is_u8500v20_or_later(void)
{
- return cpu_is_u8500() && !cpu_is_u8500v10() && !cpu_is_u8500v11();
+ return cpu_is_u9540() ||
+ (cpu_is_u8500() && ((dbx500_revision() & 0xf0) >= 0xB0));
}
static inline bool ux500_is_svp(void)
diff --git a/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h b/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h
index 7d34c52798b..5610e1dc2db 100644
--- a/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h
+++ b/arch/arm/mach-ux500/include/mach/irqs-board-mop500.h
@@ -43,6 +43,8 @@
#define MOP500_AB8500_VIR_GPIO_IRQ_BASE \
MOP500_STMPE1601_IRQ_END
+#define MOP500_AB8500_VIR_GPIO_IRQ(x) \
+ (MOP500_AB8500_VIR_GPIO_IRQ_BASE + (x))
#define MOP500_AB8500_VIR_GPIO_IRQ_END \
(MOP500_AB8500_VIR_GPIO_IRQ_BASE + AB8500_VIR_GPIO_NR_IRQS)
@@ -57,7 +59,7 @@
*/
#if MOP500_IRQ_END > IRQ_BOARD_END
#undef IRQ_BOARD_END
-#define IRQ_BOARD_END MOP500_IRQ_END
+#define IRQ_BOARD_END MOP500_IRQ_END
#endif
#endif
diff --git a/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h b/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h
index 29d972c7717..2294a47b3a2 100644
--- a/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h
+++ b/arch/arm/mach-ux500/include/mach/irqs-board-u5500.h
@@ -7,13 +7,20 @@
#ifndef __MACH_IRQS_BOARD_U5500_H
#define __MACH_IRQS_BOARD_U5500_H
-#define AB5500_NR_IRQS 5
+#include <linux/mfd/abx500/ab5500.h>
+
+#define AB5500_NR_IRQS (AB5500_NUM_IRQ_REGS * 8)
#define IRQ_AB5500_BASE IRQ_BOARD_START
#define IRQ_AB5500_END (IRQ_AB5500_BASE + AB5500_NR_IRQS)
#define U5500_IRQ_END IRQ_AB5500_END
-#if IRQ_BOARD_END < U5500_IRQ_END
+/*
+ * We may have several boards, but only one will run at a
+ * time, so the one with most IRQs will bump this ahead,
+ * but the IRQ_BOARD_START remains the same for either board.
+ */
+#if U5500_IRQ_END > IRQ_BOARD_END
#undef IRQ_BOARD_END
#define IRQ_BOARD_END U5500_IRQ_END
#endif
diff --git a/arch/arm/mach-ux500/include/mach/irqs-db5500.h b/arch/arm/mach-ux500/include/mach/irqs-db5500.h
index 77239776a6f..234cf4ac4e1 100644
--- a/arch/arm/mach-ux500/include/mach/irqs-db5500.h
+++ b/arch/arm/mach-ux500/include/mach/irqs-db5500.h
@@ -85,6 +85,37 @@
#ifdef CONFIG_UX500_SOC_DB5500
+/* Virtual interrupts corresponding to the PRCMU wakeups. */
+#define IRQ_DB5500_PRCMU_BASE IRQ_SOC_START
+
+#define IRQ_DB5500_PRCMU_RTC (IRQ_DB5500_PRCMU_BASE)
+#define IRQ_DB5500_PRCMU_RTT0 (IRQ_DB5500_PRCMU_BASE + 1)
+#define IRQ_DB5500_PRCMU_RTT1 (IRQ_DB5500_PRCMU_BASE + 2)
+#define IRQ_DB5500_PRCMU_CD_IRQ (IRQ_DB5500_PRCMU_BASE + 3)
+#define IRQ_DB5500_PRCMU_SRP_TIM (IRQ_DB5500_PRCMU_BASE + 4)
+#define IRQ_DB5500_PRCMU_APE_REQ (IRQ_DB5500_PRCMU_BASE + 5)
+#define IRQ_DB5500_PRCMU_USB (IRQ_DB5500_PRCMU_BASE + 6)
+#define IRQ_DB5500_PRCMU_ABB (IRQ_DB5500_PRCMU_BASE + 7)
+#define IRQ_DB5500_PRCMU_ARM (IRQ_DB5500_PRCMU_BASE + 8)
+#define IRQ_DB5500_PRCMU_MODEM_SW_RESET_REQ (IRQ_DB5500_PRCMU_BASE + 9)
+#define IRQ_DB5500_PRCMU_AC_WAKE_ACK (IRQ_DB5500_PRCMU_BASE + 10)
+#define IRQ_DB5500_PRCMU_GPIO0 (IRQ_DB5500_PRCMU_BASE + 11)
+#define IRQ_DB5500_PRCMU_GPIO1 (IRQ_DB5500_PRCMU_BASE + 12)
+#define IRQ_DB5500_PRCMU_GPIO2 (IRQ_DB5500_PRCMU_BASE + 13)
+#define IRQ_DB5500_PRCMU_GPIO3 (IRQ_DB5500_PRCMU_BASE + 14)
+#define IRQ_DB5500_PRCMU_GPIO4 (IRQ_DB5500_PRCMU_BASE + 15)
+#define IRQ_DB5500_PRCMU_GPIO5 (IRQ_DB5500_PRCMU_BASE + 16)
+#define IRQ_DB5500_PRCMU_GPIO6 (IRQ_DB5500_PRCMU_BASE + 17)
+#define IRQ_DB5500_PRCMU_GPIO7 (IRQ_DB5500_PRCMU_BASE + 18)
+#define IRQ_DB5500_PRCMU_AC_REL_ACK (IRQ_DB5500_PRCMU_BASE + 19)
+#define IRQ_DB5500_PRCMU_LOW_POWER_AUDIO (IRQ_DB5500_PRCMU_BASE + 20)
+#define IRQ_DB5500_PRCMU_TEMP_SENSOR_LOW (IRQ_DB5500_PRCMU_BASE + 21)
+#define IRQ_DB5500_PRCMU_TEMP_SENSOR_HIGH (IRQ_DB5500_PRCMU_BASE + 22)
+#define IRQ_DB5500_PRCMU_END (IRQ_DB5500_PRCMU_BASE + 23)
+#define IRQ_DB5500_PRCMU_APE_REL 0x200
+
+#define NUM_DB5500_PRCMU_WAKEUPS (IRQ_DB5500_PRCMU_END - IRQ_DB5500_PRCMU_BASE)
+
/*
* After the GPIO ones we reserve a range of IRQ:s in which virtual
* IRQ:s representing modem IRQ:s can be allocated
diff --git a/arch/arm/mach-ux500/include/mach/irqs-db9540.h b/arch/arm/mach-ux500/include/mach/irqs-db9540.h
new file mode 100644
index 00000000000..6a616e4d320
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/irqs-db9540.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Sebastien Pasdeloup <sebastien.pasdeloup-nonst@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#ifndef __MACH_IRQS_DB9540_H
+#define __MACH_IRQS_DB9540_H
+
+#define IRQ_AP9540_VSENSOR (IRQ_SHPI_START + 30)
+#define IRQ_AP9540_SLIMBUS0 (IRQ_SHPI_START + 101)
+#define IRQ_AP9540_THSENS (IRQ_SHPI_START + 102)
+#define IRQ_AP9540_DDR0 (IRQ_SHPI_START + 103)
+#define IRQ_AP9540_CTIEXTRIG0 (IRQ_SHPI_START + 111)
+#define IRQ_AP9540_SGX (IRQ_SHPI_START + 112)
+#define IRQ_AP9540_CTIEXTRIG1 (IRQ_SHPI_START + 117)
+#define IRQ_AP9540_C2C_GENO0 (IRQ_SHPI_START + 128)
+#define IRQ_AP9540_C2C_GENO1 (IRQ_SHPI_START + 129)
+#define IRQ_AP9540_C2C_GENO2 (IRQ_SHPI_START + 130)
+#define IRQ_AP9540_C2C_GENO3 (IRQ_SHPI_START + 131)
+#define IRQ_AP9540_C2C_GENO4 (IRQ_SHPI_START + 132)
+#define IRQ_AP9540_C2C_GENO5 (IRQ_SHPI_START + 133)
+#define IRQ_AP9540_C2C_GENO6 (IRQ_SHPI_START + 134)
+#define IRQ_AP9540_C2C_GENO7 (IRQ_SHPI_START + 135)
+#define IRQ_AP9540_C2C_GENO8 (IRQ_SHPI_START + 136)
+#define IRQ_AP9540_C2C_GENO9 (IRQ_SHPI_START + 137)
+#define IRQ_AP9540_C2C_GENO10 (IRQ_SHPI_START + 138)
+#define IRQ_AP9540_C2C_GENO11 (IRQ_SHPI_START + 139)
+#define IRQ_AP9540_C2C_GENO12 (IRQ_SHPI_START + 140)
+#define IRQ_AP9540_C2C_GENO13 (IRQ_SHPI_START + 141)
+#define IRQ_AP9540_C2C_GENO14 (IRQ_SHPI_START + 142)
+#define IRQ_AP9540_C2C_GENO15 (IRQ_SHPI_START + 143)
+#define IRQ_AP9540_C2C_GENO16 (IRQ_SHPI_START + 144)
+#define IRQ_AP9540_C2C_GENO17 (IRQ_SHPI_START + 145)
+#define IRQ_AP9540_C2C_GENO18 (IRQ_SHPI_START + 146)
+#define IRQ_AP9540_C2C_GENO19 (IRQ_SHPI_START + 147)
+#define IRQ_AP9540_C2C_GENO20 (IRQ_SHPI_START + 148)
+#define IRQ_AP9540_C2C_GENO21 (IRQ_SHPI_START + 149)
+#define IRQ_AP9540_C2C_GENO22 (IRQ_SHPI_START + 150)
+#define IRQ_AP9540_C2C_GENO23 (IRQ_SHPI_START + 151)
+#define IRQ_AP9540_C2C_GENO24 (IRQ_SHPI_START + 152)
+#define IRQ_AP9540_C2C_GENO25 (IRQ_SHPI_START + 153)
+#define IRQ_AP9540_C2C_GENO26 (IRQ_SHPI_START + 154)
+#define IRQ_AP9540_C2C_GENO27 (IRQ_SHPI_START + 155)
+#define IRQ_AP9540_C2C_GENO28 (IRQ_SHPI_START + 156)
+#define IRQ_AP9540_C2C_GENO29 (IRQ_SHPI_START + 157)
+#define IRQ_AP9540_C2C_GENO30 (IRQ_SHPI_START + 158)
+#define IRQ_AP9540_C2C_GENO31 (IRQ_SHPI_START + 159)
+#define IRQ_AP9540_C2C_IRQ0 (IRQ_SHPI_START + 160)
+#define IRQ_AP9540_C2C_IRQ1 (IRQ_SHPI_START + 161)
+#define IRQ_AP9540_HVA_ITS (IRQ_SHPI_START + 162)
+#define IRQ_AP9540_HVA_ERR (IRQ_SHPI_START + 163)
+#define IRQ_AP9540_C2C_G1 (IRQ_SHPI_START + 164)
+#define IRQ_AP9540_C2C_DDR1 (IRQ_SHPI_START + 165)
+#define IRQ_AP9540_C2C_SGX_IDLE (IRQ_SHPI_START + 166)
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/irqs.h b/arch/arm/mach-ux500/include/mach/irqs.h
index c23a6b5f0c4..f96628fa147 100644
--- a/arch/arm/mach-ux500/include/mach/irqs.h
+++ b/arch/arm/mach-ux500/include/mach/irqs.h
@@ -11,9 +11,7 @@
#define ASM_ARCH_IRQS_H
#include <mach/hardware.h>
-
-#define IRQ_LOCALTIMER 29
-#define IRQ_LOCALWDOG 30
+#include <linux/gpio.h>
/* Shared Peripheral Interrupt (SHPI) */
#define IRQ_SHPI_START 32
@@ -22,26 +20,35 @@
* MTU0 preserved for now until plat-nomadik is taught not to use it. Don't
* add any other IRQs here, use the irqs-dbx500.h files.
*/
-#define IRQ_MTU0 (IRQ_SHPI_START + 4)
+#define IRQ_MTU0 (IRQ_SHPI_START + 4)
+
+#define IRQ_LOCALTIMER 29
+#define IRQ_LOCALWDOG 30
-#define DBX500_NR_INTERNAL_IRQS 160
+/*********************************************************************/
+
+#define DBX500_NR_INTERNAL_IRQS 166
/* After chip-specific IRQ numbers we have the GPIO ones */
-#define NOMADIK_NR_GPIO 288
#define NOMADIK_GPIO_TO_IRQ(gpio) ((gpio) + DBX500_NR_INTERNAL_IRQS)
#define NOMADIK_IRQ_TO_GPIO(irq) ((irq) - DBX500_NR_INTERNAL_IRQS)
+
+#define GPIO_TO_IRQ NOMADIK_GPIO_TO_IRQ
+#define IRQ_TO_GPIO NOMADIK_IRQ_TO_GPIO
#define IRQ_GPIO_END NOMADIK_GPIO_TO_IRQ(NOMADIK_NR_GPIO)
-#define IRQ_SOC_START IRQ_GPIO_END
+#define IRQ_SOC_START IRQ_GPIO_END
/* This will be overridden by SoC-specific irq headers */
-#define IRQ_SOC_END IRQ_SOC_START
+#define IRQ_SOC_END IRQ_SOC_START
+
+#define IRQ_BOARD_START IRQ_SOC_END
+/* This will be overridden by board-specific irq headers */
+#define IRQ_BOARD_END IRQ_BOARD_START
#include <mach/irqs-db5500.h>
#include <mach/irqs-db8500.h>
+#include <mach/irqs-db9540.h>
-#define IRQ_BOARD_START IRQ_SOC_END
-/* This will be overridden by board-specific irq headers */
-#define IRQ_BOARD_END IRQ_BOARD_START
#ifdef CONFIG_MACH_MOP500
#include <mach/irqs-board-mop500.h>
@@ -51,6 +58,8 @@
#include <mach/irqs-board-u5500.h>
#endif
+#ifndef NR_IRQS
#define NR_IRQS IRQ_BOARD_END
+#endif
#endif /* ASM_ARCH_IRQS_H */
diff --git a/arch/arm/mach-ux500/include/mach/pm-timer.h b/arch/arm/mach-ux500/include/mach/pm-timer.h
new file mode 100644
index 00000000000..f5fafbbaa77
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/pm-timer.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ *
+ * License Terms: GNU General Public License v2
+ *
+ */
+
+#ifndef PM_TIMER_H
+#define PM_TIMER_H
+
+#include <linux/ktime.h>
+
+#ifdef CONFIG_UX500_CPUIDLE_DEBUG
+ktime_t u8500_rtc_exit_latency_get(void);
+void ux500_rtcrtt_measure_latency(bool enable);
+#else
+static inline ktime_t u8500_rtc_exit_latency_get(void)
+{
+ return ktime_set(0, 0);
+}
+static inline void ux500_rtcrtt_measure_latency(bool enable) { }
+
+#endif
+
+void ux500_rtcrtt_off(void);
+void ux500_rtcrtt_next(u32 time_us);
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/pm.h b/arch/arm/mach-ux500/include/mach/pm.h
new file mode 100644
index 00000000000..c6f1b0adca5
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/pm.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#ifndef PM_COMMON_H
+#define PM_COMMON_H
+
+#ifdef CONFIG_PM
+#include <linux/mfd/dbx500-prcmu.h>
+
+/**
+ * ux500_pm_gic_decouple()
+ *
+ * Decouple GIC from the interrupt bus.
+ */
+void ux500_pm_gic_decouple(void);
+
+/**
+ * ux500_pm_gic_recouple()
+ *
+ * Recouple GIC with the interrupt bus.
+ */
+void ux500_pm_gic_recouple(void);
+
+/**
+ * ux500_pm_gic_pending_interrupt()
+ *
+ * returns true, if there are pending interrupts.
+ */
+bool ux500_pm_gic_pending_interrupt(void);
+
+/**
+ * ux500_pm_prcmu_pending_interrupt()
+ *
+ * returns true, if there are pending interrupts.
+ */
+bool ux500_pm_prcmu_pending_interrupt(void);
+
+/**
+ * ux500_pm_prcmu_set_ioforce()
+ *
+ * @enable: Enable/disable
+ *
+ * Enable/disable the gpio-ring
+ */
+void ux500_pm_prcmu_set_ioforce(bool enable);
+
+/**
+ * ux500_pm_prcmu_copy_gic_settings()
+ *
+ * This function copies all the gic interrupt settings to the prcmu.
+ * This is needed for the system to catch interrupts in ApIdle
+ */
+void ux500_pm_prcmu_copy_gic_settings(void);
+
+/**
+ * ux500_pm_gpio_save_wake_up_status()
+ *
+ * This function is called when the prcmu has woken the ARM
+ * but before ioforce is disabled.
+ */
+void ux500_pm_gpio_save_wake_up_status(void);
+
+/**
+ * ux500_pm_gpio_read_wake_up_status()
+ *
+ * @bank_number: The gpio bank.
+ *
+ * Returns the WKS register settings for given bank number.
+ * The WKS register is cleared when ioforce is released therefore
+ * this function is needed.
+ */
+u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_number);
+
+/**
+ * ux500_pm_other_cpu_wfi()
+ *
+ * Returns true if the other CPU is in WFI.
+ */
+bool ux500_pm_other_cpu_wfi(void);
+
+struct dev_pm_domain;
+extern struct dev_pm_domain ux500_dev_power_domain;
+extern struct dev_pm_domain ux500_amba_dev_power_domain;
+
+#else
+u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_number)
+{
+ return 0;
+}
+
+/**
+ * ux500_pm_prcmu_set_ioforce()
+ *
+ * @enable: Enable/disable
+ *
+ * Enable/disable the gpio-ring
+ */
+static inline void ux500_pm_prcmu_set_ioforce(bool enable) { }
+
+#endif
+
+extern int ux500_console_uart_gpio_pin;
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/prcmu-debug.h b/arch/arm/mach-ux500/include/mach/prcmu-debug.h
new file mode 100644
index 00000000000..38f5ad94864
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/prcmu-debug.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Martin Persson for ST-Ericsson
+ * Etienne Carriere <etienne.carriere@stericsson.com> for ST-Ericsson
+ *
+ */
+
+#ifndef PRCMU_DEBUG_H
+#define PRCMU_DEBUG_H
+
+#ifdef CONFIG_DBX500_PRCMU_DEBUG
+void prcmu_debug_ape_opp_log(u8 opp);
+void prcmu_debug_ddr_opp_log(u8 opp);
+void prcmu_debug_arm_opp_log(u32 value);
+void prcmu_debug_dump_data_mem(void);
+void prcmu_debug_dump_regs(void);
+void prcmu_debug_register_interrupt(u32 mailbox);
+void prcmu_debug_register_mbox0_event(u32 ev, u32 mask);
+#else
+static inline void prcmu_debug_ape_opp_log(u8 opp) {}
+static inline void prcmu_debug_ddr_opp_log(u8 opp) {}
+static inline void prcmu_debug_arm_opp_log(u32 value) {}
+static inline void prcmu_debug_dump_data_mem(void) {}
+static inline void prcmu_debug_dump_regs(void) {}
+static inline void prcmu_debug_register_interrupt(u32 mailbox) {}
+static inline void prcmu_debug_register_mbox0_event(u32 ev, u32 mask) {}
+#endif
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/reboot_reasons.h b/arch/arm/mach-ux500/include/mach/reboot_reasons.h
new file mode 100644
index 00000000000..4d0592b61d4
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/reboot_reasons.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Rickard Evertsson <rickard.evertsson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Use this file to customize your reboot / sw reset reasons. Add, remove or
+ * modify reasons in reboot_reasons[].
+ * The reboot reasons will be saved to a secure location in TCDM memory and
+ * can be read at bootup by e.g. the bootloader.
+ */
+
+#ifndef _REBOOT_REASONS_H
+#define _REBOOT_REASONS_H
+
+/*
+ * These defines contains the codes that will be written down to a secure
+ * location before resetting. These values are just dummy values and does not,
+ * at the moment, affect anything.
+ */
+#define SW_RESET_NO_ARGUMENT 0xBEEF
+#define SW_RESET_FACTORY_RESET 0x4242
+#define SW_RESET_CRASH 0xDEAD
+#define SW_RESET_NORMAL 0xc001
+#define SW_RESET_CHARGING 0xCAFE
+#define SW_RESET_COLDSTART 0x0
+#define SW_RESET_RECOVERY 0x5502
+#define SW_RESET_CHGONLY_EXIT 0xCAFF
+
+/*
+ * The array reboot_reasons[] is used when you want to map a string to a reboot
+ * reason code
+ */
+struct reboot_reason {
+ const char *reason;
+ unsigned short code;
+};
+
+extern struct reboot_reason reboot_reasons[];
+
+extern unsigned int reboot_reasons_size;
+
+#endif
diff --git a/arch/arm/mach-ux500/include/mach/setup.h b/arch/arm/mach-ux500/include/mach/setup.h
index 3dc00ffa7bf..5caffc97e36 100644
--- a/arch/arm/mach-ux500/include/mach/setup.h
+++ b/arch/arm/mach-ux500/include/mach/setup.h
@@ -14,6 +14,8 @@
#include <asm/mach/time.h>
#include <linux/init.h>
+extern void ux500_restart(char, const char *);
+
void __init ux500_map_io(void);
extern void __init u5500_map_io(void);
extern void __init u8500_map_io(void);
@@ -26,6 +28,7 @@ extern void __init ux500_init_irq(void);
extern void __init u5500_sdi_init(struct device *parent);
extern void __init db5500_dma_init(struct device *parent);
+extern void __init db8500_dma_init(struct device *parent);
extern struct device *ux500_soc_device_init(const char *soc_id);
@@ -49,4 +52,11 @@ extern struct sys_timer ux500_timer;
.type = MT_MEMORY, \
}
+#define __MEM_DEV_DESC_DB9540_ROM(x, sz) { \
+ .virtual = IO_ADDRESS_DB9540_ROM(x), \
+ .pfn = __phys_to_pfn(x), \
+ .length = sz, \
+ .type = MT_MEMORY, \
+}
+
#endif /* __ASM_ARCH_SETUP_H */
diff --git a/arch/arm/mach-ux500/ste-dma40-db5500.h b/arch/arm/mach-ux500/include/mach/ste-dma40-db5500.h
index cb2110c3285..0ddd4ab9020 100644
--- a/arch/arm/mach-ux500/ste-dma40-db5500.h
+++ b/arch/arm/mach-ux500/include/mach/ste-dma40-db5500.h
@@ -42,7 +42,9 @@ enum dma_src_dev_type {
DB5500_DMA_DEV26_SDMMC2_RX = 26,
DB5500_DMA_DEV27_SDMMC3_RX = 27,
DB5500_DMA_DEV28_SDMMC4_RX = 28,
- /* 29 - 32 not used */
+ /* 29, 30 not used */
+ DB5500_DMA_DEV31_CRYPTO1_RX = 31, /* v2 */
+ /* 32 not used */
DB5500_DMA_DEV33_SDMMC0_RX = 33,
DB5500_DMA_DEV34_SDMMC1_RX = 34,
DB5500_DMA_DEV35_SDMMC2_RX = 35,
@@ -56,7 +58,7 @@ enum dma_src_dev_type {
DB5500_DMA_DEV43_USB_OTG_IEP_5_13 = 43,
DB5500_DMA_DEV44_USB_OTG_IEP_6_14 = 44,
DB5500_DMA_DEV45_USB_OTG_IEP_7_15 = 45,
- /* 46 not used */
+ DB5500_DMA_DEV46_CRYPTO1_RX = 46, /* v2 */
DB5500_DMA_DEV47_MCDE_RX = 47,
DB5500_DMA_DEV48_CRYPTO1_RX = 48,
/* 49, 50 not used */
@@ -98,7 +100,9 @@ enum dma_dest_dev_type {
DB5500_DMA_DEV26_SDMMC2_TX = 26,
DB5500_DMA_DEV27_SDMMC3_TX = 27,
DB5500_DMA_DEV28_SDMMC4_TX = 28,
- /* 29 - 31 not used */
+ /* 29 not used */
+ DB5500_DMA_DEV30_HASH1_TX = 30, /* v2 */
+ DB5500_DMA_DEV31_CRYPTO1_TX = 31, /* v2 */
DB5500_DMA_DEV32_FSMC_TX = 32,
DB5500_DMA_DEV33_SDMMC0_TX = 33,
DB5500_DMA_DEV34_SDMMC1_TX = 34,
@@ -113,7 +117,7 @@ enum dma_dest_dev_type {
DB5500_DMA_DEV43_USB_OTG_OEP_5_13 = 43,
DB5500_DMA_DEV44_USB_OTG_OEP_6_14 = 44,
DB5500_DMA_DEV45_USB_OTG_OEP_7_15 = 45,
- /* 46 not used */
+ DB5500_DMA_DEV46_CRYPTO1_TX = 46, /* v2 */
DB5500_DMA_DEV47_STM_TX = 47,
DB5500_DMA_DEV48_CRYPTO1_TX = 48,
DB5500_DMA_DEV49_CRYPTO1_TX_HASH1_TX = 49,
diff --git a/arch/arm/mach-ux500/ste-dma40-db8500.h b/arch/arm/mach-ux500/include/mach/ste-dma40-db8500.h
index a616419bea7..65799a75199 100644
--- a/arch/arm/mach-ux500/ste-dma40-db8500.h
+++ b/arch/arm/mach-ux500/include/mach/ste-dma40-db8500.h
@@ -1,16 +1,19 @@
/*
- * arch/arm/mach-ux500/ste_dma40_db8500.h
- * DB8500-SoC-specific configuration for DMA40
- *
- * Copyright (C) ST-Ericsson 2007-2010
+ * Copyright (C) ST-Ericsson SA 2007-2010
+ * Author: Per Friden <per.friden@stericsson.com> for ST-Ericsson
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
* License terms: GNU General Public License (GPL) version 2
- * Author: Per Friden <per.friden@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ *
+ * DB8500-SoC-specific configuration for DMA40
*/
#ifndef STE_DMA40_DB8500_H
#define STE_DMA40_DB8500_H
#define DB8500_DMA_NR_DEV 64
+/*
+ * All entries with double names are multiplexed
+ * and can never be used at the same time.
+ */
enum dma_src_dev_type {
DB8500_DMA_DEV0_SPI0_RX = 0,
@@ -20,7 +23,7 @@ enum dma_src_dev_type {
DB8500_DMA_DEV4_I2C1_RX = 4,
DB8500_DMA_DEV5_I2C3_RX = 5,
DB8500_DMA_DEV6_I2C2_RX = 6,
- DB8500_DMA_DEV7_I2C4_RX = 7, /* Only on V1 and later */
+ DB8500_DMA_DEV7_I2C4_RX = 7,
DB8500_DMA_DEV8_SSP0_RX = 8,
DB8500_DMA_DEV9_SSP1_RX = 9,
DB8500_DMA_DEV10_MCDE_RX = 10,
@@ -43,8 +46,6 @@ enum dma_src_dev_type {
DB8500_DMA_DEV27_SRC_SXA3_RX_TX = 27,
DB8500_DMA_DEV28_SD_MM2_RX = 28,
DB8500_DMA_DEV29_SD_MM0_RX = 29,
- DB8500_DMA_DEV30_MSP1_RX = 30,
- /* On DB8500v2, MSP3 RX replaces MSP1 RX */
DB8500_DMA_DEV30_MSP3_RX = 30,
DB8500_DMA_DEV31_MSP0_RX_SLIM0_CH0_RX = 31,
DB8500_DMA_DEV32_SD_MM1_RX = 32,
@@ -82,7 +83,7 @@ enum dma_dest_dev_type {
DB8500_DMA_DEV4_I2C1_TX = 4,
DB8500_DMA_DEV5_I2C3_TX = 5,
DB8500_DMA_DEV6_I2C2_TX = 6,
- DB8500_DMA_DEV7_I2C4_TX = 7, /* Only on V1 and later */
+ DB8500_DMA_DEV7_I2C4_TX = 7,
DB8500_DMA_DEV8_SSP0_TX = 8,
DB8500_DMA_DEV9_SSP1_TX = 9,
/* 10 is not used*/
diff --git a/arch/arm/mach-ux500/include/mach/suspend.h b/arch/arm/mach-ux500/include/mach/suspend.h
new file mode 100644
index 00000000000..5a8df72be2e
--- /dev/null
+++ b/arch/arm/mach-ux500/include/mach/suspend.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+#ifndef __MACH_SUSPEND_H
+#define __MACH_SUSPEND_H
+
+#ifdef CONFIG_UX500_SUSPEND
+void suspend_block_sleep(void);
+void suspend_unblock_sleep(void);
+void suspend_set_pins_force_fn(void (*force)(void), void (*force_mux)(void));
+#else
+static inline void suspend_block_sleep(void) { }
+static inline void suspend_unblock_sleep(void) { }
+static inline void suspend_set_pins_force_fn(void (*force)(void),
+ void (*force_mux)(void)) { }
+#endif
+
+#endif /* __MACH_SUSPEND_H */
diff --git a/arch/arm/mach-ux500/include/mach/timex.h b/arch/arm/mach-ux500/include/mach/timex.h
index d0942c17401..0ba497bd9d7 100644
--- a/arch/arm/mach-ux500/include/mach/timex.h
+++ b/arch/arm/mach-ux500/include/mach/timex.h
@@ -2,5 +2,6 @@
#define __ASM_ARCH_TIMEX_H
#define CLOCK_TICK_RATE 110000000
+#define ARCH_HAS_READ_CURRENT_TIMER
#endif
diff --git a/arch/arm/mach-ux500/include/mach/usb.h b/arch/arm/mach-ux500/include/mach/usb.h
index 4c1cc50a595..700d9dbb1be 100644
--- a/arch/arm/mach-ux500/include/mach/usb.h
+++ b/arch/arm/mach-ux500/include/mach/usb.h
@@ -12,6 +12,8 @@
#define UX500_MUSB_DMA_NUM_RX_CHANNELS 8
#define UX500_MUSB_DMA_NUM_TX_CHANNELS 8
+struct musb;
+
struct ux500_musb_board_data {
void **dma_rx_param_array;
void **dma_tx_param_array;
@@ -22,4 +24,14 @@ struct ux500_musb_board_data {
void ux500_add_usb(struct device *parent, resource_size_t base,
int irq, int *dma_rx_cfg, int *dma_tx_cfg);
+
+/* Only used for u5500 */
+struct abx500_usbgpio_platform_data {
+ int (*get)(struct device *device);
+ void (*enable)(void);
+ void (*disable)(void);
+ void (*put)(void);
+ int usb_cs;
+};
+void ux500_restore_context(struct musb *musb);
#endif
diff --git a/arch/arm/mach-ux500/l2x0-prefetch.c b/arch/arm/mach-ux500/l2x0-prefetch.c
new file mode 100644
index 00000000000..4085c351f20
--- /dev/null
+++ b/arch/arm/mach-ux500/l2x0-prefetch.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/tee.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+#include <asm/hardware/cache-l2x0.h>
+
+static struct tee_session session;
+static struct tee_context context;
+static void __iomem *l2x0_base;
+
+#define L2X0_PREFETCH_CTRL_REG (0x00000F60)
+#define L2X0_PREFETCH_CTRL_BIT_DATA_EN (1 << 28)
+#define L2X0_PREFETCH_CTRL_BIT_INST_EN (1 << 29)
+
+#define L2X0_UUID_TEE_TA_START_LOW 0xBC765EDE
+#define L2X0_UUID_TEE_TA_START_MID 0x6724
+#define L2X0_UUID_TEE_TA_START_HIGH 0x11DF
+#define L2X0_UUID_TEE_TA_START_CLOCKSEQ \
+ {0x8E, 0x12, 0xEC, 0xDB, 0xDF, 0xD7, 0x20, 0x85}
+
+static void prefetch_enable(void)
+{
+ struct tee_operation operation;
+ u32 data;
+ int err;
+ int origin_err;
+
+ data = readl(l2x0_base + L2X0_PREFETCH_CTRL_REG);
+
+ pr_debug("l2x0-prefetch: %s start, preftect_ctrl=0x%08x\n", __func__,
+ data);
+ if (!(data & L2X0_PREFETCH_CTRL_BIT_INST_EN) ||
+ !(data & L2X0_PREFETCH_CTRL_BIT_DATA_EN)) {
+
+ data |= (L2X0_PREFETCH_CTRL_BIT_INST_EN |
+ L2X0_PREFETCH_CTRL_BIT_DATA_EN);
+
+ operation.shm[0].buffer = &data;
+ operation.shm[0].size = sizeof(data);
+ operation.shm[0].flags = TEEC_MEM_INPUT;
+ operation.flags = TEEC_MEMREF_0_USED;
+
+ err = teec_invoke_command(&session,
+ TEE_STA_SET_L2CC_PREFETCH_CTRL_REGISTER,
+ &operation, &origin_err);
+ if (err)
+ pr_err("l2x0-prefetch: prefetch enable failed, err=%d",
+ err);
+ }
+ pr_debug("l2x0-prefetch: %s end, prefetch_ctrl=0x%08x\n", __func__,
+ readl(l2x0_base + L2X0_PREFETCH_CTRL_REG));
+}
+
+static void prefetch_disable(void)
+{
+ struct tee_operation operation;
+ u32 data;
+ int err;
+ int origin_err;
+
+ data = readl(l2x0_base + L2X0_PREFETCH_CTRL_REG);
+
+ pr_debug("l2x0-prefetch: %s start, preftect_ctrl=0x%08x\n", __func__,
+ data);
+ if (data & (L2X0_PREFETCH_CTRL_BIT_INST_EN |
+ L2X0_PREFETCH_CTRL_BIT_DATA_EN)) {
+
+ data &= ~(L2X0_PREFETCH_CTRL_BIT_INST_EN |
+ L2X0_PREFETCH_CTRL_BIT_DATA_EN);
+
+ operation.shm[0].buffer = &data;
+ operation.shm[0].size = sizeof(data);
+ operation.shm[0].flags = TEEC_MEM_INPUT;
+ operation.flags = TEEC_MEMREF_0_USED;
+
+ err = teec_invoke_command(&session,
+ TEE_STA_SET_L2CC_PREFETCH_CTRL_REGISTER,
+ &operation, &origin_err);
+ if (err)
+ pr_err("l2x0-prefetch: prefetch disable failed, err=%d",
+ err);
+ }
+ pr_debug("l2x0-prefetch: %s end, prefetch_ctrl=0x%08x\n", __func__,
+ readl(l2x0_base + L2X0_PREFETCH_CTRL_REG));
+}
+
+static int __init prefetch_ctrl_init(void)
+{
+ int err;
+ int origin_err;
+ /* Selects trustzone application needed for the job. */
+ struct tee_uuid static_uuid = {
+ L2X0_UUID_TEE_TA_START_LOW,
+ L2X0_UUID_TEE_TA_START_MID,
+ L2X0_UUID_TEE_TA_START_HIGH,
+ L2X0_UUID_TEE_TA_START_CLOCKSEQ,
+ };
+
+ /* Get PL310 base address. It will be used as readonly. */
+ if (cpu_is_u5500())
+ l2x0_base = __io_address(U5500_L2CC_BASE);
+ else if (cpu_is_u8500() || cpu_is_u9540())
+ l2x0_base = __io_address(U8500_L2CC_BASE);
+ else
+ ux500_unknown_soc();
+
+ err = teec_initialize_context(NULL, &context);
+ if (err) {
+ pr_err("l2x0-prefetch: unable to initialize tee context,"
+ " err = %d\n", err);
+ err = -EINVAL;
+ goto error0;
+ }
+
+ err = teec_open_session(&context, &session, &static_uuid,
+ TEEC_LOGIN_PUBLIC, NULL, NULL, &origin_err);
+ if (err) {
+ pr_err("l2x0-prefetch: unable to open tee session,"
+ " tee error = %d, origin error = %d\n",
+ err, origin_err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ outer_cache.prefetch_enable = prefetch_enable;
+ outer_cache.prefetch_disable = prefetch_disable;
+
+ pr_info("l2x0-prefetch: initialized.\n");
+
+ return 0;
+
+error1:
+ (void)teec_finalize_context(&context);
+error0:
+ return err;
+}
+
+static void __exit prefetch_ctrl_exit(void)
+{
+ outer_cache.prefetch_enable = NULL;
+ outer_cache.prefetch_disable = NULL;
+
+ (void)teec_close_session(&session);
+ (void)teec_finalize_context(&context);
+}
+
+/* Wait for TEE driver to be initialized. */
+late_initcall(prefetch_ctrl_init);
+module_exit(prefetch_ctrl_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PL310 prefetch control");
diff --git a/arch/arm/mach-ux500/mbox-db5500.c b/arch/arm/mach-ux500/mbox-db5500.c
deleted file mode 100644
index 0127490218c..00000000000
--- a/arch/arm/mach-ux500/mbox-db5500.c
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- * Author: Stefan Nilsson <stefan.xk.nilsson@stericsson.com> for ST-Ericsson.
- * Author: Martin Persson <martin.persson@stericsson.com> for ST-Ericsson.
- * License terms: GNU General Public License (GPL), version 2.
- */
-
-/*
- * Mailbox nomenclature:
- *
- * APE MODEM
- * mbox pairX
- * ..........................
- * . .
- * . peer .
- * . send ---- .
- * . --> | | .
- * . | | .
- * . ---- .
- * . .
- * . local .
- * . rec ---- .
- * . | | <-- .
- * . | | .
- * . ---- .
- * .........................
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/errno.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/platform_device.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/completion.h>
-#include <mach/mbox-db5500.h>
-
-#define MBOX_NAME "mbox"
-
-#define MBOX_FIFO_DATA 0x000
-#define MBOX_FIFO_ADD 0x004
-#define MBOX_FIFO_REMOVE 0x008
-#define MBOX_FIFO_THRES_FREE 0x00C
-#define MBOX_FIFO_THRES_OCCUP 0x010
-#define MBOX_FIFO_STATUS 0x014
-
-#define MBOX_DISABLE_IRQ 0x4
-#define MBOX_ENABLE_IRQ 0x0
-#define MBOX_LATCH 1
-
-/* Global list of all mailboxes */
-static struct list_head mboxs = LIST_HEAD_INIT(mboxs);
-
-static struct mbox *get_mbox_with_id(u8 id)
-{
- u8 i;
- struct list_head *pos = &mboxs;
- for (i = 0; i <= id; i++)
- pos = pos->next;
-
- return (struct mbox *) list_entry(pos, struct mbox, list);
-}
-
-int mbox_send(struct mbox *mbox, u32 mbox_msg, bool block)
-{
- int res = 0;
-
- spin_lock(&mbox->lock);
-
- dev_dbg(&(mbox->pdev->dev),
- "About to buffer 0x%X to mailbox 0x%X."
- " ri = %d, wi = %d\n",
- mbox_msg, (u32)mbox, mbox->read_index,
- mbox->write_index);
-
- /* Check if write buffer is full */
- while (((mbox->write_index + 1) % MBOX_BUF_SIZE) == mbox->read_index) {
- if (!block) {
- dev_dbg(&(mbox->pdev->dev),
- "Buffer full in non-blocking call! "
- "Returning -ENOMEM!\n");
- res = -ENOMEM;
- goto exit;
- }
- spin_unlock(&mbox->lock);
- dev_dbg(&(mbox->pdev->dev),
- "Buffer full in blocking call! Sleeping...\n");
- mbox->client_blocked = 1;
- wait_for_completion(&mbox->buffer_available);
- dev_dbg(&(mbox->pdev->dev),
- "Blocking send was woken up! Trying again...\n");
- spin_lock(&mbox->lock);
- }
-
- mbox->buffer[mbox->write_index] = mbox_msg;
- mbox->write_index = (mbox->write_index + 1) % MBOX_BUF_SIZE;
-
- /*
- * Indicate that we want an IRQ as soon as there is a slot
- * in the FIFO
- */
- writel(MBOX_ENABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
-
-exit:
- spin_unlock(&mbox->lock);
- return res;
-}
-EXPORT_SYMBOL(mbox_send);
-
-#if defined(CONFIG_DEBUG_FS)
-/*
- * Expected input: <value> <nbr sends>
- * Example: "echo 0xdeadbeef 4 > mbox-node" sends 0xdeadbeef 4 times
- */
-static ssize_t mbox_write_fifo(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t count)
-{
- unsigned long mbox_mess;
- unsigned long nbr_sends;
- unsigned long i;
- char int_buf[16];
- char *token;
- char *val;
-
- struct mbox *mbox = (struct mbox *) dev->platform_data;
-
- strncpy((char *) &int_buf, buf, sizeof(int_buf));
- token = (char *) &int_buf;
-
- /* Parse message */
- val = strsep(&token, " ");
- if ((val == NULL) || (strict_strtoul(val, 16, &mbox_mess) != 0))
- mbox_mess = 0xDEADBEEF;
-
- val = strsep(&token, " ");
- if ((val == NULL) || (strict_strtoul(val, 10, &nbr_sends) != 0))
- nbr_sends = 1;
-
- dev_dbg(dev, "Will write 0x%lX %ld times using data struct at 0x%X\n",
- mbox_mess, nbr_sends, (u32) mbox);
-
- for (i = 0; i < nbr_sends; i++)
- mbox_send(mbox, mbox_mess, true);
-
- return count;
-}
-
-static ssize_t mbox_read_fifo(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int mbox_value;
- struct mbox *mbox = (struct mbox *) dev->platform_data;
-
- if ((readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7) <= 0)
- return sprintf(buf, "Mailbox is empty\n");
-
- mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA);
- writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE));
-
- return sprintf(buf, "0x%X\n", mbox_value);
-}
-
-static DEVICE_ATTR(fifo, S_IWUSR | S_IRUGO, mbox_read_fifo, mbox_write_fifo);
-
-static int mbox_show(struct seq_file *s, void *data)
-{
- struct list_head *pos;
- u8 mbox_index = 0;
-
- list_for_each(pos, &mboxs) {
- struct mbox *m =
- (struct mbox *) list_entry(pos, struct mbox, list);
- if (m == NULL) {
- seq_printf(s,
- "Unable to retrieve mailbox %d\n",
- mbox_index);
- continue;
- }
-
- spin_lock(&m->lock);
- if ((m->virtbase_peer == NULL) || (m->virtbase_local == NULL)) {
- seq_printf(s, "MAILBOX %d not setup or corrupt\n",
- mbox_index);
- spin_unlock(&m->lock);
- continue;
- }
-
- seq_printf(s,
- "===========================\n"
- " MAILBOX %d\n"
- " PEER MAILBOX DUMP\n"
- "---------------------------\n"
- "FIFO: 0x%X (%d)\n"
- "Free Threshold: 0x%.2X (%d)\n"
- "Occupied Threshold: 0x%.2X (%d)\n"
- "Status: 0x%.2X (%d)\n"
- " Free spaces (ot): %d (%d)\n"
- " Occup spaces (ot): %d (%d)\n"
- "===========================\n"
- " LOCAL MAILBOX DUMP\n"
- "---------------------------\n"
- "FIFO: 0x%.X (%d)\n"
- "Free Threshold: 0x%.2X (%d)\n"
- "Occupied Threshold: 0x%.2X (%d)\n"
- "Status: 0x%.2X (%d)\n"
- " Free spaces (ot): %d (%d)\n"
- " Occup spaces (ot): %d (%d)\n"
- "===========================\n"
- "write_index: %d\n"
- "read_index : %d\n"
- "===========================\n"
- "\n",
- mbox_index,
- readl(m->virtbase_peer + MBOX_FIFO_DATA),
- readl(m->virtbase_peer + MBOX_FIFO_DATA),
- readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE),
- readl(m->virtbase_peer + MBOX_FIFO_THRES_FREE),
- readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP),
- readl(m->virtbase_peer + MBOX_FIFO_THRES_OCCUP),
- readl(m->virtbase_peer + MBOX_FIFO_STATUS),
- readl(m->virtbase_peer + MBOX_FIFO_STATUS),
- (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 4) & 0x7,
- (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 7) & 0x1,
- (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 0) & 0x7,
- (readl(m->virtbase_peer + MBOX_FIFO_STATUS) >> 3) & 0x1,
- readl(m->virtbase_local + MBOX_FIFO_DATA),
- readl(m->virtbase_local + MBOX_FIFO_DATA),
- readl(m->virtbase_local + MBOX_FIFO_THRES_FREE),
- readl(m->virtbase_local + MBOX_FIFO_THRES_FREE),
- readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP),
- readl(m->virtbase_local + MBOX_FIFO_THRES_OCCUP),
- readl(m->virtbase_local + MBOX_FIFO_STATUS),
- readl(m->virtbase_local + MBOX_FIFO_STATUS),
- (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 4) & 0x7,
- (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 7) & 0x1,
- (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 0) & 0x7,
- (readl(m->virtbase_local + MBOX_FIFO_STATUS) >> 3) & 0x1,
- m->write_index, m->read_index);
- mbox_index++;
- spin_unlock(&m->lock);
- }
-
- return 0;
-}
-
-static int mbox_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mbox_show, NULL);
-}
-
-static const struct file_operations mbox_operations = {
- .owner = THIS_MODULE,
- .open = mbox_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-#endif
-
-static irqreturn_t mbox_irq(int irq, void *arg)
-{
- u32 mbox_value;
- int nbr_occup;
- int nbr_free;
- struct mbox *mbox = (struct mbox *) arg;
-
- spin_lock(&mbox->lock);
-
- dev_dbg(&(mbox->pdev->dev),
- "mbox IRQ [%d] received. ri = %d, wi = %d\n",
- irq, mbox->read_index, mbox->write_index);
-
- /*
- * Check if we have any outgoing messages, and if there is space for
- * them in the FIFO.
- */
- if (mbox->read_index != mbox->write_index) {
- /*
- * Check by reading FREE for LOCAL since that indicates
- * OCCUP for PEER
- */
- nbr_free = (readl(mbox->virtbase_local + MBOX_FIFO_STATUS)
- >> 4) & 0x7;
- dev_dbg(&(mbox->pdev->dev),
- "Status indicates %d empty spaces in the FIFO!\n",
- nbr_free);
-
- while ((nbr_free > 0) &&
- (mbox->read_index != mbox->write_index)) {
- /* Write the message and latch it into the FIFO */
- writel(mbox->buffer[mbox->read_index],
- (mbox->virtbase_peer + MBOX_FIFO_DATA));
- writel(MBOX_LATCH,
- (mbox->virtbase_peer + MBOX_FIFO_ADD));
- dev_dbg(&(mbox->pdev->dev),
- "Wrote message 0x%X to addr 0x%X\n",
- mbox->buffer[mbox->read_index],
- (u32) (mbox->virtbase_peer + MBOX_FIFO_DATA));
-
- nbr_free--;
- mbox->read_index =
- (mbox->read_index + 1) % MBOX_BUF_SIZE;
- }
-
- /*
- * Check if we still want IRQ:s when there is free
- * space to send
- */
- if (mbox->read_index != mbox->write_index) {
- dev_dbg(&(mbox->pdev->dev),
- "Still have messages to send, but FIFO full. "
- "Request IRQ again!\n");
- writel(MBOX_ENABLE_IRQ,
- mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
- } else {
- dev_dbg(&(mbox->pdev->dev),
- "No more messages to send. "
- "Do not request IRQ again!\n");
- writel(MBOX_DISABLE_IRQ,
- mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
- }
-
- /*
- * Check if we can signal any blocked clients that it is OK to
- * start buffering again
- */
- if (mbox->client_blocked &&
- (((mbox->write_index + 1) % MBOX_BUF_SIZE)
- != mbox->read_index)) {
- dev_dbg(&(mbox->pdev->dev),
- "Waking up blocked client\n");
- complete(&mbox->buffer_available);
- mbox->client_blocked = 0;
- }
- }
-
- /* Check if we have any incoming messages */
- nbr_occup = readl(mbox->virtbase_local + MBOX_FIFO_STATUS) & 0x7;
- if (nbr_occup == 0)
- goto exit;
-
- if (mbox->cb == NULL) {
- dev_dbg(&(mbox->pdev->dev), "No receive callback registered, "
- "leaving %d incoming messages in fifo!\n", nbr_occup);
- goto exit;
- }
-
- /* Read and acknowledge the message */
- mbox_value = readl(mbox->virtbase_local + MBOX_FIFO_DATA);
- writel(MBOX_LATCH, (mbox->virtbase_local + MBOX_FIFO_REMOVE));
-
- /* Notify consumer of new mailbox message */
- dev_dbg(&(mbox->pdev->dev), "Calling callback for message 0x%X!\n",
- mbox_value);
- mbox->cb(mbox_value, mbox->client_data);
-
-exit:
- dev_dbg(&(mbox->pdev->dev), "Exit mbox IRQ. ri = %d, wi = %d\n",
- mbox->read_index, mbox->write_index);
- spin_unlock(&mbox->lock);
-
- return IRQ_HANDLED;
-}
-
-/* Setup is executed once for each mbox pair */
-struct mbox *mbox_setup(u8 mbox_id, mbox_recv_cb_t *mbox_cb, void *priv)
-{
- struct resource *resource;
- int irq;
- int res;
- struct mbox *mbox;
-
- mbox = get_mbox_with_id(mbox_id);
- if (mbox == NULL) {
- dev_err(&(mbox->pdev->dev), "Incorrect mailbox id: %d!\n",
- mbox_id);
- goto exit;
- }
-
- /*
- * Check if mailbox has been allocated to someone else,
- * otherwise allocate it
- */
- if (mbox->allocated) {
- dev_err(&(mbox->pdev->dev), "Mailbox number %d is busy!\n",
- mbox_id);
- mbox = NULL;
- goto exit;
- }
- mbox->allocated = true;
-
- dev_dbg(&(mbox->pdev->dev), "Initiating mailbox number %d: 0x%X...\n",
- mbox_id, (u32)mbox);
-
- mbox->client_data = priv;
- mbox->cb = mbox_cb;
-
- /* Get addr for peer mailbox and ioremap it */
- resource = platform_get_resource_byname(mbox->pdev,
- IORESOURCE_MEM,
- "mbox_peer");
- if (resource == NULL) {
- dev_err(&(mbox->pdev->dev),
- "Unable to retrieve mbox peer resource\n");
- mbox = NULL;
- goto exit;
- }
- dev_dbg(&(mbox->pdev->dev),
- "Resource name: %s start: 0x%X, end: 0x%X\n",
- resource->name, resource->start, resource->end);
- mbox->virtbase_peer = ioremap(resource->start, resource_size(resource));
- if (!mbox->virtbase_peer) {
- dev_err(&(mbox->pdev->dev), "Unable to ioremap peer mbox\n");
- mbox = NULL;
- goto exit;
- }
- dev_dbg(&(mbox->pdev->dev),
- "ioremapped peer physical: (0x%X-0x%X) to virtual: 0x%X\n",
- resource->start, resource->end, (u32) mbox->virtbase_peer);
-
- /* Get addr for local mailbox and ioremap it */
- resource = platform_get_resource_byname(mbox->pdev,
- IORESOURCE_MEM,
- "mbox_local");
- if (resource == NULL) {
- dev_err(&(mbox->pdev->dev),
- "Unable to retrieve mbox local resource\n");
- mbox = NULL;
- goto exit;
- }
- dev_dbg(&(mbox->pdev->dev),
- "Resource name: %s start: 0x%X, end: 0x%X\n",
- resource->name, resource->start, resource->end);
- mbox->virtbase_local = ioremap(resource->start, resource_size(resource));
- if (!mbox->virtbase_local) {
- dev_err(&(mbox->pdev->dev), "Unable to ioremap local mbox\n");
- mbox = NULL;
- goto exit;
- }
- dev_dbg(&(mbox->pdev->dev),
- "ioremapped local physical: (0x%X-0x%X) to virtual: 0x%X\n",
- resource->start, resource->end, (u32) mbox->virtbase_peer);
-
- init_completion(&mbox->buffer_available);
- mbox->client_blocked = 0;
-
- /* Get IRQ for mailbox and allocate it */
- irq = platform_get_irq_byname(mbox->pdev, "mbox_irq");
- if (irq < 0) {
- dev_err(&(mbox->pdev->dev),
- "Unable to retrieve mbox irq resource\n");
- mbox = NULL;
- goto exit;
- }
-
- dev_dbg(&(mbox->pdev->dev), "Allocating irq %d...\n", irq);
- res = request_irq(irq, mbox_irq, 0, mbox->name, (void *) mbox);
- if (res < 0) {
- dev_err(&(mbox->pdev->dev),
- "Unable to allocate mbox irq %d\n", irq);
- mbox = NULL;
- goto exit;
- }
-
- /* Set up mailbox to not launch IRQ on free space in mailbox */
- writel(MBOX_DISABLE_IRQ, mbox->virtbase_peer + MBOX_FIFO_THRES_FREE);
-
- /*
- * Set up mailbox to launch IRQ on new message if we have
- * a callback set. If not, do not raise IRQ, but keep message
- * in FIFO for manual retrieval
- */
- if (mbox_cb != NULL)
- writel(MBOX_ENABLE_IRQ,
- mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP);
- else
- writel(MBOX_DISABLE_IRQ,
- mbox->virtbase_local + MBOX_FIFO_THRES_OCCUP);
-
-#if defined(CONFIG_DEBUG_FS)
- res = device_create_file(&(mbox->pdev->dev), &dev_attr_fifo);
- if (res != 0)
- dev_warn(&(mbox->pdev->dev),
- "Unable to create mbox sysfs entry");
-
- (void) debugfs_create_file("mbox", S_IFREG | S_IRUGO, NULL,
- NULL, &mbox_operations);
-#endif
-
- dev_info(&(mbox->pdev->dev),
- "Mailbox driver with index %d initiated!\n", mbox_id);
-
-exit:
- return mbox;
-}
-EXPORT_SYMBOL(mbox_setup);
-
-
-int __init mbox_probe(struct platform_device *pdev)
-{
- struct mbox local_mbox;
- struct mbox *mbox;
- int res = 0;
- dev_dbg(&(pdev->dev), "Probing mailbox (pdev = 0x%X)...\n", (u32) pdev);
-
- memset(&local_mbox, 0x0, sizeof(struct mbox));
-
- /* Associate our mbox data with the platform device */
- res = platform_device_add_data(pdev,
- (void *) &local_mbox,
- sizeof(struct mbox));
- if (res != 0) {
- dev_err(&(pdev->dev),
- "Unable to allocate driver platform data!\n");
- goto exit;
- }
-
- mbox = (struct mbox *) pdev->dev.platform_data;
- mbox->pdev = pdev;
- mbox->write_index = 0;
- mbox->read_index = 0;
-
- INIT_LIST_HEAD(&(mbox->list));
- list_add_tail(&(mbox->list), &mboxs);
-
- sprintf(mbox->name, "%s", MBOX_NAME);
- spin_lock_init(&mbox->lock);
-
- dev_info(&(pdev->dev), "Mailbox driver loaded\n");
-
-exit:
- return res;
-}
-
-static struct platform_driver mbox_driver = {
- .driver = {
- .name = MBOX_NAME,
- .owner = THIS_MODULE,
- },
-};
-
-static int __init mbox_init(void)
-{
- return platform_driver_probe(&mbox_driver, mbox_probe);
-}
-
-module_init(mbox_init);
-
-void __exit mbox_exit(void)
-{
- platform_driver_unregister(&mbox_driver);
-}
-
-module_exit(mbox_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("MBOX driver");
diff --git a/arch/arm/mach-ux500/pins-db8500.h b/arch/arm/mach-ux500/pins-db8500.h
index 8b1d1a7a679..062c7acf457 100644
--- a/arch/arm/mach-ux500/pins-db8500.h
+++ b/arch/arm/mach-ux500/pins-db8500.h
@@ -35,40 +35,40 @@
#define GPIO4_GPIO PIN_CFG(4, GPIO)
#define GPIO4_U1_RXD PIN_CFG(4, ALT_A)
-#define GPIO4_I2C4_SCL PIN_CFG_INPUT(4, ALT_B, PULLUP)
+#define GPIO4_I2C4_SCL PIN_CFG(4, ALT_B)
#define GPIO4_IP_TRSTn PIN_CFG(4, ALT_C)
#define GPIO5_GPIO PIN_CFG(5, GPIO)
#define GPIO5_U1_TXD PIN_CFG(5, ALT_A)
-#define GPIO5_I2C4_SDA PIN_CFG_INPUT(5, ALT_B, PULLUP)
+#define GPIO5_I2C4_SDA PIN_CFG(5, ALT_B)
#define GPIO5_IP_GPIO6 PIN_CFG(5, ALT_C)
#define GPIO6_GPIO PIN_CFG(6, GPIO)
#define GPIO6_U1_CTSn PIN_CFG(6, ALT_A)
-#define GPIO6_I2C1_SCL PIN_CFG_INPUT(6, ALT_B, PULLUP)
+#define GPIO6_I2C1_SCL PIN_CFG(6, ALT_B)
#define GPIO6_IP_GPIO0 PIN_CFG(6, ALT_C)
#define GPIO7_GPIO PIN_CFG(7, GPIO)
#define GPIO7_U1_RTSn PIN_CFG(7, ALT_A)
-#define GPIO7_I2C1_SDA PIN_CFG_INPUT(7, ALT_B, PULLUP)
+#define GPIO7_I2C1_SDA PIN_CFG(7, ALT_B)
#define GPIO7_IP_GPIO1 PIN_CFG(7, ALT_C)
#define GPIO8_GPIO PIN_CFG(8, GPIO)
-#define GPIO8_IPI2C_SDA PIN_CFG_INPUT(8, ALT_A, PULLUP)
-#define GPIO8_I2C2_SDA PIN_CFG_INPUT(8, ALT_B, PULLUP)
+#define GPIO8_IPI2C_SDA PIN_CFG(8, ALT_A)
+#define GPIO8_I2C2_SDA PIN_CFG(8, ALT_B)
#define GPIO9_GPIO PIN_CFG(9, GPIO)
-#define GPIO9_IPI2C_SCL PIN_CFG_INPUT(9, ALT_A, PULLUP)
-#define GPIO9_I2C2_SCL PIN_CFG_INPUT(9, ALT_B, PULLUP)
+#define GPIO9_IPI2C_SCL PIN_CFG(9, ALT_A)
+#define GPIO9_I2C2_SCL PIN_CFG(9, ALT_B)
#define GPIO10_GPIO PIN_CFG(10, GPIO)
-#define GPIO10_IPI2C_SDA PIN_CFG_INPUT(10, ALT_A, PULLUP)
-#define GPIO10_I2C2_SDA PIN_CFG_INPUT(10, ALT_B, PULLUP)
+#define GPIO10_IPI2C_SDA PIN_CFG(10, ALT_A)
+#define GPIO10_I2C2_SDA PIN_CFG(10, ALT_B)
#define GPIO10_IP_GPIO3 PIN_CFG(10, ALT_C)
#define GPIO11_GPIO PIN_CFG(11, GPIO)
-#define GPIO11_IPI2C_SCL PIN_CFG_INPUT(11, ALT_A, PULLUP)
-#define GPIO11_I2C2_SCL PIN_CFG_INPUT(11, ALT_B, PULLUP)
+#define GPIO11_IPI2C_SCL PIN_CFG(11, ALT_A)
+#define GPIO11_I2C2_SCL PIN_CFG(11, ALT_B)
#define GPIO11_IP_GPIO2 PIN_CFG(11, ALT_C)
#define GPIO12_GPIO PIN_CFG(12, GPIO)
@@ -87,12 +87,12 @@
#define GPIO16_GPIO PIN_CFG(16, GPIO)
#define GPIO16_MSP0_RFS PIN_CFG(16, ALT_A)
-#define GPIO16_I2C1_SCL PIN_CFG_INPUT(16, ALT_B, PULLUP)
+#define GPIO16_I2C1_SCL PIN_CFG(16, ALT_B)
#define GPIO16_SLIM0_DAT PIN_CFG(16, ALT_C)
#define GPIO17_GPIO PIN_CFG(17, GPIO)
#define GPIO17_MSP0_RCK PIN_CFG(17, ALT_A)
-#define GPIO17_I2C1_SDA PIN_CFG_INPUT(17, ALT_B, PULLUP)
+#define GPIO17_I2C1_SDA PIN_CFG(17, ALT_B)
#define GPIO17_SLIM0_CLK PIN_CFG(17, ALT_C)
#define GPIO18_GPIO PIN_CFG(18, GPIO)
@@ -434,10 +434,10 @@
#define GPIO146_SSP0_TXD PIN_CFG(146, ALT_A)
#define GPIO147_GPIO PIN_CFG(147, GPIO)
-#define GPIO147_I2C0_SCL PIN_CFG_INPUT(147, ALT_A, PULLUP)
+#define GPIO147_I2C0_SCL PIN_CFG(147, ALT_A)
#define GPIO148_GPIO PIN_CFG(148, GPIO)
-#define GPIO148_I2C0_SDA PIN_CFG_INPUT(148, ALT_A, PULLUP)
+#define GPIO148_I2C0_SDA PIN_CFG(148, ALT_A)
#define GPIO149_GPIO PIN_CFG(149, GPIO)
#define GPIO149_IP_GPIO0 PIN_CFG(149, ALT_A)
@@ -459,82 +459,82 @@
#define GPIO152_KP_O9 PIN_CFG(152, ALT_C)
#define GPIO153_GPIO PIN_CFG(153, GPIO)
-#define GPIO153_KP_I7 PIN_CFG_INPUT(153, ALT_A, PULLDOWN)
+#define GPIO153_KP_I7 PIN_CFG(153, ALT_A)
#define GPIO153_LCD_D24 PIN_CFG(153, ALT_B)
#define GPIO153_U2_RXD PIN_CFG(153, ALT_C)
#define GPIO154_GPIO PIN_CFG(154, GPIO)
-#define GPIO154_KP_I6 PIN_CFG_INPUT(154, ALT_A, PULLDOWN)
+#define GPIO154_KP_I6 PIN_CFG(154, ALT_A)
#define GPIO154_LCD_D25 PIN_CFG(154, ALT_B)
#define GPIO154_U2_TXD PIN_CFG(154, ALT_C)
#define GPIO155_GPIO PIN_CFG(155, GPIO)
-#define GPIO155_KP_I5 PIN_CFG_INPUT(155, ALT_A, PULLDOWN)
+#define GPIO155_KP_I5 PIN_CFG(155, ALT_A)
#define GPIO155_LCD_D26 PIN_CFG(155, ALT_B)
#define GPIO155_STMAPE_CLK PIN_CFG(155, ALT_C)
#define GPIO156_GPIO PIN_CFG(156, GPIO)
-#define GPIO156_KP_I4 PIN_CFG_INPUT(156, ALT_A, PULLDOWN)
+#define GPIO156_KP_I4 PIN_CFG(156, ALT_A)
#define GPIO156_LCD_D27 PIN_CFG(156, ALT_B)
#define GPIO156_STMAPE_DAT3 PIN_CFG(156, ALT_C)
#define GPIO157_GPIO PIN_CFG(157, GPIO)
-#define GPIO157_KP_O7 PIN_CFG_INPUT(157, ALT_A, PULLUP)
+#define GPIO157_KP_O7 PIN_CFG(157, ALT_A)
#define GPIO157_LCD_D28 PIN_CFG(157, ALT_B)
#define GPIO157_STMAPE_DAT2 PIN_CFG(157, ALT_C)
#define GPIO158_GPIO PIN_CFG(158, GPIO)
-#define GPIO158_KP_O6 PIN_CFG_INPUT(158, ALT_A, PULLUP)
+#define GPIO158_KP_O6 PIN_CFG(158, ALT_A)
#define GPIO158_LCD_D29 PIN_CFG(158, ALT_B)
#define GPIO158_STMAPE_DAT1 PIN_CFG(158, ALT_C)
#define GPIO159_GPIO PIN_CFG(159, GPIO)
-#define GPIO159_KP_O5 PIN_CFG_INPUT(159, ALT_A, PULLUP)
+#define GPIO159_KP_O5 PIN_CFG(159, ALT_A)
#define GPIO159_LCD_D30 PIN_CFG(159, ALT_B)
#define GPIO159_STMAPE_DAT0 PIN_CFG(159, ALT_C)
#define GPIO160_GPIO PIN_CFG(160, GPIO)
-#define GPIO160_KP_O4 PIN_CFG_INPUT(160, ALT_A, PULLUP)
+#define GPIO160_KP_O4 PIN_CFG(160, ALT_A)
#define GPIO160_LCD_D31 PIN_CFG(160, ALT_B)
#define GPIO160_NONE PIN_CFG(160, ALT_C)
#define GPIO161_GPIO PIN_CFG(161, GPIO)
-#define GPIO161_KP_I3 PIN_CFG_INPUT(161, ALT_A, PULLDOWN)
+#define GPIO161_KP_I3 PIN_CFG(161, ALT_A)
#define GPIO161_LCD_D32 PIN_CFG(161, ALT_B)
#define GPIO161_UARTMOD_RXD PIN_CFG(161, ALT_C)
#define GPIO162_GPIO PIN_CFG(162, GPIO)
-#define GPIO162_KP_I2 PIN_CFG_INPUT(162, ALT_A, PULLDOWN)
+#define GPIO162_KP_I2 PIN_CFG(162, ALT_A)
#define GPIO162_LCD_D33 PIN_CFG(162, ALT_B)
#define GPIO162_UARTMOD_TXD PIN_CFG(162, ALT_C)
#define GPIO163_GPIO PIN_CFG(163, GPIO)
-#define GPIO163_KP_I1 PIN_CFG_INPUT(163, ALT_A, PULLDOWN)
+#define GPIO163_KP_I1 PIN_CFG(163, ALT_A)
#define GPIO163_LCD_D34 PIN_CFG(163, ALT_B)
#define GPIO163_STMMOD_CLK PIN_CFG(163, ALT_C)
#define GPIO164_GPIO PIN_CFG(164, GPIO)
-#define GPIO164_KP_I0 PIN_CFG_INPUT(164, ALT_A, PULLUP)
+#define GPIO164_KP_I0 PIN_CFG(164, ALT_A)
#define GPIO164_LCD_D35 PIN_CFG(164, ALT_B)
#define GPIO164_STMMOD_DAT3 PIN_CFG(164, ALT_C)
#define GPIO165_GPIO PIN_CFG(165, GPIO)
-#define GPIO165_KP_O3 PIN_CFG_INPUT(165, ALT_A, PULLUP)
+#define GPIO165_KP_O3 PIN_CFG(165, ALT_A)
#define GPIO165_LCD_D36 PIN_CFG(165, ALT_B)
#define GPIO165_STMMOD_DAT2 PIN_CFG(165, ALT_C)
#define GPIO166_GPIO PIN_CFG(166, GPIO)
-#define GPIO166_KP_O2 PIN_CFG_INPUT(166, ALT_A, PULLUP)
+#define GPIO166_KP_O2 PIN_CFG(166, ALT_A)
#define GPIO166_LCD_D37 PIN_CFG(166, ALT_B)
#define GPIO166_STMMOD_DAT1 PIN_CFG(166, ALT_C)
#define GPIO167_GPIO PIN_CFG(167, GPIO)
-#define GPIO167_KP_O1 PIN_CFG_INPUT(167, ALT_A, PULLUP)
+#define GPIO167_KP_O1 PIN_CFG(167, ALT_A)
#define GPIO167_LCD_D38 PIN_CFG(167, ALT_B)
#define GPIO167_STMMOD_DAT0 PIN_CFG(167, ALT_C)
#define GPIO168_GPIO PIN_CFG(168, GPIO)
-#define GPIO168_KP_O0 PIN_CFG_INPUT(168, ALT_A, PULLUP)
+#define GPIO168_KP_O0 PIN_CFG(168, ALT_A)
#define GPIO168_LCD_D39 PIN_CFG(168, ALT_B)
#define GPIO168_NONE PIN_CFG(168, ALT_C)
@@ -637,7 +637,7 @@
#define GPIO216_GPIO PIN_CFG(216, GPIO)
#define GPIO216_MC1_DAT2DIR PIN_CFG(216, ALT_A)
#define GPIO216_MC3_CMDDIR PIN_CFG(216, ALT_B)
-#define GPIO216_I2C3_SDA PIN_CFG_INPUT(216, ALT_C, PULLUP)
+#define GPIO216_I2C3_SDA PIN_CFG(216, ALT_C)
#define GPIO216_SPI2_FRM PIN_CFG(216, ALT_C)
#define GPIO217_GPIO PIN_CFG(217, GPIO)
@@ -649,7 +649,7 @@
#define GPIO218_GPIO PIN_CFG(218, GPIO)
#define GPIO218_MC1_DAT31DIR PIN_CFG(218, ALT_A)
#define GPIO218_MC3_DAT0DIR PIN_CFG(218, ALT_B)
-#define GPIO218_I2C3_SCL PIN_CFG_INPUT(218, ALT_C, PULLUP)
+#define GPIO218_I2C3_SCL PIN_CFG(218, ALT_C)
#define GPIO218_SPI2_RXD PIN_CFG(218, ALT_C)
#define GPIO219_GPIO PIN_CFG(219, GPIO)
@@ -698,12 +698,12 @@
#define GPIO229_GPIO PIN_CFG(229, GPIO)
#define GPIO229_CLKOUT1 PIN_CFG(229, ALT_A)
#define GPIO229_PWL PIN_CFG(229, ALT_B)
-#define GPIO229_I2C3_SDA PIN_CFG_INPUT(229, ALT_C, PULLUP)
+#define GPIO229_I2C3_SDA PIN_CFG(229, ALT_C)
#define GPIO230_GPIO PIN_CFG(230, GPIO)
#define GPIO230_CLKOUT2 PIN_CFG(230, ALT_A)
#define GPIO230_PWL PIN_CFG(230, ALT_B)
-#define GPIO230_I2C3_SCL PIN_CFG_INPUT(230, ALT_C, PULLUP)
+#define GPIO230_I2C3_SCL PIN_CFG(230, ALT_C)
#define GPIO256_GPIO PIN_CFG(256, GPIO)
#define GPIO256_USB_NXT PIN_CFG(256, ALT_A)
diff --git a/arch/arm/mach-ux500/pins.c b/arch/arm/mach-ux500/pins.c
new file mode 100644
index 00000000000..ed2ed7333ff
--- /dev/null
+++ b/arch/arm/mach-ux500/pins.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <plat/pincfg.h>
+#include <linux/gpio.h>
+
+#include "pins.h"
+
+static LIST_HEAD(pin_lookups);
+static DEFINE_MUTEX(pin_lookups_mutex);
+static DEFINE_SPINLOCK(pins_lock);
+
+void __init ux500_pins_add(struct ux500_pin_lookup *pl, size_t num)
+{
+ mutex_lock(&pin_lookups_mutex);
+
+ while (num--) {
+ list_add_tail(&pl->node, &pin_lookups);
+ pl++;
+ }
+
+ mutex_unlock(&pin_lookups_mutex);
+}
+
+struct ux500_pins *ux500_pins_get(const char *name)
+{
+ struct ux500_pins *pins = NULL;
+ struct ux500_pin_lookup *pl;
+
+ mutex_lock(&pin_lookups_mutex);
+
+ list_for_each_entry(pl, &pin_lookups, node) {
+ if (!strcmp(pl->name, name)) {
+ pins = pl->pins;
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&pin_lookups_mutex);
+ return pins;
+}
+
+int ux500_pins_enable(struct ux500_pins *pins)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&pins_lock, flags);
+
+ if (pins->usage++ == 0)
+ ret = nmk_config_pins(pins->cfg, pins->num);
+
+ spin_unlock_irqrestore(&pins_lock, flags);
+ return ret;
+}
+
+int ux500_pins_disable(struct ux500_pins *pins)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&pins_lock, flags);
+
+ if (WARN_ON(pins->usage == 0))
+ goto out;
+
+ if (--pins->usage == 0)
+ ret = nmk_config_pins_sleep(pins->cfg, pins->num);
+
+out:
+ spin_unlock_irqrestore(&pins_lock, flags);
+ return ret;
+}
+
+void ux500_pins_put(struct ux500_pins *pins)
+{
+ WARN_ON(!pins);
+}
+
+void __init ux500_offchip_gpio_init(struct ux500_pins *pins)
+{
+ int err;
+ int i;
+ int gpio;
+ int output;
+ int value;
+ pin_cfg_t cfg;
+
+ for (i = 0; i < pins->num; i++) {
+ cfg = pins->cfg[i];
+ gpio = PIN_NUM(cfg);
+ output = PIN_DIR(cfg);
+ value = PIN_VAL(cfg);
+
+ err = gpio_request(gpio, "offchip_gpio_init");
+ if (err < 0) {
+ pr_err("pins: gpio_request for gpio=%d failed with"
+ "err: %d\n", gpio, err);
+ /* Pin already requested. Try to configure rest. */
+ continue;
+ }
+
+ if (!output) {
+ err = gpio_direction_input(gpio);
+ if (err < 0)
+ pr_err("pins: gpio_direction_input for gpio=%d"
+ "failed with err: %d\n", gpio, err);
+ } else {
+ err = gpio_direction_output(gpio, value);
+ if (err < 0)
+ pr_err("pins: gpio_direction_output for gpio="
+ "%d failed with err: %d\n", gpio, err);
+ }
+ gpio_free(gpio);
+ }
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/gpio/nomadik.h>
+
+#include <mach/gpio.h>
+
+static void show_pin(struct seq_file *s, pin_cfg_t pin)
+{
+ static char *afnames[] = {
+ [NMK_GPIO_ALT_GPIO] = "GPIO",
+ [NMK_GPIO_ALT_A] = "A",
+ [NMK_GPIO_ALT_B] = "B",
+ [NMK_GPIO_ALT_C] = "C"
+ };
+ static char *pullnames[] = {
+ [NMK_GPIO_PULL_NONE] = "none",
+ [NMK_GPIO_PULL_UP] = "up",
+ [NMK_GPIO_PULL_DOWN] = "down",
+ [3] /* illegal */ = "??"
+ };
+
+ int pin_num = PIN_NUM(pin);
+ int pull = PIN_PULL(pin);
+ int af = PIN_ALT(pin);
+ int slpm = PIN_SLPM(pin);
+ int output = PIN_DIR(pin);
+ int val = PIN_VAL(pin);
+ int slpm_pull = PIN_SLPM_PULL(pin);
+ int slpm_dir = PIN_SLPM_DIR(pin);
+ int slpm_val = PIN_SLPM_VAL(pin);
+
+ seq_printf(s,
+ " pin %d [%#lx]: af %s, pull %s (%s%s) - slpm: %s%s%s%s\n",
+ pin_num, pin, afnames[af],
+ pullnames[pull],
+ output ? "output " : "input",
+ output ? (val ? "high" : "low") : "",
+ slpm ? "no-change/no-wakeup " : "input/wakeup ",
+ slpm_dir ? (slpm_dir == 1 ? "input " : "output ") : "",
+ slpm_dir == 1 ? (slpm_pull == 0 ? "pull: none " :
+ (slpm_pull == NMK_GPIO_PULL_UP ?
+ "pull: up " : "pull: down ") ): "",
+ slpm_dir == 2 ? (slpm_val == 1 ? "low " : "high ") : "");
+}
+
+static int pins_dbg_show(struct seq_file *s, void *iter)
+{
+ struct ux500_pin_lookup *pl;
+ int i;
+ bool *pins;
+ int prev = -2;
+ int first = 0;
+
+ pins = kzalloc(sizeof(bool) * NOMADIK_NR_GPIO, GFP_KERNEL);
+
+ mutex_lock(&pin_lookups_mutex);
+
+ list_for_each_entry(pl, &pin_lookups, node) {
+ seq_printf(s, "\n%s (%d) usage: %d\n",
+ pl->name, pl->pins->num, pl->pins->usage);
+ for (i = 0; i < pl->pins->num; i++) {
+ show_pin(s, pl->pins->cfg[i]);
+ pins[PIN_NUM(pl->pins->cfg[i])] = true;
+ }
+ }
+ mutex_unlock(&pin_lookups_mutex);
+
+ seq_printf(s, "\nSummary allocated pins:\n");
+ for (i = 0; i < NOMADIK_NR_GPIO; i++) {
+ if (prev == i - 1) {
+ if (pins[i])
+ prev = i;
+ else
+ if (prev > 0) {
+ if (first != prev)
+ seq_printf(s, "-%d, ", prev);
+ else
+ seq_printf(s, ", ");
+ }
+ continue;
+ }
+ if (pins[i]) {
+ seq_printf(s, "%d", i);
+ prev = i;
+ first = i;
+ }
+ }
+ if (prev == i - 1 && first != prev)
+ seq_printf(s, "-%d", prev);
+
+ seq_printf(s, "\n");
+
+ kfree(pins);
+
+ return 0;
+}
+
+static int pins_dbg_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, pins_dbg_show, inode->i_private);
+}
+
+static const struct file_operations pins_fops = {
+ .open = pins_dbg_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int __init pins_dbg_init(void)
+{
+ (void) debugfs_create_file("pins", S_IRUGO,
+ NULL,
+ NULL,
+ &pins_fops);
+ return 0;
+}
+late_initcall(pins_dbg_init);
+#endif
diff --git a/arch/arm/mach-ux500/pins.h b/arch/arm/mach-ux500/pins.h
new file mode 100644
index 00000000000..0d36af2e7d9
--- /dev/null
+++ b/arch/arm/mach-ux500/pins.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL), version 2
+ */
+
+#ifndef __MACH_UX500_PINS_H
+#define __MACH_UX500_PINS_H
+
+#include <linux/list.h>
+#include <plat/pincfg.h>
+
+#define PIN_LOOKUP(_name, _pins) \
+{ \
+ .name = _name, \
+ .pins = _pins, \
+}
+
+#define UX500_PINS(name, pins...) \
+struct ux500_pins name = { \
+ .cfg = (pin_cfg_t[]) {pins}, \
+ .num = ARRAY_SIZE(((pin_cfg_t[]) {pins})), \
+}
+
+struct ux500_pins {
+ int usage;
+ int num;
+ pin_cfg_t *cfg;
+};
+
+struct ux500_pin_lookup {
+ struct list_head node;
+ const char *name;
+ struct ux500_pins *pins;
+};
+
+void __init ux500_pins_add(struct ux500_pin_lookup *pl, size_t num);
+void __init ux500_offchip_gpio_init(struct ux500_pins *pins);
+struct ux500_pins *ux500_pins_get(const char *name);
+int ux500_pins_enable(struct ux500_pins *pins);
+int ux500_pins_disable(struct ux500_pins *pins);
+void ux500_pins_put(struct ux500_pins *pins);
+int pins_for_u9500(void);
+
+#endif
diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c
index eff5842f623..dc661de8bca 100644
--- a/arch/arm/mach-ux500/platsmp.c
+++ b/arch/arm/mach-ux500/platsmp.c
@@ -50,7 +50,7 @@ static void __iomem *scu_base_addr(void)
{
if (cpu_is_u5500())
return __io_address(U5500_SCU_BASE);
- else if (cpu_is_u8500())
+ else if (cpu_is_u8500() || cpu_is_u9540())
return __io_address(U8500_SCU_BASE);
else
ux500_unknown_soc();
@@ -122,7 +122,7 @@ static void __init wakeup_secondary(void)
if (cpu_is_u5500())
backupram = __io_address(U5500_BACKUPRAM0_BASE);
- else if (cpu_is_u8500())
+ else if (cpu_is_u8500() || cpu_is_u9540())
backupram = __io_address(U8500_BACKUPRAM0_BASE);
else
ux500_unknown_soc();
diff --git a/arch/arm/mach-ux500/pm/Kconfig b/arch/arm/mach-ux500/pm/Kconfig
new file mode 100644
index 00000000000..12004ba9858
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/Kconfig
@@ -0,0 +1,70 @@
+config DBX500_PRCMU_QOS_POWER
+ bool "DBX500 PRCMU power QoS support"
+ depends on (MFD_DB5500_PRCMU || MFD_DB8500_PRCMU)
+ default y
+ help
+ Add support for PRCMU power Quality of Service
+
+config UX500_CONTEXT
+ bool "Context save/restore support for UX500"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && PM
+ help
+ This is needed for ApSleep and deeper sleep states.
+
+config UX500_PM_PERFORMANCE
+ bool "Performance supervision"
+ depends on DBX500_PRCMU_QOS_POWER
+ default y
+ help
+ Enable supervision of events which may require a boost
+ of platform performance.
+
+config UX500_CONSOLE_UART_GPIO_PIN
+ int "The pin number of the console UART GPIO pin"
+ default 29
+ depends on UX500_SUSPEND_DBG_WAKE_ON_UART || UX500_CPUIDLE_DEBUG
+ help
+ GPIO pin number of the GPIO pin connected to the console UART RX line.
+
+ Board-specific code can change this.
+
+config UX500_SUSPEND
+ bool "Suspend to mem and standby support"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && PM && SUSPEND
+ select UX500_CONTEXT
+ help
+ Add support for suspend.
+
+config UX500_SUSPEND_STANDBY
+ bool "Suspend Standby goes to ApSleep"
+ depends on UX500_SUSPEND
+ help
+ If yes, echo standby > /sys/power/state puts the system into ApSleep.
+
+config UX500_SUSPEND_MEM
+ bool "Suspend Mem goes to ApDeepSleep"
+ depends on UX500_SUSPEND
+ help
+ If yes, echo mem > /sys/power/state puts the system into ApDeepSleep else
+ it will do the same as echo standby > /sys/power/state.
+
+config UX500_SUSPEND_DBG
+ bool "Suspend debug"
+ depends on UX500_SUSPEND && DEBUG_FS
+ help
+ Add debug support for suspend.
+
+config UX500_SUSPEND_DBG_WAKE_ON_UART
+ bool "Suspend wakes on console UART"
+ depends on UX500_SUSPEND_DBG
+ help
+ Wake up on uart interrupts. Makes it possible for the console to wake up system.
+
+config UX500_USECASE_GOVERNOR
+ bool "UX500 use-case governor"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && \
+ (CPU_FREQ && CPU_IDLE && HOTPLUG_CPU && \
+ EARLYSUSPEND && UX500_L2X0_PREFETCH_CTRL && PM)
+ default y
+ help
+ Adjusts CPU_IDLE, CPU_FREQ, HOTPLUG_CPU and L2 cache parameters
diff --git a/arch/arm/mach-ux500/pm/Makefile b/arch/arm/mach-ux500/pm/Makefile
new file mode 100644
index 00000000000..ef33bdf0d4e
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/Makefile
@@ -0,0 +1,12 @@
+#
+# Power save related files
+#
+obj-y := pm.o runtime.o
+
+obj-$(CONFIG_DBX500_PRCMU_QOS_POWER) += prcmu-qos-power.o
+obj-$(CONFIG_UX500_CONTEXT) += context.o context_arm.o context-db8500.o context-db5500.o context-db9540.o
+obj-$(CONFIG_UX500_CPUIDLE) += timer.o
+obj-$(CONFIG_UX500_SUSPEND) += suspend.o
+obj-$(CONFIG_UX500_SUSPEND_DBG) += suspend_dbg.o
+obj-$(CONFIG_UX500_PM_PERFORMANCE) += performance.o
+obj-$(CONFIG_UX500_USECASE_GOVERNOR) += usecase_gov.o
diff --git a/arch/arm/mach-ux500/pm/context-db5500.c b/arch/arm/mach-ux500/pm/context-db5500.c
new file mode 100644
index 00000000000..9842785c05a
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context-db5500.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>,
+ * Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Sundar Iyer <sundar.iyer@stericsson.com>,
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+/* These registers are DB5500 specific */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x0
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x4
+
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x18
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x1C
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x20
+
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x24
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x28
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x2C
+
+#define NODE_HIBW1_DDR_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR_IN_2_PRIORITY 0x408
+
+#define NODE_HIBW1_DDR_IN_0_LIMIT 0x424
+#define NODE_HIBW1_DDR_IN_1_LIMIT 0x428
+#define NODE_HIBW1_DDR_IN_2_LIMIT 0x42C
+
+#define NODE_HIBW1_DDR_OUT_0_PRIORITY 0x430
+
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+
+#define NODE_HIBW2_DDR_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR_IN_1_PRIORITY 0xC04
+#define NODE_HIBW2_DDR_IN_2_PRIORITY 0xC08
+#define NODE_HIBW2_DDR_IN_3_PRIORITY 0xC0C
+
+#define NODE_HIBW2_DDR_IN_0_LIMIT 0xC30
+#define NODE_HIBW2_DDR_IN_1_LIMIT 0xC34
+#define NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+#define NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+
+#define NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+
+#define NODE_ESRAM0_IN_0_LIMIT 0x1024
+#define NODE_ESRAM0_IN_1_LIMIT 0x1028
+#define NODE_ESRAM0_IN_2_LIMIT 0x102C
+#define NODE_ESRAM0_OUT_0_PRIORITY 0x1030
+
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1424
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1428
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x142C
+#define NODE_ESRAM1_2_OUT_0_PRIORITY 0x1430
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1824
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1828
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x182C
+#define NODE_ESRAM3_4_OUT_0_PRIORITY 0x1830
+
+/*
+ * Save ICN (Interconnect or Interconnect nodes) configuration registers
+ * TODO: This can be optimized, for example if we have
+ * a static ICN configuration.
+ */
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[2];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_ddr_in_prio[3];
+ u32 hibw1_ddr_in_limit[3];
+ u32 hibw1_ddr_out_prio_reg;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr_in_prio[4];
+ u32 hibw2_ddr_in_limit[4];
+ u32 hibw2_ddr_out_prio_reg;
+
+ /* ESRAM node registers */
+ u32 esram_in_prio[3];
+ u32 esram_in_lim[3];
+ u32 esram_out_prio_reg;
+
+ u32 esram12_in_prio[3];
+ u32 esram12_in_arb_lim[3];
+ u32 esram12_out_prio_reg;
+
+ u32 esram34_in_prio[3];
+ u32 esram34_in_arb_lim[3];
+ u32 esram34_out_prio;
+} context_icn;
+
+
+void u5500_context_save_icn(void)
+{
+ void __iomem *base = context_icn.base;
+
+ /* hibw1 */
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(base + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr_in_prio[0] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[1] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[2] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw1_ddr_in_limit[0] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_0_LIMIT);
+ context_icn.hibw1_ddr_in_limit[1] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_1_LIMIT);
+ context_icn.hibw1_ddr_in_limit[2] =
+ readl_relaxed(base + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ context_icn.hibw1_ddr_out_prio_reg =
+ readl_relaxed(base + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ /* hibw2 */
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(base + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr_in_prio[0] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[1] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[2] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[3] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_3_PRIORITY);
+
+ context_icn.hibw2_ddr_in_limit[0] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_0_LIMIT);
+ context_icn.hibw2_ddr_in_limit[1] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_1_LIMIT);
+ context_icn.hibw2_ddr_in_limit[2] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_2_LIMIT);
+ context_icn.hibw2_ddr_in_limit[3] =
+ readl_relaxed(base + NODE_HIBW2_DDR_IN_3_LIMIT);
+
+ context_icn.hibw2_ddr_out_prio_reg =
+ readl_relaxed(base + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ /* ESRAM0 */
+ context_icn.esram_in_prio[0] =
+ readl_relaxed(base + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram_in_prio[1] =
+ readl_relaxed(base + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram_in_prio[2] =
+ readl_relaxed(base + NODE_ESRAM0_IN_2_PRIORITY);
+
+ context_icn.esram_in_lim[0] =
+ readl_relaxed(base + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram_in_lim[1] =
+ readl_relaxed(base + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram_in_lim[2] =
+ readl_relaxed(base + NODE_ESRAM0_IN_2_LIMIT);
+
+ context_icn.esram_out_prio_reg =
+ readl_relaxed(base + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ /* ESRAM1-2 */
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_2_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(base + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+
+ context_icn.esram12_out_prio_reg =
+ readl_relaxed(base + NODE_ESRAM1_2_OUT_0_PRIORITY);
+
+ /* ESRAM3-4 */
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_2_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(base + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+
+ context_icn.esram34_out_prio =
+ readl_relaxed(base + NODE_ESRAM3_4_OUT_0_PRIORITY);
+}
+
+/*
+ * Restore ICN configuration registers
+ */
+void u5500_context_restore_icn(void)
+{
+ void __iomem *base = context_icn.base;
+
+ /* hibw1 */
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ base + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ base + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ base + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ base + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ base + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ base + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ base + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ base + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[0],
+ base + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[1],
+ base + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[2],
+ base + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[0],
+ base + NODE_HIBW1_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[1],
+ base + NODE_HIBW1_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[2],
+ base + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_out_prio_reg,
+ base + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ /* hibw2 */
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ base + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ base + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ base + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ base + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ base + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ base + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ base + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ base + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[0],
+ base + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[1],
+ base + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[2],
+ base + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[3],
+ base + NODE_HIBW2_DDR_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[0],
+ base + NODE_HIBW2_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[1],
+ base + NODE_HIBW2_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[2],
+ base + NODE_HIBW2_DDR_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[3],
+ base + NODE_HIBW2_DDR_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_out_prio_reg,
+ base + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ /* ESRAM0 */
+ writel_relaxed(context_icn.esram_in_prio[0],
+ base + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram_in_prio[1],
+ base + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram_in_prio[2],
+ base + NODE_ESRAM0_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.esram_in_lim[0],
+ base + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram_in_lim[1],
+ base + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram_in_lim[2],
+ base + NODE_ESRAM0_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.esram_out_prio_reg,
+ base + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ /* ESRAM1-2 */
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ base + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ base + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ base + NODE_ESRAM1_2_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ base + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ base + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ base + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+
+ writel_relaxed(context_icn.esram12_out_prio_reg,
+ base + NODE_ESRAM1_2_OUT_0_PRIORITY);
+
+ /* ESRAM3-4 */
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ base + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ base + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ base + NODE_ESRAM3_4_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ base + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ base + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ base + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+
+ writel_relaxed(context_icn.esram34_out_prio,
+ base + NODE_ESRAM3_4_OUT_0_PRIORITY);
+
+}
+
+void u5500_context_init(void)
+{
+ context_icn.base = ioremap(U5500_ICN_BASE, SZ_8K);
+}
diff --git a/arch/arm/mach-ux500/pm/context-db8500.c b/arch/arm/mach-ux500/pm/context-db8500.c
new file mode 100644
index 00000000000..3ba73e51a6d
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context-db8500.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer for ST-Ericsson
+ *
+ */
+
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+/*
+ * ST-Interconnect context
+ */
+
+/* priority, bw limiter register offsets */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x00
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x04
+#define NODE_HIBW1_ESRAM_IN_2_PRIORITY 0x08
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x24
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x28
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x2C
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x30
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x34
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x38
+#define NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT 0x3C
+#define NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT 0x40
+#define NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT 0x44
+#define NODE_HIBW1_DDR_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR_IN_2_PRIORITY 0x408
+#define NODE_HIBW1_DDR_IN_0_LIMIT 0x424
+#define NODE_HIBW1_DDR_IN_1_LIMIT 0x428
+#define NODE_HIBW1_DDR_IN_2_LIMIT 0x42C
+#define NODE_HIBW1_DDR_OUT_0_PRIORITY 0x430
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+#define NODE_HIBW2_DDR_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR_IN_1_PRIORITY 0xC04
+#define NODE_HIBW2_DDR_IN_2_PRIORITY 0xC08
+
+#define NODE_HIBW2_DDR_IN_0_LIMIT 0xC24
+#define NODE_HIBW2_DDR_IN_1_LIMIT 0xC28
+#define NODE_HIBW2_DDR_IN_2_LIMIT 0xC2C
+#define NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC30
+
+/*
+ * Note the following addresses are presented in
+ * db8500 design spec v3.1 and v3.3, table 10.
+ * But their addresses are not the same as in the
+ * description. The addresses in the description
+ * of each registers are correct.
+ * NODE_HIBW2_DDR_IN_3_LIMIT is only present in v1.
+ *
+ * Faulty registers addresses in table 10:
+ * NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+ * NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+ * NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+ */
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+#define NODE_ESRAM0_IN_3_PRIORITY 0x100C
+#define NODE_ESRAM0_IN_0_LIMIT 0x1030
+#define NODE_ESRAM0_IN_1_LIMIT 0x1034
+#define NODE_ESRAM0_IN_2_LIMIT 0x1038
+#define NODE_ESRAM0_IN_3_LIMIT 0x103C
+/* common */
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+#define NODE_ESRAM1_2_IN_3_PRIORITY 0x140C
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1430
+#define NODE_ESRAM1_2_IN_0_ARB_2_LIMIT 0x1434
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1438
+#define NODE_ESRAM1_2_IN_1_ARB_2_LIMIT 0x143C
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x1440
+#define NODE_ESRAM1_2_IN_2_ARB_2_LIMIT 0x1444
+#define NODE_ESRAM1_2_IN_3_ARB_1_LIMIT 0x1448
+#define NODE_ESRAM1_2_IN_3_ARB_2_LIMIT 0x144C
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+#define NODE_ESRAM3_4_IN_3_PRIORITY 0x180C
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1830
+#define NODE_ESRAM3_4_IN_0_ARB_2_LIMIT 0x1834
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1838
+#define NODE_ESRAM3_4_IN_1_ARB_2_LIMIT 0x183C
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x1840
+#define NODE_ESRAM3_4_IN_2_ARB_2_LIMIT 0x1844
+#define NODE_ESRAM3_4_IN_3_ARB_1_LIMIT 0x1848
+#define NODE_ESRAM3_4_IN_3_ARB_2_LIMIT 0x184C
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[3];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_esram_in2_arb[3];
+ u32 hibw1_ddr_in_prio[3];
+ u32 hibw1_ddr_in_limit[3];
+ u32 hibw1_ddr_out_prio;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr_in_prio[4];
+ u32 hibw2_ddr_in_limit[4];
+ u32 hibw2_ddr_out_prio;
+
+ /* ESRAM node registers */
+ u32 esram_in_prio[4];
+ u32 esram_in_lim[4];
+ u32 esram0_in_prio[4];
+ u32 esram0_in_lim[4];
+ u32 esram12_in_prio[4];
+ u32 esram12_in_arb_lim[8];
+ u32 esram34_in_prio[4];
+ u32 esram34_in_arb_lim[8];
+} context_icn;
+
+/**
+ * u8500_context_save_icn() - save ICN context
+ *
+ */
+void u8500_context_save_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ context_icn.hibw1_esram_in_pri[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in2_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in2_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in2_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ context_icn.hibw1_ddr_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw1_ddr_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_0_LIMIT);
+ context_icn.hibw1_ddr_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_1_LIMIT);
+ context_icn.hibw1_ddr_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ context_icn.hibw1_ddr_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ context_icn.hibw2_ddr_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_2_PRIORITY);
+
+ context_icn.hibw2_ddr_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_0_LIMIT);
+ context_icn.hibw2_ddr_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_1_LIMIT);
+
+ context_icn.hibw2_ddr_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW2_DDR_IN_2_LIMIT);
+
+ context_icn.hibw2_ddr_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ context_icn.esram0_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram0_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram0_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_PRIORITY);
+ context_icn.esram0_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ context_icn.esram0_in_lim[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram0_in_lim[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram0_in_lim[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_LIMIT);
+ context_icn.esram0_in_lim[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_LIMIT);
+
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ context_icn.esram12_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ context_icn.esram34_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+}
+
+/**
+ * u8500_context_restore_icn() - restore ICN context
+ *
+ */
+void u8500_context_restore_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[2],
+ b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[0],
+ b + NODE_HIBW1_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[1],
+ b + NODE_HIBW1_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr_in_prio[2],
+ b + NODE_HIBW1_DDR_IN_2_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[0],
+ b + NODE_HIBW1_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[1],
+ b + NODE_HIBW1_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr_in_limit[2],
+ b + NODE_HIBW1_DDR_IN_2_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr_out_prio,
+ b + NODE_HIBW1_DDR_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[0],
+ b + NODE_HIBW2_DDR_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[1],
+ b + NODE_HIBW2_DDR_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_prio[2],
+ b + NODE_HIBW2_DDR_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[0],
+ b + NODE_HIBW2_DDR_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[1],
+ b + NODE_HIBW2_DDR_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_in_limit[2],
+ b + NODE_HIBW2_DDR_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr_out_prio,
+ b + NODE_HIBW2_DDR_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_prio[0],
+ b + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[1],
+ b + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[2],
+ b + NODE_ESRAM0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[3],
+ b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_lim[0],
+ b + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[1],
+ b + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[2],
+ b + NODE_ESRAM0_IN_2_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[3],
+ b + NODE_ESRAM0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[3],
+ b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[3],
+ b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[4],
+ b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[5],
+ b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[6],
+ b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[7],
+ b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[3],
+ b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[3],
+ b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[4],
+ b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[5],
+ b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[6],
+ b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[7],
+ b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+}
+
+void u8500_context_init(void)
+{
+ context_icn.base = ioremap(U8500_ICN_BASE, SZ_8K);
+}
diff --git a/arch/arm/mach-ux500/pm/context-db9540.c b/arch/arm/mach-ux500/pm/context-db9540.c
new file mode 100644
index 00000000000..8d8aaeb9d8f
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context-db9540.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer for ST-Ericsson
+ *
+ */
+
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+/*
+ * ST-Interconnect context
+ */
+
+/* priority, bw limiter register offsets */
+#define NODE_HIBW1_ESRAM_IN_0_PRIORITY 0x00
+#define NODE_HIBW1_ESRAM_IN_1_PRIORITY 0x04
+#define NODE_HIBW1_ESRAM_IN_2_PRIORITY 0x08
+#define NODE_HIBW1_ESRAM_IN_3_PRIORITY 0x0C
+#define NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT 0x30
+#define NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT 0x34
+#define NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT 0x38
+#define NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT 0x3C
+#define NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT 0x40
+#define NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT 0x44
+#define NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT 0x48
+#define NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT 0x4C
+#define NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT 0x50
+#define NODE_HIBW1_ESRAM_IN_3_ARB_1_LIMIT 0x54
+#define NODE_HIBW1_ESRAM_IN_3_ARB_2_LIMIT 0x58
+#define NODE_HIBW1_ESRAM_IN_3_ARB_3_LIMIT 0x5C
+#define NODE_HIBW1_DDR0_IN_0_PRIORITY 0x400
+#define NODE_HIBW1_DDR0_IN_1_PRIORITY 0x404
+#define NODE_HIBW1_DDR0_IN_2_PRIORITY 0x408
+#define NODE_HIBW1_DDR0_IN_3_PRIORITY 0x40C
+#define NODE_HIBW1_DDR0_IN_0_LIMIT 0x430
+#define NODE_HIBW1_DDR0_IN_1_LIMIT 0x434
+#define NODE_HIBW1_DDR0_IN_2_LIMIT 0x438
+#define NODE_HIBW1_DDR0_IN_3_LIMIT 0x43C
+#define NODE_HIBW1_DDR0_OUT_0_PRIORITY 0x440
+#define NODE_HIBW2_ESRAM_IN_0_PRIORITY 0x800
+#define NODE_HIBW2_ESRAM_IN_1_PRIORITY 0x804
+#define NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT 0x818
+#define NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT 0x81C
+#define NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT 0x820
+#define NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT 0x824
+#define NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT 0x828
+#define NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT 0x82C
+
+#define NODE_HIBW2_DDR0_IN_0_PRIORITY 0xC00
+#define NODE_HIBW2_DDR0_IN_1_PRIORITY 0xC04
+
+#define NODE_HIBW2_DDR0_IN_0_LIMIT 0xC18
+#define NODE_HIBW2_DDR0_IN_1_LIMIT 0xC1C
+
+#define NODE_HIBW2_DDR0_OUT_0_PRIORITY 0xC20
+
+/*
+ * Note the following addresses are presented in
+ * db8500 design spec v3.1 and v3.3, table 10.
+ * But their addresses are not the same as in the
+ * description. The addresses in the description
+ * of each registers are correct.
+ * NODE_HIBW2_DDR_IN_3_LIMIT is only present in v1.
+ *
+ * Faulty registers addresses in table 10:
+ * NODE_HIBW2_DDR_IN_2_LIMIT 0xC38
+ * NODE_HIBW2_DDR_IN_3_LIMIT 0xC3C
+ * NODE_HIBW2_DDR_OUT_0_PRIORITY 0xC40
+ */
+
+#define NODE_ESRAM0_IN_0_PRIORITY 0x1000
+#define NODE_ESRAM0_IN_1_PRIORITY 0x1004
+#define NODE_ESRAM0_IN_2_PRIORITY 0x1008
+#define NODE_ESRAM0_IN_3_PRIORITY 0x100C
+#define NODE_ESRAM0_IN_0_LIMIT 0x1030
+#define NODE_ESRAM0_IN_1_LIMIT 0x1034
+#define NODE_ESRAM0_IN_2_LIMIT 0x1038
+#define NODE_ESRAM0_IN_3_LIMIT 0x103C
+#define NODE_ESRAM0_OUT_0_PRIORITY 0x1040
+
+/* common */
+#define NODE_ESRAM1_2_IN_0_PRIORITY 0x1400
+#define NODE_ESRAM1_2_IN_1_PRIORITY 0x1404
+#define NODE_ESRAM1_2_IN_2_PRIORITY 0x1408
+#define NODE_ESRAM1_2_IN_3_PRIORITY 0x140C
+#define NODE_ESRAM1_2_IN_0_ARB_1_LIMIT 0x1430
+#define NODE_ESRAM1_2_IN_0_ARB_2_LIMIT 0x1434
+#define NODE_ESRAM1_2_IN_1_ARB_1_LIMIT 0x1438
+#define NODE_ESRAM1_2_IN_1_ARB_2_LIMIT 0x143C
+#define NODE_ESRAM1_2_IN_2_ARB_1_LIMIT 0x1440
+#define NODE_ESRAM1_2_IN_2_ARB_2_LIMIT 0x1444
+#define NODE_ESRAM1_2_IN_3_ARB_1_LIMIT 0x1448
+#define NODE_ESRAM1_2_IN_3_ARB_2_LIMIT 0x144C
+
+#define NODE_ESRAM3_4_IN_0_PRIORITY 0x1800
+#define NODE_ESRAM3_4_IN_1_PRIORITY 0x1804
+#define NODE_ESRAM3_4_IN_2_PRIORITY 0x1808
+#define NODE_ESRAM3_4_IN_3_PRIORITY 0x180C
+#define NODE_ESRAM3_4_IN_0_ARB_1_LIMIT 0x1830
+#define NODE_ESRAM3_4_IN_0_ARB_2_LIMIT 0x1834
+#define NODE_ESRAM3_4_IN_1_ARB_1_LIMIT 0x1838
+#define NODE_ESRAM3_4_IN_1_ARB_2_LIMIT 0x183C
+#define NODE_ESRAM3_4_IN_2_ARB_1_LIMIT 0x1840
+#define NODE_ESRAM3_4_IN_2_ARB_2_LIMIT 0x1844
+#define NODE_ESRAM3_4_IN_3_ARB_1_LIMIT 0x1848
+#define NODE_ESRAM3_4_IN_3_ARB_2_LIMIT 0x184C
+
+#define NODE_HIBW1_DDR1_IN_0_PRIORITY_REG 0x1C00
+#define NODE_HIBW1_DDR1_IN_1_PRIORITY_REG 0x1C04
+#define NODE_HIBW1_DDR1_IN_2_PRIORITY_REG 0x1C08
+#define NODE_HIBW1_DDR1_IN_3_PRIORITY_REG 0x1C0C
+
+#define NODE_HIBW1_DDR1_IN_0_LIMIT_REG 0x1C30
+#define NODE_HIBW1_DDR1_IN_1_LIMIT_REG 0x1C34
+#define NODE_HIBW1_DDR1_IN_2_LIMIT_REG 0x1C38
+#define NODE_HIBW1_DDR1_IN_3_LIMIT_REG 0x1C3C
+
+#define NODE_HIBW1_DDR1_OUT_0_PRIORITY_REG 0x1C40
+
+#define NODE_HIBW2_DDR1_IN_0_PRIORITY_REG 0x2000
+#define NODE_HIBW2_DDR1_IN_1_PRIORITY_REG 0x2004
+
+#define NODE_HIBW2_DDR1_IN_0_LIMIT_REG 0x2018
+#define NODE_HIBW2_DDR1_IN_1_LIMIT_REG 0x201C
+#define NODE_HIBW2_DDR1_OUT_0_PRIORITY_REG 0x2020
+#define NODE_DDR_AWQOS_DSP_DDR0_REG 0x340C
+#define NODE_DDR_AWQOS_DSP_DDR1_REG 0x3400
+#define NODE_DDR_ARQOS_DSP_DDR0_REG 0x3408
+#define NODE_DDR_ARQOS_DSP_DDR1_REG 0x3404
+#define NODE_DDR_AWQOS_ARM_DDR_REG 0x3410
+#define NODE_DDR_ARQOS_ARM_DDR_REG 0x3414
+#define NODE_SGA_AWQOS_DDR0_HIBW2_REG 0x3418
+#define NODE_SGA_ARQOS_DDR0_HIBW2_REG 0x341C
+#define NODE_SGA_AWQOS_SGA_HIBW2_REG 0x3420
+#define NODE_SGA_ARQOS_SGA_HIBW2_REG 0x3424
+
+static struct {
+ void __iomem *base;
+ u32 hibw1_esram_in_pri[4];
+ u32 hibw1_esram_in0_arb[3];
+ u32 hibw1_esram_in1_arb[3];
+ u32 hibw1_esram_in2_arb[3];
+ u32 hibw1_esram_in3_arb[3];
+ u32 hibw1_ddr0_in_prio[4];
+ u32 hibw1_ddr0_in_limit[4];
+ u32 hibw1_ddr0_out_prio;
+ u32 hibw1_ddr1_in_prio[4];
+ u32 hibw1_ddr1_in_limit[4];
+ u32 hibw1_ddr1_out_prio;
+
+ /* HiBw2 node registers */
+ u32 hibw2_esram_in_pri[2];
+ u32 hibw2_esram_in0_arblimit[3];
+ u32 hibw2_esram_in1_arblimit[3];
+ u32 hibw2_ddr0_in_prio[2];
+ u32 hibw2_ddr0_in_limit[2];
+ u32 hibw2_ddr0_out_prio;
+ u32 hibw2_ddr1_in_prio[2];
+ u32 hibw2_ddr1_in_limit[2];
+ u32 hibw2_ddr1_out_prio;
+
+ /* ESRAM node registers */
+ u32 esram0_in_prio[4];
+ u32 esram0_in_lim[4];
+ u32 esram0_out_prio;
+ u32 esram12_in_prio[4];
+ u32 esram12_in_arb_lim[8];
+ u32 esram34_in_prio[4];
+ u32 esram34_in_arb_lim[8];
+
+ u32 ddr_awqos_dsp_ddr0;
+ u32 ddr_awqos_dsp_ddr1;
+ u32 ddr_arqos_dsp_ddr0;
+ u32 ddr_arqos_dsp_ddr1;
+ u32 ddr_awqos_arm_ddr;
+ u32 ddr_arqos_arm_ddr;
+
+ u32 sga_awqos_ddr0_hibw2;
+ u32 sga_arqos_ddr0_hibw2;
+
+ u32 sga_awqos_sga_hibw2;
+ u32 sga_arqos_sga_hibw2;
+} context_icn;
+
+/**
+ * u8500_context_save_icn() - save ICN context
+ *
+ */
+void u9540_context_save_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ context_icn.hibw1_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw1_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ context_icn.hibw1_esram_in_pri[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+ context_icn.hibw1_esram_in_pri[3] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_PRIORITY);
+
+ context_icn.hibw1_esram_in0_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in0_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in0_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in1_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in1_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in1_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in2_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in2_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in2_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ context_icn.hibw1_esram_in3_arb[0] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_ARB_1_LIMIT);
+ context_icn.hibw1_esram_in3_arb[1] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_ARB_2_LIMIT);
+ context_icn.hibw1_esram_in3_arb[2] =
+ readl_relaxed(b + NODE_HIBW1_ESRAM_IN_3_ARB_3_LIMIT);
+
+ context_icn.hibw1_ddr0_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_0_PRIORITY);
+ context_icn.hibw1_ddr0_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_1_PRIORITY);
+ context_icn.hibw1_ddr0_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_2_PRIORITY);
+ context_icn.hibw1_ddr0_in_prio[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_3_PRIORITY);
+
+ context_icn.hibw1_ddr0_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_0_LIMIT);
+ context_icn.hibw1_ddr0_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_1_LIMIT);
+ context_icn.hibw1_ddr0_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_2_LIMIT);
+ context_icn.hibw1_ddr0_in_limit[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR0_IN_3_LIMIT);
+
+ context_icn.hibw1_ddr0_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR0_OUT_0_PRIORITY);
+
+ context_icn.hibw1_ddr1_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_0_PRIORITY_REG);
+ context_icn.hibw1_ddr1_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_1_PRIORITY_REG);
+ context_icn.hibw1_ddr1_in_prio[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_2_PRIORITY_REG);
+ context_icn.hibw1_ddr1_in_prio[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_3_PRIORITY_REG);
+
+ context_icn.hibw1_ddr1_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_0_LIMIT_REG);
+ context_icn.hibw1_ddr1_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_1_LIMIT_REG);
+ context_icn.hibw1_ddr1_in_limit[2] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_2_LIMIT_REG);
+ context_icn.hibw1_ddr1_in_limit[3] =
+ readl_relaxed(b + NODE_HIBW1_DDR1_IN_3_LIMIT_REG);
+
+ context_icn.hibw1_ddr1_out_prio =
+ readl_relaxed(b + NODE_HIBW1_DDR1_OUT_0_PRIORITY_REG);
+
+ context_icn.hibw2_esram_in_pri[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ context_icn.hibw2_esram_in_pri[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ context_icn.hibw2_esram_in0_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in0_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ context_icn.hibw2_esram_in1_arblimit[0] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[1] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ context_icn.hibw2_esram_in1_arblimit[2] =
+ readl_relaxed(b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ context_icn.hibw2_ddr0_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_0_PRIORITY);
+ context_icn.hibw2_ddr0_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_1_PRIORITY);
+
+ context_icn.hibw2_ddr0_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_0_LIMIT);
+ context_icn.hibw2_ddr0_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR0_IN_1_LIMIT);
+
+ context_icn.hibw2_ddr0_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR0_OUT_0_PRIORITY);
+
+ context_icn.hibw2_ddr1_in_prio[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_0_PRIORITY_REG);
+ context_icn.hibw2_ddr1_in_prio[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_1_PRIORITY_REG);
+
+ context_icn.hibw2_ddr1_in_limit[0] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_0_LIMIT_REG);
+ context_icn.hibw2_ddr1_in_limit[1] =
+ readl_relaxed(b + NODE_HIBW2_DDR1_IN_1_LIMIT_REG);
+
+ context_icn.hibw2_ddr1_out_prio =
+ readl_relaxed(b + NODE_HIBW2_DDR1_OUT_0_PRIORITY_REG);
+
+ context_icn.esram0_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_PRIORITY);
+ context_icn.esram0_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_PRIORITY);
+ context_icn.esram0_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_PRIORITY);
+ context_icn.esram0_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ context_icn.esram0_in_lim[0] =
+ readl_relaxed(b + NODE_ESRAM0_IN_0_LIMIT);
+ context_icn.esram0_in_lim[1] =
+ readl_relaxed(b + NODE_ESRAM0_IN_1_LIMIT);
+ context_icn.esram0_in_lim[2] =
+ readl_relaxed(b + NODE_ESRAM0_IN_2_LIMIT);
+ context_icn.esram0_in_lim[3] =
+ readl_relaxed(b + NODE_ESRAM0_IN_3_LIMIT);
+
+ context_icn.esram0_out_prio =
+ readl_relaxed(b + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ context_icn.esram12_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ context_icn.esram12_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ context_icn.esram12_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ context_icn.esram12_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ context_icn.esram12_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ context_icn.esram12_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ context_icn.esram12_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ context_icn.esram34_in_prio[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ context_icn.esram34_in_prio[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ context_icn.esram34_in_prio[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ context_icn.esram34_in_prio[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ context_icn.esram34_in_arb_lim[0] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[1] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[2] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[3] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[4] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[5] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ context_icn.esram34_in_arb_lim[6] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ context_icn.esram34_in_arb_lim[7] =
+ readl_relaxed(b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+
+ context_icn.ddr_awqos_dsp_ddr0 =
+ readl_relaxed(b + NODE_DDR_AWQOS_DSP_DDR0_REG);
+ context_icn.ddr_awqos_dsp_ddr1 =
+ readl_relaxed(b + NODE_DDR_AWQOS_DSP_DDR1_REG);
+ context_icn.ddr_arqos_dsp_ddr0 =
+ readl_relaxed(b + NODE_DDR_ARQOS_DSP_DDR0_REG);
+ context_icn.ddr_arqos_dsp_ddr1 =
+ readl_relaxed(b + NODE_DDR_ARQOS_DSP_DDR1_REG);
+ context_icn.ddr_awqos_arm_ddr =
+ readl_relaxed(b + NODE_DDR_AWQOS_ARM_DDR_REG);
+ context_icn.ddr_arqos_arm_ddr =
+ readl_relaxed(b + NODE_DDR_ARQOS_ARM_DDR_REG);
+
+ context_icn.sga_awqos_ddr0_hibw2 =
+ readl_relaxed(b + NODE_SGA_AWQOS_DDR0_HIBW2_REG);
+ context_icn.sga_arqos_ddr0_hibw2 =
+ readl_relaxed(b + NODE_SGA_ARQOS_DDR0_HIBW2_REG);
+
+ context_icn.sga_awqos_sga_hibw2 =
+ readl_relaxed(b + NODE_SGA_AWQOS_SGA_HIBW2_REG);
+ context_icn.sga_arqos_sga_hibw2 =
+ readl_relaxed(b + NODE_SGA_ARQOS_SGA_HIBW2_REG);
+}
+
+/**
+ * u9540_context_restore_icn() - restore ICN context
+ *
+ */
+void u9540_context_restore_icn(void)
+{
+ void __iomem *b = context_icn.base;
+
+ writel_relaxed(context_icn.hibw1_esram_in_pri[0],
+ b + NODE_HIBW1_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[1],
+ b + NODE_HIBW1_ESRAM_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[2],
+ b + NODE_HIBW1_ESRAM_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw1_esram_in_pri[3],
+ b + NODE_HIBW1_ESRAM_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in0_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in1_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in2_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_2_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_esram_in3_arb[0],
+ b + NODE_HIBW1_ESRAM_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in3_arb[1],
+ b + NODE_HIBW1_ESRAM_IN_3_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_esram_in3_arb[2],
+ b + NODE_HIBW1_ESRAM_IN_3_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[0],
+ b + NODE_HIBW1_DDR0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[1],
+ b + NODE_HIBW1_DDR0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[2],
+ b + NODE_HIBW1_DDR0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.hibw1_ddr0_in_prio[3],
+ b + NODE_HIBW1_DDR0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[0],
+ b + NODE_HIBW1_DDR0_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[1],
+ b + NODE_HIBW1_DDR0_IN_1_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[2],
+ b + NODE_HIBW1_DDR0_IN_2_LIMIT);
+ writel_relaxed(context_icn.hibw1_ddr0_in_limit[3],
+ b + NODE_HIBW1_DDR0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw1_ddr0_out_prio,
+ b + NODE_HIBW1_DDR0_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[0],
+ b + NODE_HIBW1_DDR1_IN_0_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[1],
+ b + NODE_HIBW1_DDR1_IN_1_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[2],
+ b + NODE_HIBW1_DDR1_IN_2_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_prio[3],
+ b + NODE_HIBW1_DDR1_IN_3_PRIORITY_REG);
+
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[0],
+ b + NODE_HIBW1_DDR1_IN_0_LIMIT_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[1],
+ b + NODE_HIBW1_DDR1_IN_1_LIMIT_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[2],
+ b + NODE_HIBW1_DDR1_IN_2_LIMIT_REG);
+ writel_relaxed(context_icn.hibw1_ddr1_in_limit[3],
+ b + NODE_HIBW1_DDR1_IN_3_LIMIT_REG);
+
+ writel_relaxed(context_icn.hibw1_ddr1_out_prio,
+ b + NODE_HIBW1_DDR1_OUT_0_PRIORITY_REG);
+
+ writel_relaxed(context_icn.hibw2_esram_in_pri[0],
+ b + NODE_HIBW2_ESRAM_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_esram_in_pri[1],
+ b + NODE_HIBW2_ESRAM_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in0_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_0_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[0],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[1],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.hibw2_esram_in1_arblimit[2],
+ b + NODE_HIBW2_ESRAM_IN_1_ARB_3_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr0_in_prio[0],
+ b + NODE_HIBW2_DDR0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.hibw2_ddr0_in_prio[1],
+ b + NODE_HIBW2_DDR0_IN_1_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_ddr0_in_limit[0],
+ b + NODE_HIBW2_DDR0_IN_0_LIMIT);
+ writel_relaxed(context_icn.hibw2_ddr0_in_limit[1],
+ b + NODE_HIBW2_DDR0_IN_1_LIMIT);
+
+ writel_relaxed(context_icn.hibw2_ddr0_out_prio,
+ b + NODE_HIBW2_DDR0_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.hibw2_ddr1_in_prio[0],
+ b + NODE_HIBW2_DDR1_IN_0_PRIORITY_REG);
+ writel_relaxed(context_icn.hibw2_ddr1_in_prio[1],
+ b + NODE_HIBW2_DDR1_IN_1_PRIORITY_REG);
+
+ writel_relaxed(context_icn.hibw2_ddr1_in_limit[0],
+ b + NODE_HIBW2_DDR1_IN_0_LIMIT_REG);
+ writel_relaxed(context_icn.hibw2_ddr1_in_limit[1],
+ b + NODE_HIBW2_DDR1_IN_1_LIMIT_REG);
+
+ writel_relaxed(context_icn.hibw2_ddr1_out_prio,
+ b + NODE_HIBW2_DDR1_OUT_0_PRIORITY_REG);
+
+ writel_relaxed(context_icn.esram0_in_prio[0],
+ b + NODE_ESRAM0_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[1],
+ b + NODE_ESRAM0_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[2],
+ b + NODE_ESRAM0_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram0_in_prio[3],
+ b + NODE_ESRAM0_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram0_in_lim[0],
+ b + NODE_ESRAM0_IN_0_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[1],
+ b + NODE_ESRAM0_IN_1_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[2],
+ b + NODE_ESRAM0_IN_2_LIMIT);
+ writel_relaxed(context_icn.esram0_in_lim[3],
+ b + NODE_ESRAM0_IN_3_LIMIT);
+
+ writel_relaxed(context_icn.esram0_out_prio,
+ b + NODE_ESRAM0_OUT_0_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_prio[0],
+ b + NODE_ESRAM1_2_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[1],
+ b + NODE_ESRAM1_2_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[2],
+ b + NODE_ESRAM1_2_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram12_in_prio[3],
+ b + NODE_ESRAM1_2_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram12_in_arb_lim[0],
+ b + NODE_ESRAM1_2_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[1],
+ b + NODE_ESRAM1_2_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[2],
+ b + NODE_ESRAM1_2_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[3],
+ b + NODE_ESRAM1_2_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[4],
+ b + NODE_ESRAM1_2_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[5],
+ b + NODE_ESRAM1_2_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[6],
+ b + NODE_ESRAM1_2_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram12_in_arb_lim[7],
+ b + NODE_ESRAM1_2_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.esram34_in_prio[0],
+ b + NODE_ESRAM3_4_IN_0_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[1],
+ b + NODE_ESRAM3_4_IN_1_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[2],
+ b + NODE_ESRAM3_4_IN_2_PRIORITY);
+ writel_relaxed(context_icn.esram34_in_prio[3],
+ b + NODE_ESRAM3_4_IN_3_PRIORITY);
+
+ writel_relaxed(context_icn.esram34_in_arb_lim[0],
+ b + NODE_ESRAM3_4_IN_0_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[1],
+ b + NODE_ESRAM3_4_IN_0_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[2],
+ b + NODE_ESRAM3_4_IN_1_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[3],
+ b + NODE_ESRAM3_4_IN_1_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[4],
+ b + NODE_ESRAM3_4_IN_2_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[5],
+ b + NODE_ESRAM3_4_IN_2_ARB_2_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[6],
+ b + NODE_ESRAM3_4_IN_3_ARB_1_LIMIT);
+ writel_relaxed(context_icn.esram34_in_arb_lim[7],
+ b + NODE_ESRAM3_4_IN_3_ARB_2_LIMIT);
+
+ writel_relaxed(context_icn.ddr_awqos_dsp_ddr0,
+ b + NODE_DDR_AWQOS_DSP_DDR0_REG);
+ writel_relaxed(context_icn.ddr_awqos_dsp_ddr1,
+ b + NODE_DDR_AWQOS_DSP_DDR1_REG);
+ writel_relaxed(context_icn.ddr_arqos_dsp_ddr0,
+ b + NODE_DDR_ARQOS_DSP_DDR0_REG);
+ writel_relaxed(context_icn.ddr_arqos_dsp_ddr1,
+ b + NODE_DDR_ARQOS_DSP_DDR1_REG);
+ writel_relaxed(context_icn.ddr_awqos_arm_ddr,
+ b + NODE_DDR_AWQOS_ARM_DDR_REG);
+ writel_relaxed(context_icn.ddr_arqos_arm_ddr,
+ b + NODE_DDR_ARQOS_ARM_DDR_REG);
+
+ writel_relaxed(context_icn.sga_awqos_ddr0_hibw2,
+ b + NODE_SGA_AWQOS_DDR0_HIBW2_REG);
+ writel_relaxed(context_icn.sga_arqos_ddr0_hibw2,
+ b + NODE_SGA_ARQOS_DDR0_HIBW2_REG);
+
+ writel_relaxed(context_icn.sga_awqos_sga_hibw2,
+ b + NODE_SGA_AWQOS_SGA_HIBW2_REG);
+ writel_relaxed(context_icn.sga_arqos_sga_hibw2,
+ b + NODE_SGA_ARQOS_SGA_HIBW2_REG);
+}
+
+void u9540_context_init(void)
+{
+ context_icn.base = ioremap(U8500_ICN_BASE, SZ_16K);
+}
diff --git a/arch/arm/mach-ux500/pm/context.c b/arch/arm/mach-ux500/pm/context.c
new file mode 100644
index 00000000000..5902b861930
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context.c
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>,
+ * Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com>,
+ * Sundar Iyer for ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio/nomadik.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/pm.h>
+#include <mach/context.h>
+
+#include <asm/hardware/gic.h>
+#include <asm/smp_twd.h>
+
+#include "scu.h"
+#include "../product.h"
+#include "../prcc.h"
+
+#include "../board-pins-sleep-force.h"
+
+#define GPIO_NUM_BANKS 9
+#define GPIO_NUM_SAVE_REGISTERS 7
+
+/*
+ * TODO:
+ * - Use the "UX500*"-macros instead where possible
+ */
+
+#define U8500_BACKUPRAM_SIZE SZ_64K
+
+#define U8500_PUBLIC_BOOT_ROM_BASE (U8500_BOOT_ROM_BASE + 0x17000)
+#define U9540_PUBLIC_BOOT_ROM_BASE (U9540_BOOT_ROM_BASE + 0x17000)
+#define U5500_PUBLIC_BOOT_ROM_BASE (U5500_BOOT_ROM_BASE + 0x18000)
+
+/*
+ * Special dedicated addresses in backup RAM. The 5500 addresses are identical
+ * to the 8500 ones.
+ */
+#define U8500_EXT_RAM_LOC_BACKUPRAM_ADDR 0x80151FDC
+#define U8500_CPU0_CP15_CR_BACKUPRAM_ADDR 0x80151F80
+#define U8500_CPU1_CP15_CR_BACKUPRAM_ADDR 0x80151FA0
+
+#define U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR 0x80151FD8
+#define U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR 0x80151FE0
+
+#define GIC_DIST_ENABLE_NS 0x0
+
+/* 32 interrupts fits in 4 bytes */
+#define GIC_DIST_ENABLE_SET_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - \
+ IRQ_SHPI_START) / 32)
+#define GIC_DIST_ENABLE_SET_CPU_NUM (IRQ_SHPI_START / 32)
+#define GIC_DIST_ENABLE_SET_SPI0 GIC_DIST_ENABLE_SET
+#define GIC_DIST_ENABLE_SET_SPI32 (GIC_DIST_ENABLE_SET + IRQ_SHPI_START / 8)
+
+#define GIC_DIST_ENABLE_CLEAR_0 GIC_DIST_ENABLE_CLEAR
+#define GIC_DIST_ENABLE_CLEAR_32 (GIC_DIST_ENABLE_CLEAR + 4)
+#define GIC_DIST_ENABLE_CLEAR_64 (GIC_DIST_ENABLE_CLEAR + 8)
+#define GIC_DIST_ENABLE_CLEAR_96 (GIC_DIST_ENABLE_CLEAR + 12)
+#define GIC_DIST_ENABLE_CLEAR_128 (GIC_DIST_ENABLE_CLEAR + 16)
+
+#define GIC_DIST_PRI_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - IRQ_SHPI_START) / 4)
+#define GIC_DIST_PRI_CPU_NUM (IRQ_SHPI_START / 4)
+#define GIC_DIST_PRI_SPI0 GIC_DIST_PRI
+#define GIC_DIST_PRI_SPI32 (GIC_DIST_PRI + IRQ_SHPI_START)
+
+#define GIC_DIST_SPI_TARGET_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - \
+ IRQ_SHPI_START) / 4)
+#define GIC_DIST_SPI_TARGET_CPU_NUM (IRQ_SHPI_START / 4)
+#define GIC_DIST_SPI_TARGET_SPI0 GIC_DIST_TARGET
+#define GIC_DIST_SPI_TARGET_SPI32 (GIC_DIST_TARGET + IRQ_SHPI_START)
+
+/* 16 interrupts per 4 bytes */
+#define GIC_DIST_CONFIG_COMMON_NUM ((DBX500_NR_INTERNAL_IRQS - IRQ_SHPI_START) \
+ / 16)
+#define GIC_DIST_CONFIG_CPU_NUM (IRQ_SHPI_START / 16)
+#define GIC_DIST_CONFIG_SPI0 GIC_DIST_CONFIG
+#define GIC_DIST_CONFIG_SPI32 (GIC_DIST_CONFIG + IRQ_SHPI_START / 4)
+
+/* TODO! Move STM reg offsets to suitable place */
+#define STM_CR_OFFSET 0x00
+#define STM_MMC_OFFSET 0x08
+#define STM_TER_OFFSET 0x10
+
+#define TPIU_PORT_SIZE 0x4
+#define TPIU_TRIGGER_COUNTER 0x104
+#define TPIU_TRIGGER_MULTIPLIER 0x108
+#define TPIU_CURRENT_TEST_PATTERN 0x204
+#define TPIU_TEST_PATTERN_REPEAT 0x208
+#define TPIU_FORMATTER 0x304
+#define TPIU_FORMATTER_SYNC 0x308
+#define TPIU_LOCK_ACCESS_REGISTER 0xFB0
+
+#define TPIU_UNLOCK_CODE 0xc5acce55
+
+#define SCU_FILTER_STARTADDR 0x40
+#define SCU_FILTER_ENDADDR 0x44
+#define SCU_ACCESS_CTRL_SAC 0x50
+
+/* The context of the Trace Port Interface Unit (TPIU) */
+static struct {
+ void __iomem *base;
+ u32 port_size;
+ u32 trigger_counter;
+ u32 trigger_multiplier;
+ u32 current_test_pattern;
+ u32 test_pattern_repeat;
+ u32 formatter;
+ u32 formatter_sync;
+} context_tpiu;
+
+static struct {
+ void __iomem *base;
+ u32 cr;
+ u32 mmc;
+ u32 ter;
+} context_stm_ape;
+
+struct context_gic_cpu {
+ void __iomem *base;
+ u32 ctrl;
+ u32 primask;
+ u32 binpoint;
+};
+static DEFINE_PER_CPU(struct context_gic_cpu, context_gic_cpu);
+
+static struct {
+ void __iomem *base;
+ u32 ns;
+ u32 enable_set[GIC_DIST_ENABLE_SET_COMMON_NUM]; /* IRQ 32 to 160 */
+ u32 priority_level[GIC_DIST_PRI_COMMON_NUM];
+ u32 spi_target[GIC_DIST_SPI_TARGET_COMMON_NUM];
+ u32 config[GIC_DIST_CONFIG_COMMON_NUM];
+} context_gic_dist_common;
+
+struct context_gic_dist_cpu {
+ void __iomem *base;
+ u32 enable_set[GIC_DIST_ENABLE_SET_CPU_NUM]; /* IRQ 0 to 31 */
+ u32 priority_level[GIC_DIST_PRI_CPU_NUM];
+ u32 spi_target[GIC_DIST_SPI_TARGET_CPU_NUM];
+ u32 config[GIC_DIST_CONFIG_CPU_NUM];
+};
+static DEFINE_PER_CPU(struct context_gic_dist_cpu, context_gic_dist_cpu);
+
+static struct {
+ void __iomem *base;
+ u32 ctrl;
+ u32 cpu_pwrstatus;
+ u32 inv_all_nonsecure;
+ u32 filter_start_addr;
+ u32 filter_end_addr;
+ u32 access_ctrl_sac;
+} context_scu;
+
+#define UX500_NR_PRCC_BANKS 5
+static struct {
+ void __iomem *base;
+ struct clk *clk;
+ u32 bus_clk;
+ u32 kern_clk;
+} context_prcc[UX500_NR_PRCC_BANKS];
+
+static u32 backup_sram_storage[NR_CPUS] = {
+ IO_ADDRESS(U8500_CPU0_CP15_CR_BACKUPRAM_ADDR),
+ IO_ADDRESS(U8500_CPU1_CP15_CR_BACKUPRAM_ADDR),
+};
+
+static u32 gpio_bankaddr[GPIO_NUM_BANKS] = {IO_ADDRESS(U8500_GPIOBANK0_BASE),
+ IO_ADDRESS(U8500_GPIOBANK1_BASE),
+ IO_ADDRESS(U8500_GPIOBANK2_BASE),
+ IO_ADDRESS(U8500_GPIOBANK3_BASE),
+ IO_ADDRESS(U8500_GPIOBANK4_BASE),
+ IO_ADDRESS(U8500_GPIOBANK5_BASE),
+ IO_ADDRESS(U8500_GPIOBANK6_BASE),
+ IO_ADDRESS(U8500_GPIOBANK7_BASE),
+ IO_ADDRESS(U8500_GPIOBANK8_BASE)
+};
+
+static u32 gpio_save[GPIO_NUM_BANKS][GPIO_NUM_SAVE_REGISTERS];
+
+void __iomem *fsmc_base_addr;
+static u32 fsmc_bcr0;
+/*
+ * Stacks and stack pointers
+ */
+static DEFINE_PER_CPU(u32[128], varm_registers_backup_stack);
+static DEFINE_PER_CPU(u32 *, varm_registers_pointer);
+
+static DEFINE_PER_CPU(u32[128], varm_cp15_backup_stack);
+static DEFINE_PER_CPU(u32 *, varm_cp15_pointer);
+
+static ATOMIC_NOTIFIER_HEAD(context_ape_notifier_list);
+static ATOMIC_NOTIFIER_HEAD(context_arm_notifier_list);
+
+/*
+ * Register a simple callback for handling vape context save/restore
+ */
+int context_ape_notifier_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&context_ape_notifier_list, nb);
+}
+EXPORT_SYMBOL(context_ape_notifier_register);
+
+/*
+ * Remove a previously registered callback
+ */
+int context_ape_notifier_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&context_ape_notifier_list,
+ nb);
+}
+EXPORT_SYMBOL(context_ape_notifier_unregister);
+
+/*
+ * Register a simple callback for handling varm context save/restore
+ */
+int context_arm_notifier_register(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&context_arm_notifier_list, nb);
+}
+EXPORT_SYMBOL(context_arm_notifier_register);
+
+/*
+ * Remove a previously registered callback
+ */
+int context_arm_notifier_unregister(struct notifier_block *nb)
+{
+ return atomic_notifier_chain_unregister(&context_arm_notifier_list,
+ nb);
+}
+EXPORT_SYMBOL(context_arm_notifier_unregister);
+
+static void save_prcc(void)
+{
+ int i;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ clk_enable(context_prcc[i].clk);
+
+ context_prcc[i].bus_clk =
+ readl(context_prcc[i].base + PRCC_PCKSR);
+ context_prcc[i].kern_clk =
+ readl(context_prcc[i].base + PRCC_KCKSR);
+
+ clk_disable(context_prcc[i].clk);
+ }
+}
+
+static void restore_prcc(void)
+{
+ int i;
+
+ for (i = 0; i < UX500_NR_PRCC_BANKS; i++) {
+ clk_enable(context_prcc[i].clk);
+
+ writel(~context_prcc[i].bus_clk,
+ context_prcc[i].base + PRCC_PCKDIS);
+ writel(~context_prcc[i].kern_clk,
+ context_prcc[i].base + PRCC_KCKDIS);
+
+ writel(context_prcc[i].bus_clk,
+ context_prcc[i].base + PRCC_PCKEN);
+ writel(context_prcc[i].kern_clk,
+ context_prcc[i].base + PRCC_KCKEN);
+ /*
+ * Consider having a while over KCK/BCK_STATUS
+ * to check that all clocks get disabled/enabled
+ */
+
+ clk_disable(context_prcc[i].clk);
+ }
+}
+
+static void save_stm_ape(void)
+{
+ /*
+ * TODO: Check with PRCMU developers how STM is handled by PRCMU
+ * firmware. According to DB5500 design spec there is a "flush"
+ * mechanism supposed to be used by the PRCMU before power down,
+ * PRCMU fw might save/restore the following three registers
+ * at the same time.
+ */
+ context_stm_ape.cr = readl(context_stm_ape.base +
+ STM_CR_OFFSET);
+ context_stm_ape.mmc = readl(context_stm_ape.base +
+ STM_MMC_OFFSET);
+ context_stm_ape.ter = readl(context_stm_ape.base +
+ STM_TER_OFFSET);
+}
+
+static void restore_stm_ape(void)
+{
+ writel(context_stm_ape.ter,
+ context_stm_ape.base + STM_TER_OFFSET);
+ writel(context_stm_ape.mmc,
+ context_stm_ape.base + STM_MMC_OFFSET);
+ writel(context_stm_ape.cr,
+ context_stm_ape.base + STM_CR_OFFSET);
+}
+
+static bool inline tpiu_clocked(void)
+{
+ return ux500_jtag_enabled();
+}
+
+/*
+ * Save the context of the Trace Port Interface Unit (TPIU).
+ * Saving/restoring is needed for the PTM tracing to work together
+ * with the sleep states ApSleep and ApDeepSleep.
+ */
+static void save_tpiu(void)
+{
+ if (!tpiu_clocked())
+ return;
+
+ context_tpiu.port_size = readl(context_tpiu.base +
+ TPIU_PORT_SIZE);
+ context_tpiu.trigger_counter = readl(context_tpiu.base +
+ TPIU_TRIGGER_COUNTER);
+ context_tpiu.trigger_multiplier = readl(context_tpiu.base +
+ TPIU_TRIGGER_MULTIPLIER);
+ context_tpiu.current_test_pattern = readl(context_tpiu.base +
+ TPIU_CURRENT_TEST_PATTERN);
+ context_tpiu.test_pattern_repeat = readl(context_tpiu.base +
+ TPIU_TEST_PATTERN_REPEAT);
+ context_tpiu.formatter = readl(context_tpiu.base +
+ TPIU_FORMATTER);
+ context_tpiu.formatter_sync = readl(context_tpiu.base +
+ TPIU_FORMATTER_SYNC);
+}
+
+/*
+ * Restore the context of the Trace Port Interface Unit (TPIU).
+ * Saving/restoring is needed for the PTM tracing to work together
+ * with the sleep states ApSleep and ApDeepSleep.
+ */
+static void restore_tpiu(void)
+{
+ if (!tpiu_clocked())
+ return;
+
+ writel(TPIU_UNLOCK_CODE,
+ context_tpiu.base + TPIU_LOCK_ACCESS_REGISTER);
+
+ writel(context_tpiu.port_size,
+ context_tpiu.base + TPIU_PORT_SIZE);
+ writel(context_tpiu.trigger_counter,
+ context_tpiu.base + TPIU_TRIGGER_COUNTER);
+ writel(context_tpiu.trigger_multiplier,
+ context_tpiu.base + TPIU_TRIGGER_MULTIPLIER);
+ writel(context_tpiu.current_test_pattern,
+ context_tpiu.base + TPIU_CURRENT_TEST_PATTERN);
+ writel(context_tpiu.test_pattern_repeat,
+ context_tpiu.base + TPIU_TEST_PATTERN_REPEAT);
+ writel(context_tpiu.formatter,
+ context_tpiu.base + TPIU_FORMATTER);
+ writel(context_tpiu.formatter_sync,
+ context_tpiu.base + TPIU_FORMATTER_SYNC);
+}
+
+/*
+ * Save GIC CPU IF registers
+ *
+ * This is per cpu so it needs to be called for each one.
+ */
+static void save_gic_if_cpu(struct context_gic_cpu *c_gic_cpu)
+{
+ c_gic_cpu->ctrl = readl_relaxed(c_gic_cpu->base + GIC_CPU_CTRL);
+ c_gic_cpu->primask = readl_relaxed(c_gic_cpu->base + GIC_CPU_PRIMASK);
+ c_gic_cpu->binpoint = readl_relaxed(c_gic_cpu->base + GIC_CPU_BINPOINT);
+}
+
+/*
+ * Restore GIC CPU IF registers
+ *
+ * This is per cpu so it needs to be called for each one.
+ */
+static void restore_gic_if_cpu(struct context_gic_cpu *c_gic_cpu)
+{
+ writel_relaxed(c_gic_cpu->ctrl, c_gic_cpu->base + GIC_CPU_CTRL);
+ writel_relaxed(c_gic_cpu->primask, c_gic_cpu->base + GIC_CPU_PRIMASK);
+ writel_relaxed(c_gic_cpu->binpoint, c_gic_cpu->base + GIC_CPU_BINPOINT);
+}
+
+/*
+ * Save GIC Distributor Common registers
+ *
+ * This context is common. Only one CPU needs to call.
+ *
+ * Save SPI (Shared Peripheral Interrupt) settings, IRQ 32-159.
+ */
+static void save_gic_dist_common(void)
+{
+ int i;
+
+ context_gic_dist_common.ns = readl_relaxed(context_gic_dist_common.base
+ + GIC_DIST_ENABLE_NS);
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_COMMON_NUM; i++)
+ context_gic_dist_common.enable_set[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_ENABLE_SET_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_COMMON_NUM; i++)
+ context_gic_dist_common.priority_level[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_PRI_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_COMMON_NUM; i++)
+ context_gic_dist_common.spi_target[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_SPI_TARGET_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_CONFIG_COMMON_NUM; i++)
+ context_gic_dist_common.config[i] =
+ readl_relaxed(context_gic_dist_common.base +
+ GIC_DIST_CONFIG_SPI32 + i * 4);
+}
+
+/*
+ * Restore GIC Distributor Common registers
+ *
+ * This context is common. Only one CPU needs to call.
+ *
+ * Save SPI (Shared Peripheral Interrupt) settings, IRQ 32-159.
+ */
+static void restore_gic_dist_common(void)
+{
+ int i;
+
+ for (i = 0; i < GIC_DIST_CONFIG_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.config[i],
+ context_gic_dist_common.base +
+ GIC_DIST_CONFIG_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.spi_target[i],
+ context_gic_dist_common.base +
+ GIC_DIST_SPI_TARGET_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.priority_level[i],
+ context_gic_dist_common.base +
+ GIC_DIST_PRI_SPI32 + i * 4);
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_COMMON_NUM; i++)
+ writel_relaxed(context_gic_dist_common.enable_set[i],
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_SET_SPI32 + i * 4);
+
+ writel_relaxed(context_gic_dist_common.ns,
+ context_gic_dist_common.base + GIC_DIST_ENABLE_NS);
+}
+
+/*
+ * Save GIC Dist CPU registers
+ *
+ * This needs to be called by all cpu:s which will not call
+ * save_gic_dist_common(). Only the registers of the GIC which are
+ * banked will be saved.
+ */
+static void save_gic_dist_cpu(struct context_gic_dist_cpu *c_gic)
+{
+ int i;
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_CPU_NUM; i++)
+ c_gic->enable_set[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_ENABLE_SET_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_CPU_NUM; i++)
+ c_gic->priority_level[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_PRI_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_CPU_NUM; i++)
+ c_gic->spi_target[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_SPI_TARGET_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_CONFIG_CPU_NUM; i++)
+ c_gic->config[i] =
+ readl_relaxed(c_gic->base +
+ GIC_DIST_CONFIG_SPI0 + i * 4);
+}
+
+/*
+ * Restore GIC Dist CPU registers
+ *
+ * This needs to be called by all cpu:s which will not call
+ * restore_gic_dist_common(). Only the registers of the GIC which are
+ * banked will be saved.
+ */
+static void restore_gic_dist_cpu(struct context_gic_dist_cpu *c_gic)
+{
+ int i;
+
+ for (i = 0; i < GIC_DIST_CONFIG_CPU_NUM; i++)
+ writel_relaxed(c_gic->config[i],
+ c_gic->base +
+ GIC_DIST_CONFIG_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_SPI_TARGET_CPU_NUM; i++)
+ writel_relaxed(c_gic->spi_target[i],
+ c_gic->base +
+ GIC_DIST_SPI_TARGET_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_PRI_CPU_NUM; i++)
+ writel_relaxed(c_gic->priority_level[i],
+ c_gic->base +
+ GIC_DIST_PRI_SPI0 + i * 4);
+
+ for (i = 0; i < GIC_DIST_ENABLE_SET_CPU_NUM; i++)
+ writel_relaxed(c_gic->enable_set[i],
+ c_gic->base +
+ GIC_DIST_ENABLE_SET_SPI0 + i * 4);
+}
+
+/*
+ * Disable interrupts that are not necessary
+ * to have turned on during ApDeepSleep.
+ */
+void context_gic_dist_disable_unneeded_irqs(void)
+{
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_0);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_32);
+
+ /* Leave PRCMU IRQ 0 and 1 enabled */
+ writel(0xffff3fff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_64);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_96);
+
+ writel(0xffffffff,
+ context_gic_dist_common.base +
+ GIC_DIST_ENABLE_CLEAR_128);
+}
+
+static void save_scu(void)
+{
+ context_scu.ctrl =
+ readl_relaxed(context_scu.base + SCU_CTRL);
+ context_scu.cpu_pwrstatus =
+ readl_relaxed(context_scu.base + SCU_CPU_STATUS);
+ context_scu.inv_all_nonsecure =
+ readl_relaxed(context_scu.base + SCU_INVALIDATE);
+ context_scu.filter_start_addr =
+ readl_relaxed(context_scu.base + SCU_FILTER_STARTADDR);
+ context_scu.filter_end_addr =
+ readl_relaxed(context_scu.base + SCU_FILTER_ENDADDR);
+ context_scu.access_ctrl_sac =
+ readl_relaxed(context_scu.base + SCU_ACCESS_CTRL_SAC);
+}
+
+static void restore_scu(void)
+{
+ writel_relaxed(context_scu.ctrl,
+ context_scu.base + SCU_CTRL);
+ writel_relaxed(context_scu.cpu_pwrstatus,
+ context_scu.base + SCU_CPU_STATUS);
+ writel_relaxed(context_scu.inv_all_nonsecure,
+ context_scu.base + SCU_INVALIDATE);
+ writel_relaxed(context_scu.filter_start_addr,
+ context_scu.base + SCU_FILTER_STARTADDR);
+ writel_relaxed(context_scu.filter_end_addr,
+ context_scu.base + SCU_FILTER_ENDADDR);
+ writel_relaxed(context_scu.access_ctrl_sac,
+ context_scu.base + SCU_ACCESS_CTRL_SAC);
+}
+
+/*
+ * Save VAPE context
+ */
+void context_vape_save(void)
+{
+ atomic_notifier_call_chain(&context_ape_notifier_list,
+ CONTEXT_APE_SAVE, NULL);
+
+ if (cpu_is_u5500())
+ u5500_context_save_icn();
+ if (cpu_is_u8500())
+ u8500_context_save_icn();
+ if (cpu_is_u9540())
+ u9540_context_save_icn();
+
+ save_stm_ape();
+
+ save_tpiu();
+
+ save_prcc();
+}
+
+/*
+ * Restore VAPE context
+ */
+void context_vape_restore(void)
+{
+ restore_prcc();
+
+ restore_tpiu();
+
+ restore_stm_ape();
+
+ if (cpu_is_u5500())
+ u5500_context_restore_icn();
+ if (cpu_is_u8500())
+ u8500_context_restore_icn();
+ if (cpu_is_u9540())
+ u9540_context_restore_icn();
+
+ atomic_notifier_call_chain(&context_ape_notifier_list,
+ CONTEXT_APE_RESTORE, NULL);
+}
+
+/*
+ * Save FSMC registers that will be reset
+ * during power save.
+ */
+void context_fsmc_save(void)
+{
+ fsmc_base_addr = ioremap_nocache(U8500_FSMC_BASE, 8);
+ fsmc_bcr0 = readl(fsmc_base_addr);
+}
+
+/*
+ * Restore FSMC registers that will be reset
+ * during power save.
+ */
+void context_fsmc_restore(void)
+{
+ writel(fsmc_bcr0, fsmc_base_addr);
+ iounmap(fsmc_base_addr);
+}
+
+/*
+ * Save GPIO registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_save(void)
+{
+ int i;
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ gpio_save[i][0] = readl(gpio_bankaddr[i] + NMK_GPIO_AFSLA);
+ gpio_save[i][1] = readl(gpio_bankaddr[i] + NMK_GPIO_AFSLB);
+ gpio_save[i][2] = readl(gpio_bankaddr[i] + NMK_GPIO_PDIS);
+ gpio_save[i][3] = readl(gpio_bankaddr[i] + NMK_GPIO_DIR);
+ gpio_save[i][4] = readl(gpio_bankaddr[i] + NMK_GPIO_DAT);
+ gpio_save[i][6] = readl(gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ }
+
+}
+
+/*
+ * Restore GPIO registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_restore(void)
+{
+ int i;
+ u32 output_state;
+ u32 pull_up;
+ u32 pull_down;
+ u32 pull;
+
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(gpio_save[i][2], gpio_bankaddr[i] + NMK_GPIO_PDIS);
+
+ writel(gpio_save[i][3], gpio_bankaddr[i] + NMK_GPIO_DIR);
+
+ /* Set the high outputs. outpute_state = GPIO_DIR & GPIO_DAT */
+ output_state = gpio_save[i][3] & gpio_save[i][4];
+ writel(output_state, gpio_bankaddr[i] + NMK_GPIO_DATS);
+
+ /*
+ * Set the low outputs.
+ * outpute_state = ~(GPIO_DIR & GPIO_DAT) & GPIO_DIR
+ */
+ output_state = ~(gpio_save[i][3] & gpio_save[i][4]) &
+ gpio_save[i][3];
+ writel(output_state, gpio_bankaddr[i] + NMK_GPIO_DATC);
+
+ /*
+ * Restore pull up/down.
+ * Only write pull up/down settings on inputs where
+ * PDIS is not set.
+ * pull = (~GPIO_DIR & ~GPIO_PDIS)
+ */
+ pull = (~gpio_save[i][3] & ~gpio_save[i][2]);
+ nmk_gpio_read_pull(i, &pull_up);
+
+ pull_down = pull & ~pull_up;
+ pull_up = pull & pull_up;
+ /* Set pull ups */
+ writel(pull_up, gpio_bankaddr[i] + NMK_GPIO_DATS);
+ /* Set pull downs */
+ writel(pull_down, gpio_bankaddr[i] + NMK_GPIO_DATC);
+
+ writel(gpio_save[i][6], gpio_bankaddr[i] + NMK_GPIO_SLPC);
+ }
+
+}
+
+/*
+ * Restore GPIO mux registers that might be modified
+ * for power save reasons.
+ */
+void context_gpio_restore_mux(void)
+{
+ int i;
+
+ /* Change mux settings */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(gpio_save[i][0], gpio_bankaddr[i] + NMK_GPIO_AFSLA);
+ writel(gpio_save[i][1], gpio_bankaddr[i] + NMK_GPIO_AFSLB);
+ }
+}
+
+/*
+ * Safe sequence used to switch IOs between GPIO and Alternate-C mode:
+ * - Save SLPM registers (Not done.)
+ * - Set SLPM=0 for the IOs you want to switch. (We assume that all
+ * SLPM registers already are 0 except for the ones that wants to
+ * have the mux connected in sleep (e.g modem STM)).
+ * - Configure the GPIO registers for the IOs that are being switched
+ * - Set IOFORCE=1
+ * - Modify the AFLSA/B registers for the IOs that are being switched
+ * - Set IOFORCE=0
+ * - Restore SLPM registers (Not done.)
+ * - Any spurious wake up event during switch sequence to be ignored
+ * and cleared
+ */
+void context_gpio_mux_safe_switch(bool begin)
+{
+ int i;
+
+ static u32 rwimsc[GPIO_NUM_BANKS];
+ static u32 fwimsc[GPIO_NUM_BANKS];
+
+ if (begin) {
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ /* Save registers */
+ rwimsc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ fwimsc[i] = readl(gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+
+ /* Prevent spurious wakeups */
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ writel(0, gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+ }
+
+ ux500_pm_prcmu_set_ioforce(true);
+ } else {
+ ux500_pm_prcmu_set_ioforce(false);
+
+ /* Restore wake up settings */
+ for (i = 0; i < GPIO_NUM_BANKS; i++) {
+ writel(rwimsc[i], gpio_bankaddr[i] + NMK_GPIO_RWIMSC);
+ writel(fwimsc[i], gpio_bankaddr[i] + NMK_GPIO_FWIMSC);
+ }
+ }
+}
+
+/*
+ * Save common
+ *
+ * This function must be called once for all cores before going to deep sleep.
+ */
+void context_varm_save_common(void)
+{
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_COMMON_SAVE, NULL);
+
+ /* Save common parts */
+ save_gic_dist_common();
+ save_scu();
+}
+
+/*
+ * Restore common
+ *
+ * This function must be called once for all cores when waking up from deep
+ * sleep.
+ */
+void context_varm_restore_common(void)
+{
+ /* Restore common parts */
+ restore_scu();
+ restore_gic_dist_common();
+
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_COMMON_RESTORE, NULL);
+}
+
+/*
+ * Save core
+ *
+ * This function must be called once for each cpu core before going to deep
+ * sleep.
+ */
+void context_varm_save_core(void)
+{
+ int cpu = smp_processor_id();
+
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_CORE_SAVE, NULL);
+
+ per_cpu(varm_cp15_pointer, cpu) = per_cpu(varm_cp15_backup_stack, cpu);
+
+ /* Save core */
+ twd_save();
+ save_gic_if_cpu(&per_cpu(context_gic_cpu, cpu));
+ save_gic_dist_cpu(&per_cpu(context_gic_dist_cpu, cpu));
+ context_save_cp15_registers(&per_cpu(varm_cp15_pointer, cpu));
+}
+
+/*
+ * Restore core
+ *
+ * This function must be called once for each cpu core when waking up from
+ * deep sleep.
+ */
+void context_varm_restore_core(void)
+{
+ int cpu = smp_processor_id();
+
+ /* Restore core */
+ context_restore_cp15_registers(&per_cpu(varm_cp15_pointer, cpu));
+ restore_gic_dist_cpu(&per_cpu(context_gic_dist_cpu, cpu));
+ restore_gic_if_cpu(&per_cpu(context_gic_cpu, cpu));
+ twd_restore();
+
+ atomic_notifier_call_chain(&context_arm_notifier_list,
+ CONTEXT_ARM_CORE_RESTORE, NULL);
+}
+
+/*
+ * Save CPU registers
+ *
+ * This function saves ARM registers.
+ */
+void context_save_cpu_registers(void)
+{
+ int cpu = smp_processor_id();
+
+ per_cpu(varm_registers_pointer, cpu) =
+ per_cpu(varm_registers_backup_stack, cpu);
+ context_save_arm_registers(&per_cpu(varm_registers_pointer, cpu));
+}
+
+/*
+ * Restore CPU registers
+ *
+ * This function restores ARM registers.
+ */
+void context_restore_cpu_registers(void)
+{
+ int cpu = smp_processor_id();
+
+ context_restore_arm_registers(&per_cpu(varm_registers_pointer, cpu));
+}
+
+/*
+ * This function stores CP15 registers related to cache and mmu
+ * in backup SRAM. It also stores stack pointer, CPSR
+ * and return address for the PC in backup SRAM and
+ * does wait for interrupt.
+ */
+void context_save_to_sram_and_wfi(bool cleanL2cache)
+{
+ int cpu = smp_processor_id();
+
+ context_save_to_sram_and_wfi_internal(backup_sram_storage[cpu],
+ cleanL2cache);
+}
+
+static int __init context_init(void)
+{
+ int i;
+ void __iomem *ux500_backup_ptr;
+
+ /* allocate backup pointer for RAM data */
+ ux500_backup_ptr = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(U8500_BACKUPRAM_SIZE));
+
+ if (!ux500_backup_ptr) {
+ pr_warning("context: could not allocate backup memory\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * ROM code addresses to store backup contents,
+ * pass the physical address of back up to ROM code
+ */
+ writel(virt_to_phys(ux500_backup_ptr),
+ IO_ADDRESS(U8500_EXT_RAM_LOC_BACKUPRAM_ADDR));
+
+ if (cpu_is_u5500()) {
+ writel(IO_ADDRESS(U5500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS(U5500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ context_tpiu.base = ioremap(U5500_TPIU_BASE, SZ_4K);
+ context_stm_ape.base = ioremap(U5500_STM_REG_BASE, SZ_4K);
+ context_scu.base = ioremap(U5500_SCU_BASE, SZ_4K);
+
+ context_prcc[0].base = ioremap(U5500_CLKRST1_BASE, SZ_4K);
+ context_prcc[1].base = ioremap(U5500_CLKRST2_BASE, SZ_4K);
+ context_prcc[2].base = ioremap(U5500_CLKRST3_BASE, SZ_4K);
+ context_prcc[3].base = ioremap(U5500_CLKRST5_BASE, SZ_4K);
+ context_prcc[4].base = ioremap(U5500_CLKRST6_BASE, SZ_4K);
+
+ context_gic_dist_common.base = ioremap(U5500_GIC_DIST_BASE, SZ_4K);
+ per_cpu(context_gic_cpu, 0).base = ioremap(U5500_GIC_CPU_BASE, SZ_4K);
+ } else if (cpu_is_u8500() || cpu_is_u9540()) {
+ /* Give logical address to backup RAM. For both CPUs */
+ if (cpu_is_u9540()) {
+ writel(IO_ADDRESS_DB9540_ROM(U9540_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS_DB9540_ROM(U9540_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+ } else {
+ writel(IO_ADDRESS(U8500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU0_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+
+ writel(IO_ADDRESS(U8500_PUBLIC_BOOT_ROM_BASE),
+ IO_ADDRESS(U8500_CPU1_BACKUPRAM_ADDR_PUBLIC_BOOT_ROM_LOG_ADDR));
+ }
+
+ context_tpiu.base = ioremap(U8500_TPIU_BASE, SZ_4K);
+ context_stm_ape.base = ioremap(U8500_STM_REG_BASE, SZ_4K);
+ context_scu.base = ioremap(U8500_SCU_BASE, SZ_4K);
+
+ /* PERIPH4 is always on, so no need saving prcc */
+ context_prcc[0].base = ioremap(U8500_CLKRST1_BASE, SZ_4K);
+ context_prcc[1].base = ioremap(U8500_CLKRST2_BASE, SZ_4K);
+ context_prcc[2].base = ioremap(U8500_CLKRST3_BASE, SZ_4K);
+ context_prcc[3].base = ioremap(U8500_CLKRST5_BASE, SZ_4K);
+ context_prcc[4].base = ioremap(U8500_CLKRST6_BASE, SZ_4K);
+
+ context_gic_dist_common.base = ioremap(U8500_GIC_DIST_BASE, SZ_4K);
+ per_cpu(context_gic_cpu, 0).base = ioremap(U8500_GIC_CPU_BASE, SZ_4K);
+ }
+
+ per_cpu(context_gic_dist_cpu, 0).base = context_gic_dist_common.base;
+
+ for (i = 1; i < num_possible_cpus(); i++) {
+ per_cpu(context_gic_cpu, i).base
+ = per_cpu(context_gic_cpu, 0).base;
+ per_cpu(context_gic_dist_cpu, i).base
+ = per_cpu(context_gic_dist_cpu, 0).base;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(context_prcc); i++) {
+ const int clusters[] = {1, 2, 3, 5, 6};
+ char clkname[10];
+
+ snprintf(clkname, sizeof(clkname), "PERIPH%d", clusters[i]);
+
+ context_prcc[i].clk = clk_get_sys(clkname, NULL);
+ BUG_ON(IS_ERR(context_prcc[i].clk));
+ }
+
+ if (cpu_is_u8500()) {
+ u8500_context_init();
+ } else if (cpu_is_u5500()) {
+ u5500_context_init();
+ } else if (cpu_is_u9540()) {
+ u9540_context_init();
+ } else {
+ printk(KERN_ERR "context: unknown hardware!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+subsys_initcall(context_init);
diff --git a/arch/arm/mach-ux500/pm/context_arm.S b/arch/arm/mach-ux500/pm/context_arm.S
new file mode 100644
index 00000000000..edb894d6a35
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/context_arm.S
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ * Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/linkage.h>
+#include <mach/hardware.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/*
+ * Save and increment macro
+ */
+.macro SAVE_AND_INCREMENT FROM_REG TO_REG
+ str \FROM_REG, [\TO_REG], #+4
+.endm
+
+/*
+ * Decrement and restore macro
+ */
+.macro DECREMENT_AND_RESTORE FROM_REG TO_REG
+ ldr \TO_REG, [\FROM_REG, #-4]!
+.endm
+
+/*
+ * Save ARM registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ *
+ * Backup stack operations:
+ * + {sp, lr}^
+ * + cpsr
+ * + {r3, r8-r14} (FIQ mode: r3=spsr)
+ * + {r3, r13, r14} (IRQ mode: r3=spsr)
+ * + {r3, r13, r14} (abort mode: r3=spsr)
+ * + {r3, r13, r14} (undef mode: r3=spsr)
+ */
+ .align
+ .section ".text", "ax"
+ENTRY(context_save_arm_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack
+ ldr r1, [r0] @ Read backup stack pointer
+
+ARM( stmia r1, {sp, lr}^ ) @ Store user mode sp and lr
+ @ registers
+ARM( add r1, r1, #8 ) @ Update backup pointer (not
+ @ done in previous instruction)
+THUMB( str sp, [r1], #+4 )
+THUMB( str lr, [r1], #+4 )
+
+ mrs r2, cpsr @ Get CPSR
+ SAVE_AND_INCREMENT r2 r1 @ Save CPSR register
+ orr r2, r2, #0xc0 @ Disable FIQ and IRQ
+ bic r2, r2, #0x1f @ Setup r2 to change mode
+
+ @ The suffix to CPSR refers to which field(s) of the CPSR is
+ @ rereferenced (you can specify one or more). Defined fields are:
+ @
+ @ c - control
+ @ x - extension
+ @ s - status
+ @ f - flags
+
+ orr r3, r2, #0x11 @ Save FIQ mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r8-r14} )
+THUMB( stmia r1!, {r3, r8-r12, r14} )
+THUMB( str r13, [r1], #+4 )
+
+
+ orr r3, r2, #0x12 @ Save IRQ mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r13, r14} )
+THUMB( stmia r1!, {r3, r14} )
+THUMB( str r13, [r1], #+4 )
+
+ orr r3, r2, #0x17 @ Save abort mode registers +
+ @ common mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r13, r14} )
+THUMB( stmia r1!, {r3, r14} )
+THUMB( str r13, [r1], #+4 )
+
+ orr r3, r2, #0x1B @ Save undef mode registers
+ msr cpsr_cxsf, r3
+ mrs r3, spsr
+ARM( stmia r1!, {r3, r13, r14} )
+THUMB( stmia r1!, {r3, r14} )
+THUMB( str r13, [r1], #+4 )
+
+ orr r3, r2, #0x13 @ Return to supervisor mode
+ msr cpsr_cxsf, r3
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+
+/*
+ * Restore ARM registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ *
+ * Backup stack operations:
+ * - {r3, r13, r14} (undef mode: spsr=r3)
+ * - {r3, r13, r14} (abort mode: spsr=r3)
+ * - {r3, r13, r14} (IRQ mode: spsr=r3)
+ * - {r3, r8-r14} (FIQ mode: spsr=r3)
+ * - cpsr
+ * - {sp, lr}^
+ */
+ .align
+ .section ".text", "ax"
+ENTRY(context_restore_arm_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack
+ ldr r1, [r0] @ Read backup stack pointer
+
+ mrs r2, cpsr @ Get CPSR
+ orr r2, r2, #0xc0 @ Disable FIQ and IRQ
+ bic r2, r2, #0x1f @ Setup r2 to change mode
+
+ orr r3, r2, #0x1b @ Restore undef mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r13, r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r14} )
+ msr spsr_cxsf, r3
+
+ orr r3, r2, #0x17 @ Restore abort mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r13, r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r14} )
+ msr spsr_cxsf, r3
+
+ orr r3, r2, #0x12 @ Restore IRQ mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r13, r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r14} )
+ msr spsr_cxsf, r3
+
+ orr r3, r2, #0x11 @ Restore FIQ mode registers
+ msr cpsr_cxsf, r3
+ARM( ldmdb r1!, {r3, r8-r14} )
+THUMB( ldr r13, [r1], #-4 )
+THUMB( ldmdb r1!, {r3, r8-r12, r14} )
+
+ msr spsr_cxsf, r3
+
+ DECREMENT_AND_RESTORE r1 r3 @ Restore cpsr register
+ msr cpsr_cxsf, r3
+
+ARM( ldmdb r1, {sp, lr}^ ) @ Restore sp and lr registers
+ARM( sub r1, r1, #8 ) @ Update backup pointer (not
+ @ done in previous instruction)
+THUMB( ldr lr, [r1], #-4 )
+THUMB( ldr sp, [r1], #-4 )
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+
+/*
+ * Save CP15 registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ *
+ * TTBR0, TTBR1, TTBRC, DACR CP15 registers are restored by boot ROM from SRAM.
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_save_cp15_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack (r3 is saved due
+ @ to 8 byte aligned stack)
+ ldr r1, [r0] @ Read backup stack pointer
+
+ mrc p15, 0, r2, c12, c0, 0 @ Read Non-secure Vector Base
+ @ Address Register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c10, c2, 0 @ Access primary memory region
+ @ remap register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c10, c2, 1 @ Access normal memory region
+ @ remap register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c13, c0, 1 @ Read Context ID Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c13, c0, 2 @ Read Thread ID registers,
+ @ this register is both user
+ @ and privileged R/W accessible
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c13, c0, 3 @ Read Thread ID registers,
+ @ this register is user
+ @ read-only and privileged R/W
+ @ accessible.
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c13, c0, 4 @ Read Thread ID registers,
+ @ this register is privileged
+ @ R/W accessible only.
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 2, r2, c0, c0, 0 @ Cache Size Selection Register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c9, c12, 0 @ Read PMNC Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c12, 1 @ Read PMCNTENSET Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c12, 5 @ Read PMSELR Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c13, 0 @ Read PMCCNTR Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c13, 1 @ Read PMXEVTYPER Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c14, 0 @ Read PMUSERENR Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c14, 1 @ Read PMINTENSET Register
+ SAVE_AND_INCREMENT r2 r1
+ mrc p15, 0, r2, c9, c14, 2 @ Read PMINTENCLR Register
+ SAVE_AND_INCREMENT r2 r1
+
+ mrc p15, 0, r2, c1, c0, 2 @ Read CPACR Register
+ SAVE_AND_INCREMENT r2 r1
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+
+/*
+ * Restore CP15 registers
+ *
+ * This function must be called in supervisor mode.
+ *
+ * r0 = address to backup stack pointer
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_restore_cp15_registers)
+ stmfd sp!, {r1, r2, r3, lr} @ Save on stack (r3 is saved due
+ @ to 8 byte aligned stack)
+ ldr r1, [r0] @ Read backup stack pointer
+
+ DECREMENT_AND_RESTORE r1 r2 @ Write CPACR register
+ mcr p15, 0, r2, c1, c0, 2
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c14, 2 @ Write PMINTENCLR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c14, 1 @ Write PMINTENSET Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c14, 0 @ Write PMUSERENR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c13, 1 @ Write PMXEVTYPER Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c13, 0 @ Write PMCCNTR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c12, 5 @ Write PMSELR Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c12, 1 @ Write PMCNTENSET Register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c9, c12, 0 @ Write PMNC Register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 2, r2, c0, c0, 0 @ Cache Size Selection Register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 4 @ Write Thread ID registers,
+ @ this register is privileged
+ @ R/W accessible only
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 3 @ Write Thread ID registers,
+ @ this register is user
+ @ read-only and privileged R/W
+ @ accessible
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 2 @ Write Thread ID registers,
+ @ this register is both user
+ @ and privileged R/W accessible
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c13, c0, 1 @ Write Context ID Register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c10, c2, 1 @ Access normal memory region
+ @ remap register
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c10, c2, 0 @ Access primary memory region
+ @ remap register
+
+ DECREMENT_AND_RESTORE r1 r2
+ mcr p15, 0, r2, c12, c0, 0 @ Write Non-secure Vector Base
+ @ Address Register
+
+ str r1, [r0] @ Write backup stack pointer
+ ldmfd sp!, {r1, r2, r3, pc} @ Restore registers and return
+
+
+/*
+ * L1 cache clean function. Commit 'dirty' data from L1
+ * to L2 cache.
+ *
+ * r0, r1, r2, used locally
+ *
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_clean_l1_cache_all)
+
+ mov r0, #0 @ swith to cache level 0
+ @ (L1 cache)
+ mcr p15, 2, r0, c0, c0, 0 @ select current cache level
+ @ in cssr
+
+ dmb
+ mov r1, #0 @ r1 = way index
+wayLoopL1clean:
+ mov r0, #0 @ r0 = line index
+lineLoopL1clean:
+ mov r2, r1, lsl #30 @ TODO: OK to hard-code
+ @ SoC-specific L1 cache details?
+ mov r3, r0, lsl #5
+ add r2, r3
+@ add r2, r0, lsl #5
+ mcr p15, 0, r2, c7, c10, 2 @ Clean cache by set/way
+ add r0, r0, #1
+ cmp r0, #256 @ TODO: Ok with hard-coded
+ @ set/way sizes or do we have to
+ @ read them from ARM regs? Is it
+ @ set correctly in silicon?
+ bne lineLoopL1clean
+ add r1, r1, #1
+ cmp r1, #4 @ TODO: Ditto, sizes...
+ bne wayLoopL1clean
+
+ dsb
+ isb
+ mov pc, lr
+
+ENDPROC(context_clean_l1_cache_all)
+
+/*
+ * Last saves to backup RAM, cache clean and WFI
+ *
+ * r0 = address to backup_sram_storage base adress
+ * r1 = indicate whether also L2 cache should be cleaned
+ */
+ .align 4
+ .section ".text", "ax"
+ENTRY(context_save_to_sram_and_wfi_internal)
+
+ stmfd sp!, {r2-r12, lr} @ save on stack.
+
+ mrc p15, 0, r2, c1, c0, 0 @ read cp15 system control
+ @ register
+ str r2, [r0, #0x00]
+ mrc p15, 0, r2, c2, c0, 0 @ read cp15 ttb0 register
+ str r2, [r0, #0x04]
+ mrc p15, 0, r2, c2, c0, 1 @ read cp15 ttb1 register
+ str r2, [r0, #0x08]
+ mrc p15, 0, r2, c2, c0, 2 @ read cp15 ttb control register
+ str r2, [r0, #0x0C]
+ mrc p15, 0, r2, c3, c0, 0 @ read domain access control
+ @ register
+ str r2, [r0, #0x10]
+
+ ldr r2, =return_here
+ str r2, [r0, #0x14] @ save program counter restore
+ @ value to backup_sram_storage
+ mrs r2, cpsr
+ str r2, [r0, #0x18] @ save cpsr to
+ @ backup_sram_storage
+ str sp, [r0, #0x1c] @ save sp to backup_sram_storage
+
+ mov r4, r1 @ Set r4 = cleanL2cache, r1
+ @ will be destroyed by
+ @ v7_clean_l1_cache_all
+
+ bl context_clean_l1_cache_all @ Commit all dirty data in L1
+ @ cache to L2 without
+ @ invalidating
+
+ dsb @ data synchronization barrier
+ isb @ instruction synchronization
+ @ barrier
+ wfi @ wait for interrupt
+
+return_here: @ both cores return here
+ @ now we are out deep sleep
+ @ with all the context lost
+ @ except pc, sp and cpsr
+
+ ldmfd sp!, {r2-r12, pc} @ restore from stack
+
diff --git a/arch/arm/mach-ux500/pm/performance.c b/arch/arm/mach-ux500/pm/performance.c
new file mode 100644
index 00000000000..04aca3cb5bd
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/performance.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Johan Rudholm <johan.rudholm@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/genhd.h>
+#include <linux/major.h>
+#include <linux/cdev.h>
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/kernel.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/cpu.h>
+#include <linux/pm_qos.h>
+
+#include <mach/irqs.h>
+
+#define WLAN_PROBE_DELAY 3000 /* 3 seconds */
+#define WLAN_LIMIT (3000/3) /* If we have more than 1000 irqs per second */
+
+/*
+ * MMC TODO:
+ * o Develop a more power-aware algorithm
+ * o Make the parameters visible through debugfs
+ * o Get the value of CONFIG_MMC_BLOCK_MINORS in runtime instead, since
+ * it may be altered by drivers/mmc/card/block.c
+ */
+
+/* Sample reads and writes every n ms */
+#define PERF_MMC_PROBE_DELAY 1000
+/* Read threshold, sectors/second */
+#define PERF_MMC_LIMIT_READ 10240
+/* Write threshold, sectors/second */
+#define PERF_MMC_LIMIT_WRITE 8192
+/* Nr of MMC devices */
+#define PERF_MMC_HOSTS 8
+
+/*
+ * Rescan for new MMC devices every
+ * PERF_MMC_PROBE_DELAY * PERF_MMC_RESCAN_CYCLES ms
+ */
+#define PERF_MMC_RESCAN_CYCLES 10
+
+#ifdef CONFIG_MMC_BLOCK
+static struct delayed_work work_mmc;
+#endif
+
+static struct delayed_work work_wlan_workaround;
+static struct pm_qos_request wlan_pm_qos_latency;
+static bool wlan_pm_qos_is_latency_0;
+
+static void wlan_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB8500_SDMMC1, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > WLAN_LIMIT) {
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "wlan", 125);
+ if (!wlan_pm_qos_is_latency_0) {
+ /*
+ * The wake up latency is set to 0 to prevent
+ * the system from going to sleep. This improves
+ * the wlan throughput in DMA mode.
+ * The wake up latency from sleep adds ~5% overhead
+ * for TX in some cases.
+ * This change doesn't increase performance for wlan
+ * PIO since the CPU usage prevents sleep in this mode.
+ */
+ pm_qos_add_request(&wlan_pm_qos_latency,
+ PM_QOS_CPU_DMA_LATENCY, 0);
+ wlan_pm_qos_is_latency_0 = true;
+ }
+ } else {
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "wlan", 25);
+ if (wlan_pm_qos_is_latency_0) {
+ pm_qos_remove_request(&wlan_pm_qos_latency);
+ wlan_pm_qos_is_latency_0 = false;
+ }
+ }
+
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &work_wlan_workaround,
+ msecs_to_jiffies(WLAN_PROBE_DELAY));
+}
+
+#ifdef CONFIG_MMC_BLOCK
+/*
+ * Loop through every CONFIG_MMC_BLOCK_MINORS'th minor device for
+ * MMC_BLOCK_MAJOR, get the struct gendisk for each device. Returns
+ * nr of found disks. Populate mmc_disks.
+ */
+static int scan_mmc_devices(struct gendisk *mmc_disks[])
+{
+ dev_t devnr;
+ int i, j = 0, part;
+ struct gendisk *mmc_devices[256 / CONFIG_MMC_BLOCK_MINORS];
+
+ memset(&mmc_devices, 0, sizeof(mmc_devices));
+
+ for (i = 0; i * CONFIG_MMC_BLOCK_MINORS < 256; i++) {
+ devnr = MKDEV(MMC_BLOCK_MAJOR, i * CONFIG_MMC_BLOCK_MINORS);
+ mmc_devices[i] = get_gendisk(devnr, &part);
+
+ /* Invalid capacity of device, do not add to list */
+ if (!mmc_devices[i] || !get_capacity(mmc_devices[i]))
+ continue;
+
+ mmc_disks[j] = mmc_devices[i];
+ j++;
+
+ if (j == PERF_MMC_HOSTS)
+ break;
+ }
+
+ return j;
+}
+
+/*
+ * Sample sectors read and written to any MMC devices, update PRCMU
+ * qos requirement
+ */
+static void mmc_load(struct work_struct *work)
+{
+ static unsigned long long old_sectors_read[PERF_MMC_HOSTS];
+ static unsigned long long old_sectors_written[PERF_MMC_HOSTS];
+ static struct gendisk *mmc_disks[PERF_MMC_HOSTS];
+ static int cycle, nrdisk;
+ static bool old_mode;
+ unsigned long long sectors;
+ bool new_mode = false;
+ int i;
+
+ if (!cycle) {
+ memset(&mmc_disks, 0, sizeof(mmc_disks));
+ nrdisk = scan_mmc_devices(mmc_disks);
+ cycle = PERF_MMC_RESCAN_CYCLES;
+ }
+ cycle--;
+
+ for (i = 0; i < nrdisk; i++) {
+ sectors = part_stat_read(&(mmc_disks[i]->part0),
+ sectors[READ]);
+
+ if (old_sectors_read[i] &&
+ sectors > old_sectors_read[i] &&
+ (sectors - old_sectors_read[i]) >
+ PERF_MMC_LIMIT_READ)
+ new_mode = true;
+
+ old_sectors_read[i] = sectors;
+ sectors = part_stat_read(&(mmc_disks[i]->part0),
+ sectors[WRITE]);
+
+ if (old_sectors_written[i] &&
+ sectors > old_sectors_written[i] &&
+ (sectors - old_sectors_written[i]) >
+ PERF_MMC_LIMIT_WRITE)
+ new_mode = true;
+
+ old_sectors_written[i] = sectors;
+ }
+
+ if (!old_mode && new_mode)
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "mmc", 125);
+
+ if (old_mode && !new_mode)
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "mmc", 25);
+
+ old_mode = new_mode;
+
+ schedule_delayed_work(&work_mmc,
+ msecs_to_jiffies(PERF_MMC_PROBE_DELAY));
+
+}
+#endif /* CONFIG_MMC_BLOCK */
+
+static int __init performance_register(void)
+{
+ int ret;
+
+#ifdef CONFIG_MMC_BLOCK
+ ret = prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "mmc", 25);
+ if (ret) {
+ pr_err("%s: Failed to add PRCMU req for mmc\n", __func__);
+ goto out;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&work_mmc, mmc_load);
+
+ schedule_delayed_work(&work_mmc,
+ msecs_to_jiffies(PERF_MMC_PROBE_DELAY));
+#endif
+
+ ret = prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "wlan", 25);
+ if (ret) {
+ pr_err("%s: Failed to add PRCMU req for wlan\n", __func__);
+ goto out;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&work_wlan_workaround,
+ wlan_load);
+
+ schedule_delayed_work_on(0, &work_wlan_workaround,
+ msecs_to_jiffies(WLAN_PROBE_DELAY));
+out:
+ return ret;
+}
+late_initcall(performance_register);
diff --git a/arch/arm/mach-ux500/pm/pm.c b/arch/arm/mach-ux500/pm/pm.c
new file mode 100644
index 00000000000..880e9763a0f
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/pm.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <asm/hardware/gic.h>
+#include <asm/processor.h>
+
+#include <mach/hardware.h>
+#include <mach/pm.h>
+
+#define STABILIZATION_TIME 30 /* us */
+#define GIC_FREEZE_DELAY 1 /* us */
+
+#define PRCM_ARM_WFI_STANDBY_CPU0_WFI 0x8
+#define PRCM_ARM_WFI_STANDBY_CPU1_WFI 0x10
+
+/* Dual A9 core interrupt management unit registers */
+#define PRCM_A9_MASK_REQ 0x328
+#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1
+#define PRCM_A9_MASK_ACK 0x32c
+
+#define PRCM_ARMITMSK31TO0 0x11c
+#define PRCM_ARMITMSK63TO32 0x120
+#define PRCM_ARMITMSK95TO64 0x124
+#define PRCM_ARMITMSK127TO96 0x128
+#define PRCM_POWER_STATE_VAL 0x25C
+#define PRCM_ARMITVAL31TO0 0x260
+#define PRCM_ARMITVAL63TO32 0x264
+#define PRCM_ARMITVAL95TO64 0x268
+#define PRCM_ARMITVAL127TO96 0x26C
+
+/* ARM WFI Standby signal register */
+#define PRCM_ARM_WFI_STANDBY 0x130
+
+/* IO force */
+#define PRCM_IOCR 0x310
+#define PRCM_IOCR_IOFORCE 0x1
+#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
+int ux500_console_uart_gpio_pin = CONFIG_UX500_CONSOLE_UART_GPIO_PIN;
+#endif
+static u32 u8500_gpio_banks[] = {U8500_GPIOBANK0_BASE,
+ U8500_GPIOBANK1_BASE,
+ U8500_GPIOBANK2_BASE,
+ U8500_GPIOBANK3_BASE,
+ U8500_GPIOBANK4_BASE,
+ U8500_GPIOBANK5_BASE,
+ U8500_GPIOBANK6_BASE,
+ U8500_GPIOBANK7_BASE,
+ U8500_GPIOBANK8_BASE};
+
+static u32 u5500_gpio_banks[] = {U5500_GPIOBANK0_BASE,
+ U5500_GPIOBANK1_BASE,
+ U5500_GPIOBANK2_BASE,
+ U5500_GPIOBANK3_BASE,
+ U5500_GPIOBANK4_BASE,
+ U5500_GPIOBANK5_BASE,
+ U5500_GPIOBANK6_BASE,
+ U5500_GPIOBANK7_BASE};
+
+static u32 ux500_gpio_wks[ARRAY_SIZE(u8500_gpio_banks)];
+
+inline int ux500_pm_arm_on_ext_clk(bool leave_arm_pll_on)
+{
+ return 0;
+}
+
+/* Decouple GIC from the interrupt bus */
+void ux500_pm_gic_decouple(void)
+{
+ prcmu_write_masked(PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ);
+
+ (void)prcmu_read(PRCM_A9_MASK_REQ);
+
+ /* TODO: Use the ack bit when possible */
+ udelay(GIC_FREEZE_DELAY); /* Wait for the GIC to freeze */
+}
+
+/* Recouple GIC with the interrupt bus */
+void ux500_pm_gic_recouple(void)
+{
+ prcmu_write_masked(PRCM_A9_MASK_REQ,
+ PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ,
+ 0);
+
+ /* TODO: Use the ack bit when possible */
+}
+
+#define GIC_NUMBER_REGS 5
+bool ux500_pm_gic_pending_interrupt(void)
+{
+ u32 pr; /* Pending register */
+ u32 er; /* Enable register */
+ int i;
+
+ /* 5 registers. STI & PPI not skipped */
+ for (i = 0; i < GIC_NUMBER_REGS; i++) {
+
+ pr = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
+ GIC_DIST_PENDING_SET + i * 4);
+ er = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
+ GIC_DIST_ENABLE_SET + i * 4);
+
+ if (pr & er)
+ return true; /* There is a pending interrupt */
+ }
+ return false;
+}
+
+#define GIC_NUMBER_SPI_REGS 4
+bool ux500_pm_prcmu_pending_interrupt(void)
+{
+ u32 it;
+ u32 im;
+ int i;
+
+ for (i = 0; i < GIC_NUMBER_SPI_REGS; i++) { /* There are 4 registers */
+
+ it = prcmu_read(PRCM_ARMITVAL31TO0 + i * 4);
+ im = prcmu_read(PRCM_ARMITMSK31TO0 + i * 4);
+
+ if (it & im)
+ return true; /* There is a pending interrupt */
+ }
+
+ return false;
+}
+
+void ux500_pm_prcmu_set_ioforce(bool enable)
+{
+ if (enable)
+ prcmu_write_masked(PRCM_IOCR,
+ PRCM_IOCR_IOFORCE,
+ PRCM_IOCR_IOFORCE);
+ else
+ prcmu_write_masked(PRCM_IOCR,
+ PRCM_IOCR_IOFORCE,
+ 0);
+}
+
+void ux500_pm_prcmu_copy_gic_settings(void)
+{
+ u32 er; /* Enable register */
+ int i;
+
+ for (i = 0; i < GIC_NUMBER_SPI_REGS; i++) { /* 4*32 SPI interrupts */
+ /* +1 due to skip STI and PPI */
+ er = readl_relaxed(__io_address(U8500_GIC_DIST_BASE) +
+ GIC_DIST_ENABLE_SET + (i + 1) * 4);
+ prcmu_write(PRCM_ARMITMSK31TO0 + i * 4, er);
+ }
+}
+
+void ux500_pm_gpio_save_wake_up_status(void)
+{
+ int num_banks;
+ u32 *banks;
+ int i;
+
+ if (cpu_is_u5500()) {
+ num_banks = ARRAY_SIZE(u5500_gpio_banks);
+ banks = u5500_gpio_banks;
+ } else {
+ num_banks = ARRAY_SIZE(u8500_gpio_banks);
+ banks = u8500_gpio_banks;
+ }
+
+ nmk_gpio_clocks_enable();
+
+ for (i = 0; i < num_banks; i++)
+ ux500_gpio_wks[i] = readl(__io_address(banks[i]) + NMK_GPIO_WKS);
+
+ nmk_gpio_clocks_disable();
+}
+
+u32 ux500_pm_gpio_read_wake_up_status(unsigned int bank_num)
+{
+ if (WARN_ON(cpu_is_u5500() && bank_num >=
+ ARRAY_SIZE(u5500_gpio_banks)))
+ return 0;
+
+ if (WARN_ON(cpu_is_u8500() && bank_num >=
+ ARRAY_SIZE(u8500_gpio_banks)))
+ return 0;
+
+ return ux500_gpio_wks[bank_num];
+}
+
+/* Check if the other CPU is in WFI */
+bool ux500_pm_other_cpu_wfi(void)
+{
+ if (smp_processor_id()) {
+ /* We are CPU 1 => check if CPU0 is in WFI */
+ if (prcmu_read(PRCM_ARM_WFI_STANDBY) &
+ PRCM_ARM_WFI_STANDBY_CPU0_WFI)
+ return true;
+ } else {
+ /* We are CPU 0 => check if CPU1 is in WFI */
+ if (prcmu_read(PRCM_ARM_WFI_STANDBY) &
+ PRCM_ARM_WFI_STANDBY_CPU1_WFI)
+ return true;
+ }
+
+ return false;
+}
diff --git a/arch/arm/mach-ux500/pm/prcmu-qos-power.c b/arch/arm/mach-ux500/pm/prcmu-qos-power.c
new file mode 100644
index 00000000000..3bd99b766af
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/prcmu-qos-power.c
@@ -0,0 +1,711 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Martin Persson
+ * Per Fransson <per.xx.fransson@stericsson.com>
+ *
+ * Quality of Service for the U8500 PRCM Unit interface driver
+ *
+ * Strongly influenced by kernel/pm_qos_params.c.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/cpufreq.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/cpufreq-dbx500.h>
+
+#include <mach/prcmu-debug.h>
+
+#define ARM_THRESHOLD_FREQ (400000)
+
+static int qos_delayed_cpufreq_notifier(struct notifier_block *,
+ unsigned long, void *);
+
+static s32 cpufreq_requirement_queued;
+static s32 cpufreq_requirement_set;
+
+/*
+ * locking rule: all changes to requirements or prcmu_qos_object list
+ * and prcmu_qos_objects need to happen with prcmu_qos_lock
+ * held, taken with _irqsave. One lock to rule them all
+ */
+struct requirement_list {
+ struct list_head list;
+ union {
+ s32 value;
+ s32 usec;
+ s32 kbps;
+ };
+ char *name;
+};
+
+static s32 max_compare(s32 v1, s32 v2);
+
+struct prcmu_qos_object {
+ struct requirement_list requirements;
+ struct blocking_notifier_head *notifiers;
+ struct miscdevice prcmu_qos_power_miscdev;
+ char *name;
+ s32 default_value;
+ s32 force_value;
+ atomic_t target_value;
+ s32 (*comparitor)(s32, s32);
+};
+
+static struct prcmu_qos_object null_qos;
+static BLOCKING_NOTIFIER_HEAD(prcmu_ape_opp_notifier);
+static BLOCKING_NOTIFIER_HEAD(prcmu_ddr_opp_notifier);
+
+static struct prcmu_qos_object ape_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(ape_opp_qos.requirements.list)
+ },
+ .notifiers = &prcmu_ape_opp_notifier,
+ .name = "ape_opp",
+ /* Target value in % APE OPP */
+ .default_value = 50,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(50),
+ .comparitor = max_compare
+};
+
+static struct prcmu_qos_object ddr_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(ddr_opp_qos.requirements.list)
+ },
+ .notifiers = &prcmu_ddr_opp_notifier,
+ .name = "ddr_opp",
+ /* Target value in % DDR OPP */
+ .default_value = 25,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(25),
+ .comparitor = max_compare
+};
+
+static struct prcmu_qos_object arm_opp_qos = {
+ .requirements = {
+ LIST_HEAD_INIT(arm_opp_qos.requirements.list)
+ },
+ /*
+ * No notifier on ARM opp qos request, since this won't actually
+ * do anything, except changing limits for cpufreq
+ */
+ .name = "arm_opp",
+ /* Target value in % ARM OPP, note can be 125% */
+ .default_value = 25,
+ .force_value = 0,
+ .target_value = ATOMIC_INIT(25),
+ .comparitor = max_compare
+};
+
+static struct prcmu_qos_object *prcmu_qos_array[] = {
+ &null_qos,
+ &ape_opp_qos,
+ &ddr_opp_qos,
+ &arm_opp_qos,
+};
+
+static DEFINE_MUTEX(prcmu_qos_mutex);
+static DEFINE_SPINLOCK(prcmu_qos_lock);
+
+static bool ape_opp_50_partly_25_enabled;
+
+static unsigned long cpufreq_opp_delay = HZ / 5;
+
+unsigned long prcmu_qos_get_cpufreq_opp_delay(void)
+{
+ return cpufreq_opp_delay;
+}
+
+static struct notifier_block qos_delayed_cpufreq_notifier_block = {
+ .notifier_call = qos_delayed_cpufreq_notifier,
+};
+
+void prcmu_qos_set_cpufreq_opp_delay(unsigned long n)
+{
+ if (n == 0) {
+ cpufreq_unregister_notifier(&qos_delayed_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ prcmu_qos_update_requirement(PRCMU_QOS_DDR_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ cpufreq_requirement_set = PRCMU_QOS_DEFAULT_VALUE;
+ cpufreq_requirement_queued = PRCMU_QOS_DEFAULT_VALUE;
+ } else if (cpufreq_opp_delay != 0) {
+ cpufreq_register_notifier(&qos_delayed_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ }
+ cpufreq_opp_delay = n;
+}
+#ifdef CONFIG_CPU_FREQ
+static void update_cpu_limits(s32 extreme_value)
+{
+ int cpu;
+ struct cpufreq_policy policy;
+ int ret;
+ int min_freq, max_freq;
+
+ for_each_online_cpu(cpu) {
+ ret = cpufreq_get_policy(&policy, cpu);
+ if (ret) {
+ pr_err("prcmu qos: get cpufreq policy failed (cpu%d)\n",
+ cpu);
+ continue;
+ }
+
+ ret = dbx500_cpufreq_get_limits(cpu, extreme_value,
+ &min_freq, &max_freq);
+ if (ret)
+ continue;
+ /*
+ * cpufreq fw does not allow frequency change if
+ * "current min freq" > "new max freq" or
+ * "current max freq" < "new min freq".
+ * Thus the intermediate steps below.
+ */
+ if (policy.min > max_freq) {
+ ret = cpufreq_update_freq(cpu, min_freq, policy.max);
+ if (ret)
+ pr_err("prcmu qos: update min cpufreq failed (1)\n");
+ }
+ if (policy.max < min_freq) {
+ ret = cpufreq_update_freq(cpu, policy.min, max_freq);
+ if (ret)
+ pr_err("prcmu qos: update max cpufreq failed (2)\n");
+ }
+
+ ret = cpufreq_update_freq(cpu, min_freq, max_freq);
+ if (ret)
+ pr_err("prcmu qos: update max cpufreq failed (3)\n");
+ }
+
+}
+#else
+static inline void update_cpu_limits(s32 extreme_value) { }
+#endif
+/* static helper function */
+static s32 max_compare(s32 v1, s32 v2)
+{
+ return max(v1, v2);
+}
+
+static void update_target(int target)
+{
+ s32 extreme_value;
+ struct requirement_list *node;
+ unsigned long flags;
+ bool update = false;
+ u8 op;
+
+ mutex_lock(&prcmu_qos_mutex);
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ extreme_value = prcmu_qos_array[target]->default_value;
+
+ if (prcmu_qos_array[target]->force_value != 0) {
+ extreme_value = prcmu_qos_array[target]->force_value;
+ update = true;
+ } else {
+ list_for_each_entry(node,
+ &prcmu_qos_array[target]->requirements.list,
+ list) {
+ extreme_value = prcmu_qos_array[target]->comparitor(
+ extreme_value, node->value);
+ }
+ if (atomic_read(&prcmu_qos_array[target]->target_value)
+ != extreme_value) {
+ update = true;
+ atomic_set(&prcmu_qos_array[target]->target_value,
+ extreme_value);
+ pr_debug("prcmu qos: new target for qos %d is %d\n",
+ target, atomic_read(
+ &prcmu_qos_array[target]->target_value
+ ));
+ }
+ }
+
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+
+ if (!update)
+ goto unlock_and_return;
+
+ if (prcmu_qos_array[target]->notifiers)
+ blocking_notifier_call_chain(prcmu_qos_array[target]->notifiers,
+ (unsigned long)extreme_value,
+ NULL);
+ switch (target) {
+ case PRCMU_QOS_DDR_OPP:
+ switch (extreme_value) {
+ case 50:
+ op = DDR_50_OPP;
+ pr_debug("prcmu qos: set ddr opp to 50%%\n");
+ break;
+ case 100:
+ op = DDR_100_OPP;
+ pr_debug("prcmu qos: set ddr opp to 100%%\n");
+ break;
+ case 25:
+ /* 25% DDR OPP is not supported on 5500 */
+ if (!cpu_is_u5500()) {
+ op = DDR_25_OPP;
+ pr_debug("prcmu qos: set ddr opp to 25%%\n");
+ break;
+ }
+ default:
+ pr_err("prcmu qos: Incorrect ddr target value (%d)",
+ extreme_value);
+ goto unlock_and_return;
+ }
+ prcmu_set_ddr_opp(op);
+ prcmu_debug_ddr_opp_log(op);
+ break;
+ case PRCMU_QOS_APE_OPP:
+ switch (extreme_value) {
+ case 50:
+ if (ape_opp_50_partly_25_enabled)
+ op = APE_50_PARTLY_25_OPP;
+ else
+ op = APE_50_OPP;
+ pr_debug("prcmu qos: set ape opp to 50%%\n");
+ break;
+ case 100:
+ op = APE_100_OPP;
+ pr_debug("prcmu qos: set ape opp to 100%%\n");
+ break;
+ default:
+ pr_err("prcmu qos: Incorrect ape target value (%d)",
+ extreme_value);
+ goto unlock_and_return;
+ }
+ (void)prcmu_set_ape_opp(op);
+ prcmu_debug_ape_opp_log(op);
+ break;
+ case PRCMU_QOS_ARM_OPP:
+ {
+ mutex_unlock(&prcmu_qos_mutex);
+ /*
+ * We can't hold the mutex since changing cpufreq
+ * will trigger an prcmu fw callback.
+ */
+ update_cpu_limits(extreme_value);
+ /* Return since the lock is unlocked */
+ return;
+
+ break;
+ }
+ default:
+ pr_err("prcmu qos: Incorrect target\n");
+ break;
+ }
+
+unlock_and_return:
+ mutex_unlock(&prcmu_qos_mutex);
+}
+
+void prcmu_qos_force_opp(int prcmu_qos_class, s32 i)
+{
+ prcmu_qos_array[prcmu_qos_class]->force_value = i;
+ update_target(prcmu_qos_class);
+}
+
+void prcmu_qos_voice_call_override(bool enable)
+{
+ int ape_opp;
+
+ mutex_lock(&prcmu_qos_mutex);
+
+ ape_opp_50_partly_25_enabled = enable;
+
+ ape_opp = prcmu_get_ape_opp();
+
+ if (ape_opp == APE_50_OPP) {
+ if (enable)
+ prcmu_set_ape_opp(APE_50_PARTLY_25_OPP);
+ else
+ prcmu_set_ape_opp(APE_50_OPP);
+ }
+
+ mutex_unlock(&prcmu_qos_mutex);
+}
+
+/**
+ * prcmu_qos_requirement - returns current prcmu qos expectation
+ * @prcmu_qos_class: identification of which qos value is requested
+ *
+ * This function returns the current target value in an atomic manner.
+ */
+int prcmu_qos_requirement(int prcmu_qos_class)
+{
+ return atomic_read(&prcmu_qos_array[prcmu_qos_class]->target_value);
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_requirement);
+
+/**
+ * prcmu_qos_add_requirement - inserts new qos request into the list
+ * @prcmu_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * This function inserts a new entry in the prcmu_qos_class list of requested
+ * qos performance characteristics. It recomputes the aggregate QoS
+ * expectations for the prcmu_qos_class of parameters.
+ */
+int prcmu_qos_add_requirement(int prcmu_qos_class, char *name, s32 value)
+{
+ struct requirement_list *dep;
+ unsigned long flags;
+
+ dep = kzalloc(sizeof(struct requirement_list), GFP_KERNEL);
+ if (dep == NULL)
+ return -ENOMEM;
+
+ if (value == PRCMU_QOS_DEFAULT_VALUE)
+ dep->value = prcmu_qos_array[prcmu_qos_class]->default_value;
+ else
+ dep->value = value;
+ dep->name = kstrdup(name, GFP_KERNEL);
+ if (!dep->name)
+ goto cleanup;
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ list_add(&dep->list,
+ &prcmu_qos_array[prcmu_qos_class]->requirements.list);
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+ update_target(prcmu_qos_class);
+
+ return 0;
+
+cleanup:
+ kfree(dep);
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_add_requirement);
+
+/**
+ * prcmu_qos_update_requirement - modifies an existing qos request
+ * @prcmu_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ * @value: defines the qos request
+ *
+ * Updates an existing qos requirement for the prcmu_qos_class of parameters
+ * along with updating the target prcmu_qos_class value.
+ *
+ * If the named request isn't in the list then no change is made.
+ */
+int prcmu_qos_update_requirement(int prcmu_qos_class, char *name, s32 new_value)
+{
+ unsigned long flags;
+ struct requirement_list *node;
+ int pending_update = 0;
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ list_for_each_entry(node,
+ &prcmu_qos_array[prcmu_qos_class]->requirements.list, list) {
+ if (strcmp(node->name, name) == 0) {
+ if (new_value == PRCMU_QOS_DEFAULT_VALUE)
+ node->value =
+ prcmu_qos_array[prcmu_qos_class]->default_value;
+ else
+ node->value = new_value;
+ pending_update = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+ if (pending_update)
+ update_target(prcmu_qos_class);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_update_requirement);
+
+/**
+ * prcmu_qos_remove_requirement - modifies an existing qos request
+ * @prcmu_qos_class: identifies which list of qos request to us
+ * @name: identifies the request
+ *
+ * Will remove named qos request from prcmu_qos_class list of parameters and
+ * recompute the current target value for the prcmu_qos_class.
+ */
+void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name)
+{
+ unsigned long flags;
+ struct requirement_list *node;
+ int pending_update = 0;
+
+ spin_lock_irqsave(&prcmu_qos_lock, flags);
+ list_for_each_entry(node,
+ &prcmu_qos_array[prcmu_qos_class]->requirements.list, list) {
+ if (strcmp(node->name, name) == 0) {
+ kfree(node->name);
+ list_del(&node->list);
+ kfree(node);
+ pending_update = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&prcmu_qos_lock, flags);
+ if (pending_update)
+ update_target(prcmu_qos_class);
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_remove_requirement);
+
+/**
+ * prcmu_qos_add_notifier - sets notification entry for changes to target value
+ * @prcmu_qos_class: identifies which qos target changes should be notified.
+ * @notifier: notifier block managed by caller.
+ *
+ * will register the notifier into a notification chain that gets called
+ * upon changes to the prcmu_qos_class target value.
+ */
+int prcmu_qos_add_notifier(int prcmu_qos_class, struct notifier_block *notifier)
+{
+ int retval = -EINVAL;
+
+ if (prcmu_qos_array[prcmu_qos_class]->notifiers)
+ retval = blocking_notifier_chain_register(
+ prcmu_qos_array[prcmu_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_add_notifier);
+
+/**
+ * prcmu_qos_remove_notifier - deletes notification entry from chain.
+ * @prcmu_qos_class: identifies which qos target changes are notified.
+ * @notifier: notifier block to be removed.
+ *
+ * will remove the notifier from the notification chain that gets called
+ * upon changes to the prcmu_qos_class target value.
+ */
+int prcmu_qos_remove_notifier(int prcmu_qos_class,
+ struct notifier_block *notifier)
+{
+ int retval = -EINVAL;
+ if (prcmu_qos_array[prcmu_qos_class]->notifiers)
+ retval = blocking_notifier_chain_unregister(
+ prcmu_qos_array[prcmu_qos_class]->notifiers, notifier);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(prcmu_qos_remove_notifier);
+
+#define USER_QOS_NAME_LEN 32
+
+static int prcmu_qos_power_open(struct inode *inode, struct file *filp,
+ long prcmu_qos_class)
+{
+ int ret;
+ char name[USER_QOS_NAME_LEN];
+
+ filp->private_data = (void *)prcmu_qos_class;
+ snprintf(name, USER_QOS_NAME_LEN, "file_%08x", (unsigned int)filp);
+ ret = prcmu_qos_add_requirement(prcmu_qos_class, name,
+ PRCMU_QOS_DEFAULT_VALUE);
+ if (ret >= 0)
+ return 0;
+
+ return -EPERM;
+}
+
+
+static int prcmu_qos_ape_power_open(struct inode *inode, struct file *filp)
+{
+ return prcmu_qos_power_open(inode, filp, PRCMU_QOS_APE_OPP);
+}
+
+static int prcmu_qos_ddr_power_open(struct inode *inode, struct file *filp)
+{
+ return prcmu_qos_power_open(inode, filp, PRCMU_QOS_DDR_OPP);
+}
+
+static int prcmu_qos_arm_power_open(struct inode *inode, struct file *filp)
+{
+ return prcmu_qos_power_open(inode, filp, PRCMU_QOS_ARM_OPP);
+}
+
+static int prcmu_qos_power_release(struct inode *inode, struct file *filp)
+{
+ int prcmu_qos_class;
+ char name[USER_QOS_NAME_LEN];
+
+ prcmu_qos_class = (long)filp->private_data;
+ snprintf(name, USER_QOS_NAME_LEN, "file_%08x", (unsigned int)filp);
+ prcmu_qos_remove_requirement(prcmu_qos_class, name);
+
+ return 0;
+}
+
+static ssize_t prcmu_qos_power_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ s32 value;
+ int prcmu_qos_class;
+ char name[USER_QOS_NAME_LEN];
+
+ prcmu_qos_class = (long)filp->private_data;
+ if (count != sizeof(s32))
+ return -EINVAL;
+ if (copy_from_user(&value, buf, sizeof(s32)))
+ return -EFAULT;
+ snprintf(name, USER_QOS_NAME_LEN, "file_%08x", (unsigned int)filp);
+ prcmu_qos_update_requirement(prcmu_qos_class, name, value);
+
+ return sizeof(s32);
+}
+
+/* Functions to provide QoS to user space */
+static const struct file_operations prcmu_qos_ape_power_fops = {
+ .write = prcmu_qos_power_write,
+ .open = prcmu_qos_ape_power_open,
+ .release = prcmu_qos_power_release,
+};
+
+/* Functions to provide QoS to user space */
+static const struct file_operations prcmu_qos_ddr_power_fops = {
+ .write = prcmu_qos_power_write,
+ .open = prcmu_qos_ddr_power_open,
+ .release = prcmu_qos_power_release,
+};
+
+static const struct file_operations prcmu_qos_arm_power_fops = {
+ .write = prcmu_qos_power_write,
+ .open = prcmu_qos_arm_power_open,
+ .release = prcmu_qos_power_release,
+};
+
+static int register_prcmu_qos_misc(struct prcmu_qos_object *qos,
+ const struct file_operations *fops)
+{
+ qos->prcmu_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR;
+ qos->prcmu_qos_power_miscdev.name = qos->name;
+ qos->prcmu_qos_power_miscdev.fops = fops;
+
+ return misc_register(&qos->prcmu_qos_power_miscdev);
+}
+
+static void qos_delayed_work_up_fn(struct work_struct *work)
+{
+ prcmu_qos_update_requirement(PRCMU_QOS_DDR_OPP, "cpufreq", 100);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "cpufreq", 100);
+ cpufreq_requirement_set = 100;
+}
+
+static void qos_delayed_work_down_fn(struct work_struct *work)
+{
+ prcmu_qos_update_requirement(PRCMU_QOS_DDR_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ cpufreq_requirement_set = PRCMU_QOS_DEFAULT_VALUE;
+}
+
+static DECLARE_DELAYED_WORK(qos_delayed_work_up, qos_delayed_work_up_fn);
+static DECLARE_DELAYED_WORK(qos_delayed_work_down, qos_delayed_work_down_fn);
+
+static int qos_delayed_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ s32 new_ddr_target;
+
+ /* Only react once per transition and only for one core, e.g. core 0 */
+ if (event != CPUFREQ_POSTCHANGE || freq->cpu != 0)
+ return 0;
+
+ /*
+ * APE and DDR OPP are always handled together in this solution.
+ * Hence no need to check both DDR and APE opp in the code below.
+ */
+
+ /* Which DDR OPP are we aiming for? */
+ if (freq->new > ARM_THRESHOLD_FREQ)
+ new_ddr_target = 100;
+ else
+ new_ddr_target = PRCMU_QOS_DEFAULT_VALUE;
+
+ if (new_ddr_target == cpufreq_requirement_queued) {
+ /*
+ * We're already at, or going to, the target requirement.
+ * This is only a fluctuation within the interval
+ * corresponding to the same DDR requirement.
+ */
+ return 0;
+ }
+ cpufreq_requirement_queued = new_ddr_target;
+
+ if (freq->new > ARM_THRESHOLD_FREQ) {
+ cancel_delayed_work_sync(&qos_delayed_work_down);
+ /*
+ * Only schedule this requirement if it is not the current
+ * one.
+ */
+ if (new_ddr_target != cpufreq_requirement_set)
+ schedule_delayed_work(&qos_delayed_work_up,
+ cpufreq_opp_delay);
+ } else {
+ cancel_delayed_work_sync(&qos_delayed_work_up);
+ /*
+ * Only schedule this requirement if it is not the current
+ * one.
+ */
+ if (new_ddr_target != cpufreq_requirement_set)
+ schedule_delayed_work(&qos_delayed_work_down,
+ cpufreq_opp_delay);
+ }
+
+ return 0;
+}
+
+static int __init prcmu_qos_power_init(void)
+{
+ int ret;
+
+ /* 25% DDR OPP is not supported on u5500 */
+ if (cpu_is_u5500()) {
+ ddr_opp_qos.default_value = 50;
+ atomic_set(&ddr_opp_qos.target_value, 50);
+ }
+
+ ret = register_prcmu_qos_misc(&ape_opp_qos, &prcmu_qos_ape_power_fops);
+ if (ret < 0) {
+ pr_err("prcmu ape qos: setup failed\n");
+ return ret;
+ }
+
+ ret = register_prcmu_qos_misc(&ddr_opp_qos, &prcmu_qos_ddr_power_fops);
+ if (ret < 0) {
+ pr_err("prcmu ddr qos: setup failed\n");
+ return ret;
+ }
+
+ ret = register_prcmu_qos_misc(&arm_opp_qos, &prcmu_qos_arm_power_fops);
+ if (ret < 0) {
+ pr_err("prcmu arm qos: setup failed\n");
+ return ret;
+ }
+
+ prcmu_qos_add_requirement(PRCMU_QOS_DDR_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, "cpufreq",
+ PRCMU_QOS_DEFAULT_VALUE);
+ cpufreq_requirement_set = PRCMU_QOS_DEFAULT_VALUE;
+ cpufreq_requirement_queued = PRCMU_QOS_DEFAULT_VALUE;
+
+ cpufreq_register_notifier(&qos_delayed_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ return ret;
+}
+
+late_initcall(prcmu_qos_power_init);
diff --git a/arch/arm/mach-ux500/pm/runtime.c b/arch/arm/mach-ux500/pm/runtime.c
new file mode 100644
index 00000000000..8608c43479e
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/runtime.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Rabin Vincent <rabin.vincent@stericsson> for ST-Ericsson
+ *
+ * Based on:
+ * Runtime PM support code for SuperH Mobile ARM
+ * Copyright (C) 2009-2010 Magnus Damm
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/regulator/dbx500-prcmu.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <plat/pincfg.h>
+
+#include "../pins.h"
+
+#ifdef CONFIG_PM_RUNTIME
+#define BIT_ONCE 0
+#define BIT_ACTIVE 1
+#define BIT_ENABLED 2
+
+struct pm_runtime_data {
+ unsigned long flags;
+ struct ux500_regulator *regulator;
+ struct ux500_pins *pins;
+};
+
+static void __devres_release(struct device *dev, void *res)
+{
+ struct pm_runtime_data *prd = res;
+
+ dev_dbg(dev, "__devres_release()\n");
+
+ if (test_bit(BIT_ENABLED, &prd->flags)) {
+ if (prd->pins)
+ ux500_pins_disable(prd->pins);
+ if (prd->regulator)
+ ux500_regulator_atomic_disable(prd->regulator);
+ }
+
+ if (test_bit(BIT_ACTIVE, &prd->flags)) {
+ if (prd->pins)
+ ux500_pins_put(prd->pins);
+ if (prd->regulator)
+ ux500_regulator_put(prd->regulator);
+ }
+}
+
+static struct pm_runtime_data *__to_prd(struct device *dev)
+{
+ return devres_find(dev, __devres_release, NULL, NULL);
+}
+
+static void platform_pm_runtime_init(struct device *dev,
+ struct pm_runtime_data *prd)
+{
+ prd->pins = ux500_pins_get(dev_name(dev));
+
+ prd->regulator = ux500_regulator_get(dev);
+ if (IS_ERR(prd->regulator))
+ prd->regulator = NULL;
+
+ if (prd->pins || prd->regulator) {
+ dev_info(dev, "managed by runtime pm: %s%s\n",
+ prd->pins ? "pins " : "",
+ prd->regulator ? "regulator " : "");
+
+ set_bit(BIT_ACTIVE, &prd->flags);
+ }
+}
+
+static void platform_pm_runtime_bug(struct device *dev,
+ struct pm_runtime_data *prd)
+{
+ if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags))
+ dev_err(dev, "runtime pm suspend before resume\n");
+}
+
+static void platform_pm_runtime_used(struct device *dev,
+ struct pm_runtime_data *prd)
+{
+ if (prd)
+ set_bit(BIT_ONCE, &prd->flags);
+}
+
+static int ux500_pd_runtime_idle(struct device *dev)
+{
+ return pm_runtime_suspend(dev);
+}
+
+static void ux500_pd_disable(struct pm_runtime_data *prd)
+{
+ if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
+
+ if (prd->pins)
+ ux500_pins_disable(prd->pins);
+
+ if (prd->regulator)
+ ux500_regulator_atomic_disable(prd->regulator);
+
+ clear_bit(BIT_ENABLED, &prd->flags);
+ }
+}
+
+static int ux500_pd_runtime_suspend(struct device *dev)
+{
+ int ret;
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ platform_pm_runtime_bug(dev, prd);
+
+ ret = pm_generic_runtime_suspend(dev);
+ if (ret)
+ return ret;
+
+ ux500_pd_disable(prd);
+
+ return 0;
+}
+
+static void ux500_pd_enable(struct pm_runtime_data *prd)
+{
+ if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
+ if (prd->pins)
+ ux500_pins_enable(prd->pins);
+
+ if (prd->regulator)
+ ux500_regulator_atomic_enable(prd->regulator);
+
+ set_bit(BIT_ENABLED, &prd->flags);
+ }
+}
+
+static int ux500_pd_runtime_resume(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ platform_pm_runtime_used(dev, prd);
+ ux500_pd_enable(prd);
+
+ return pm_generic_runtime_resume(dev);
+}
+
+static int ux500_pd_suspend_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /* Only handle devices that use runtime pm */
+ if (!prd || !test_bit(BIT_ONCE, &prd->flags))
+ return 0;
+
+ /* Already is runtime suspended? Nothing to do. */
+ if (pm_runtime_status_suspended(dev))
+ return 0;
+
+ /*
+ * We get here only if the device was not runtime suspended for some
+ * reason. We still need to do the power save stuff when going into
+ * suspend, so force it here.
+ */
+ return ux500_pd_runtime_suspend(dev);
+}
+
+static int ux500_pd_resume_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /* Only handle devices that use runtime pm */
+ if (!prd || !test_bit(BIT_ONCE, &prd->flags))
+ return 0;
+
+ /*
+ * Already was runtime suspended? No need to resume here, runtime
+ * resume will take care of it.
+ */
+ if (pm_runtime_status_suspended(dev))
+ return 0;
+
+ /*
+ * We get here only if the device was not runtime suspended,
+ * but we forced it down in suspend_noirq above. Bring it
+ * up since pm-runtime thinks it is not suspended.
+ */
+ return ux500_pd_runtime_resume(dev);
+}
+#ifdef CONFIG_UX500_SUSPEND
+static int ux500_pd_amba_suspend_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret = 0;
+ bool is_suspended = pm_runtime_status_suspended(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->suspend_noirq;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_suspend_noirq(dev);
+
+ if (!ret && !is_suspended)
+ ux500_pd_disable(prd);
+
+ return ret;
+}
+
+static int ux500_pd_amba_resume_noirq(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret = 0;
+ bool is_suspended = pm_runtime_status_suspended(dev);
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->resume_noirq;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_resume_noirq(dev);
+
+ if (!ret && !is_suspended)
+ ux500_pd_enable(prd);
+
+ return ret;
+}
+#else
+static int ux500_pd_amba_suspend_noirq(struct device *dev)
+{
+ return 0;
+}
+static int ux500_pd_amba_resume_noirq(struct device *dev)
+{
+ return 0;
+}
+#endif
+static int ux500_pd_amba_runtime_suspend(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do this first, to make sure pins is not in undefined state after
+ * drivers has run their runtime suspend. This also means that drivers
+ * are not able to use their pins/regulators during runtime suspend.
+ */
+ ux500_pd_disable(prd);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->runtime_suspend;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_runtime_suspend(dev);
+
+ if (ret)
+ ux500_pd_enable(prd);
+
+ return ret;
+}
+
+static int ux500_pd_amba_runtime_resume(struct device *dev)
+{
+ struct pm_runtime_data *prd = __to_prd(dev);
+ int (*callback)(struct device *) = NULL;
+ int ret;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus pm functions by calling generic
+ * pm directly. A future fix could be to implement a
+ * "pm_bus_generic_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->runtime_resume;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_runtime_resume(dev);
+
+ /*
+ * Restore pins/regulator after drivers has runtime resumed, due
+ * to that we must not have pins in undefined state. This also means
+ * that drivers are not able to use their pins/regulators during
+ * runtime resume.
+ */
+ if (!ret)
+ ux500_pd_enable(prd);
+
+ return ret;
+}
+
+static int ux500_pd_amba_runtime_idle(struct device *dev)
+{
+ int (*callback)(struct device *) = NULL;
+ int ret;
+
+ dev_vdbg(dev, "%s()\n", __func__);
+
+ /*
+ * Do not bypass AMBA bus runtime functions by calling generic runtime
+ * directly. A future fix could be to implement a
+ * "pm_bus_generic_runtime_*" API which we can use instead.
+ */
+ if (dev->bus && dev->bus->pm)
+ callback = dev->bus->pm->runtime_idle;
+
+ if (callback)
+ ret = callback(dev);
+ else
+ ret = pm_generic_runtime_idle(dev);
+
+ return ret;
+}
+
+static int ux500_pd_bus_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *data,
+ bool enable)
+{
+ struct device *dev = data;
+ struct pm_runtime_data *prd;
+
+ dev_dbg(dev, "%s() %ld !\n", __func__, action);
+
+ if (action == BUS_NOTIFY_BIND_DRIVER) {
+ prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
+ if (prd) {
+ devres_add(dev, prd);
+ platform_pm_runtime_init(dev, prd);
+ if (enable)
+ ux500_pd_enable(prd);
+ } else
+ dev_err(dev, "unable to alloc memory for runtime pm\n");
+ }
+
+ return 0;
+}
+
+static int ux500_pd_plat_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ return ux500_pd_bus_notify(nb, action, data, false);
+}
+
+static int ux500_pd_amba_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ return ux500_pd_bus_notify(nb, action, data, true);
+}
+
+#else /* CONFIG_PM_RUNTIME */
+
+#define ux500_pd_suspend_noirq NULL
+#define ux500_pd_resume_noirq NULL
+#define ux500_pd_runtime_idle NULL
+#define ux500_pd_runtime_suspend NULL
+#define ux500_pd_runtime_resume NULL
+
+static int ux500_pd_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct ux500_regulator *regulator = NULL;
+ struct ux500_pins *pins = NULL;
+ struct device *dev = data;
+ const char *onoff = NULL;
+
+ dev_dbg(dev, "%s() %ld !\n", __func__, action);
+
+ switch (action) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ pins = ux500_pins_get(dev_name(dev));
+ if (pins) {
+ ux500_pins_enable(pins);
+ ux500_pins_put(pins);
+ }
+
+ regulator = ux500_regulator_get(dev);
+ if (IS_ERR(regulator))
+ regulator = NULL;
+ else {
+ ux500_regulator_atomic_enable(regulator);
+ ux500_regulator_put(regulator);
+ }
+
+ onoff = "on";
+ break;
+ case BUS_NOTIFY_UNBOUND_DRIVER:
+ pins = ux500_pins_get(dev_name(dev));
+ if (pins) {
+ ux500_pins_disable(pins);
+ ux500_pins_put(pins);
+ }
+
+ regulator = ux500_regulator_get(dev);
+ if (IS_ERR(regulator))
+ regulator = NULL;
+ else {
+ ux500_regulator_atomic_disable(regulator);
+ ux500_regulator_put(regulator);
+ }
+
+ onoff = "off";
+ break;
+ }
+
+ if (pins || regulator) {
+ dev_info(dev, "runtime pm disabled, forced %s: %s%s\n",
+ onoff,
+ pins ? "pins " : "",
+ regulator ? "regulator " : "");
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_PM_RUNTIME */
+
+struct dev_pm_domain ux500_amba_dev_power_domain = {
+ .ops = {
+ .suspend = amba_pm_suspend,
+ .resume = amba_pm_resume,
+ .freeze = amba_pm_freeze,
+ .thaw = amba_pm_thaw,
+ .poweroff = amba_pm_poweroff,
+ .restore = amba_pm_restore,
+ SET_RUNTIME_PM_OPS(ux500_pd_amba_runtime_suspend,
+ ux500_pd_amba_runtime_resume,
+ ux500_pd_amba_runtime_idle)
+ .suspend_noirq = ux500_pd_amba_suspend_noirq,
+ .resume_noirq = ux500_pd_amba_resume_noirq,
+ },
+};
+
+struct dev_pm_domain ux500_dev_power_domain = {
+ .ops = {
+ SET_RUNTIME_PM_OPS(ux500_pd_runtime_suspend,
+ ux500_pd_runtime_resume,
+ ux500_pd_runtime_idle)
+ USE_PLATFORM_PM_SLEEP_OPS
+ .suspend_noirq = ux500_pd_suspend_noirq,
+ .resume_noirq = ux500_pd_resume_noirq,
+ },
+};
+
+static struct notifier_block ux500_pd_platform_notifier = {
+ .notifier_call = ux500_pd_plat_bus_notify,
+};
+
+static struct notifier_block ux500_pd_amba_notifier = {
+ .notifier_call = ux500_pd_amba_bus_notify,
+};
+
+static int __init ux500_pm_runtime_platform_init(void)
+{
+ bus_register_notifier(&platform_bus_type, &ux500_pd_platform_notifier);
+ return 0;
+}
+core_initcall(ux500_pm_runtime_platform_init);
+
+/*
+ * The amba bus itself gets registered in a core_initcall, so we can't use
+ * that.
+ */
+static int __init ux500_pm_runtime_amba_init(void)
+{
+ bus_register_notifier(&amba_bustype, &ux500_pd_amba_notifier);
+ return 0;
+}
+arch_initcall(ux500_pm_runtime_amba_init);
diff --git a/arch/arm/mach-ux500/pm/scu.h b/arch/arm/mach-ux500/pm/scu.h
new file mode 100644
index 00000000000..a09e86a9d3c
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/scu.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 ST-Ericsson SA
+ *
+ * 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.
+ */
+
+#ifndef __ASMARM_ARCH_SCU_H
+#define __ASMARM_ARCH_SCU_H
+
+#include <mach/hardware.h>
+
+#define SCU_BASE U8500_SCU_BASE
+/*
+ * * SCU registers
+ * */
+#define SCU_CTRL 0x00
+#define SCU_CONFIG 0x04
+#define SCU_CPU_STATUS 0x08
+#define SCU_INVALIDATE 0x0c
+#define SCU_FPGA_REVISION 0x10
+
+#endif
diff --git a/arch/arm/mach-ux500/pm/suspend.c b/arch/arm/mach-ux500/pm/suspend.c
new file mode 100644
index 00000000000..df527964182
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/suspend.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com>,
+ * Sundar Iyer for ST-Ericsson.
+ */
+
+#include <linux/suspend.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/gpio/nomadik.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/ab8500-debug.h>
+#include <linux/regulator/dbx500-prcmu.h>
+
+#include <mach/context.h>
+#include <mach/pm.h>
+#include <mach/id.h>
+
+#include "suspend_dbg.h"
+
+static void (*pins_suspend_force)(void);
+static void (*pins_suspend_force_mux)(void);
+
+static suspend_state_t suspend_state = PM_SUSPEND_ON;
+
+void suspend_set_pins_force_fn(void (*force)(void), void (*force_mux)(void))
+{
+ pins_suspend_force = force;
+ pins_suspend_force_mux = force_mux;
+}
+
+static atomic_t block_sleep = ATOMIC_INIT(0);
+
+void suspend_block_sleep(void)
+{
+ atomic_inc(&block_sleep);
+}
+
+void suspend_unblock_sleep(void)
+{
+ atomic_dec(&block_sleep);
+}
+
+static bool sleep_is_blocked(void)
+{
+ return (atomic_read(&block_sleep) != 0);
+}
+
+static int suspend(bool do_deepsleep)
+{
+ bool pins_force = pins_suspend_force_mux && pins_suspend_force;
+ int ret = 0;
+
+ if (sleep_is_blocked()) {
+ pr_info("suspend/resume: interrupted by modem.\n");
+ return -EBUSY;
+ }
+
+ nmk_gpio_clocks_enable();
+
+ ux500_suspend_dbg_add_wake_on_uart();
+
+ nmk_gpio_wakeups_suspend();
+
+ /* configure the prcm for a sleep wakeup */
+ if (cpu_is_u9500())
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(HSI0));
+ else
+#if defined(CONFIG_RTC_DRV_PL031)
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(RTC));
+#else
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ABB));
+#endif
+
+ context_vape_save();
+
+ context_fsmc_save();
+
+ if (pins_force) {
+ /*
+ * Save GPIO settings before applying power save
+ * settings
+ */
+ context_gpio_save();
+
+ /* Apply GPIO power save mux settings */
+ context_gpio_mux_safe_switch(true);
+ pins_suspend_force_mux();
+ context_gpio_mux_safe_switch(false);
+
+ /* Apply GPIO power save settings */
+ pins_suspend_force();
+ }
+
+ ux500_pm_gic_decouple();
+
+ if (ux500_pm_gic_pending_interrupt()) {
+ pr_info("suspend/resume: pending interrupt\n");
+
+ /* Recouple GIC with the interrupt bus */
+ ux500_pm_gic_recouple();
+ ret = -EBUSY;
+
+ goto exit;
+ }
+ ux500_pm_prcmu_set_ioforce(true);
+
+ if (do_deepsleep) {
+ context_varm_save_common();
+ context_varm_save_core();
+ context_gic_dist_disable_unneeded_irqs();
+ context_save_cpu_registers();
+
+ /*
+ * Due to we have only 100us between requesting a powerstate
+ * and wfi, we clean the cache before as well to assure the
+ * final cache clean before wfi has as little as possible to
+ * do.
+ */
+ context_clean_l1_cache_all();
+
+ (void) prcmu_set_power_state(PRCMU_AP_DEEP_SLEEP,
+ false, false);
+ context_save_to_sram_and_wfi(true);
+
+ context_restore_cpu_registers();
+ context_varm_restore_core();
+ context_varm_restore_common();
+
+ } else {
+
+ context_clean_l1_cache_all();
+ (void) prcmu_set_power_state(APEXECUTE_TO_APSLEEP,
+ false, false);
+ dsb();
+ __asm__ __volatile__("wfi\n\t" : : : "memory");
+ }
+
+ context_vape_restore();
+
+ context_fsmc_restore();
+
+ /* If GPIO woke us up then save the pins that caused the wake up */
+ ux500_pm_gpio_save_wake_up_status();
+
+ ux500_suspend_dbg_sleep_status(do_deepsleep);
+
+ /* APE was turned off, restore IO ring */
+ ux500_pm_prcmu_set_ioforce(false);
+
+exit:
+ if (pins_force) {
+ /* Restore gpio settings */
+ context_gpio_mux_safe_switch(true);
+ context_gpio_restore_mux();
+ context_gpio_mux_safe_switch(false);
+ context_gpio_restore();
+ }
+
+ /* This is what cpuidle wants */
+ if (cpu_is_u9500())
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB) | PRCMU_WAKEUP(HSI0));
+ else
+ prcmu_enable_wakeups(PRCMU_WAKEUP(ARM) | PRCMU_WAKEUP(RTC) |
+ PRCMU_WAKEUP(ABB));
+
+ nmk_gpio_wakeups_resume();
+
+ ux500_suspend_dbg_remove_wake_on_uart();
+
+ nmk_gpio_clocks_disable();
+
+ return ret;
+}
+
+static int ux500_suspend_enter(suspend_state_t state)
+{
+ if (ux500_suspend_enabled()) {
+ if (ux500_suspend_deepsleep_enabled() &&
+ state == PM_SUSPEND_MEM)
+ return suspend(true);
+ if (ux500_suspend_sleep_enabled())
+ return suspend(false);
+ }
+
+ ux500_suspend_dbg_add_wake_on_uart();
+ /*
+ * Set IOFORCE in order to wake on GPIO the same way
+ * as in deeper sleep.
+ * (U5500 is not ready for IOFORCE)
+ */
+ if (!cpu_is_u5500())
+ ux500_pm_prcmu_set_ioforce(true);
+
+ dsb();
+ __asm__ __volatile__("wfi\n\t" : : : "memory");
+
+ if (!cpu_is_u5500())
+ ux500_pm_prcmu_set_ioforce(false);
+ ux500_suspend_dbg_remove_wake_on_uart();
+
+ return 0;
+}
+
+static int ux500_suspend_valid(suspend_state_t state)
+{
+ return state == PM_SUSPEND_MEM || state == PM_SUSPEND_STANDBY;
+}
+
+static int ux500_suspend_prepare(void)
+{
+ int ret;
+
+ ret = regulator_suspend_prepare(suspend_state);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int ux500_suspend_prepare_late(void)
+{
+ /* ESRAM to retention instead of OFF until ROM is fixed */
+ (void) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+
+ ab8500_regulator_debug_force();
+ ux500_regulator_suspend_debug();
+ return 0;
+}
+
+static void ux500_suspend_wake(void)
+{
+ ux500_regulator_resume_debug();
+ ab8500_regulator_debug_restore();
+ (void) prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+}
+
+static void ux500_suspend_finish(void)
+{
+ (void)regulator_suspend_finish();
+}
+
+static int ux500_suspend_begin(suspend_state_t state)
+{
+ (void) prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "suspend", 125);
+ suspend_state = state;
+ return ux500_suspend_dbg_begin(state);
+}
+
+static void ux500_suspend_end(void)
+{
+ (void) prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "suspend", 25);
+ suspend_state = PM_SUSPEND_ON;
+}
+
+static struct platform_suspend_ops ux500_suspend_ops = {
+ .enter = ux500_suspend_enter,
+ .valid = ux500_suspend_valid,
+ .prepare = ux500_suspend_prepare,
+ .prepare_late = ux500_suspend_prepare_late,
+ .wake = ux500_suspend_wake,
+ .finish = ux500_suspend_finish,
+ .begin = ux500_suspend_begin,
+ .end = ux500_suspend_end,
+};
+
+static __init int ux500_suspend_init(void)
+{
+ ux500_suspend_dbg_init();
+ prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "suspend", 25);
+ suspend_set_ops(&ux500_suspend_ops);
+ return 0;
+}
+device_initcall(ux500_suspend_init);
diff --git a/arch/arm/mach-ux500/pm/suspend_dbg.c b/arch/arm/mach-ux500/pm/suspend_dbg.c
new file mode 100644
index 00000000000..1b7d871ba52
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/suspend_dbg.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Rickard Andersson <rickard.andersson@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/suspend.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/pm.h>
+
+#ifdef CONFIG_UX500_SUSPEND_STANDBY
+static u32 sleep_enabled = 1;
+#else
+static u32 sleep_enabled;
+#endif
+
+#ifdef CONFIG_UX500_SUSPEND_MEM
+static u32 deepsleep_enabled = 1;
+#else
+static u32 deepsleep_enabled;
+#endif
+
+static u32 suspend_enabled = 1;
+
+static u32 deepsleeps_done;
+static u32 deepsleeps_failed;
+static u32 sleeps_done;
+static u32 sleeps_failed;
+static u32 suspend_count;
+
+#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
+void ux500_suspend_dbg_add_wake_on_uart(void)
+{
+ irq_set_irq_wake(GPIO_TO_IRQ(ux500_console_uart_gpio_pin), 1);
+ irq_set_irq_type(GPIO_TO_IRQ(ux500_console_uart_gpio_pin),
+ IRQ_TYPE_EDGE_BOTH);
+}
+
+void ux500_suspend_dbg_remove_wake_on_uart(void)
+{
+ irq_set_irq_wake(GPIO_TO_IRQ(ux500_console_uart_gpio_pin), 0);
+}
+#endif
+
+bool ux500_suspend_enabled(void)
+{
+ return suspend_enabled != 0;
+}
+
+bool ux500_suspend_sleep_enabled(void)
+{
+ return sleep_enabled != 0;
+}
+
+bool ux500_suspend_deepsleep_enabled(void)
+{
+ return deepsleep_enabled != 0;
+}
+
+void ux500_suspend_dbg_sleep_status(bool is_deepsleep)
+{
+ enum prcmu_power_status prcmu_status;
+
+ prcmu_status = prcmu_get_power_state_result();
+
+ if (is_deepsleep) {
+ pr_info("Returning from ApDeepSleep. PRCMU ret: 0x%x - %s\n",
+ prcmu_status,
+ prcmu_status == PRCMU_DEEP_SLEEP_OK ?
+ "Success" : "Fail!");
+ if (prcmu_status == PRCMU_DEEP_SLEEP_OK)
+ deepsleeps_done++;
+ else
+ deepsleeps_failed++;
+ } else {
+ pr_info("Returning from ApSleep. PRCMU ret: 0x%x - %s\n",
+ prcmu_status,
+ prcmu_status == PRCMU_SLEEP_OK ? "Success" : "Fail!");
+ if (prcmu_status == PRCMU_SLEEP_OK)
+ sleeps_done++;
+ else
+ sleeps_failed++;
+ }
+}
+
+int ux500_suspend_dbg_begin(suspend_state_t state)
+{
+ suspend_count++;
+ return 0;
+}
+
+void ux500_suspend_dbg_init(void)
+{
+ struct dentry *suspend_dir;
+ struct dentry *file;
+
+ suspend_dir = debugfs_create_dir("suspend", NULL);
+ if (IS_ERR_OR_NULL(suspend_dir))
+ return;
+
+ file = debugfs_create_bool("sleep", S_IWUGO | S_IRUGO,
+ suspend_dir,
+ &sleep_enabled);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_bool("deepsleep", S_IWUGO | S_IRUGO,
+ suspend_dir,
+ &deepsleep_enabled);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_bool("enable", S_IWUGO | S_IRUGO,
+ suspend_dir,
+ &suspend_enabled);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("count", S_IRUGO,
+ suspend_dir,
+ &suspend_count);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("sleep_count", S_IRUGO,
+ suspend_dir,
+ &sleeps_done);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("deepsleep_count", S_IRUGO,
+ suspend_dir,
+ &deepsleeps_done);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+
+ file = debugfs_create_u32("sleep_failed", S_IRUGO,
+ suspend_dir,
+ &sleeps_failed);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ file = debugfs_create_u32("deepsleep_failed", S_IRUGO,
+ suspend_dir,
+ &deepsleeps_failed);
+ if (IS_ERR_OR_NULL(file))
+ goto error;
+
+ return;
+error:
+ if (!IS_ERR_OR_NULL(suspend_dir))
+ debugfs_remove_recursive(suspend_dir);
+}
diff --git a/arch/arm/mach-ux500/pm/suspend_dbg.h b/arch/arm/mach-ux500/pm/suspend_dbg.h
new file mode 100644
index 00000000000..29bfec7e269
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/suspend_dbg.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ */
+
+#ifndef UX500_SUSPEND_DBG_H
+#define UX500_SUSPEND_DBG_H
+
+#include <linux/kernel.h>
+#include <linux/suspend.h>
+
+#ifdef CONFIG_UX500_SUSPEND_DBG_WAKE_ON_UART
+void ux500_suspend_dbg_add_wake_on_uart(void);
+void ux500_suspend_dbg_remove_wake_on_uart(void);
+#else
+static inline void ux500_suspend_dbg_add_wake_on_uart(void) { }
+static inline void ux500_suspend_dbg_remove_wake_on_uart(void) { }
+#endif
+
+#ifdef CONFIG_UX500_SUSPEND_DBG
+bool ux500_suspend_enabled(void);
+bool ux500_suspend_sleep_enabled(void);
+bool ux500_suspend_deepsleep_enabled(void);
+void ux500_suspend_dbg_sleep_status(bool is_deepsleep);
+void ux500_suspend_dbg_init(void);
+int ux500_suspend_dbg_begin(suspend_state_t state);
+
+#else
+static inline bool ux500_suspend_enabled(void)
+{
+ return true;
+}
+static inline bool ux500_suspend_sleep_enabled(void)
+{
+#ifdef CONFIG_UX500_SUSPEND_STANDBY
+ return true;
+#else
+ return false;
+#endif
+}
+static inline bool ux500_suspend_deepsleep_enabled(void)
+{
+#ifdef CONFIG_UX500_SUSPEND_MEM
+ return true;
+#else
+ return false;
+#endif
+}
+static inline void ux500_suspend_dbg_sleep_status(bool is_deepsleep) { }
+static inline void ux500_suspend_dbg_init(void) { }
+
+static inline int ux500_suspend_dbg_begin(suspend_state_t state)
+{
+ return 0;
+}
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-ux500/pm/timer.c b/arch/arm/mach-ux500/pm/timer.c
new file mode 100644
index 00000000000..fc81c2c15ad
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/timer.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * The RTC timer block is a ST Microelectronics variant of ARM PL031.
+ * Clockwatch part is the same as PL031, while the timer part is only
+ * present on the ST Microelectronics variant.
+ * Here only the timer part is used.
+ *
+ * The timer part is quite troublesome to program correctly. Lots
+ * of long delays must be there in order to secure that you actually get what
+ * you wrote.
+ *
+ * In other words, this timer is and should only used from cpuidle during
+ * special conditions when the surroundings are know in order to be able
+ * to remove the number of delays.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ktime.h>
+#include <linux/delay.h>
+
+#include <asm/errno.h>
+
+#include <mach/hardware.h>
+
+#define RTC_IMSC 0x10
+#define RTC_MIS 0x18
+#define RTC_ICR 0x1C
+#define RTC_TDR 0x20
+#define RTC_TLR1 0x24
+#define RTC_TCR 0x28
+
+#define RTC_TLR2 0x2C
+#define RTC_TPR1 0x3C
+
+#define RTC_TCR_RTTOS (1 << 0)
+#define RTC_TCR_RTTEN (1 << 1)
+#define RTC_TCR_RTTSS (1 << 2)
+
+#define RTC_IMSC_TIMSC (1 << 1)
+#define RTC_ICR_TIC (1 << 1)
+#define RTC_MIS_RTCTMIS (1 << 1)
+
+#define RTC_TCR_RTTPS_2 (1 << 4)
+#define RTC_TCR_RTTPS_3 (2 << 4)
+#define RTC_TCR_RTTPS_4 (3 << 4)
+#define RTC_TCR_RTTPS_5 (4 << 4)
+#define RTC_TCR_RTTPS_6 (5 << 4)
+#define RTC_TCR_RTTPS_7 (6 << 4)
+#define RTC_TCR_RTTPS_8 (7 << 4)
+
+#define WRITE_DELAY 130 /* 4 cycles plus margin */
+
+/*
+ * Count down measure point. It just have to be high to differ
+ * from scheduled values.
+ */
+#define MEASURE_VAL 0xffffffff
+
+/* Just a value bigger than any reason able scheduled timeout. */
+#define MEASURE_VAL_LIMIT 0xf0000000
+
+#define TICKS_TO_NS(x) ((s64)x * 30518)
+#define US_TO_TICKS(x) ((u32)((1000 * x) / 30518))
+
+static void __iomem *rtc_base;
+static bool measure_latency;
+
+#ifdef CONFIG_UX500_CPUIDLE_DEBUG
+
+/*
+ * The plan here is to be able to measure the ApSleep/ApDeepSleep exit latency
+ * by having a know timer pattern.
+ * The first entry in the pattern, LR1, is the value that the scheduler
+ * wants us to sleep. The second pattern in a high value, too large to be
+ * scheduled, so we can differ between a running scheduled value and a
+ * time measure value.
+ * When a RTT interrupt has occured, the block will automatically start
+ * to execute the measure value in LR2 and when the ARM is awake, it reads
+ * how far the RTT has decreased the value loaded from LR2 and from that
+ * calculate how long time it took to wake up.
+ */
+ktime_t u8500_rtc_exit_latency_get(void)
+{
+ u32 ticks;
+
+ if (measure_latency) {
+ ticks = MEASURE_VAL - readl(rtc_base + RTC_TDR);
+
+ /*
+ * Check if we are actually counting on a LR2 value.
+ * If not we have woken on another interrupt.
+ */
+ if (ticks < MEASURE_VAL_LIMIT) {
+ /* convert 32 kHz ticks to ns */
+ return ktime_set(0, TICKS_TO_NS(ticks));
+ }
+ }
+ return ktime_set(0, 0);
+}
+
+static void measure_latency_start(void)
+{
+ udelay(WRITE_DELAY);
+ /*
+ * Disable RTT and clean self-start due to we want to restart,
+ * not continue from current pattern. (See below)
+ */
+ writel(0, rtc_base + RTC_TCR);
+ udelay(WRITE_DELAY);
+
+ /*
+ * Program LR2 (load register two) to maximum value to ease
+ * identification of timer interrupt vs other.
+ */
+ writel(MEASURE_VAL, rtc_base + RTC_TLR2);
+ /*
+ * Set Load Register execution pattern, bit clear
+ * means pick LR1, bit set means LR2
+ * 0xfe, binary 11111110 means first do LR1 then do
+ * LR2 seven times
+ */
+ writel(0xfe, rtc_base + RTC_TPR1);
+
+ udelay(WRITE_DELAY);
+
+ /*
+ * Enable self-start, plus a pattern of eight.
+ */
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTPS_8,
+ rtc_base + RTC_TCR);
+ udelay(WRITE_DELAY);
+}
+
+void ux500_rtcrtt_measure_latency(bool enable)
+{
+ if (enable) {
+ measure_latency_start();
+ } else {
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
+ writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
+ writel(RTC_IMSC_TIMSC, rtc_base + RTC_IMSC);
+ }
+ measure_latency = enable;
+}
+#else
+static inline void measure_latency_start(void) { }
+static inline void ux500_rtcrtt_measure_latency(bool enable)
+{
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
+ writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
+ writel(RTC_IMSC_TIMSC, rtc_base + RTC_IMSC);
+}
+#endif
+
+void ux500_rtcrtt_off(void)
+{
+ if (measure_latency) {
+ measure_latency_start();
+ } else {
+ /* Disable, self start and oneshot mode */
+ writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
+
+ /* Clear eventual interrupts */
+ if (readl(rtc_base + RTC_MIS) & RTC_MIS_RTCTMIS)
+ writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
+ }
+}
+
+void ux500_rtcrtt_next(u32 time_us)
+{
+ writel(US_TO_TICKS(time_us), rtc_base + RTC_TLR1);
+}
+
+static int __init ux500_rtcrtt_init(void)
+{
+ if (cpu_is_u8500() || cpu_is_u9540()) {
+ rtc_base = __io_address(U8500_RTC_BASE);
+ } else if (cpu_is_u5500()) {
+ rtc_base = __io_address(U5500_RTC_BASE);
+ } else {
+ pr_err("timer-rtt: Unknown DB Asic!\n");
+ return -EINVAL;
+ }
+ ux500_rtcrtt_measure_latency(false);
+ return 0;
+}
+subsys_initcall(ux500_rtcrtt_init);
diff --git a/arch/arm/mach-ux500/pm/usecase_gov.c b/arch/arm/mach-ux500/pm/usecase_gov.c
new file mode 100644
index 00000000000..1fd4fbb9830
--- /dev/null
+++ b/arch/arm/mach-ux500/pm/usecase_gov.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Alexandre Torgue <alexandre.torgue@stericsson.com> for ST-Ericsson
+ * Author: Vincent Guittot <vincent.guittot@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/io.h>
+#include <linux/earlysuspend.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/tick.h>
+#include <linux/workqueue.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/kernel_stat.h>
+#include <linux/ktime.h>
+#include <linux/cpufreq.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/cpufreq-dbx500.h>
+
+#include "../../../../drivers/cpuidle/cpuidle-dbx500.h"
+
+
+#define CPULOAD_MEAS_DELAY 3000 /* 3 secondes of delta */
+
+/* debug */
+static unsigned long debug;
+
+#define hp_printk \
+ if (debug) \
+ printk \
+
+enum ux500_uc {
+ UX500_UC_NORMAL = 0,
+ UX500_UC_AUTO, /* Add use case below this. */
+ UX500_UC_VC,
+ UX500_UC_LPA,
+ UX500_UC_USER, /* Add use case above this. */
+ UX500_UC_MAX,
+};
+
+/* cpu load monitor struct */
+#define LOAD_MONITOR 4
+struct hotplug_cpu_info {
+ cputime64_t prev_cpu_wall;
+ cputime64_t prev_cpu_idle;
+ cputime64_t prev_cpu_io;
+ unsigned int load[LOAD_MONITOR];
+ unsigned int idx;
+};
+
+static DEFINE_PER_CPU(struct hotplug_cpu_info, hotplug_info);
+
+/* Auto trigger criteria */
+/* loadavg threshold */
+static unsigned long lower_threshold = 175;
+static unsigned long upper_threshold = 450;
+/* load balancing */
+static unsigned long max_unbalance = 210;
+/* trend load */
+static unsigned long trend_unbalance = 40;
+static unsigned long min_trend = 5;
+/* instant load */
+static unsigned long max_instant = 85;
+
+/* Number of interrupts per second before exiting auto mode */
+static u32 exit_irq_per_s = 1000;
+static u64 old_num_irqs;
+
+static DEFINE_MUTEX(usecase_mutex);
+static DEFINE_MUTEX(state_mutex);
+static bool user_config_updated;
+static enum ux500_uc current_uc = UX500_UC_MAX;
+static bool is_work_scheduled;
+static bool is_early_suspend;
+static bool uc_master_enable = true;
+
+static unsigned int cpuidle_deepest_state;
+
+struct usecase_config {
+ char *name;
+ /* Minimum required ARM OPP. if no requirement set 25 */
+ unsigned int min_arm_opp;
+ unsigned int max_arm_opp;
+ unsigned long cpuidle_multiplier;
+ bool second_cpu_online;
+ bool l2_prefetch_en;
+ bool enable;
+ unsigned int forced_state; /* Forced cpu idle state. */
+ bool vc_override; /* QOS override for voice-call. */
+};
+
+static struct usecase_config usecase_conf[UX500_UC_MAX] = {
+ [UX500_UC_NORMAL] = {
+ .name = "normal",
+ .min_arm_opp = 25,
+ .cpuidle_multiplier = 1024,
+ .second_cpu_online = true,
+ .l2_prefetch_en = true,
+ .enable = true,
+ .forced_state = 0,
+ .vc_override = false,
+ },
+ [UX500_UC_AUTO] = {
+ .name = "auto",
+ .min_arm_opp = 25,
+ .cpuidle_multiplier = 0,
+ .second_cpu_online = false,
+ .l2_prefetch_en = true,
+ .enable = false,
+ .forced_state = 0,
+ .vc_override = false,
+ },
+ [UX500_UC_VC] = {
+ .name = "voice-call",
+ .min_arm_opp = 50,
+ .cpuidle_multiplier = 0,
+ .second_cpu_online = true,
+ .l2_prefetch_en = false,
+ .enable = false,
+ .forced_state = 0,
+ .vc_override = true,
+ },
+ [UX500_UC_LPA] = {
+ .name = "low-power-audio",
+ .min_arm_opp = 50,
+ .cpuidle_multiplier = 0,
+ .second_cpu_online = false,
+ .l2_prefetch_en = false,
+ .enable = false,
+ .forced_state = 0, /* Updated dynamically */
+ .vc_override = false,
+ },
+};
+
+/* daemon */
+static struct delayed_work work_usecase;
+static struct early_suspend usecase_early_suspend;
+
+/* calculate loadavg */
+#define LOAD_INT(x) ((x) >> FSHIFT)
+#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
+
+extern int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max);
+extern int cpuidle_set_multiplier(unsigned int value);
+extern int cpuidle_force_state(unsigned int state);
+
+static unsigned long determine_loadavg(void)
+{
+ unsigned long avg = 0;
+ unsigned long avnrun[3];
+
+ get_avenrun(avnrun, FIXED_1 / 200, 0);
+ avg += (LOAD_INT(avnrun[0]) * 100) + (LOAD_FRAC(avnrun[0]) % 100);
+
+ return avg;
+}
+
+static unsigned long determine_cpu_load(void)
+{
+ int i;
+ unsigned long total_load = 0;
+
+ /* get cpu load of each cpu */
+ for_each_online_cpu(i) {
+ unsigned int load;
+ unsigned int idle_time, wall_time;
+ cputime64_t cur_wall_time, cur_idle_time;
+ struct hotplug_cpu_info *info;
+
+ info = &per_cpu(hotplug_info, i);
+
+ /* update both cur_idle_time and cur_wall_time */
+ cur_idle_time = get_cpu_idle_time_us(i, &cur_wall_time);
+
+ /* how much wall time has passed since last iteration? */
+ wall_time = (unsigned int) cputime64_sub(cur_wall_time,
+ info->prev_cpu_wall);
+ info->prev_cpu_wall = cur_wall_time;
+
+ /* how much idle time has passed since last iteration? */
+ idle_time = (unsigned int) cputime64_sub(cur_idle_time,
+ info->prev_cpu_idle);
+ info->prev_cpu_idle = cur_idle_time;
+
+ if (unlikely(!wall_time || wall_time < idle_time))
+ continue;
+
+ /* load is the percentage of time not spent in idle */
+ load = 100 * (wall_time - idle_time) / wall_time;
+ info->load[info->idx++] = load;
+ if (info->idx >= LOAD_MONITOR)
+ info->idx = 0;
+
+ hp_printk("cpu %d load %u ", i, load);
+
+ total_load += load;
+ }
+
+ return total_load;
+}
+
+static unsigned long determine_cpu_load_trend(void)
+{
+ int i, k;
+ unsigned long total_load = 0;
+
+ /* Get cpu load of each cpu */
+ for_each_online_cpu(i) {
+ unsigned int load = 0;
+ struct hotplug_cpu_info *info;
+
+ info = &per_cpu(hotplug_info, i);
+
+ for (k = 0; k < LOAD_MONITOR; k++)
+ load += info->load[k];
+
+ load /= LOAD_MONITOR;
+
+ hp_printk("cpu %d load trend %u\n", i, load);
+
+ total_load += load;
+ }
+
+ return total_load;
+}
+
+static unsigned long determine_cpu_balance_trend(void)
+{
+ int i, k;
+ unsigned long total_load = 0;
+ unsigned long min_load = (unsigned long) (-1);
+
+ /* Get cpu load of each cpu */
+ for_each_online_cpu(i) {
+ unsigned int load = 0;
+ struct hotplug_cpu_info *info;
+ info = &per_cpu(hotplug_info, i);
+
+ for (k = 0; k < LOAD_MONITOR; k++)
+ load += info->load[k];
+
+ load /= LOAD_MONITOR;
+
+ if (min_load > load)
+ min_load = load;
+ total_load += load;
+ }
+
+ if (min_load > min_trend)
+ total_load = (100 * total_load) / min_load;
+ else
+ total_load = 50 << num_online_cpus();
+
+ return total_load;
+}
+
+static void init_cpu_load_trend(void)
+{
+ int i;
+
+ for_each_possible_cpu(i) {
+ struct hotplug_cpu_info *info;
+ int j;
+
+ info = &per_cpu(hotplug_info, i);
+
+ info->prev_cpu_idle = get_cpu_idle_time_us(i,
+ &(info->prev_cpu_wall));
+ info->prev_cpu_io = get_cpu_iowait_time_us(i,
+ &(info->prev_cpu_wall));
+
+ for (j = 0; j < LOAD_MONITOR; j++) {
+ info->load[j] = 100;
+ }
+ info->idx = 0;
+ }
+}
+
+static u32 get_num_interrupts_per_s(void)
+{
+ int cpu;
+ int i;
+ u64 num_irqs = 0;
+ ktime_t now;
+ static ktime_t last;
+ unsigned int delta;
+ u32 irqs = 0;
+
+ now = ktime_get();
+
+ for_each_possible_cpu(cpu) {
+ for (i = 0; i < NR_IRQS; i++)
+ num_irqs += kstat_irqs_cpu(i, cpu);
+ }
+ pr_debug("%s: total num irqs: %lld, previous %lld\n",
+ __func__, num_irqs, old_num_irqs);
+
+ if (old_num_irqs > 0) {
+ delta = (u32)ktime_to_ms(ktime_sub(now, last)) / 1000;
+ irqs = ((u32)(num_irqs - old_num_irqs)) / delta;
+ }
+
+ old_num_irqs = num_irqs;
+ last = now;
+
+ pr_debug("delta irqs per sec:%d\n", irqs);
+
+ return irqs;
+}
+
+static int set_cpufreq(int cpu, int min_freq, int max_freq)
+{
+ int ret;
+ struct cpufreq_policy policy;
+
+ pr_debug("set cpu freq: min %d max: %d\n", min_freq, max_freq);
+
+ ret = cpufreq_get_policy(&policy, cpu);
+ if (ret < 0) {
+ pr_err("usecase-gov: failed to read policy\n");
+ return ret;
+ }
+
+ if (policy.min > max_freq) {
+ ret = cpufreq_update_freq(cpu, min_freq, policy.max);
+ if (ret)
+ pr_err("usecase-gov: update min cpufreq failed (1)\n");
+ }
+ if (policy.max < min_freq) {
+ ret = cpufreq_update_freq(cpu, policy.min, max_freq);
+ if (ret)
+ pr_err("usecase-gov: update max cpufreq failed (2)\n");
+ }
+
+ ret = cpufreq_update_freq(cpu, min_freq, max_freq);
+ if (ret)
+ pr_err("usecase-gov: update min-max cpufreq failed\n");
+
+ return ret;
+}
+
+static void set_cpu_config(enum ux500_uc new_uc)
+{
+ bool update = false;
+ int cpu;
+ int max_freq, min_freq;
+
+ if (new_uc != current_uc)
+ update = true;
+ else if ((user_config_updated) && (new_uc == UX500_UC_USER))
+ update = true;
+
+ pr_debug("%s: new_usecase=%d, current_usecase=%d, update=%d\n",
+ __func__, new_uc, current_uc, update);
+
+ if (!update)
+ goto exit;
+
+ /* Cpu hotplug */
+ if (!(usecase_conf[new_uc].second_cpu_online) &&
+ (num_online_cpus() > 1))
+ cpu_down(1);
+ else if ((usecase_conf[new_uc].second_cpu_online) &&
+ (num_online_cpus() < 2))
+ cpu_up(1);
+
+ if(usecase_conf[new_uc].max_arm_opp)
+ max_freq = dbx500_cpufreq_percent2freq(usecase_conf[new_uc].max_arm_opp);
+ else
+ /* Maximum OPP is 125% */
+ max_freq = dbx500_cpufreq_percent2freq(125);
+
+ min_freq = dbx500_cpufreq_percent2freq(usecase_conf[new_uc].min_arm_opp);
+
+ for_each_online_cpu(cpu) {
+ set_cpufreq(cpu,
+ min_freq,
+ max_freq);
+ }
+
+ /* Kinda doing the job twice, but this is needed for reference keeping */
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "usecase", usecase_conf[new_uc].min_arm_opp);
+
+ /* Cpu idle */
+ cpuidle_set_multiplier(usecase_conf[new_uc].cpuidle_multiplier);
+
+ /* L2 prefetch */
+ if (usecase_conf[new_uc].l2_prefetch_en)
+ outer_prefetch_enable();
+ else
+ outer_prefetch_disable();
+
+ /* Force cpuidle state */
+ cpuidle_force_state(usecase_conf[new_uc].forced_state);
+
+ /* QOS override */
+ prcmu_qos_voice_call_override(usecase_conf[new_uc].vc_override);
+
+ current_uc = new_uc;
+
+exit:
+ /* Its ok to clear even if new_uc != UX500_UC_USER */
+ user_config_updated = false;
+}
+
+void usecase_update_governor_state(void)
+{
+ bool cancel_work = false;
+
+ /*
+ * usecase_mutex will have to be unlocked to ensure safe exit of
+ * delayed_usecase_work(). Protect this function with its own mutex
+ * from being executed by multiple threads at that point.
+ */
+ mutex_lock(&state_mutex);
+ mutex_lock(&usecase_mutex);
+
+ if (uc_master_enable && (usecase_conf[UX500_UC_AUTO].enable ||
+ usecase_conf[UX500_UC_USER].enable)) {
+ /*
+ * Usecases are enabled. If we are in early suspend put
+ * governor to work.
+ */
+ if (is_early_suspend && !is_work_scheduled) {
+ schedule_delayed_work_on(0, &work_usecase,
+ msecs_to_jiffies(CPULOAD_MEAS_DELAY));
+ is_work_scheduled = true;
+ } else if (!is_early_suspend && is_work_scheduled) {
+ /* Exiting from early suspend. */
+ cancel_work = true;
+ }
+
+ } else if (is_work_scheduled) {
+ /* No usecase enabled or governor is not enabled. */
+ cancel_work = true;
+ }
+
+ if (cancel_work) {
+ /*
+ * usecase_mutex is used by delayed_usecase_work() so it must
+ * be unlocked before we call to cacnel the work.
+ */
+ mutex_unlock(&usecase_mutex);
+ cancel_delayed_work_sync(&work_usecase);
+ mutex_lock(&usecase_mutex);
+
+ is_work_scheduled = false;
+
+ /* Set the default settings before exiting. */
+ set_cpu_config(UX500_UC_NORMAL);
+ }
+
+ mutex_unlock(&usecase_mutex);
+ mutex_unlock(&state_mutex);
+}
+
+/*
+ * Start load measurment every 6 s in order detrmine if can unplug one CPU.
+ * In order to not corrupt measurment, the first load average is not done
+ * here call in early suspend.
+ */
+static void usecase_earlysuspend_callback(struct early_suspend *h)
+{
+ init_cpu_load_trend();
+
+ is_early_suspend = true;
+
+ usecase_update_governor_state();
+}
+
+/* Stop measurement, call LCD early resume */
+static void usecase_lateresume_callback(struct early_suspend *h)
+{
+ is_early_suspend = false;
+
+ usecase_update_governor_state();
+}
+
+static void delayed_usecase_work(struct work_struct *work)
+{
+ unsigned long avg, load, trend, balance;
+ bool inc_perf = false;
+ bool dec_perf = false;
+ u32 irqs_per_s;
+
+ /* determine loadavg */
+ avg = determine_loadavg();
+ hp_printk("loadavg = %lu lower th %lu upper th %lu\n",
+ avg, lower_threshold, upper_threshold);
+
+ /* determine instant load */
+ load = determine_cpu_load();
+ hp_printk("cpu instant load = %lu max %lu\n", load, max_instant);
+
+ /* determine load trend */
+ trend = determine_cpu_load_trend();
+ hp_printk("cpu load trend = %lu min %lu unbal %lu\n",
+ trend, min_trend, trend_unbalance);
+
+ /* determine load balancing */
+ balance = determine_cpu_balance_trend();
+ hp_printk("load balancing trend = %lu min %lu\n",
+ balance, max_unbalance);
+
+ irqs_per_s = get_num_interrupts_per_s();
+
+ /* Dont let configuration change in the middle of our calculations. */
+ mutex_lock(&usecase_mutex);
+
+ /* detect "instant" load increase */
+ if (load > max_instant || irqs_per_s > exit_irq_per_s) {
+ inc_perf = true;
+ } else if (!usecase_conf[UX500_UC_USER].enable &&
+ usecase_conf[UX500_UC_AUTO].enable) {
+ /* detect high loadavg use case */
+ if (avg > upper_threshold)
+ inc_perf = true;
+ /* detect idle use case */
+ else if (trend < min_trend)
+ dec_perf = true;
+ /* detect unbalanced low cpu load use case */
+ else if ((balance > max_unbalance) && (trend < trend_unbalance))
+ dec_perf = true;
+ /* detect low loadavg use case */
+ else if (avg < lower_threshold)
+ dec_perf = true;
+ /* All user use cases disabled, current load not triggering
+ * any change.
+ */
+ else if (user_config_updated)
+ dec_perf = true;
+ } else {
+ dec_perf = true;
+ }
+
+ /*
+ * set_cpu_config() will not update the config unless it has been
+ * changed.
+ */
+ if (dec_perf) {
+ if (usecase_conf[UX500_UC_USER].enable)
+ set_cpu_config(UX500_UC_USER);
+ else if (usecase_conf[UX500_UC_AUTO].enable)
+ set_cpu_config(UX500_UC_AUTO);
+ } else if (inc_perf) {
+ set_cpu_config(UX500_UC_NORMAL);
+ }
+
+ mutex_unlock(&usecase_mutex);
+
+ /* reprogramm scheduled work */
+ schedule_delayed_work_on(0, &work_usecase,
+ msecs_to_jiffies(CPULOAD_MEAS_DELAY));
+
+}
+
+static struct dentry *usecase_dir;
+
+#ifdef CONFIG_DEBUG_FS
+#define define_set(_name) \
+static ssize_t set_##_name(struct file *file, \
+ const char __user *user_buf, \
+ size_t count, loff_t *ppos) \
+{ \
+ int err; \
+ long unsigned i; \
+ \
+ err = kstrtoul_from_user(user_buf, count, 0, &i); \
+ \
+ if (err) \
+ return err; \
+ \
+ _name = i; \
+ hp_printk("New value : %lu\n", _name); \
+ \
+ return count; \
+}
+
+define_set(upper_threshold);
+define_set(lower_threshold);
+define_set(max_unbalance);
+define_set(trend_unbalance);
+define_set(min_trend);
+define_set(max_instant);
+define_set(debug);
+
+#define define_print(_name) \
+static ssize_t print_##_name(struct seq_file *s, void *p) \
+{ \
+ return seq_printf(s, "%lu\n", _name); \
+}
+
+define_print(upper_threshold);
+define_print(lower_threshold);
+define_print(max_unbalance);
+define_print(trend_unbalance);
+define_print(min_trend);
+define_print(max_instant);
+define_print(debug);
+
+#define define_open(_name) \
+static ssize_t open_##_name(struct inode *inode, struct file *file) \
+{ \
+ return single_open(file, print_##_name, inode->i_private); \
+}
+
+define_open(upper_threshold);
+define_open(lower_threshold);
+define_open(max_unbalance);
+define_open(trend_unbalance);
+define_open(min_trend);
+define_open(max_instant);
+define_open(debug);
+
+#define define_dbg_file(_name) \
+static const struct file_operations fops_##_name = { \
+ .open = open_##_name, \
+ .write = set_##_name, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+ .owner = THIS_MODULE, \
+}; \
+static struct dentry *file_##_name;
+
+define_dbg_file(upper_threshold);
+define_dbg_file(lower_threshold);
+define_dbg_file(max_unbalance);
+define_dbg_file(trend_unbalance);
+define_dbg_file(min_trend);
+define_dbg_file(max_instant);
+define_dbg_file(debug);
+
+struct dbg_file {
+ struct dentry **file;
+ const struct file_operations *fops;
+ const char *name;
+};
+
+#define define_dbg_entry(_name) \
+{ \
+ .file = &file_##_name, \
+ .fops = &fops_##_name, \
+ .name = #_name \
+}
+
+static struct dbg_file debug_entry[] = {
+ define_dbg_entry(upper_threshold),
+ define_dbg_entry(lower_threshold),
+ define_dbg_entry(max_unbalance),
+ define_dbg_entry(trend_unbalance),
+ define_dbg_entry(min_trend),
+ define_dbg_entry(max_instant),
+ define_dbg_entry(debug),
+};
+
+static int setup_debugfs(void)
+{
+ int i;
+ usecase_dir = debugfs_create_dir("usecase", NULL);
+
+ if (IS_ERR_OR_NULL(usecase_dir))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(debug_entry); i++) {
+ if (IS_ERR_OR_NULL(debugfs_create_file(debug_entry[i].name,
+ S_IWUGO | S_IRUGO,
+ usecase_dir,
+ NULL,
+ debug_entry[i].fops)))
+ goto fail;
+ }
+
+ if (IS_ERR_OR_NULL(debugfs_create_u32("exit_irq_per_s",
+ S_IWUGO | S_IRUGO, usecase_dir,
+ &exit_irq_per_s)))
+ goto fail;
+ return 0;
+fail:
+ debugfs_remove_recursive(usecase_dir);
+ return -EINVAL;
+}
+#else
+static int setup_debugfs(void)
+{
+ return 0;
+}
+#endif
+
+static void usecase_update_user_config(void)
+{
+ int i;
+ bool config_enable = false;
+ struct usecase_config *user_conf = &usecase_conf[UX500_UC_USER];
+
+ mutex_lock(&usecase_mutex);
+
+ user_conf->min_arm_opp = 25;
+ user_conf->max_arm_opp = 0;
+ user_conf->cpuidle_multiplier = 0;
+ user_conf->second_cpu_online = false;
+ user_conf->l2_prefetch_en = false;
+ user_conf->forced_state = cpuidle_deepest_state;
+ user_conf->vc_override = true; /* A single false will clear it. */
+
+ /* Dont include Auto and Normal modes in this */
+ for (i = (UX500_UC_AUTO + 1); i < UX500_UC_USER; i++) {
+ if (!usecase_conf[i].enable)
+ continue;
+
+ config_enable = true;
+
+ /* It's the highest arm opp requirement that should be used */
+ if (usecase_conf[i].min_arm_opp > user_conf->min_arm_opp)
+ user_conf->min_arm_opp = usecase_conf[i].min_arm_opp;
+
+ if (usecase_conf[i].max_arm_opp > user_conf->max_arm_opp)
+ user_conf->max_arm_opp = usecase_conf[i].max_arm_opp;
+
+ if (usecase_conf[i].cpuidle_multiplier >
+ user_conf->cpuidle_multiplier)
+ user_conf->cpuidle_multiplier =
+ usecase_conf[i].cpuidle_multiplier;
+
+ user_conf->second_cpu_online |=
+ usecase_conf[i].second_cpu_online;
+
+ user_conf->l2_prefetch_en |=
+ usecase_conf[i].l2_prefetch_en;
+
+ /* Take the shallowest state. */
+ if (usecase_conf[i].forced_state < user_conf->forced_state)
+ user_conf->forced_state = usecase_conf[i].forced_state;
+
+ /* Only override QOS if all enabled configurations are
+ * requesting it.
+ */
+ if (!usecase_conf[i].vc_override)
+ user_conf->vc_override = false;
+ }
+
+ user_conf->enable = config_enable;
+ user_config_updated = true;
+
+ mutex_unlock(&usecase_mutex);
+}
+
+struct usecase_devclass_attr {
+ struct sysdev_class_attribute class_attr;
+ u32 index;
+};
+
+/* One for each usecase except "user" + current + enable */
+#define UX500_NUM_SYSFS_NODES (UX500_UC_USER + 2)
+#define UX500_CURRENT_NODE_INDEX (UX500_NUM_SYSFS_NODES - 1)
+#define UX500_ENABLE_NODE_INDEX (UX500_NUM_SYSFS_NODES - 2)
+
+static struct usecase_devclass_attr usecase_dc_attr[UX500_NUM_SYSFS_NODES];
+
+static struct attribute *dbs_attributes[UX500_NUM_SYSFS_NODES + 1] = {NULL};
+
+static struct attribute_group dbs_attr_group = {
+ .attrs = dbs_attributes,
+ .name = "usecase",
+};
+
+static ssize_t show_current(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ enum ux500_uc display_uc = (current_uc == UX500_UC_MAX) ?
+ UX500_UC_NORMAL : current_uc;
+
+ return sprintf(buf, "min_arm_opp: %d\n"
+ "max_arm_opp: %d\n"
+ "cpuidle_multiplier: %ld\n"
+ "second_cpu_online: %s\n"
+ "l2_prefetch_en: %s\n"
+ "forced_state: %d\n"
+ "vc_override: %s\n",
+ usecase_conf[display_uc].min_arm_opp,
+ usecase_conf[display_uc].max_arm_opp,
+ usecase_conf[display_uc].cpuidle_multiplier,
+ usecase_conf[display_uc].second_cpu_online ? "true" : "false",
+ usecase_conf[display_uc].l2_prefetch_en ? "true" : "false",
+ usecase_conf[display_uc].forced_state,
+ usecase_conf[display_uc].vc_override ? "true" : "false");
+}
+
+static ssize_t show_enable(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", uc_master_enable);
+}
+
+static ssize_t store_enable(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ ret = sscanf(buf, "%u", &input);
+ if (ret != 1)
+ return -EINVAL;
+
+ uc_master_enable = (bool) input;
+
+ usecase_update_governor_state();
+
+ return count;
+}
+
+static ssize_t show_dc_attr(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr, char *buf)
+{
+ struct usecase_devclass_attr *uattr =
+ container_of(attr, struct usecase_devclass_attr, class_attr);
+
+ return sprintf(buf, "%u\n",
+ usecase_conf[uattr->index].enable);
+}
+
+static ssize_t store_dc_attr(struct sysdev_class *class,
+ struct sysdev_class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ struct usecase_devclass_attr *uattr =
+ container_of(attr, struct usecase_devclass_attr, class_attr);
+
+ ret = sscanf(buf, "%u", &input);
+
+ /* Normal mode cant be changed. */
+ if ((ret != 1) || (uattr->index == 0))
+ return -EINVAL;
+
+ usecase_conf[uattr->index].enable = (bool)input;
+
+ if (uattr->index == UX500_UC_VC)
+ prcmu_vc(usecase_conf[UX500_UC_VC].enable);
+
+ usecase_update_user_config();
+
+ usecase_update_governor_state();
+
+ return count;
+}
+
+static int usecase_sysfs_init(void)
+{
+ int err;
+ int i;
+
+ /* Last two nodes are not based on usecase configurations */
+ for (i = 0; i < (UX500_NUM_SYSFS_NODES - 2); i++) {
+ usecase_dc_attr[i].class_attr.attr.name = usecase_conf[i].name;
+ usecase_dc_attr[i].class_attr.attr.mode = 0644;
+ usecase_dc_attr[i].class_attr.show = show_dc_attr;
+ usecase_dc_attr[i].class_attr.store = store_dc_attr;
+ usecase_dc_attr[i].index = i;
+
+ dbs_attributes[i] = &(usecase_dc_attr[i].class_attr.attr);
+ }
+
+ /* sysfs current */
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr.name =
+ "current";
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr.mode =
+ 0644;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.show =
+ show_current;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.store =
+ NULL;
+ usecase_dc_attr[UX500_CURRENT_NODE_INDEX].index =
+ 0;
+ dbs_attributes[UX500_CURRENT_NODE_INDEX] =
+ &(usecase_dc_attr[UX500_CURRENT_NODE_INDEX].class_attr.attr);
+
+ /* sysfs enable */
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr.name =
+ "enable";
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr.mode =
+ 0644;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.show =
+ show_enable;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.store =
+ store_enable;
+ usecase_dc_attr[UX500_ENABLE_NODE_INDEX].index =
+ 0;
+ dbs_attributes[UX500_ENABLE_NODE_INDEX] =
+ &(usecase_dc_attr[UX500_ENABLE_NODE_INDEX].class_attr.attr);
+
+ err = sysfs_create_group(&(cpu_sysdev_class.kset.kobj),
+ &dbs_attr_group);
+ if (err)
+ pr_err("usecase-gov: sysfs_create_group"
+ " failed with error = %d\n", err);
+
+ return err;
+}
+
+static void usecase_cpuidle_init(void)
+{
+ int max_states;
+ int i;
+ struct cstate *state = ux500_ci_get_cstates(&max_states);
+
+ for (i = 0; i < max_states; i++)
+ if ((state[i].APE == APE_OFF) && (state[i].ARM == ARM_RET))
+ break;
+
+ usecase_conf[UX500_UC_LPA].forced_state = i;
+
+ cpuidle_deepest_state = max_states - 1;
+}
+
+/* initialize devices */
+static int __init init_usecase_devices(void)
+{
+ int err;
+
+ pr_info("Use-case governor initialized\n");
+
+ /* add early_suspend callback */
+ usecase_early_suspend.level = 200;
+ usecase_early_suspend.suspend = usecase_earlysuspend_callback;
+ usecase_early_suspend.resume = usecase_lateresume_callback;
+ register_early_suspend(&usecase_early_suspend);
+
+ /* register delayed queuework */
+ INIT_DELAYED_WORK_DEFERRABLE(&work_usecase,
+ delayed_usecase_work);
+
+ init_cpu_load_trend();
+
+ err = setup_debugfs();
+ if (err)
+ goto error;
+ err = usecase_sysfs_init();
+ if (err)
+ goto error2;
+
+ usecase_cpuidle_init();
+
+ prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "usecase", 25);
+
+ return 0;
+error2:
+ debugfs_remove_recursive(usecase_dir);
+error:
+ unregister_early_suspend(&usecase_early_suspend);
+ return err;
+}
+
+device_initcall(init_usecase_devices);
diff --git a/arch/arm/mach-ux500/prcc.h b/arch/arm/mach-ux500/prcc.h
new file mode 100644
index 00000000000..4224e478348
--- /dev/null
+++ b/arch/arm/mach-ux500/prcc.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009-2011 ST-Ericsson SA
+ *
+ * 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_UX500_PRCC_H__
+
+#define PRCC_PCKEN 0x000
+#define PRCC_PCKDIS 0x004
+#define PRCC_KCKEN 0x008
+#define PRCC_KCKDIS 0x00C
+#define PRCC_PCKSR 0x010
+#define PRCC_KCKSR 0x014
+#define PRCC_K_SOFTRST_SET 0x018
+#define PRCC_K_SOFTRST_CLR 0x01C
+#define PRCC_K_RST_STATUS 0x020
+
+#endif
diff --git a/arch/arm/mach-ux500/prcmu-debug.c b/arch/arm/mach-ux500/prcmu-debug.c
new file mode 100644
index 00000000000..6842e4b68fe
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-debug.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Martin Persson for ST-Ericsson
+ * Etienne Carriere <etienne.carriere@stericsson.com> for ST-Ericsson
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include <mach/hardware.h>
+
+#define MAX_STATES 5
+#define MAX_NAMELEN 16
+#define U8500_PRCMU_TCDM_SIZE 4096
+
+struct state_history {
+ ktime_t start;
+ u32 state;
+ u32 counter[MAX_STATES];
+ u8 opps[MAX_STATES];
+ int max_states;
+ int req;
+ bool reqs[MAX_STATES];
+ ktime_t time[MAX_STATES];
+ int state_names[MAX_STATES];
+ char prefix[MAX_NAMELEN];
+ spinlock_t lock;
+};
+
+static struct state_history ape_sh = {
+ .prefix = "APE",
+ .req = PRCMU_QOS_APE_OPP,
+ .opps = {APE_50_OPP, APE_100_OPP},
+ .state_names = {50, 100},
+ .max_states = 2,
+};
+
+static struct state_history ddr_sh = {
+ .prefix = "DDR",
+ .req = PRCMU_QOS_DDR_OPP,
+ .opps = {DDR_25_OPP, DDR_50_OPP, DDR_100_OPP},
+ .state_names = {25, 50, 100},
+ .max_states = 3,
+};
+
+static struct state_history arm_sh = {
+ .prefix = "ARM",
+ .req = PRCMU_QOS_ARM_OPP,
+ .opps = {ARM_EXTCLK, ARM_50_OPP, ARM_100_OPP, ARM_MAX_OPP},
+ .state_names = {25, 50, 100, 125},
+ .max_states = 4,
+};
+
+static const u16 u8500_prcmu_dump_regs[] = {
+ /*ARMCLKFIX_MGT*/ 0x0, /*ACLK_MGT*/ 0x4,
+ /*SVAMMDSPCLK_MGT*/ 0x8, /*SIAMMDSPCLK_MGT*/ 0xc,
+ /*SGACLK_MGT*/ 0x14, /*UARTCLK_MGT*/ 0x18,
+ /*MSP02CLK_MGT*/ 0x1c, /*I2CCLK_MGT*/ 0x20,
+ /*SDMMCCLK_MGT*/ 0x24, /*SLIMCLK_MGT*/ 0x28,
+ /*PER1CLK_MGT*/ 0x2c, /*PER2CLK_MGT*/ 0x30,
+ /*PER3CLK_MGT*/ 0x34, /*PER5CLK_MGT*/ 0x38,
+ /*PER6CLK_MGT*/ 0x3c, /*PER7CLK_MGT*/ 0x40,
+ /*LCDCLK_MGT*/ 0x44, /*SPARE1CLK_MGT*/ 0x48,
+ /*BMLCLK_MGT*/ 0x4c, /*HSITXCLK_MGT*/ 0x50,
+ /*HSIRXCLK_MGT*/ 0x54, /*HDMICLK_MGT*/ 0x58,
+ /*APEATCLK_MGT*/ 0x5c, /*APETRACECLK_MGT*/ 0x60,
+ /*MCDECLK_MGT*/ 0x64, /*IPI2CCLK_MGT*/ 0x68,
+ /*DSIALTCLK_MGT*/ 0x6c, /*SPARE2CLK_MGT*/ 0x70,
+ /*DMACLK_MGT*/ 0x74, /*B2R2CLK_MGT*/ 0x78,
+ /*TVCLK_MGT*/ 0x7c, /*PLLSOC0_FREQ*/ 0x80,
+ /*PLLSOC1_FREQ*/ 0x84, /*PLLARM_FREQ*/ 0x88,
+ /*PLLDDR_FREQ*/ 0x8c, /*PLLSOC0_ENABLE*/ 0x90,
+ /*PLLSOC1_ENABLE*/ 0x94, /*PLLARM_ENABLE*/ 0x98,
+ /*PLLDDR_ENABLE*/ 0x9c, /*PLLSOC0_LOCKP*/ 0xa0,
+ /*PLLSOC1_LOCKP*/ 0xa4, /*PLLARM_LOCKP*/ 0xa8,
+ /*PLLDDR_LOCKP*/ 0xac, /*XP70CLK_MGT*/ 0xb0,
+ /*TIMER_0_REF*/ 0xb4, /*TIMER_0_DOWNCOUNT*/ 0xb8,
+ /*TIMER_0_MODE*/ 0xbc, /*TIMER_1_REF*/ 0xc0,
+ /*TIMER_1_DOWNCOUNT*/ 0xc4, /*TIMER_1_MODE*/ 0xc8,
+ /*TIMER_2_REF*/ 0xcc, /*TIMER_2_DOWNCOUNT*/ 0xd0,
+ /*TIMER_2_MODE*/ 0xd4, /*CLK009_MGT*/ 0xe4,
+ /*MODECLK*/ 0xe8, /*4500_CLK_REQ*/ 0xf8,
+ /*MBOX_CPU_VAL*/ 0xfc, /*PLL32K_ENABLE*/ 0x10c,
+ /*PLL32K_LOCKP*/ 0x110, /*ARM_CHGCLKREQ*/ 0x114,
+ /*ARM_PLLDIVPS*/ 0x118, /*ARMITMSK31TO0*/ 0x11c,
+ /*ARMITMSK63TO32*/ 0x120, /*ARMITMSK95TO64*/ 0x124,
+ /*ARMITMSK127TO96*/ 0x128, /*ARMSTANDBY_STATUS*/ 0x130,
+ /*CGATING_BYPASS*/ 0x134, /*GPIOCR*/ 0x138,
+ /*LEMI*/ 0x13c, /*COMPCR*/ 0x140,
+ /*COMPSTA*/ 0x144, /*ITSTATUS0*/ 0x148,
+ /*ITSTATUS1*/ 0x150, /*ITSTATUS2*/ 0x158,
+ /*ITSTATUS3*/ 0x160, /*ITSTATUS4*/ 0x168,
+ /*LINE_VALUE*/ 0x170, /*HOLD_EVT*/ 0x174,
+ /*EDGE_SENS_L*/ 0x178, /*EDGE_SENS_H*/ 0x17c,
+ /*DEBUG_CTRL_VAL*/ 0x190, /*DEBUG_NOPWRDOWN_VAL*/ 0x194,
+ /*DEBUG_CTRL_ACK*/ 0x198, /*A9PL_FORCE_CLKEN*/ 0x19c,
+ /*TPIU_FLUSHIN_REQ*/ 0x1a0, /*TPIU_FLUSHIN_ACK*/ 0x1a4,
+ /*STP_FLUSHIN_REQ*/ 0x1a8, /*STP_FLUSHIN_ACK*/ 0x1ac,
+ /*HWI2C_DIV*/ 0x1b0, /*HWI2C_CMD*/ 0x1b8,
+ /*HWI2C_DATA123*/ 0x1bc, /*HWI2C_SR*/ 0x1c0,
+ /*REMAPCR*/ 0x1c4, /*TCR*/ 0x1c8,
+ /*CLKOCR*/ 0x1cc, /*ITSTATUS_DBG*/ 0x1d0,
+ /*LINE_VALUE_DBG*/ 0x1d8, /*DBG_HOLD*/ 0x1dc,
+ /*EDGE_SENS_DBG*/ 0x1e0, /*APE_RESETN_VAL*/ 0x1ec,
+ /*A9_RESETN_SET*/ 0x1f0, /*A9_RESETN_VAL*/ 0x1f8,
+ /*MOD_RESETN_VAL*/ 0x204, /*GPIO_RESETN_VAL*/ 0x210,
+ /*4500_RESETN_VAL*/ 0x21c, /*SWD_RST_TEMPO*/ 0x238,
+ /*RST_4500_TEMPO*/ 0x23c, /*SVAMMDSP_IT*/ 0x240,
+ /*SIAMMDSP_IT*/ 0x248, /*POWER_STATE_VAL*/ 0x25c,
+ /*ARMITVALUE31TO0*/ 0x260, /*ARMITVALUE63TO32*/ 0x264,
+ /*ARMITVALUE95TO64*/ 0x268, /*ARMITVALUE127TO96*/ 0x26c,
+ /*REDUN_LOAD*/ 0x270, /*REDUN_STATUS*/ 0x274,
+ /*UNIPROCLK_MGT*/ 0x278, /*UICCCLK_MGT*/ 0x27c,
+ /*SSPCLK_MGT*/ 0x280, /*RNGCLK_MGT*/ 0x284,
+ /*MSP1CLK_MGT*/ 0x288, /*DAP_RESETN_SET*/ 0x2a0,
+ /*DAP_RESETN_VAL*/ 0x2a8, /*SRAM_DEDCSTOV*/ 0x300,
+ /*SRAM_LS_SLEEP*/ 0x304, /*SRAM_A9*/ 0x308,
+ /*ARM_LS_CLAMP*/ 0x30c, /*IOCR*/ 0x310,
+ /*MODEM_SYSCLKOK*/ 0x314, /*SYSCLKOK_DELAY*/ 0x318,
+ /*SYSCLKSTATUS*/ 0x31c, /*DSI_SW_RESET*/ 0x324,
+ /*A9_MASK_REQ*/ 0x328, /*A9_MASK_ACK*/ 0x32c,
+ /*HOSTACCESS_REQ*/ 0x334, /*TIMER_3_REF*/ 0x338,
+ /*TIMER_3_DOWNCOUNT*/ 0x33c, /*TIMER_3_MODE*/ 0x340,
+ /*PMB_SENS_CTRL*/ 0x344, /*PMB_REF_COUNTER*/ 0x348,
+ /*PMB_SENSOR_STATUS*/ 0x34c, /*APE_EPOD_CFG*/ 0x404,
+ /*DDR_EPOD_CFG*/ 0x408, /*EPOD_C_VAL*/ 0x418,
+ /*EPOD_VOK*/ 0x41c, /*MMIP_LS_CLAMP_VAL*/ 0x428,
+ /*VSAFE_LS_CLAMP_VAL*/ 0x434, /*DDRSUBSYS_APE_MINBW*/ 0x438,
+ /*DDRSUBSYS_STATUS*/ 0x43c, /*DDRSUBSYS_CONTROL*/ 0x440,
+ /*DDRSUBSYS_HIGH_LEAK_COND*/ 0x444, /*DDRSUBSYS_CONFIG*/ 0x448,
+ /*TIMER_4_REF*/ 0x450, /*TIMER_4_DOWNCOUNT*/ 0x454,
+ /*TIMER_4_MODE*/ 0x458, /*TIMER_5_REF*/ 0x45c,
+ /*TIMER_5_DOWNCOUNT*/ 0x460, /*TIMER_5_MODE*/ 0x464,
+ /*APE_MEM_REQ*/ 0x470, /*DBG_FRCS_APE_MEM_REQ*/ 0x474,
+ /*APE_MEM_WFX_EN*/ 0x478, /*APE_MEM_LATENCY*/ 0x47c,
+ /*APE_MEM_ACK*/ 0x480, /*ITSTATUS5*/ 0x484,
+ /*ARM_IT1_VAL*/ 0x494, /*MOD_PWR_OK*/ 0x498,
+ /*MOD_AUXCLKOK*/ 0x49c, /*MOD_AWAKE_STATUS*/ 0x4a0,
+ /*MOD_SWRESET_IRQ_ACK*/ 0x4a4, /*MOD_SWRESET_ACK*/ 0x4a8,
+ /*DBG_PWRCTL*/ 0x4ac, /*HWOBS_H*/ 0x4b0,
+ /*HWOBS_L*/ 0x4b4, /*PLLDSI_FREQ*/ 0x500,
+ /*PLLDSI_ENABLE*/ 0x504, /*PLLDSI_LOCKP*/ 0x508,
+ /*RNG_ENABLE*/ 0x50c, /*YYCLKEN0_MGT_SET*/ 0x510,
+ /*YYCLKEN0_MGT_VAL*/ 0x520, /*YYCLKEN1_MGT_VAL*/ 0x524,
+ /*XP70CLK_MGT2*/ 0x528, /*DSITVCLK_DIV*/ 0x52c,
+ /*DSI_PLLOUT_SEL*/ 0x530, /*DSI_GLITCHFREE_EN*/ 0x534,
+ /*CLKACTIV*/ 0x538, /*SIA_MMDSP_MEM_MGT*/ 0x53c,
+ /*SVA_MMDSP_MEM_MGT*/ 0x540, /*SXAMMDSP_FORCE_CLKEN*/ 0x544,
+ /*UICC_NANDTREE*/ 0x570, /*GPIOCR2*/ 0x574,
+ /*MDM_ACWAKE*/ 0x578, /*MOD_MEM_REQ*/ 0x5a4,
+ /*MOD_MEM_ACK*/ 0x5a8, /*ARM_PLLDIVPS_REQ*/ 0x5b0,
+ /*ARM_PLLDIVPS_ACK*/ 0x5b4, /*SRPTIMER_VAL*/ 0x5d0,
+};
+
+/* Offsets from secure base which is U8500_PRCMU_BASE + SZ_4K */
+static const u16 u8500_prcmu_dump_secure_regs[] = {
+ /*SECNONSEWM*/ 0x00, /*ESRAM0_INITN*/ 0x04,
+ /*ARMITMSKSEC_31TO0*/ 0x08, /*ARMITMSKSEC_63TO32*/ 0x0C,
+ /*ARMITMSKSEC_95TO64*/ 0x10, /*ARMITMSKSEC_127TO96*/ 0x14,
+ /*ARMIT_MASKXP70_IT*/ 0x18, /*ESRAM0_EPOD_CFG*/ 0x1C,
+ /*ESRAM0_EPOD_C_VAL*/ 0x20, /*ESRAM0_EPOD_VOK*/ 0x2C,
+ /*ESRAM0_LS_SLEEP*/ 0x30, /*SECURE_ONGOING*/ 0x34,
+ /*I2C_SECURE*/ 0x38, /*RESET_STATUS*/ 0x3C,
+ /*PERIPH4_RESETN_VAL*/ 0x48, /*SPAREOUT_SEC*/ 0x4C,
+ /*PIPELINEDCR*/ 0xD8,
+};
+
+static int ape_voltage_count;
+
+static void log_set(struct state_history *sh, u8 opp)
+{
+ ktime_t now;
+ ktime_t dtime;
+ unsigned long flags;
+ int state;
+
+ now = ktime_get();
+ spin_lock_irqsave(&sh->lock, flags);
+
+ for (state = 0 ; sh->opps[state] != opp; state++)
+ ;
+ BUG_ON(state >= sh->max_states);
+
+ dtime = ktime_sub(now, sh->start);
+ sh->time[sh->state] = ktime_add(sh->time[sh->state], dtime);
+ sh->start = now;
+ sh->counter[sh->state]++;
+ sh->state = state;
+
+ spin_unlock_irqrestore(&sh->lock, flags);
+}
+
+void prcmu_debug_ape_opp_log(u8 opp)
+{
+ if (opp == APE_50_PARTLY_25_OPP)
+ opp = APE_50_OPP;
+
+ log_set(&ape_sh, opp);
+}
+
+void prcmu_debug_ddr_opp_log(u8 opp)
+{
+ log_set(&ddr_sh, opp);
+}
+
+void prcmu_debug_arm_opp_log(u8 opp)
+{
+ log_set(&arm_sh, opp);
+}
+
+static void log_reset(struct state_history *sh)
+{
+ unsigned long flags;
+ int i;
+
+ pr_info("reset\n");
+
+ spin_lock_irqsave(&sh->lock, flags);
+ for (i = 0; i < sh->max_states; i++) {
+ sh->counter[i] = 0;
+ sh->time[i] = ktime_set(0, 0);
+ }
+
+ sh->start = ktime_get();
+ spin_unlock_irqrestore(&sh->lock, flags);
+
+}
+
+static ssize_t ape_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&ape_sh);
+ return count;
+}
+
+static ssize_t ddr_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&ddr_sh);
+ return count;
+}
+
+static ssize_t arm_stats_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ log_reset(&arm_sh);
+ return count;
+}
+
+static int log_print(struct seq_file *s, struct state_history *sh)
+{
+ int i;
+ unsigned long flags;
+ ktime_t total;
+ ktime_t dtime;
+ s64 t_ms;
+ s64 perc;
+ s64 total_ms;
+
+ spin_lock_irqsave(&sh->lock, flags);
+
+ dtime = ktime_sub(ktime_get(), sh->start);
+
+ total = dtime;
+
+ for (i = 0; i < sh->max_states; i++)
+ total = ktime_add(total, sh->time[i]);
+ total_ms = ktime_to_ms(total);
+
+ for (i = 0; i < sh->max_states; i++) {
+ ktime_t t = sh->time[i];
+ if (sh->state == i)
+ t = ktime_add(t, dtime);
+
+ t_ms = ktime_to_ms(t);
+ perc = 100 * t_ms;
+ do_div(perc, total_ms);
+
+ seq_printf(s, "%s OPP %d: # %u in %lld ms %d%%\n",
+ sh->prefix, sh->state_names[i],
+ sh->counter[i] + (int)(sh->state == i),
+ t_ms, (u32)perc);
+
+ }
+ spin_unlock_irqrestore(&sh->lock, flags);
+ return 0;
+}
+
+static int ape_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &ape_sh);
+ return 0;
+}
+
+static int ddr_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &ddr_sh);
+ return 0;
+}
+
+static int arm_stats_print(struct seq_file *s, void *p)
+{
+ log_print(s, &arm_sh);
+ return 0;
+}
+
+static int opp_read(struct seq_file *s, void *p)
+{
+ int opp;
+
+ struct state_history *sh = (struct state_history *)s->private;
+
+ switch (sh->req) {
+ case PRCMU_QOS_DDR_OPP:
+ opp = prcmu_get_ddr_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == DDR_100_OPP) ? "100%" :
+ (opp == DDR_50_OPP) ? "50%" :
+ (opp == DDR_25_OPP) ? "25%" :
+ "unknown", opp);
+ break;
+ case PRCMU_QOS_APE_OPP:
+ opp = prcmu_get_ape_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == APE_100_OPP) ? "100%" :
+ (opp == APE_50_OPP) ? "50%" :
+ "unknown", opp);
+ break;
+ case PRCMU_QOS_ARM_OPP:
+ opp = prcmu_get_arm_opp();
+ seq_printf(s, "%s (%d)\n",
+ (opp == ARM_MAX_OPP) ? "max" :
+ (opp == ARM_MAX_FREQ100OPP) ? "max-freq100" :
+ (opp == ARM_100_OPP) ? "100%" :
+ (opp == ARM_50_OPP) ? "50%" :
+ (opp == ARM_EXTCLK) ? "25% (extclk)" :
+ "unknown", opp);
+ break;
+ default:
+ break;
+ }
+ return 0;
+
+}
+
+static ssize_t opp_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ long unsigned i;
+ int err;
+ struct state_history *sh = (struct state_history *)
+ ((struct seq_file *)file->private_data)->private;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &i);
+
+ if (err)
+ return err;
+
+ prcmu_qos_force_opp(sh->req, i);
+
+ pr_info("prcmu debug: forced OPP for %s to %d\n", sh->prefix, (int)i);
+
+ return count;
+}
+
+static int cpufreq_delay_read(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "%lu\n", prcmu_qos_get_cpufreq_opp_delay());
+}
+
+static int ape_voltage_read(struct seq_file *s, void *p)
+{
+ return seq_printf(s, "This reference count only includes "
+ "requests via debugfs.\nCount: %d\n",
+ ape_voltage_count);
+}
+
+static ssize_t ape_voltage_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ long unsigned i;
+ int err;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &i);
+
+ if (err)
+ return err;
+
+ switch (i) {
+ case 0:
+ if (ape_voltage_count == 0)
+ pr_info("prcmu debug: reference count is already 0\n");
+ else {
+ err = prcmu_request_ape_opp_100_voltage(false);
+ if (err)
+ pr_err("prcmu debug: drop request failed\n");
+ else
+ ape_voltage_count--;
+ }
+ break;
+ case 1:
+ err = prcmu_request_ape_opp_100_voltage(true);
+ if (err)
+ pr_err("prcmu debug: request failed\n");
+ else
+ ape_voltage_count++;
+ break;
+ default:
+ pr_info("prcmu debug: value not equal to 0 or 1\n");
+ }
+ return count;
+}
+
+static ssize_t cpufreq_delay_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err;
+ long unsigned i;
+
+ err = kstrtoul_from_user(user_buf, count, 0, &i);
+
+ if (err)
+ return err;
+
+ prcmu_qos_set_cpufreq_opp_delay(i);
+
+ pr_info("prcmu debug: changed delay between cpufreq change and QoS "
+ "requirement to %lu.\n", i);
+
+ return count;
+}
+
+/* These are only for u8500 */
+#define PRCM_AVS_BASE 0x2FC
+#define AVS_VBB_RET 0x0
+#define AVS_VBB_MAX_OPP 0x1
+#define AVS_VBB_100_OPP 0x2
+#define AVS_VBB_50_OPP 0x3
+#define AVS_VARM_MAX_OPP 0x4
+#define AVS_VARM_100_OPP 0x5
+#define AVS_VARM_50_OPP 0x6
+#define AVS_VARM_RET 0x7
+#define AVS_VAPE_100_OPP 0x8
+#define AVS_VAPE_50_OPP 0x9
+#define AVS_VMOD_100_OPP 0xA
+#define AVS_VMOD_50_OPP 0xB
+#define AVS_VSAFE 0xC
+#define AVS_VSAFE_RET 0xD
+#define AVS_SIZE 14
+
+static int avs_read(struct seq_file *s, void *p)
+{
+
+ u8 avs[AVS_SIZE];
+ void __iomem *tcdm_base;
+
+ if (cpu_is_u8500()) {
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ memcpy_fromio(avs, tcdm_base + PRCM_AVS_BASE, AVS_SIZE);
+
+ seq_printf(s, "VBB_RET : 0x%02x\n", avs[AVS_VBB_RET]);
+ seq_printf(s, "VBB_MAX_OPP : 0x%02x\n", avs[AVS_VBB_MAX_OPP]);
+ seq_printf(s, "VBB_100_OPP : 0x%02x\n", avs[AVS_VBB_100_OPP]);
+ seq_printf(s, "VBB_50_OPP : 0x%02x\n", avs[AVS_VBB_50_OPP]);
+ seq_printf(s, "VARM_MAX_OPP : 0x%02x\n", avs[AVS_VARM_MAX_OPP]);
+ seq_printf(s, "VARM_100_OPP : 0x%02x\n", avs[AVS_VARM_100_OPP]);
+ seq_printf(s, "VARM_50_OPP : 0x%02x\n", avs[AVS_VARM_50_OPP]);
+ seq_printf(s, "VARM_RET : 0x%02x\n", avs[AVS_VARM_RET]);
+ seq_printf(s, "VAPE_100_OPP : 0x%02x\n", avs[AVS_VAPE_100_OPP]);
+ seq_printf(s, "VAPE_50_OPP : 0x%02x\n", avs[AVS_VAPE_50_OPP]);
+ seq_printf(s, "VMOD_100_OPP : 0x%02x\n", avs[AVS_VMOD_100_OPP]);
+ seq_printf(s, "VMOD_50_OPP : 0x%02x\n", avs[AVS_VMOD_50_OPP]);
+ seq_printf(s, "VSAFE : 0x%02x\n", avs[AVS_VSAFE]);
+ seq_printf(s, "VSAFE_RET : 0x%02x\n", avs[AVS_VSAFE_RET]);
+ } else {
+ seq_printf(s, "Only u8500 supported.\n");
+ }
+
+ return 0;
+}
+
+static void prcmu_data_mem_print(struct seq_file *s)
+{
+ int i;
+ int err;
+ void __iomem *tcdm_base;
+ u32 dmem[4];
+
+ if (cpu_is_u8500()) {
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ for (i = 0; i < U8500_PRCMU_TCDM_SIZE; i += 16) {
+ dmem[0] = readl(tcdm_base + i + 0);
+ dmem[1] = readl(tcdm_base + i + 4);
+ dmem[2] = readl(tcdm_base + i + 8);
+ dmem[3] = readl(tcdm_base + i + 12);
+
+ if (s) {
+ err = seq_printf(s,
+ "0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ ((int)tcdm_base) + i, dmem[0],
+ dmem[1], dmem[2], dmem[3]);
+ if (err < 0) {
+ pr_err("%s: seq_printf overflow, addr=%x\n",
+ __func__, ((int)tcdm_base) + i);
+ /* Can't do much here */
+ return;
+ }
+ } else {
+ printk(KERN_INFO
+ "0x%x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ ((int)tcdm_base) + i, dmem[0],
+ dmem[1], dmem[2], dmem[3]);
+ }
+ }
+ }
+}
+
+void prcmu_debug_dump_data_mem(void)
+{
+ printk(KERN_INFO "PRCMU data memory dump:\n");
+ prcmu_data_mem_print(NULL);
+}
+
+static int prcmu_debugfs_data_mem_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "PRCMU data memory:\n");
+ prcmu_data_mem_print(s);
+
+ return 0;
+}
+
+static void prcmu_regs_print(struct seq_file *s)
+{
+ int i;
+ int err;
+ void __iomem *prcmu_base;
+ u32 reg_val;
+
+ if (cpu_is_u8500()) {
+ prcmu_base = __io_address(U8500_PRCMU_BASE);
+
+ for (i = 0; i < ARRAY_SIZE(u8500_prcmu_dump_regs); i++) {
+ reg_val = readl(prcmu_base +
+ u8500_prcmu_dump_regs[i]);
+
+ if (s) {
+ err = seq_printf(s, "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_regs[i], reg_val);
+ if (err < 0) {
+ pr_err("%s: seq_printf overflow,"
+ "offset=%x\n", __func__,
+ u8500_prcmu_dump_regs[i]);
+ /* Can't do much here */
+ return;
+ }
+ } else {
+ printk(KERN_INFO
+ "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_regs[i], reg_val);
+ }
+ }
+ }
+}
+
+static void prcmu_secure_regs_print(struct seq_file *s)
+{
+ int i;
+ int err;
+ void __iomem *prcmu_sec_base;
+ u32 reg_val;
+
+ if (cpu_is_u8500()) {
+ /* PRCMU secure base starts after SZ_4K */
+ prcmu_sec_base = ioremap(U8500_PRCMU_BASE + SZ_4K, SZ_4K);
+ if (!prcmu_sec_base) {
+ pr_err("%s: ioremap faild\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(u8500_prcmu_dump_secure_regs); i++) {
+ reg_val = readl(prcmu_sec_base +
+ u8500_prcmu_dump_secure_regs[i]);
+
+ if (s) {
+ err = seq_printf(s, "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_secure_regs[i] +
+ SZ_4K,
+ reg_val);
+ if (err < 0) {
+ pr_err("%s: seq_printf overflow,"
+ "offset=%x\n", __func__,
+ u8500_prcmu_dump_secure_regs[i] +
+ SZ_4K);
+ /* Can't do much here */
+ break;
+ }
+ } else {
+ printk(KERN_INFO
+ "0x%04x: 0x%08x\n",
+ u8500_prcmu_dump_secure_regs[i] +
+ SZ_4K,
+ reg_val);
+ }
+ }
+
+ iounmap(prcmu_sec_base);
+ }
+}
+
+void prcmu_debug_dump_regs(void)
+{
+ printk(KERN_INFO "PRCMU registers dump:\n");
+ prcmu_regs_print(NULL);
+ prcmu_secure_regs_print(NULL);
+}
+
+static int prcmu_debugfs_regs_read(struct seq_file *s, void *p)
+{
+ seq_printf(s, "PRCMU registers:\n");
+ prcmu_regs_print(s);
+ prcmu_secure_regs_print(s);
+ return 0;
+}
+
+/* Interrupt debugging */
+
+/* There are eight mailboxes */
+#define NUM_MAILBOXES 8
+#define NUM_MAILBOX0_EVENTS 32
+static u32 num_mailbox_interrupts[NUM_MAILBOXES];
+static u32 num_mailbox0_events[NUM_MAILBOX0_EVENTS];
+static u32 num_mailbox0_events_garbage[NUM_MAILBOX0_EVENTS];
+
+void prcmu_debug_register_interrupt(u32 mailbox)
+{
+ if (mailbox < NUM_MAILBOXES)
+ num_mailbox_interrupts[mailbox]++;
+}
+
+void prcmu_debug_register_mbox0_event(u32 ev, u32 mask)
+{
+ int i;
+
+ for (i = 0 ; i < NUM_MAILBOX0_EVENTS; i++)
+ if (ev & (1 << i)) {
+ if (mask & (1 << i))
+ num_mailbox0_events[i]++;
+ else
+ num_mailbox0_events_garbage[i]++;
+ }
+}
+
+static int interrupt_read(struct seq_file *s, void *p)
+{
+ int i;
+ char **mbox0names;
+
+ static char *mbox0names_u8500[] = {
+ "RTC",
+ "RTT0",
+ "RTT1",
+ "HSI0",
+ "HSI1",
+ "CA_WAKE",
+ "USB",
+ "ABB",
+ "ABB_FIFO",
+ "SYSCLK_OK",
+ "CA_SLEE",
+ "AC_WAKE_ACK",
+ "SIDE_TONE_OK",
+ "ANC_OK",
+ "SW_ERROR",
+ "AC_SLEEP_ACK",
+ NULL,
+ "ARM",
+ "HOTMON_LOW",
+ "HOTMON_HIGH",
+ "MODEM_SW_RESET_REQ",
+ NULL,
+ NULL,
+ "GPIO0",
+ "GPIO1",
+ "GPIO2",
+ "GPIO3",
+ "GPIO4",
+ "GPIO5",
+ "GPIO6",
+ "GPIO7",
+ "GPIO8"};
+ static char *mbox0names_u5500[] = {
+ "RTC",
+ "RTT0",
+ "RTT1",
+ "CD_IRQ",
+ "SRP_TIM",
+ "APE_REQ",
+ "USB",
+ "ABB",
+ "LOW_POWER_AUDIO",
+ "TEMP_SENSOR_LOW",
+ "ARM",
+ "AC_WAKE_ACK",
+ NULL,
+ "TEMP_SENSOR_HIGH",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "MODEM_SW_RESET_REQ",
+ NULL,
+ NULL,
+ "GPIO0",
+ "GPIO1",
+ "GPIO2",
+ "GPIO3",
+ "GPIO4",
+ "GPIO5",
+ "GPIO6",
+ "GPIO7",
+ "AC_REL_ACK"};
+
+ if (cpu_is_u8500()) {
+ mbox0names = mbox0names_u8500;
+ } else if (cpu_is_u5500()) {
+ mbox0names = mbox0names_u5500;
+ } else {
+ seq_printf(s, "Unknown ASIC!\n");
+ return 0;
+ }
+
+ seq_printf(s, "mailbox0: %d\n", num_mailbox_interrupts[0]);
+
+ for (i = 0; i < NUM_MAILBOX0_EVENTS; i++)
+ if (mbox0names[i]) {
+ seq_printf(s, " %20s %d ", mbox0names[i],
+ num_mailbox0_events[i]
+ );
+ if (num_mailbox0_events_garbage[i])
+ seq_printf(s, "unwanted: %d",
+ num_mailbox0_events_garbage[i]);
+ seq_printf(s, "\n");
+ } else if (num_mailbox0_events[i]) {
+ seq_printf(s, " unknown (%d) %d\n",
+ i, num_mailbox0_events[i]);
+ }
+
+ for (i = 1 ; i < NUM_MAILBOXES; i++)
+ seq_printf(s, "mailbox%d: %d\n", i, num_mailbox_interrupts[i]);
+ return 0;
+}
+
+static int opp_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, opp_read, inode->i_private);
+}
+
+static int ape_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, ape_stats_print, inode->i_private);
+}
+
+static int ddr_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, ddr_stats_print, inode->i_private);
+}
+
+static int arm_stats_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, arm_stats_print, inode->i_private);
+}
+
+static int cpufreq_delay_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, cpufreq_delay_read, inode->i_private);
+}
+
+static int ape_voltage_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, ape_voltage_read, inode->i_private);
+}
+
+static int avs_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, avs_read, inode->i_private);
+}
+
+static int prcmu_data_mem_open_file(struct inode *inode, struct file *file)
+{
+ int err;
+ struct seq_file *s;
+
+ err = single_open(file, prcmu_debugfs_data_mem_read, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 4);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static int prcmu_regs_open_file(struct inode *inode, struct file *file)
+{
+ int err;
+ struct seq_file *s;
+
+ err = single_open(file, prcmu_debugfs_regs_read, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 2);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static int interrupt_open_file(struct inode *inode, struct file *file)
+{
+ return single_open(file, interrupt_read, inode->i_private);
+}
+
+static const struct file_operations opp_fops = {
+ .open = opp_open_file,
+ .write = opp_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ape_stats_fops = {
+ .open = ape_stats_open_file,
+ .write = ape_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ddr_stats_fops = {
+ .open = ddr_stats_open_file,
+ .write = ddr_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations arm_stats_fops = {
+ .open = arm_stats_open_file,
+ .write = arm_stats_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations cpufreq_delay_fops = {
+ .open = cpufreq_delay_open_file,
+ .write = cpufreq_delay_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ape_voltage_fops = {
+ .open = ape_voltage_open_file,
+ .write = ape_voltage_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations avs_fops = {
+ .open = avs_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations prcmu_data_mem_fops = {
+ .open = prcmu_data_mem_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations prcmu_regs_fops = {
+ .open = prcmu_regs_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations interrupts_fops = {
+ .open = interrupt_open_file,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int setup_debugfs(void)
+{
+ struct dentry *dir;
+ struct dentry *file;
+
+ dir = debugfs_create_dir("prcmu", NULL);
+ if (IS_ERR_OR_NULL(dir))
+ goto fail;
+
+ file = debugfs_create_file("ape_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &ape_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ddr_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &ddr_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("arm_stats", (S_IRUGO | S_IWUGO),
+ dir, NULL, &arm_stats_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ape_opp", (S_IRUGO),
+ dir, (void *)&ape_sh,
+ &opp_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ddr_opp", (S_IRUGO),
+ dir, (void *)&ddr_sh,
+ &opp_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("arm_opp", (S_IRUGO),
+ dir, (void *)&arm_sh,
+ &opp_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("opp_cpufreq_delay", (S_IRUGO),
+ dir, NULL, &cpufreq_delay_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("ape_voltage", (S_IRUGO),
+ dir, NULL, &ape_voltage_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("avs",
+ (S_IRUGO),
+ dir, NULL, &avs_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("data_mem", (S_IRUGO),
+ dir, NULL,
+ &prcmu_data_mem_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("regs", (S_IRUGO),
+ dir, NULL,
+ &prcmu_regs_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ file = debugfs_create_file("interrupts",
+ (S_IRUGO),
+ dir, NULL, &interrupts_fops);
+ if (IS_ERR_OR_NULL(file))
+ goto fail;
+
+ return 0;
+fail:
+ if (!IS_ERR_OR_NULL(dir))
+ debugfs_remove_recursive(dir);
+
+ pr_err("prcmu debug: debugfs entry failed\n");
+ return -ENOMEM;
+}
+
+static __init int prcmu_debug_init(void)
+{
+ spin_lock_init(&ape_sh.lock);
+ spin_lock_init(&ddr_sh.lock);
+ spin_lock_init(&arm_sh.lock);
+ ape_sh.start = ktime_get();
+ ddr_sh.start = ktime_get();
+ arm_sh.start = ktime_get();
+ return 0;
+}
+arch_initcall(prcmu_debug_init);
+
+static __init int prcmu_debug_debugfs_init(void)
+{
+ setup_debugfs();
+ return 0;
+}
+late_initcall(prcmu_debug_debugfs_init);
diff --git a/arch/arm/mach-ux500/product.c b/arch/arm/mach-ux500/product.c
new file mode 100644
index 00000000000..f4647b3ab29
--- /dev/null
+++ b/arch/arm/mach-ux500/product.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jens Wiklander <jens.wiklander@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/tee.h>
+#include <linux/module.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+
+#define STATIC_TEE_TA_START_LOW 0xBC765EDE
+#define STATIC_TEE_TA_START_MID 0x6724
+#define STATIC_TEE_TA_START_HIGH 0x11DF
+#define STATIC_TEE_TA_START_CLOCKSEQ \
+ {0x8E, 0x12, 0xEC, 0xDB, 0xDF, 0xD7, 0x20, 0x85}
+
+#define U5500_PRCMU_DBG_PWRCTRL (U5500_PRCMU_BASE + 0x4AC)
+#define PRCMU_DBG_PWRCTRL_A9DBGCLKEN (1 << 4)
+
+static struct tee_product_config product_config;
+
+bool ux500_jtag_enabled(void)
+{
+#ifdef CONFIG_UX500_DEBUG_NO_LAUTERBACH
+ return false;
+#else
+ if (machine_is_snowball())
+ return true;
+ if (cpu_is_u5500())
+ return readl_relaxed(__io_address(U5500_PRCMU_DBG_PWRCTRL))
+ & PRCMU_DBG_PWRCTRL_A9DBGCLKEN;
+
+ if (cpu_is_u8500() || cpu_is_u9540())
+ return (product_config.rt_flags & TEE_RT_FLAGS_JTAG_ENABLED) ==
+ TEE_RT_FLAGS_JTAG_ENABLED;
+
+ return true;
+#endif
+}
+
+static int __init product_detect(void)
+{
+ int err;
+ int origin_err;
+ struct tee_operation operation = { { { 0 } } };
+ struct tee_context context;
+ struct tee_session session;
+
+ /* Selects trustzone application needed for the job. */
+ struct tee_uuid static_uuid = {
+ STATIC_TEE_TA_START_LOW,
+ STATIC_TEE_TA_START_MID,
+ STATIC_TEE_TA_START_HIGH,
+ STATIC_TEE_TA_START_CLOCKSEQ,
+ };
+
+ if (cpu_is_u5500())
+ return -ENODEV;
+
+ err = teec_initialize_context(NULL, &context);
+ if (err) {
+ pr_err("ux500-product: unable to initialize tee context,"
+ " err = %d\n", err);
+ err = -EINVAL;
+ goto error0;
+ }
+
+ err = teec_open_session(&context, &session, &static_uuid,
+ TEEC_LOGIN_PUBLIC, NULL, NULL, &origin_err);
+ if (err) {
+ pr_err("ux500-product: unable to open tee session,"
+ " tee error = %d, origin error = %d\n",
+ err, origin_err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ operation.shm[0].buffer = &product_config;
+ operation.shm[0].size = sizeof(product_config);
+ operation.shm[0].flags = TEEC_MEM_OUTPUT;
+ operation.flags = TEEC_MEMREF_0_USED;
+
+ err = teec_invoke_command(&session,
+ TEE_STA_GET_PRODUCT_CONFIG,
+ &operation, &origin_err);
+ if (err) {
+ pr_err("ux500-product: fetching product settings failed, err=%d",
+ err);
+ err = -EINVAL;
+ goto error1;
+ }
+
+ switch (product_config.product_id) {
+ case TEE_PRODUCT_ID_8400:
+ pr_info("ux500-product: u8400 detected\n");
+ break;
+ case TEE_PRODUCT_ID_8500:
+ pr_info("ux500-product: u8500 detected\n");
+ break;
+ case TEE_PRODUCT_ID_9500:
+ pr_info("ux500-product: u9500 detected\n");
+ break;
+ case TEE_PRODUCT_ID_5500:
+ pr_info("ux500-product: u5500 detected\n");
+ break;
+ case TEE_PRODUCT_ID_7400:
+ pr_info("ux500-product: u7400 detected\n");
+ break;
+ case TEE_PRODUCT_ID_8500C:
+ pr_info("ux500-product: u8500C detected\n");
+ break;
+ case TEE_PRODUCT_ID_UNKNOWN:
+ default:
+ pr_info("ux500-product: UNKNOWN! (0x%x) detected\n",
+ product_config.product_id);
+ break;
+ }
+ pr_info("ux500-product: JTAG is %s\n",
+ ux500_jtag_enabled() ? "enabled" : "disabled");
+error1:
+ (void) teec_finalize_context(&context);
+error0:
+ return err;
+}
+device_initcall(product_detect);
diff --git a/arch/arm/mach-ux500/product.h b/arch/arm/mach-ux500/product.h
new file mode 100644
index 00000000000..502eff4df14
--- /dev/null
+++ b/arch/arm/mach-ux500/product.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jens Wiklander <jens.wiklander@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ */
+
+#ifndef UX500_PRODUCT_H
+#define UX500_PRODUCT_H
+
+#ifdef CONFIG_TEE_UX500
+
+bool ux500_jtag_enabled(void);
+
+#else
+
+static inline bool ux500_jtag_enabled(void)
+{
+ return true;
+}
+
+#endif
+#endif
diff --git a/arch/arm/mach-ux500/reboot_reasons.c b/arch/arm/mach-ux500/reboot_reasons.c
new file mode 100644
index 00000000000..7b25ef8b4ff
--- /dev/null
+++ b/arch/arm/mach-ux500/reboot_reasons.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ * Author: Rickard Evertsson <rickard.evertsson@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Use this file to customize your reboot / sw reset reasons. Add, remove or
+ * modify reasons in reboot_reasons[].
+ */
+
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <mach/reboot_reasons.h>
+
+struct reboot_reason reboot_reasons[] = {
+ {"crash", SW_RESET_CRASH},
+ {"factory-reset", SW_RESET_FACTORY_RESET},
+ {"recovery", SW_RESET_RECOVERY},
+ {"charging", SW_RESET_CHARGING},
+ {"coldstart", SW_RESET_COLDSTART},
+ {"none", SW_RESET_NO_ARGUMENT}, /* Normal Boot */
+ {"chgonly-exit", SW_RESET_CHGONLY_EXIT}, /* Exit Charge Only Mode */
+};
+
+unsigned int reboot_reasons_size = ARRAY_SIZE(reboot_reasons);
+
+/*
+ * The reboot reason string can be 255 characters long and the memory
+ * in which we save the sw reset reason is 2 bytes. Therefore we need to
+ * convert the string into a 16 bit pattern.
+ *
+ * See file reboot_reasons.h for conversion.
+ */
+u16 reboot_reason_code(const char *cmd)
+{
+ int i;
+
+ if (cmd == NULL) {
+ if (oops_in_progress) {
+ /* if we're in an oops assume it's a crash */
+ return SW_RESET_CRASH;
+ } else {
+ /* normal reboot w/o argument */
+ return SW_RESET_NO_ARGUMENT;
+ }
+ }
+
+ /* Search through reboot reason list */
+ for (i = 0; i < reboot_reasons_size; i++) {
+ if (!strcmp(reboot_reasons[i].reason, cmd))
+ return reboot_reasons[i].code;
+ }
+
+ /* No valid reboot reason found */
+ return SW_RESET_NO_ARGUMENT;
+}
+
+/*
+ * The saved sw reset reason is a 2 byte code that is translated into
+ * a reboot reason string which is up to 255 characters long by this
+ * function.
+ *
+ * See file reboot_reasons.h for conversion.
+ */
+const char *reboot_reason_string(u16 code)
+{
+ int i;
+
+ /* Search through reboot reason list */
+ for (i = 0; i < reboot_reasons_size; i++) {
+ if (reboot_reasons[i].code == code)
+ return reboot_reasons[i].reason;
+ }
+
+ /* No valid reboot reason code found */
+ return "unknown";
+}
diff --git a/arch/arm/mach-ux500/regulator-u5500.h b/arch/arm/mach-ux500/regulator-u5500.h
new file mode 100644
index 00000000000..cf3eeed9366
--- /dev/null
+++ b/arch/arm/mach-ux500/regulator-u5500.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __REGULATOR_U5500_H
+#define __REGULATOR_U5500_H
+
+enum u5500_regulator_id {
+ U5500_REGULATOR_VAPE,
+ U5500_REGULATOR_SWITCH_SGA,
+ U5500_REGULATOR_SWITCH_HVA,
+ U5500_REGULATOR_SWITCH_SIA,
+ U5500_REGULATOR_SWITCH_DISP,
+ U5500_REGULATOR_SWITCH_ESRAM12,
+ U5500_NUM_REGULATORS
+};
+
+#endif
diff --git a/arch/arm/mach-ux500/test/Kconfig b/arch/arm/mach-ux500/test/Kconfig
new file mode 100644
index 00000000000..a071166d092
--- /dev/null
+++ b/arch/arm/mach-ux500/test/Kconfig
@@ -0,0 +1,6 @@
+
+config DB8500_PWR_TEST
+ bool "Power usage module test"
+ depends on (UX500_SOC_DB8500 && DEBUG_FS && REGULATOR_AB8500_DEBUG)
+ help
+ Add power module tests for idle
diff --git a/arch/arm/mach-ux500/test/Makefile b/arch/arm/mach-ux500/test/Makefile
new file mode 100644
index 00000000000..58f1f2f3be6
--- /dev/null
+++ b/arch/arm/mach-ux500/test/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_DB8500_PWR_TEST) += pwr.o
diff --git a/arch/arm/mach-ux500/test/pwr.c b/arch/arm/mach-ux500/test/pwr.c
new file mode 100644
index 00000000000..5d5d24a38ab
--- /dev/null
+++ b/arch/arm/mach-ux500/test/pwr.c
@@ -0,0 +1,828 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * This is a module test for clocks and regulators.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/db8500-prcmu.h>
+
+#include <mach/hardware.h>
+#include <mach/pm.h>
+
+#include "../../../drivers/regulator/dbx500-prcmu.h"
+#include "../../../drivers/regulator/ab8500-debug.h"
+
+#define PRCC_PCKSR 0x010
+#define PRCC_KCKSR 0x014
+
+#define PRCM_PLLSOC0_ENABLE 0x090
+#define PRCM_PLLSOC1_ENABLE 0x094
+#define PRCM_PLL32K_ENABLE 0x10C
+#define PRCM_PLLARM_ENABLE 0x098
+#define PRCM_PLLDDR_ENABLE 0x09C
+#define PRCM_RNG_ENABLE 0x50C
+#define PRCM_PLLDSI_ENABLE 0x504
+
+#define PRCM_CLKOCR 0x1CC
+
+#define PRCM_ARMCLKFIX_MGT 0x000
+#define PRCM_ACLK_MGT 0x004
+#define PRCM_SGACLK_MGT 0x014
+#define PRCM_UARTCLK_MGT 0x018
+#define PRCM_MSP02CLK_MGT 0x01C
+#define PRCM_MSP1CLK_MGT 0x288
+#define PRCM_I2CCLK_MGT 0x020
+#define PRCM_SDMMCCLK_MGT 0x024
+#define PRCM_SLIMCLK_MGT 0x028
+#define PRCM_PER1CLK_MGT 0x02C
+#define PRCM_PER2CLK_MGT 0x030
+#define PRCM_PER3CLK_MGT 0x034
+#define PRCM_PER5CLK_MGT 0x038
+#define PRCM_PER6CLK_MGT 0x03C
+#define PRCM_PER7CLK_MGT 0x040
+#define PRCM_LCDCLK_MGT 0x044
+#define PRCM_BMLCLK_MGT 0x04C
+#define PRCM_HSITXCLK_MGT 0x050
+#define PRCM_HSIRXCLK_MGT 0x054
+#define PRCM_HDMICLK_MGT 0x058
+#define PRCM_APEATCLK_MGT 0x05C
+#define PRCM_APETRACECLK_MGT 0x060
+#define PRCM_MCDECLK_MGT 0x064
+#define PRCM_IPI2CCLK_MGT 0x068
+#define PRCM_DSIALTCLK_MGT 0x06C
+#define PRCM_SPARE2CLK_MGT 0x070
+#define PRCM_SPARE1CLK_MGT 0x048
+#define PRCM_DMACLK_MGT 0x074
+#define PRCM_B2R2CLK_MGT 0x078
+#define PRCM_TVCLK_MGT 0x07C
+#define PRCM_SSPCLK_MGT 0x280
+#define PRCM_RNGCLK_MGT 0x284
+#define PRCM_UICCCLK_MGT 0x27C
+
+#define PRCM_DSITVCLK_DIV 0x52C
+#define PRCM_DSI_PLLOUT_SEL 0x530
+
+#define PRCM_YYCLKEN0_MGT_VAL 0x520
+
+enum acc_type {
+ RAW = 0,
+ CLK_PRCMU,
+ CLK_PAR_PRCMU,
+ CLK_ABB_SYS,
+ REG_DB8500,
+ REG_AB8500,
+};
+
+struct pwr_test {
+ enum acc_type type;
+ u32 base;
+ u32 off;
+ u32 mask;
+ u32 val;
+
+ u32 par_off;
+ u32 par_mask;
+ u32 par_val;
+
+ char *txt;
+ char *txt2;
+
+ /* For AB8500 */
+ enum ab8500_regulator_mode mode;
+ enum ab8500_regulator_hwmode hwmode;
+ enum hwmode_auto hwmode_auto[4];
+ int volt_selected;
+ int alt_volt_selected;
+ int volt_len;
+ int volt[4];
+ int alt_volt[4];
+};
+
+#define RAW_TEST(_base, _off, _mask, _val) \
+ { \
+ .type = RAW, \
+ .base = _base, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .txt = #_base, \
+ .txt2 = #_off \
+ }
+
+#define CLK_TEST_PAR_PRCMU(_base, _off, _mask, _val, _par_off, \
+ _par_mask, _par_val) \
+ { \
+ .type = CLK_PAR_PRCMU, \
+ .base = _base, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .par_off = _par_off, \
+ .par_mask = _par_mask, \
+ .par_val = _par_val, \
+ .txt = #_base, \
+ .txt2 = #_off \
+ }
+
+#define CLK_TEST_PRCMU(_off, _mask, _val) \
+ { \
+ .type = CLK_PRCMU, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .txt = #_off, \
+ }
+
+#define CLK_TEST_ABB_SYS(_off, _mask, _val) \
+ { \
+ .type = CLK_ABB_SYS, \
+ .off = _off, \
+ .mask = _mask, \
+ .val = _val, \
+ .txt = #_off, \
+ }
+#define REG_TEST_DB8500(_reg, _val) \
+ { \
+ .type = REG_DB8500, \
+ .off = _reg, \
+ .val = _val, \
+ }
+
+
+static struct u8500_regulators
+{
+ struct dbx500_regulator_info *db8500_reg;
+ int db8500_num;
+} u8500_reg;
+
+/*
+ * Idle - Note: this is not suspend.
+ * This test shall pass when the screen is black, before
+ * suspend is executed.
+ */
+
+static struct pwr_test idle_test[] = {
+
+ /* Test all periph kernel and bus clocks */
+ /* bus: gpioctrl */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST1_BASE, PRCC_PCKSR, 0x07ff, BIT(9),
+ PRCM_PER1CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST1_BASE, PRCC_KCKSR, 0x037f, 0x0000,
+ PRCM_PER1CLK_MGT, BIT(8), BIT(8)),
+ /* bus: gpioctrl */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST2_BASE, PRCC_PCKSR, 0x0fff, BIT(11),
+ PRCM_PER2CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST2_BASE, PRCC_KCKSR, 0x00ff, 0x0000,
+ PRCM_PER2CLK_MGT, BIT(8), BIT(8)),
+ /* bus: uart2, gpioctrl - users??, kernel: uart2 */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST3_BASE, PRCC_PCKSR, 0x01ff, BIT(8) | BIT(6),
+ PRCM_PER3CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST3_BASE, PRCC_KCKSR, 0x00fe, BIT(6),
+ PRCM_PER3CLK_MGT, BIT(8), BIT(8)),
+ /* No user on periph5 */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST5_BASE, PRCC_PCKSR, 0x0003, 0x0000,
+ PRCM_PER5CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST5_BASE, PRCC_KCKSR, 0x0000, 0x0000,
+ PRCM_PER5CLK_MGT, BIT(8), BIT(8)),
+ /* bus: MTU0 used for scheduling ApIdle */
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST6_BASE, PRCC_PCKSR, 0x00ff, BIT(6),
+ PRCM_PER6CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PAR_PRCMU(U8500_CLKRST6_BASE, PRCC_KCKSR, 0x0001, 0x0000,
+ PRCM_PER6CLK_MGT, BIT(8), BIT(8)),
+
+ /* Test that clkout 1/2 is off */
+ CLK_TEST_PRCMU(PRCM_CLKOCR, 0x3f003f, 0x0000),
+
+ /* Test that ulp clock is off */
+ CLK_TEST_ABB_SYS(AB8500_SYSULPCLKCTRL1, 0xfc, 0x00),
+
+ /* Test that prcm clks are in proper state */
+ CLK_TEST_PRCMU(PRCM_ARMCLKFIX_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_ACLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_SGACLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_UARTCLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_MSP02CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_MSP1CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_I2CCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_SDMMCCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_SLIMCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_PER1CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER2CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER3CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER5CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_PER6CLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_PER7CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_LCDCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_BMLCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_HSITXCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_HSIRXCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_HDMICLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_APEATCLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_APETRACECLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_MCDECLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_IPI2CCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_DSIALTCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_DMACLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_B2R2CLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_TVCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_SSPCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_RNGCLK_MGT, BIT(8), BIT(8)),
+ CLK_TEST_PRCMU(PRCM_UICCCLK_MGT, BIT(8), 0x000),
+ CLK_TEST_PRCMU(PRCM_DSITVCLK_DIV, BIT(24) | BIT(25) | BIT(26),
+ 0x000),
+ CLK_TEST_PRCMU(PRCM_DSI_PLLOUT_SEL, (BIT(0) | BIT(1) | BIT(2) |
+ BIT(8) | BIT(9) | BIT(10)),
+ 0x200), /* Will change */
+ CLK_TEST_PRCMU(PRCM_YYCLKEN0_MGT_VAL, 0xBFFFFFFF,
+ (BIT(27) | BIT(23) | BIT(22) | BIT(15) |
+ BIT(13) | BIT(12) | BIT(11) | BIT(5) |
+ BIT(1) | BIT(0))),
+
+ /* Check db8500 regulator settings - enable */
+ REG_TEST_DB8500(DB8500_REGULATOR_VAPE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VARM, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VMODEM, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VPLL, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VSMPS1, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VSMPS2, 1),
+ REG_TEST_DB8500(DB8500_REGULATOR_VSMPS3, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_VRF1, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAMMDSP, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAMMDSPRET, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAPIPE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAMMDSP, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAMMDSPRET, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAPIPE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SGA, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_B2R2_MCDE, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM12, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM12RET, 0),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM34, 1),
+ REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM34RET, 0),
+
+ /* ab8500 regulators */
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VARM,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_OFF},
+ .volt_selected = 2,
+ /* Voltage and voltage selection depends on ARM_OPP */
+ .alt_volt_selected = 1,
+ .volt_len = 3,
+ .volt = {1350000, 1025000, 750000},
+ .alt_volt = {1250000, 1025000, 750000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VBBP,
+ .mode = AB8500_MODE_ON,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_INVAL},
+ .volt_selected = 1,
+ .volt_len = 2,
+ .volt = {-300000, 0},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VBBN,
+ .mode = AB8500_MODE_ON,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_INVAL},
+ .volt_selected = 1,
+ .volt_len = 2,
+ .volt = {300000, 0},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAPE,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_OFF},
+ .volt_selected = 2,
+ /* APE_OPP can get changes by cpufreq */
+ .alt_volt_selected = 1,
+ .volt_len = 3,
+ .volt = {1225000, 1000000, 1200000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VSMPS1,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 3,
+ .volt = {1200000, 1200000, 1200000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VSMPS2,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_ON, HWM_ON, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 3,
+ .volt = {1800000, 1800000, 1800000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VSMPS3,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 3,
+ .volt = {925000, 1212500, 1200000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VPLL,
+ .mode = AB8500_MODE_HW,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VREFDDR,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VMOD,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_selected = 2,
+ .volt_len = 2,
+ .volt = {1125000, 1025000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VEXTSUPPLY1,
+ .mode = AB8500_MODE_LP,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VEXTSUPPLY2,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VEXTSUPPLY3,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_ON, HWM_OFF, HWM_ON, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VRF1,
+ .mode = AB8500_MODE_HW,
+ .volt_len = 1,
+ .volt = {2150000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VANA,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUX1,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_len = 1,
+ .volt = {2800000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUX2,
+ .mode = AB8500_MODE_ON,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_len = 1,
+ .volt = {3300000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUX3,
+ .mode = AB8500_MODE_OFF,
+ .hwmode = AB8500_HWMODE_HPLP,
+ .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF},
+ .volt_len = 1,
+ .volt = {2910000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VINTCORE,
+ .mode = AB8500_MODE_OFF,
+ .volt_len = 1,
+ .volt = {1250000},
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VTVOUT,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VAUDIO,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VANAMIC1,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VANAMIC2,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VDMIC,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VUSB,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VOTG,
+ .mode = AB8500_MODE_OFF,
+ },
+ {
+ .type = REG_AB8500,
+ .off = AB8500_VBUSBIS,
+ .mode = AB8500_MODE_OFF,
+ },
+};
+
+static int read_raw(struct pwr_test *t)
+{
+ u32 res;
+ int i;
+
+ for (i = 0 ; i < 50; i++) {
+ res = readl(__io_address(t->base) + t->off);
+ if ((res & t->mask) == t->val)
+ return 0;
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: %s %s 0x%x, 0x%x reads 0x%x (masked: 0x%x), "
+ "expected 0x%x (mask: 0x%x)\n",
+ t->txt, t->txt2, t->base, t->off, res,
+ res & t->mask, t->val, t->mask);
+ return -EINVAL;
+}
+
+static int clk_read_par_prcmu(struct pwr_test *t)
+ {
+ u32 res;
+ int i;
+ bool parent_enabled = false;
+
+ /* check if parent clock is in expected configuration (usually PRCC) */
+ for (i = 0 ; i < 50; i++) {
+ res = prcmu_read(t->par_off);
+ if ((res & t->par_mask) == t->par_val) {
+ parent_enabled = true;
+ break;
+ }
+ msleep(10);
+ }
+
+ /* If parent clock is off, but clock is expected to be on --> fail */
+ if (!parent_enabled && t->val != 0) {
+ pr_err("pwr_test: ERROR: PRCMU parent clk of %s, "
+ "0x%x reads 0x%x (masked: 0x%x), "
+ "expected to be on 0x%x (mask: 0x%x)\n",
+ t->txt, t->par_off, res, res & t->par_mask,
+ t->par_val, t->par_mask);
+ return -EINVAL;
+ }
+
+ /* It's ok if parent clock is off and clock is off as well */
+ if (!parent_enabled && t->val == 0)
+ return 0;
+
+ return read_raw(t);
+}
+
+static int clk_read_prcmu(struct pwr_test *t)
+{
+ u32 res;
+ int i;
+
+ for (i = 0 ; i < 50; i++) {
+ res = prcmu_read(t->off);
+ if ((res & t->mask) == t->val)
+ return 0;
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: %s PRCMU, 0x%x reads 0x%x (masked: 0x%x), "
+ "expected 0x%x (mask: 0x%x)\n",
+ t->txt, t->off, res, res & t->mask, t->val, t->mask);
+ return -EINVAL;
+}
+
+static int clk_read_abb_sys(struct pwr_test *t)
+{
+ int ret;
+ u8 val = 0;
+ int i;
+
+ for (i = 0; i < 50; i++) {
+ ret = ab8500_sysctrl_read(t->off, &val);
+ if (ret < 0) {
+ pr_err("pwr_test: AB8500 access error: %d of "
+ "reg: 0x%x\n", ret, t->off);
+ return ret;
+ }
+ if ((val & (u8)t->mask) == t->val)
+ return 0;
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: AB8500 register 0x%x %s, reads 0x%x,"
+ " (masked 0x%x) expected 0x%x (mask: 0x%x)\n",
+ t->base, t->txt, val, val & (u8)t->mask, t->val, t->mask);
+
+ return -EINVAL;
+}
+
+static int reg_read_db8500(struct pwr_test *t)
+{
+ int i, j;
+
+ for (j = 0 ; j < u8500_reg.db8500_num ; j++)
+ if (u8500_reg.db8500_reg[j].desc.id == t->off)
+ break;
+
+ if (j == u8500_reg.db8500_num) {
+ pr_err("pwr_test: Invalid db8500 regulator\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 50; i++) {
+
+ /* powerstate is a special case */
+ if (t->off == DB8500_REGULATOR_VAPE) {
+ if ((u8500_reg.db8500_reg[j].is_enabled ||
+ power_state_active_is_enabled()) == t->val)
+ return 0;
+ } else {
+ if (u8500_reg.db8500_reg[j].is_enabled == t->val)
+ return 0;
+ }
+ msleep(10);
+ }
+
+ pr_err("pwr_test: ERROR: DB8500 regulator %s is 0x%x expected 0x%x\n",
+ u8500_reg.db8500_reg[j].desc.name,
+ t->off != DB8500_REGULATOR_VAPE ?
+ u8500_reg.db8500_reg[j].is_enabled :
+ u8500_reg.db8500_reg[j].is_enabled ||
+ power_state_active_is_enabled(),
+ t->val);
+
+ return -EINVAL;
+}
+
+static int reg_read_ab8500(struct pwr_test *t)
+{
+ int i, j;
+ int ret;
+ struct ab8500_debug_regulator_status s;
+
+ for (i = 0; i < 50; i++) {
+ ret = ab8500_regulator_debug_read(t->off, &s);
+
+ if (ret) {
+ pr_err("pwr_test: Fail to read ab8500 regulator\n");
+ return -EINVAL;
+ }
+
+ if (t->mode != s.mode)
+ goto repeat;
+
+ if (t->hwmode != s.hwmode)
+ goto repeat;
+
+ if (t->volt_selected != s.volt_selected &&
+ t->alt_volt_selected != s.volt_selected)
+ goto repeat;
+
+ if (t->volt_len != s.volt_len)
+ goto repeat;
+
+ for (j = 0; j < 4 && s.hwmode != AB8500_HWMODE_NONE; j++)
+ if (s.hwmode_auto[j] != t->hwmode_auto[j])
+ goto repeat;
+
+ for (j = 0; j < s.volt_len; j++)
+ if (s.volt[j] != t->volt[j] &&
+ s.volt[j] != t->alt_volt[j])
+ goto repeat;
+ return 0;
+
+repeat:
+ msleep(10);
+ }
+
+ pr_err("pwr test: ERROR: AB8500 regulator %s ", s.name);
+
+ if (t->mode != s.mode)
+ printk("mode is: %d expected: %d ",
+ s.mode, t->mode);
+
+ if (t->hwmode != s.hwmode)
+ printk("hwmode is: %d expected: %d ",
+ s.hwmode, t->hwmode);
+
+ if (t->volt_selected != s.volt_selected &&
+ t->alt_volt_selected != s.volt_selected) {
+ printk("volt selected is: %d expected: %d ",
+ s.volt_selected, t->volt_selected);
+ if (t->alt_volt_selected)
+ printk("(alt volt: %d) ", t->alt_volt_selected);
+ }
+
+ if (t->volt_len != s.volt_len)
+ printk("volt len is: %d expected: %d ",
+ s.volt_len, t->volt_len);
+
+ for (j = 0; j < 4; j++)
+ if (s.hwmode_auto[j] != t->hwmode_auto[j])
+ break;
+ if (j != 4 && s.hwmode != AB8500_HWMODE_NONE) {
+
+ printk("hwmode auto:: {");
+ for (j = 0; j < 4; j++) {
+ switch(s.hwmode_auto[j]) {
+ case HWM_OFF: { printk("OFF "); break; }
+ case HWM_ON: { printk("ON "); break; }
+ case HWM_INVAL: { printk("INVAL "); break; }
+ }
+ }
+ printk("}, expected: {");
+ for (j = 0; j < 4; j++) {
+ switch(t->hwmode_auto[j]) {
+ case HWM_OFF: { printk("OFF "); break; }
+ case HWM_ON: { printk("ON "); break; }
+ case HWM_INVAL: { printk("INVAL "); break; }
+ }
+ }
+ printk("} ");
+ }
+
+ if (s.volt_len == t->volt_len) {
+ for (j = 0; j < s.volt_len; j++)
+ if (s.volt[j] != t->volt[j] &&
+ t->alt_volt[j] != s.volt[j])
+ break;
+
+ } else {
+ j = 0;
+ }
+
+ if (j != s.volt_len) {
+ printk("voltage: {");
+ for (j = 0; j < s.volt_len; j++)
+ printk("%d ", s.volt[j]);
+
+ if (t->alt_volt) {
+ printk("} alt voltage: {");
+ for (j = 0; j < s.volt_len; j++)
+ printk("%d ", t->alt_volt[j]);
+ }
+ printk("} expected: {");
+ for (j = 0; j < t->volt_len; j++)
+ printk("%d ", t->volt[j]);
+ printk("} ");
+ }
+
+ printk("\n");
+
+ return -EINVAL;
+}
+
+
+static int test_execute(struct pwr_test *t, int len)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0 ; i < len ; i++) {
+ switch (t[i].type) {
+ case CLK_ABB_SYS:
+ err |= clk_read_abb_sys(&t[i]);
+ break;
+ case CLK_PRCMU:
+ err |= clk_read_prcmu(&t[i]);
+ break;
+ case CLK_PAR_PRCMU:
+ err |= clk_read_par_prcmu(&t[i]);
+ break;
+ case RAW:
+ err |= read_raw(&t[i]);
+ break;
+ case REG_DB8500:
+ err |= reg_read_db8500(&t[i]);
+ break;
+ case REG_AB8500:
+ err |= reg_read_ab8500(&t[i]);
+ break;
+ default:
+ break;
+ }
+
+ }
+ return err;
+}
+
+static int pwr_test_idle(struct seq_file *s, void *data)
+{
+ int err;
+
+ err = test_execute(idle_test, ARRAY_SIZE(idle_test));
+
+ seq_printf(s, "%s\n", err == 0 ? "PASS" : "FAIL");
+
+ return 0;
+}
+
+
+int dbx500_regulator_testcase(struct dbx500_regulator_info *regulator_info,
+ int num_regulators)
+{
+ u8500_reg.db8500_reg = regulator_info;
+ u8500_reg.db8500_num = num_regulators;
+ return 0;
+}
+
+static int pwr_test_debugfs_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file,
+ inode->i_private,
+ NULL);
+}
+
+static const struct file_operations pwr_test_debugfs_ops = {
+ .open = pwr_test_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static struct dentry *debugfs_dir;
+
+static int __init pwr_test_init(void)
+{
+ int err = 0;
+ void *err_ptr;
+
+ debugfs_dir = debugfs_create_dir("pwr_test", NULL);
+ if (IS_ERR(debugfs_dir))
+ return PTR_ERR(debugfs_dir);
+
+ err_ptr = debugfs_create_file("idle",
+ S_IFREG | S_IRUGO,
+ debugfs_dir, (void *)pwr_test_idle,
+ &pwr_test_debugfs_ops);
+ if (IS_ERR(err_ptr)) {
+ err = PTR_ERR(err_ptr);
+ goto out;
+ }
+ return 0;
+out:
+ debugfs_remove_recursive(debugfs_dir);
+ return err;
+}
+late_initcall(pwr_test_init);
diff --git a/arch/arm/mach-ux500/timer.c b/arch/arm/mach-ux500/timer.c
index d37df98b5c3..438c9715aab 100644
--- a/arch/arm/mach-ux500/timer.c
+++ b/arch/arm/mach-ux500/timer.c
@@ -7,6 +7,7 @@
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/clksrc-dbx500-prcmu.h>
+#include <linux/clksrc-db5500-mtimer.h>
#include <linux/of.h>
#include <asm/smp_twd.h>
@@ -16,6 +17,21 @@
#include <mach/setup.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
+#include <mach/context.h>
+
+#ifdef CONFIG_UX500_CONTEXT
+static int mtu_context_notifier_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ if (event == CONTEXT_APE_RESTORE)
+ nmdk_clksrc_reset();
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mtu_context_notifier = {
+ .notifier_call = mtu_context_notifier_call,
+};
+#endif
#ifdef CONFIG_HAVE_ARM_TWD
static DEFINE_TWD_LOCAL_TIMER(u5500_twd_local_timer,
@@ -51,7 +67,7 @@ static void __init ux500_timer_init(void)
if (cpu_is_u5500()) {
mtu_timer_base = __io_address(U5500_MTU0_BASE);
prcmu_timer_base = __io_address(U5500_PRCMU_TIMER_3_BASE);
- } else if (cpu_is_u8500()) {
+ } else if (cpu_is_u8500() || cpu_is_u9540()) {
mtu_timer_base = __io_address(U8500_MTU0_BASE);
prcmu_timer_base = __io_address(U8500_PRCMU_TIMER_4_BASE);
} else {
@@ -70,14 +86,24 @@ static void __init ux500_timer_init(void)
* depending on delay which is not yet calibrated. RTC-RTT is in the
* always-on powerdomain and is used as clockevent instead of twd when
* sleeping.
- * The PRCMU timer 4(3 for DB5500) register a clocksource and
- * sched_clock with higher rating then MTU since is always-on.
*
+ * The PRCMU timer 4 (3 for DB5500) registers a clocksource and
+ * sched_clock with higher rating than the MTU since it is
+ * always-on.
+ *
+ * On DB5500, the MTIMER is the best clocksource since, unlike the
+ * PRCMU timer, it doesn't occasionally go backwards.
*/
nmdk_timer_init(mtu_timer_base);
+ if (cpu_is_u5500())
+ db5500_mtimer_init(__io_address(U5500_MTIMER_BASE));
clksrc_dbx500_prcmu_init(prcmu_timer_base);
ux500_twd_init();
+
+#ifdef CONFIG_UX500_CONTEXT
+ WARN_ON(context_ape_notifier_register(&mtu_context_notifier));
+#endif
}
static void ux500_timer_reset(void)
diff --git a/arch/arm/mach-ux500/uart-db8500.c b/arch/arm/mach-ux500/uart-db8500.c
new file mode 100644
index 00000000000..fad9b9a13df
--- /dev/null
+++ b/arch/arm/mach-ux500/uart-db8500.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Arun R Murthy <arun.murthy@stericsson.com>,
+ * Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/amba/serial.h>
+#include <mach/setup.h>
+#include <mach/hardware.h>
+#include <mach/context.h>
+
+#ifdef CONFIG_UX500_CONTEXT
+
+static struct {
+ struct clk *uart_clk;
+ void __iomem *base;
+ /* dr */
+ /* rsr_err */
+ u32 dma_wm;
+ u32 timeout;
+ /* fr */
+ u32 lcrh_rx;
+ u32 ilpr;
+ u32 ibrd;
+ u32 fbrd;
+ u32 lcrh_tx;
+ u32 cr;
+ u32 ifls;
+ u32 imsc;
+ /* ris */
+ /* mis */
+ /* icr */
+ u32 dmacr;
+ u32 xfcr;
+ u32 xon1;
+ u32 xon2;
+ u32 xoff1;
+ u32 xoff2;
+ /* itcr */
+ /* itip */
+ /* itop */
+ /* tdr */
+ u32 abcr;
+ /* absr */
+ /* abfmt */
+ /* abdr */
+ /* abdfr */
+ /* abmr */
+ u32 abimsc;
+ /* abris */
+ /* abmis */
+ /* abicr */
+ /* id_product_h_xy */
+ /* id_provider */
+ /* periphid0 */
+ /* periphid1 */
+ /* periphid2 */
+ /* periphid3 */
+ /* pcellid0 */
+ /* pcellid1 */
+ /* pcellid2 */
+ /* pcellid3 */
+} context_uart;
+
+static void save_uart(void)
+{
+ void __iomem *membase;
+
+ membase = context_uart.base;
+
+ clk_enable(context_uart.uart_clk);
+
+ context_uart.dma_wm = readl_relaxed(membase + ST_UART011_DMAWM);
+ context_uart.timeout = readl_relaxed(membase + ST_UART011_TIMEOUT);
+ context_uart.lcrh_rx = readl_relaxed(membase + ST_UART011_LCRH_RX);
+ context_uart.ilpr = readl_relaxed(membase + UART01x_ILPR);
+ context_uart.ibrd = readl_relaxed(membase + UART011_IBRD);
+ context_uart.fbrd = readl_relaxed(membase + UART011_FBRD);
+ context_uart.lcrh_tx = readl_relaxed(membase + ST_UART011_LCRH_TX);
+ context_uart.cr = readl_relaxed(membase + UART011_CR);
+ context_uart.ifls = readl_relaxed(membase + UART011_IFLS);
+ context_uart.imsc = readl_relaxed(membase + UART011_IMSC);
+ context_uart.dmacr = readl_relaxed(membase + UART011_DMACR);
+ context_uart.xfcr = readl_relaxed(membase + ST_UART011_XFCR);
+ context_uart.xon1 = readl_relaxed(membase + ST_UART011_XON1);
+ context_uart.xon2 = readl_relaxed(membase + ST_UART011_XON2);
+ context_uart.xoff1 = readl_relaxed(membase + ST_UART011_XOFF1);
+ context_uart.xoff2 = readl_relaxed(membase + ST_UART011_XOFF2);
+ context_uart.abcr = readl_relaxed(membase + ST_UART011_ABCR);
+ context_uart.abimsc = readl_relaxed(membase + ST_UART011_ABIMSC);
+
+ clk_disable(context_uart.uart_clk);
+}
+
+static void restore_uart(void)
+{
+ int cnt;
+ int retries = 100;
+ unsigned int cr;
+ void __iomem *membase;
+ u16 dummy;
+ bool show_warn = false;
+
+ membase = context_uart.base;
+ clk_enable(context_uart.uart_clk);
+
+ writew_relaxed(context_uart.ifls, membase + UART011_IFLS);
+ cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
+
+ writew_relaxed(cr, membase + UART011_CR);
+ writew_relaxed(0, membase + UART011_FBRD);
+ writew_relaxed(1, membase + UART011_IBRD);
+ writew_relaxed(0, membase + ST_UART011_LCRH_RX);
+ if (context_uart.lcrh_tx != ST_UART011_LCRH_RX) {
+ int i;
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10 times
+ */
+ for (i = 0; i < 10; ++i)
+ dummy = readw(membase + ST_UART011_LCRH_RX);
+ writew_relaxed(0, membase + ST_UART011_LCRH_TX);
+ }
+ writew(0, membase + UART01x_DR);
+ do {
+ if (!(readw(membase + UART01x_FR) & UART01x_FR_BUSY))
+ break;
+ cpu_relax();
+ } while (retries-- > 0);
+ if (retries < 0)
+ /*
+ * We can't print out a warning here since the uart is
+ * not fully restored. Do it later.
+ */
+ show_warn = true;
+
+ writel_relaxed(context_uart.dma_wm, membase + ST_UART011_DMAWM);
+ writel_relaxed(context_uart.timeout, membase + ST_UART011_TIMEOUT);
+ writel_relaxed(context_uart.lcrh_rx, membase + ST_UART011_LCRH_RX);
+ writel_relaxed(context_uart.ilpr, membase + UART01x_ILPR);
+ writel_relaxed(context_uart.ibrd, membase + UART011_IBRD);
+ writel_relaxed(context_uart.fbrd, membase + UART011_FBRD);
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10-3
+ * times, as already there are 3 writes after
+ * ST_UART011_LCRH_RX
+ */
+ for (cnt = 0; cnt < 7; cnt++)
+ dummy = readw(membase + ST_UART011_LCRH_RX);
+
+ writel_relaxed(context_uart.lcrh_tx, membase + ST_UART011_LCRH_TX);
+ writel_relaxed(context_uart.ifls, membase + UART011_IFLS);
+ writel_relaxed(context_uart.dmacr, membase + UART011_DMACR);
+ writel_relaxed(context_uart.xfcr, membase + ST_UART011_XFCR);
+ writel_relaxed(context_uart.xon1, membase + ST_UART011_XON1);
+ writel_relaxed(context_uart.xon2, membase + ST_UART011_XON2);
+ writel_relaxed(context_uart.xoff1, membase + ST_UART011_XOFF1);
+ writel_relaxed(context_uart.xoff2, membase + ST_UART011_XOFF2);
+ writel_relaxed(context_uart.abcr, membase + ST_UART011_ABCR);
+ writel_relaxed(context_uart.abimsc, membase + ST_UART011_ABIMSC);
+ writel_relaxed(context_uart.cr, membase + UART011_CR);
+ writel(context_uart.imsc, membase + UART011_IMSC);
+
+ clk_disable(context_uart.uart_clk);
+
+ if (show_warn)
+ pr_warning("%s:uart tx busy\n", __func__);
+}
+
+static int uart_context_notifier_call(struct notifier_block *this,
+ unsigned long event, void *data)
+{
+ switch (event) {
+ case CONTEXT_APE_SAVE:
+ save_uart();
+ break;
+
+ case CONTEXT_APE_RESTORE:
+ restore_uart();
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block uart_context_notifier = {
+ .notifier_call = uart_context_notifier_call,
+};
+
+#define __UART_BASE(soc, x) soc##_UART##x##_BASE
+#define UART_BASE(soc, x) __UART_BASE(soc, x)
+
+static int __init uart_context_notifier_init(void)
+{
+ unsigned long base;
+ static const char clkname[] __initconst
+ = "uart" __stringify(CONFIG_UX500_DEBUG_UART);
+
+ if (cpu_is_u8500())
+ base = UART_BASE(U8500, CONFIG_UX500_DEBUG_UART);
+ else if (cpu_is_u5500())
+ base = UART_BASE(U5500, CONFIG_UX500_DEBUG_UART);
+ else
+ ux500_unknown_soc();
+
+ context_uart.base = ioremap(base, SZ_4K);
+ context_uart.uart_clk = clk_get_sys(clkname, NULL);
+
+ if (IS_ERR(context_uart.uart_clk)) {
+ pr_err("%s:unable to get clk-uart%d\n", __func__,
+ CONFIG_UX500_DEBUG_UART);
+ return -EINVAL;
+ }
+
+ return WARN_ON(context_ape_notifier_register(&uart_context_notifier));
+}
+arch_initcall(uart_context_notifier_init);
+#endif
diff --git a/arch/arm/mach-ux500/usb.c b/arch/arm/mach-ux500/usb.c
index a74af389bc6..fb1f6775154 100644
--- a/arch/arm/mach-ux500/usb.c
+++ b/arch/arm/mach-ux500/usb.c
@@ -11,6 +11,10 @@
#include <plat/ste_dma40.h>
#include <mach/hardware.h>
#include <mach/usb.h>
+#include <mach/pm.h>
+#include <plat/pincfg.h>
+#include "pins.h"
+#include "board-ux500-usb.h"
#define MUSB_DMA40_RX_CH { \
.mode = STEDMA40_MODE_LOGICAL, \
@@ -32,6 +36,8 @@
.dst_info.psize = STEDMA40_PSIZE_LOG_16, \
}
+#define USB_OTG_GPIO_CS 76
+
static struct stedma40_chan_cfg musb_dma_rx_ch[UX500_MUSB_DMA_NUM_RX_CHANNELS]
= {
MUSB_DMA40_RX_CH,
@@ -86,9 +92,54 @@ static struct ux500_musb_board_data musb_board_data = {
.dma_filter = stedma40_filter,
};
+#ifdef CONFIG_USB_UX500_DMA
static u64 ux500_musb_dmamask = DMA_BIT_MASK(32);
+#else
+static u64 ux500_musb_dmamask = DMA_BIT_MASK(0);
+#endif
+static struct ux500_pins *usb_gpio_pins;
+
+/**
+ * Fifo mode
+ * Sum of maxpacket <= 12 KB
+ * As ux500 provides 12 KB buffer size only
+ *
+ * Enable Double buffer for Mass Storage Class
+ * endpoint.
+ */
+static struct musb_fifo_cfg ux500_mode_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 6, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 6, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 7, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 7, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 8, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 8, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 9, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 9, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 10, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 10, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 11, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 11, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 12, .style = FIFO_TX, .maxpacket = 32, },
+{ .hw_ep_num = 12, .style = FIFO_RX, .maxpacket = 32, },
+{ .hw_ep_num = 13, .style = FIFO_RXTX, .maxpacket = 512, },
+{ .hw_ep_num = 14, .style = FIFO_RXTX, .maxpacket = 1024, },
+{ .hw_ep_num = 15, .style = FIFO_RXTX, .maxpacket = 1024, },
+};
static struct musb_hdrc_config musb_hdrc_config = {
+ .fifo_cfg = ux500_mode_cfg, /* Fifo configuration */
+ .fifo_cfg_size = ARRAY_SIZE(ux500_mode_cfg),
.multipoint = true,
.dyn_fifo = true,
.num_eps = 16,
@@ -120,11 +171,46 @@ struct platform_device ux500_musb_device = {
.platform_data = &musb_platform_data,
.dma_mask = &ux500_musb_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
+#ifdef CONFIG_UX500_SOC_DB8500
+ .pm_domain = &ux500_dev_power_domain,
+#endif
},
.num_resources = ARRAY_SIZE(usb_resources),
.resource = usb_resources,
};
+static void enable_gpio(void)
+{
+ ux500_pins_enable(usb_gpio_pins);
+}
+static void disable_gpio(void)
+{
+ ux500_pins_disable(usb_gpio_pins);
+}
+static int get_gpio(struct device *device)
+{
+ usb_gpio_pins = ux500_pins_get(dev_name(device));
+
+ if (usb_gpio_pins == NULL) {
+ dev_err(device, "Could not get %s:usb_gpio_pins structure\n",
+ dev_name(device));
+
+ return PTR_ERR(usb_gpio_pins);
+ }
+ return 0;
+}
+static void put_gpio(void)
+{
+ ux500_pins_put(usb_gpio_pins);
+}
+struct abx500_usbgpio_platform_data abx500_usbgpio_plat_data = {
+ .get = &get_gpio,
+ .enable = &enable_gpio,
+ .disable = &disable_gpio,
+ .put = &put_gpio,
+ .usb_cs = USB_OTG_GPIO_CS,
+};
+
static inline void ux500_usb_dma_update_rx_ch_config(int *src_dev_type)
{
u32 idx;
diff --git a/arch/arm/mm/cache-fa.S b/arch/arm/mm/cache-fa.S
index 07201637109..226f8736152 100644
--- a/arch/arm/mm/cache-fa.S
+++ b/arch/arm/mm/cache-fa.S
@@ -240,6 +240,24 @@ ENTRY(fa_dma_unmap_area)
mov pc, lr
ENDPROC(fa_dma_unmap_area)
+/*
+ * clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(fa_clean_dcache_all)
+ mov pc, lr
+ENDPROC(fa_clean_dcache_all)
+
+/*
+ * flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(fa_flush_dcache_all)
+ mov pc, lr
+ENDPROC(fa_flush_dcache_all)
+
__INITDATA
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 2a8e380501e..1768e4038fc 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -319,9 +319,6 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
aux = readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
- aux &= aux_mask;
- aux |= aux_val;
-
/* Determine the number of ways */
switch (cache_id & L2X0_CACHE_ID_PART_MASK) {
case L2X0_CACHE_ID_PART_L310:
@@ -335,6 +332,13 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
sync_reg_offset = L2X0_DUMMY_REG;
#endif
outer_cache.set_debug = pl310_set_debug;
+
+ /*
+ * Set bit 22 in the auxiliary control register. If this bit
+ * is cleared, PL310 treats Normal Shared Non-cacheable
+ * accesses as Cacheable no-allocate.
+ */
+ aux_val |= 1 << 22;
break;
case L2X0_CACHE_ID_PART_L210:
ways = (aux >> 13) & 0xf;
@@ -362,6 +366,9 @@ void __init l2x0_init(void __iomem *base, u32 aux_val, u32 aux_mask)
* accessing the below registers will fault.
*/
if (!(readl_relaxed(l2x0_base + L2X0_CTRL) & 1)) {
+ aux &= aux_mask;
+ aux |= aux_val;
+
/* Make sure that I&D is not locked down when starting */
l2x0_unlock(cache_id);
diff --git a/arch/arm/mm/cache-v3.S b/arch/arm/mm/cache-v3.S
index c2301f22610..ab5bf508a2a 100644
--- a/arch/arm/mm/cache-v3.S
+++ b/arch/arm/mm/cache-v3.S
@@ -127,6 +127,24 @@ ENTRY(v3_dma_map_area)
ENDPROC(v3_dma_unmap_area)
ENDPROC(v3_dma_map_area)
+/*
+ * clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(v3_clean_dcache_all)
+ mov pc, lr
+ENDPROC(v3_clean_dcache_all)
+
+/*
+ * flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(v3_flush_dcache_all)
+ mov pc, lr
+ENDPROC(v3_flush_dcache_all)
+
__INITDATA
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
diff --git a/arch/arm/mm/cache-v4.S b/arch/arm/mm/cache-v4.S
index fd9bb7addc8..9d3a055127e 100644
--- a/arch/arm/mm/cache-v4.S
+++ b/arch/arm/mm/cache-v4.S
@@ -139,6 +139,24 @@ ENTRY(v4_dma_map_area)
ENDPROC(v4_dma_unmap_area)
ENDPROC(v4_dma_map_area)
+/*
+ * clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(v4_clean_dcache_all)
+ mov pc, lr
+ENDPROC(v4_clean_dcache_all)
+
+/*
+ * flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(v4_flush_dcache_all)
+ mov pc, lr
+ENDPROC(v4_flush_dcache_all)
+
__INITDATA
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S
index 4f2c14151cc..54d3cda4a89 100644
--- a/arch/arm/mm/cache-v4wb.S
+++ b/arch/arm/mm/cache-v4wb.S
@@ -251,6 +251,24 @@ ENTRY(v4wb_dma_unmap_area)
mov pc, lr
ENDPROC(v4wb_dma_unmap_area)
+/*
+ * clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(v4wb_clean_dcache_all)
+ mov pc, lr
+ENDPROC(v4wb_clean_dcache_all)
+
+/*
+ * flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(v4wb_flush_dcache_all)
+ mov pc, lr
+ENDPROC(v4wb_flush_dcache_all)
+
__INITDATA
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
diff --git a/arch/arm/mm/cache-v4wt.S b/arch/arm/mm/cache-v4wt.S
index 4d7b467631c..40f7dba11f5 100644
--- a/arch/arm/mm/cache-v4wt.S
+++ b/arch/arm/mm/cache-v4wt.S
@@ -195,6 +195,24 @@ ENTRY(v4wt_dma_map_area)
ENDPROC(v4wt_dma_unmap_area)
ENDPROC(v4wt_dma_map_area)
+/*
+ * clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(v4wt_clean_dcache_all)
+ mov pc, lr
+ENDPROC(v4wt_clean_dcache_all)
+
+/*
+ * flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(v4wt_flush_dcache_all)
+ mov pc, lr
+ENDPROC(v4wt_flush_dcache_all)
+
__INITDATA
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
diff --git a/arch/arm/mm/cache-v6.S b/arch/arm/mm/cache-v6.S
index 74c2e5a33a4..b88dd4ab038 100644
--- a/arch/arm/mm/cache-v6.S
+++ b/arch/arm/mm/cache-v6.S
@@ -328,6 +328,24 @@ ENTRY(v6_dma_unmap_area)
mov pc, lr
ENDPROC(v6_dma_unmap_area)
+/*
+ * clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(v6_clean_dcache_all)
+ mov pc, lr
+ENDPROC(v6_clean_dcache_all)
+
+/*
+ * flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(v6_flush_dcache_all)
+ mov pc, lr
+ENDPROC(v6_flush_dcache_all)
+
__INITDATA
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index a655d3da386..70744d6a066 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -33,7 +33,7 @@ ENTRY(v7_flush_icache_all)
ENDPROC(v7_flush_icache_all)
/*
- * v7_flush_dcache_all()
+ * __v7_flush_dcache_all()
*
* Flush the whole D-cache.
*
@@ -41,7 +41,7 @@ ENDPROC(v7_flush_icache_all)
*
* - mm - mm_struct describing address space
*/
-ENTRY(v7_flush_dcache_all)
+ENTRY(__v7_flush_dcache_all)
dmb @ ensure ordering with previous memory accesses
mrc p15, 1, r0, c0, c0, 1 @ read clidr
ands r3, r0, #0x7000000 @ extract loc from clidr
@@ -94,9 +94,93 @@ finished:
dsb
isb
mov pc, lr
+ENDPROC(__v7_flush_dcache_all)
+
+/*
+ * __v7_clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ *
+ * Corrupted registers: r0-r7, r9-r11 (r6 only in Thumb mode)
+ */
+ENTRY(__v7_clean_dcache_all)
+ dmb @ ensure ordering with previous memory accesses
+ mrc p15, 1, r0, c0, c0, 1 @ read clidr
+ ands r3, r0, #0x7000000 @ extract loc from clidr
+ mov r3, r3, lsr #23 @ left align loc bit field
+ beq finished1 @ if loc is 0, then no need to clean
+ mov r10, #0 @ start clean at cache level 0
+loop21:
+ add r2, r10, r10, lsr #1 @ work out 3x current cache level
+ mov r1, r0, lsr r2 @ extract cache type bits from clidr
+ and r1, r1, #7 @ mask of the bits for current cache only
+ cmp r1, #2 @ see what cache we have at this level
+ blt skip1 @ skip if no cache, or just i-cache
+ mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
+ isb @ isb to sych the new cssr&csidr
+ mrc p15, 1, r1, c0, c0, 0 @ read the new csidr
+ and r2, r1, #7 @ extract the length of the cache lines
+ add r2, r2, #4 @ add 4 (line length offset)
+ ldr r4, =0x3ff
+ ands r4, r4, r1, lsr #3 @ find maximum number on the way size
+ clz r5, r4 @ find bit position of way size increment
+ ldr r7, =0x7fff
+ ands r7, r7, r1, lsr #13 @ extract max number of the index size
+loop22:
+ mov r9, r4 @ create working copy of max way size
+loop23:
+ ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11
+ THUMB( lsl r6, r9, r5 )
+ THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11
+ ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11
+ THUMB( lsl r6, r7, r2 )
+ THUMB( orr r11, r11, r6 ) @ factor index number into r11
+ mcr p15, 0, r11, c7, c10, 2 @ clean by set/way
+ subs r9, r9, #1 @ decrement the way
+ bge loop23
+ subs r7, r7, #1 @ decrement the index
+ bge loop22
+skip1:
+ add r10, r10, #2 @ increment cache number
+ cmp r3, r10
+ bgt loop21
+finished1:
+ mov r10, #0 @ swith back to cache level 0
+ mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
+ dsb
+ isb
+ mov pc, lr
+ENDPROC(__v7_clean_dcache_all)
+
+/*
+ * v7_flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ */
+ENTRY(v7_flush_dcache_all)
+ ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
+ bl __v7_flush_dcache_all
+ ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
+ mov pc, lr
ENDPROC(v7_flush_dcache_all)
/*
+ * v7_clean_dcache_all()
+ *
+ * Clean the whole D-cache.
+ */
+ENTRY(v7_clean_dcache_all)
+ ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
+ bl __v7_clean_dcache_all
+ ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
+ mov pc, lr
+ENDPROC(v7_clean_dcache_all)
+
+/*
* v7_flush_cache_all()
*
* Flush the entire cache system.
@@ -108,14 +192,12 @@ ENDPROC(v7_flush_dcache_all)
*
*/
ENTRY(v7_flush_kern_cache_all)
- ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
- THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
+ stmfd sp!, {lr}
bl v7_flush_dcache_all
mov r0, #0
ALT_SMP(mcr p15, 0, r0, c7, c1, 0) @ invalidate I-cache inner shareable
ALT_UP(mcr p15, 0, r0, c7, c5, 0) @ I+BTB cache invalidate
- ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
- THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
+ ldmfd sp!, {lr}
mov pc, lr
ENDPROC(v7_flush_kern_cache_all)
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index aa78de8bfdd..4ecd1f8bbb5 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -288,6 +288,20 @@ static struct mem_type mem_types[] = {
PMD_SECT_UNCACHED | PMD_SECT_XN,
.domain = DOMAIN_KERNEL,
},
+ /* NOTE : this is only a temporary hack!!!
+ * The U8500 ED/V1.0 cuts require such a
+ * memory type for deep sleep resume.
+ * This is expected to be solved in cut v2.0
+ * and we clean this up then. for more details
+ * look @ the commit message please
+ */
+ [MT_BACKUP_RAM] = {
+ .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |
+ L_PTE_SHARED,
+ .prot_l1 = PMD_TYPE_TABLE,
+ .prot_sect = PROT_SECT_DEVICE | PMD_SECT_S,
+ .domain = DOMAIN_IO,
+ },
};
const struct mem_type *get_mem_type(unsigned int type)
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index 2d8ff3ad86d..518ab10fea5 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -304,6 +304,8 @@ ENTRY(\name\()_cache_fns)
.long \name\()_coherent_kern_range
.long \name\()_coherent_user_range
.long \name\()_flush_kern_dcache_area
+ .long \name\()_clean_dcache_all
+ .long \name\()_flush_dcache_all
.long \name\()_dma_map_area
.long \name\()_dma_unmap_area
.long \name\()_dma_flush_range
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index c2e2b66f72b..d1c42ead765 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -172,7 +172,7 @@ __v7_ca15mp_setup:
__v7_setup:
adr r12, __v7_setup_stack @ the local stack
stmia r12, {r0-r5, r7, r9, r11, lr}
- bl v7_flush_dcache_all
+ bl __v7_flush_dcache_all
ldmia r12, {r0-r5, r7, r9, r11, lr}
mrc p15, 0, r0, c0, c0, 0 @ read main ID register
diff --git a/arch/arm/plat-nomadik/include/plat/mtu.h b/arch/arm/plat-nomadik/include/plat/mtu.h
index 582641f3dc0..489e92723b4 100644
--- a/arch/arm/plat-nomadik/include/plat/mtu.h
+++ b/arch/arm/plat-nomadik/include/plat/mtu.h
@@ -5,5 +5,7 @@ void nmdk_timer_init(void __iomem *base);
void nmdk_clkevt_reset(void);
void nmdk_clksrc_reset(void);
+struct clock_event_device *nmdk_clkevt_get(void);
+
#endif /* __PLAT_MTU_H */
diff --git a/arch/arm/plat-nomadik/include/plat/pincfg.h b/arch/arm/plat-nomadik/include/plat/pincfg.h
index 22cb97d2d8a..c015133a7ad 100644
--- a/arch/arm/plat-nomadik/include/plat/pincfg.h
+++ b/arch/arm/plat-nomadik/include/plat/pincfg.h
@@ -24,6 +24,7 @@
* bit 16..18 - SLPM pull up/down state
* bit 19..20 - SLPM direction
* bit 21..22 - SLPM Value (if output)
+ * bit 23..25 - PDIS value (if input)
*
* to facilitate the definition, the following macros are provided
*
@@ -67,6 +68,10 @@ typedef unsigned long pin_cfg_t;
/* These two replace the above in DB8500v2+ */
#define PIN_SLPM_WAKEUP_ENABLE (NMK_GPIO_SLPM_WAKEUP_ENABLE << PIN_SLPM_SHIFT)
#define PIN_SLPM_WAKEUP_DISABLE (NMK_GPIO_SLPM_WAKEUP_DISABLE << PIN_SLPM_SHIFT)
+#define PIN_SLPM_USE_MUX_SETTINGS_IN_SLEEP PIN_SLPM_WAKEUP_DISABLE
+
+#define PIN_SLPM_GPIO PIN_SLPM_WAKEUP_ENABLE /* In SLPM, pin is a gpio */
+#define PIN_SLPM_ALTFUNC PIN_SLPM_WAKEUP_DISABLE /* In SLPM, pin is altfunc */
#define PIN_DIR_SHIFT 14
#define PIN_DIR_MASK (0x1 << PIN_DIR_SHIFT)
@@ -105,6 +110,20 @@ typedef unsigned long pin_cfg_t;
#define PIN_SLPM_VAL_LOW ((1 + 0) << PIN_SLPM_VAL_SHIFT)
#define PIN_SLPM_VAL_HIGH ((1 + 1) << PIN_SLPM_VAL_SHIFT)
+#define PIN_SLPM_PDIS_SHIFT 23
+#define PIN_SLPM_PDIS_MASK (0x3 << PIN_SLPM_PDIS_SHIFT)
+#define PIN_SLPM_PDIS(x) \
+ (((x) & PIN_SLPM_PDIS_MASK) >> PIN_SLPM_PDIS_SHIFT)
+#define PIN_SLPM_PDIS_NO_CHANGE (0 << PIN_SLPM_PDIS_SHIFT)
+#define PIN_SLPM_PDIS_DISABLED (1 << PIN_SLPM_PDIS_SHIFT)
+#define PIN_SLPM_PDIS_ENABLED (2 << PIN_SLPM_PDIS_SHIFT)
+
+#define PIN_LOWEMI_SHIFT 25
+#define PIN_LOWEMI_MASK (0x1 << PIN_LOWEMI_SHIFT)
+#define PIN_LOWEMI(x) (((x) & PIN_LOWEMI_MASK) >> PIN_LOWEMI_SHIFT)
+#define PIN_LOWEMI_DISABLED (0 << PIN_LOWEMI_SHIFT)
+#define PIN_LOWEMI_ENABLED (1 << PIN_LOWEMI_SHIFT)
+
/* Shortcuts. Use these instead of separate DIR, PULL, and VAL. */
#define PIN_INPUT_PULLDOWN (PIN_DIR_INPUT | PIN_PULL_DOWN)
#define PIN_INPUT_PULLUP (PIN_DIR_INPUT | PIN_PULL_UP)
diff --git a/arch/arm/plat-nomadik/include/plat/ste_dma40.h b/arch/arm/plat-nomadik/include/plat/ste_dma40.h
index 9ff93b06568..08cc3bf8f6d 100644
--- a/arch/arm/plat-nomadik/include/plat/ste_dma40.h
+++ b/arch/arm/plat-nomadik/include/plat/ste_dma40.h
@@ -146,6 +146,7 @@ struct stedma40_chan_cfg {
* @memcpy_conf_phy: default configuration of physical channel memcpy
* @memcpy_conf_log: default configuration of logical channel memcpy
* @disabled_channels: A vector, ending with -1, that marks physical channels
+ * @use_esram_lcla: flag for mapping the lcla into esram region
* that are for different reasons not available for the driver.
*/
struct stedma40_platform_data {
@@ -162,6 +163,24 @@ struct stedma40_platform_data {
#ifdef CONFIG_STE_DMA40
+/*
+ * stedma40_get_src_addr - get current source address
+ * @chan: the DMA channel
+ *
+ * Returns the physical address of the current source element to be read by the
+ * DMA.
+ */
+dma_addr_t stedma40_get_src_addr(struct dma_chan *chan);
+
+/*
+ * stedma40_get_dst_addr - get current destination address
+ * @chan: the DMA channel
+ *
+ * Returns the physical address of the current destination element to be
+ * written by the DMA.
+ */
+dma_addr_t stedma40_get_dst_addr(struct dma_chan *chan);
+
/**
* stedma40_filter() - Provides stedma40_chan_cfg to the
* ste_dma40 dma driver via the dmaengine framework.
@@ -204,6 +223,16 @@ dma_async_tx_descriptor *stedma40_slave_mem(struct dma_chan *chan,
}
#else
+dma_addr_t stedma40_get_src_addr(struct dma_chan *chan)
+{
+ return NULL;
+}
+
+dma_addr_t stedma40_get_dst_addr(struct dma_chan *chan)
+{
+ return NULL;
+}
+
static inline bool stedma40_filter(struct dma_chan *chan, void *data)
{
return false;
diff --git a/arch/arm/plat-nomadik/timer.c b/arch/arm/plat-nomadik/timer.c
index 9222e5522a4..f26ce2fb0d1 100644
--- a/arch/arm/plat-nomadik/timer.c
+++ b/arch/arm/plat-nomadik/timer.c
@@ -17,6 +17,7 @@
#include <linux/clk.h>
#include <linux/jiffies.h>
#include <linux/err.h>
+#include <linux/delay.h>
#include <asm/mach/time.h>
#include <asm/sched_clock.h>
@@ -142,6 +143,28 @@ static struct clock_event_device nmdk_clkevt = {
.set_next_event = nmdk_clkevt_next,
};
+#ifdef ARCH_HAS_READ_CURRENT_TIMER
+static void nmdk_timer_delay_loop(unsigned long loops)
+{
+ unsigned long bclock, now;
+
+ bclock = ~readl(mtu_base + MTU_VAL(0));
+ do {
+ now = ~readl(mtu_base + MTU_VAL(0));
+ /* If timer have been cleared (suspend) or wrapped we exit */
+ if (unlikely(now < bclock))
+ return;
+ } while ((now - bclock) < loops);
+}
+
+/* Used to calibrate the delay */
+int read_current_timer(unsigned long *timer_val)
+{
+ *timer_val = ~readl(mtu_base + MTU_VAL(0));
+ return 0;
+}
+#endif
+
/*
* IRQ Handler for timer 1 of the MTU block.
*/
@@ -174,6 +197,11 @@ void nmdk_clksrc_reset(void)
mtu_base + MTU_CR(0));
}
+struct clock_event_device *nmdk_clkevt_get(void)
+{
+ return &nmdk_clkevt;
+}
+
void __init nmdk_timer_init(void __iomem *base)
{
unsigned long rate;
@@ -220,4 +248,8 @@ void __init nmdk_timer_init(void __iomem *base)
setup_irq(IRQ_MTU0, &nmdk_timer_irq);
nmdk_clkevt.cpumask = cpumask_of(0);
clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU);
+#ifdef ARCH_HAS_READ_CURRENT_TIMER
+ set_delay_fn(nmdk_timer_delay_loop);
+#endif
+
}
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index f9c9f33f8cb..bb2f9e9d6d2 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -12,531 +12,3179 @@
#
# http://www.arm.linux.org.uk/developer/machines/?action=new
#
-# This is a cut-down version of the file; it contains only machines that
-# are merged into mainline or have been edited in the machine database
-# within the last 12 months. References to machine_is_NAME() do not count!
-#
-# Last update: Tue Dec 6 11:07:38 2011
+# Last update: Thu Feb 2 12:49:17 2012
#
# machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
#
ebsa110 ARCH_EBSA110 EBSA110 0
riscpc ARCH_RPC RISCPC 1
+nexuspci ARCH_NEXUSPCI NEXUSPCI 3
ebsa285 ARCH_EBSA285 EBSA285 4
netwinder ARCH_NETWINDER NETWINDER 5
cats ARCH_CATS CATS 6
+tbox ARCH_TBOX TBOX 7
+co285 ARCH_CO285 CO285 8
+clps7110 ARCH_CLPS7110 CLPS7110 9
+archimedes ARCH_ARC ARCHIMEDES 10
+a5k ARCH_A5K A5K 11
+etoile ARCH_ETOILE ETOILE 12
+lacie_nas ARCH_LACIE_NAS LACIE_NAS 13
+clps7500 ARCH_CLPS7500 CLPS7500 14
shark ARCH_SHARK SHARK 15
brutus SA1100_BRUTUS BRUTUS 16
personal_server ARCH_PERSONAL_SERVER PERSONAL_SERVER 17
+itsy SA1100_ITSY ITSY 18
l7200 ARCH_L7200 L7200 19
pleb SA1100_PLEB PLEB 20
integrator ARCH_INTEGRATOR INTEGRATOR 21
h3600 SA1100_H3600 H3600 22
+ixp1200 ARCH_IXP1200 IXP1200 23
p720t ARCH_P720T P720T 24
assabet SA1100_ASSABET ASSABET 25
+victor SA1100_VICTOR VICTOR 26
lart SA1100_LART LART 27
+ranger SA1100_RANGER RANGER 28
graphicsclient SA1100_GRAPHICSCLIENT GRAPHICSCLIENT 29
xp860 SA1100_XP860 XP860 30
cerf SA1100_CERF CERF 31
nanoengine SA1100_NANOENGINE NANOENGINE 32
+fpic SA1100_FPIC FPIC 33
+extenex1 SA1100_EXTENEX1 EXTENEX1 34
+sherman SA1100_SHERMAN SHERMAN 35
+accelent_sa SA1100_ACCELENT ACCELENT_SA 36
+accelent_l7200 ARCH_L7200_ACCELENT ACCELENT_L7200 37
+netport SA1100_NETPORT NETPORT 38
+pangolin SA1100_PANGOLIN PANGOLIN 39
+yopy SA1100_YOPY YOPY 40
+coolidge SA1100_COOLIDGE COOLIDGE 41
+huw_webpanel SA1100_HUW_WEBPANEL HUW_WEBPANEL 42
+spotme ARCH_SPOTME SPOTME 43
+freebird ARCH_FREEBIRD FREEBIRD 44
+ti925 ARCH_TI925 TI925 45
+riscstation ARCH_RISCSTATION RISCSTATION 46
+cavy SA1100_CAVY CAVY 47
jornada720 SA1100_JORNADA720 JORNADA720 48
+omnimeter SA1100_OMNIMETER OMNIMETER 49
edb7211 ARCH_EDB7211 EDB7211 50
+citygo SA1100_CITYGO CITYGO 51
pfs168 SA1100_PFS168 PFS168 52
+spot SA1100_SPOT SPOT 53
flexanet SA1100_FLEXANET FLEXANET 54
+webpal ARCH_WEBPAL WEBPAL 55
+linpda SA1100_LINPDA LINPDA 56
+anakin ARCH_ANAKIN ANAKIN 57
+mvi SA1100_MVI MVI 58
+jupiter SA1100_JUPITER JUPITER 59
+psionw ARCH_PSIONW PSIONW 60
+aln SA1100_ALN ALN 61
+epxa ARCH_CAMELOT CAMELOT 62
+gds2200 SA1100_GDS2200 GDS2200 63
+netbook SA1100_PSION_SERIES7 PSION_SERIES7 64
+xfile SA1100_XFILE XFILE 65
+accelent_ep9312 ARCH_ACCELENT_EP9312 ACCELENT_EP9312 66
+ic200 ARCH_IC200 IC200 67
+creditlart SA1100_CREDITLART CREDITLART 68
+htm SA1100_HTM HTM 69
+iq80310 ARCH_IQ80310 IQ80310 70
+freebot SA1100_FREEBOT FREEBOT 71
+entel ARCH_ENTEL ENTEL 72
+enp3510 ARCH_ENP3510 ENP3510 73
+trizeps SA1100_TRIZEPS TRIZEPS 74
+nesa SA1100_NESA NESA 75
+venus ARCH_VENUS VENUS 76
+tardis ARCH_TARDIS TARDIS 77
+mercury ARCH_MERCURY MERCURY 78
+empeg SA1100_EMPEG EMPEG 79
+adi_evb ARCH_I80200FCC I80200FCC 80
+itt_cpb SA1100_ITT_CPB ITT_CPB 81
+svc SA1100_SVC SVC 82
+alpha2 SA1100_ALPHA2 ALPHA2 84
+alpha1 SA1100_ALPHA1 ALPHA1 85
+netarm ARCH_NETARM NETARM 86
simpad SA1100_SIMPAD SIMPAD 87
+pda1 ARCH_PDA1 PDA1 88
lubbock ARCH_LUBBOCK LUBBOCK 89
+aniko ARCH_ANIKO ANIKO 90
clep7212 ARCH_CLEP7212 CLEP7212 91
+cs89712 ARCH_CS89712 CS89712 92
+weararm SA1100_WEARARM WEARARM 93
+possio_px SA1100_POSSIO_PX POSSIO_PX 94
+sidearm SA1100_SIDEARM SIDEARM 95
+stork SA1100_STORK STORK 96
shannon SA1100_SHANNON SHANNON 97
+ace ARCH_ACE ACE 98
+ballyarm SA1100_BALLYARM BALLYARM 99
+simputer SA1100_SIMPUTER SIMPUTER 100
+nexterm SA1100_NEXTERM NEXTERM 101
+sa1100_elf SA1100_SA1100_ELF SA1100_ELF 102
+gator SA1100_GATOR GATOR 103
+granite ARCH_GRANITE GRANITE 104
consus SA1100_CONSUS CONSUS 105
aaed2000 ARCH_AAED2000 AAED2000 106
cdb89712 ARCH_CDB89712 CDB89712 107
graphicsmaster SA1100_GRAPHICSMASTER GRAPHICSMASTER 108
adsbitsy SA1100_ADSBITSY ADSBITSY 109
pxa_idp ARCH_PXA_IDP PXA_IDP 110
+plce ARCH_PLCE PLCE 111
pt_system3 SA1100_PT_SYSTEM3 PT_SYSTEM3 112
+murphy ARCH_MEDALB MEDALB 113
+eagle ARCH_EAGLE EAGLE 114
+dsc21 ARCH_DSC21 DSC21 115
+dsc24 ARCH_DSC24 DSC24 116
+ti5472 ARCH_TI5472 TI5472 117
autcpu12 ARCH_AUTCPU12 AUTCPU12 118
+uengine ARCH_UENGINE UENGINE 119
+bluestem SA1100_BLUESTEM BLUESTEM 120
+xingu8 ARCH_XINGU8 XINGU8 121
+bushstb ARCH_BUSHSTB BUSHSTB 122
+epsilon1 SA1100_EPSILON1 EPSILON1 123
+balloon SA1100_BALLOON BALLOON 124
+puppy ARCH_PUPPY PUPPY 125
+elroy SA1100_ELROY ELROY 126
+gms720 ARCH_GMS720 GMS720 127
+s24x ARCH_S24X S24X 128
+jtel_clep7312 ARCH_JTEL_CLEP7312 JTEL_CLEP7312 129
+cx821xx ARCH_CX821XX CX821XX 130
+edb7312 ARCH_EDB7312 EDB7312 131
+bsa1110 SA1100_BSA1110 BSA1110 132
+powerpin ARCH_POWERPIN POWERPIN 133
+openarm ARCH_OPENARM OPENARM 134
+whitechapel SA1100_WHITECHAPEL WHITECHAPEL 135
h3100 SA1100_H3100 H3100 136
+h3800 SA1100_H3800 H3800 137
+blue_v1 ARCH_BLUE_V1 BLUE_V1 138
+pxa_cerf ARCH_PXA_CERF PXA_CERF 139
+arm7tevb ARCH_ARM7TEVB ARM7TEVB 140
+d7400 SA1100_D7400 D7400 141
+piranha ARCH_PIRANHA PIRANHA 142
+sbcamelot SA1100_SBCAMELOT SBCAMELOT 143
+kings SA1100_KINGS KINGS 144
+smdk2400 ARCH_SMDK2400 SMDK2400 145
collie SA1100_COLLIE COLLIE 146
+idr ARCH_IDR IDR 147
badge4 SA1100_BADGE4 BADGE4 148
+webnet ARCH_WEBNET WEBNET 149
+d7300 SA1100_D7300 D7300 150
+cep SA1100_CEP CEP 151
fortunet ARCH_FORTUNET FORTUNET 152
+vc547x ARCH_VC547X VC547X 153
+filewalker SA1100_FILEWALKER FILEWALKER 154
+netgateway SA1100_NETGATEWAY NETGATEWAY 155
+symbol2800 SA1100_SYMBOL2800 SYMBOL2800 156
+suns SA1100_SUNS SUNS 157
+frodo SA1100_FRODO FRODO 158
+ms301 SA1100_MACH_TYTE_MS301 MACH_TYTE_MS301 159
mx1ads ARCH_MX1ADS MX1ADS 160
h7201 ARCH_H7201 H7201 161
h7202 ARCH_H7202 H7202 162
+amico ARCH_AMICO AMICO 163
+iam SA1100_IAM IAM 164
+tt530 SA1100_TT530 TT530 165
+sam2400 ARCH_SAM2400 SAM2400 166
+jornada56x SA1100_JORNADA56X JORNADA56X 167
+active SA1100_ACTIVE ACTIVE 168
iq80321 ARCH_IQ80321 IQ80321 169
+wid SA1100_WID WID 170
+sabinal ARCH_SABINAL SABINAL 171
+ixp425_matacumbe ARCH_IXP425_MATACUMBE IXP425_MATACUMBE 172
+miniprint SA1100_MINIPRINT MINIPRINT 173
+adm510x ARCH_ADM510X ADM510X 174
+svs200 SA1100_SVS200 SVS200 175
+atg_tcu ARCH_ATG_TCU ATG_TCU 176
+jornada820 SA1100_JORNADA820 JORNADA820 177
+s3c44b0 ARCH_S3C44B0 S3C44B0 178
+margis2 ARCH_MARGIS2 MARGIS2 179
ks8695 ARCH_KS8695 KS8695 180
+brh ARCH_BRH BRH 181
+s3c2410 ARCH_S3C2410 S3C2410 182
+possio_px30 ARCH_POSSIO_PX30 POSSIO_PX30 183
+s3c2800 ARCH_S3C2800 S3C2800 184
+fleetwood SA1100_FLEETWOOD FLEETWOOD 185
+omaha ARCH_OMAHA OMAHA 186
+ta7 ARCH_TA7 TA7 187
+nova SA1100_NOVA NOVA 188
+hmk ARCH_HMK HMK 189
karo ARCH_KARO KARO 190
+fester SA1100_FESTER FESTER 191
+gpi ARCH_GPI GPI 192
smdk2410 ARCH_SMDK2410 SMDK2410 193
+i519 ARCH_I519 I519 194
+nexio SA1100_NEXIO NEXIO 195
+bitbox SA1100_BITBOX BITBOX 196
+g200 SA1100_G200 G200 197
+gill SA1100_GILL GILL 198
+pxa_mercury ARCH_PXA_MERCURY PXA_MERCURY 199
ceiva ARCH_CEIVA CEIVA 200
+fret SA1100_FRET FRET 201
+emailphone SA1100_EMAILPHONE EMAILPHONE 202
+h3900 ARCH_H3900 H3900 203
+pxa1 ARCH_PXA1 PXA1 204
+koan369 SA1100_KOAN369 KOAN369 205
+cogent ARCH_COGENT COGENT 206
+esl_simputer ARCH_ESL_SIMPUTER ESL_SIMPUTER 207
+esl_simputer_clr ARCH_ESL_SIMPUTER_CLR ESL_SIMPUTER_CLR 208
+esl_simputer_bw ARCH_ESL_SIMPUTER_BW ESL_SIMPUTER_BW 209
+hhp_cradle ARCH_HHP_CRADLE HHP_CRADLE 210
+he500 ARCH_HE500 HE500 211
+inhandelf2 SA1100_INHANDELF2 INHANDELF2 212
+inhandftip SA1100_INHANDFTIP INHANDFTIP 213
+dnp1110 SA1100_DNP1110 DNP1110 214
+pnp1110 SA1100_PNP1110 PNP1110 215
+csb226 ARCH_CSB226 CSB226 216
+arnold SA1100_ARNOLD ARNOLD 217
voiceblue MACH_VOICEBLUE VOICEBLUE 218
+jz8028 ARCH_JZ8028 JZ8028 219
h5400 ARCH_H5400 H5400 220
+forte SA1100_FORTE FORTE 221
+acam SA1100_ACAM ACAM 222
+abox SA1100_ABOX ABOX 223
+atmel ARCH_ATMEL ATMEL 224
+sitsang ARCH_SITSANG SITSANG 225
+cpu1110lcdnet SA1100_CPU1110LCDNET CPU1110LCDNET 226
+mpl_vcma9 ARCH_MPL_VCMA9 MPL_VCMA9 227
+opus_a1 ARCH_OPUS_A1 OPUS_A1 228
+daytona ARCH_DAYTONA DAYTONA 229
+killbear SA1100_KILLBEAR KILLBEAR 230
+yoho ARCH_YOHO YOHO 231
+jasper ARCH_JASPER JASPER 232
+dsc25 ARCH_DSC25 DSC25 233
omap_innovator MACH_OMAP_INNOVATOR OMAP_INNOVATOR 234
+mnci ARCH_RAMSES RAMSES 235
+s28x ARCH_S28X S28X 236
+mport3 ARCH_MPORT3 MPORT3 237
+pxa_eagle250 ARCH_PXA_EAGLE250 PXA_EAGLE250 238
+pdb ARCH_PDB PDB 239
+blue_2g SA1100_BLUE_2G BLUE_2G 240
+bluearch SA1100_BLUEARCH BLUEARCH 241
ixdp2400 ARCH_IXDP2400 IXDP2400 242
ixdp2800 ARCH_IXDP2800 IXDP2800 243
+explorer SA1100_EXPLORER EXPLORER 244
ixdp425 ARCH_IXDP425 IXDP425 245
+chimp ARCH_CHIMP CHIMP 246
+stork_nest ARCH_STORK_NEST STORK_NEST 247
+stork_egg ARCH_STORK_EGG STORK_EGG 248
+wismo SA1100_WISMO WISMO 249
+ezlinx ARCH_EZLINX EZLINX 250
+at91rm9200 ARCH_AT91RM9200 AT91RM9200 251
+adtech_orion ARCH_ADTECH_ORION ADTECH_ORION 252
+neptune ARCH_NEPTUNE NEPTUNE 253
hackkit SA1100_HACKKIT HACKKIT 254
+pxa_wins30 ARCH_PXA_WINS30 PXA_WINS30 255
+lavinna SA1100_LAVINNA LAVINNA 256
+pxa_uengine ARCH_PXA_UENGINE PXA_UENGINE 257
+innokom ARCH_INNOKOM INNOKOM 258
+bms ARCH_BMS BMS 259
ixcdp1100 ARCH_IXCDP1100 IXCDP1100 260
+prpmc1100 ARCH_PRPMC1100 PRPMC1100 261
at91rm9200dk ARCH_AT91RM9200DK AT91RM9200DK 262
+armstick ARCH_ARMSTICK ARMSTICK 263
+armonie ARCH_ARMONIE ARMONIE 264
+mport1 ARCH_MPORT1 MPORT1 265
+s3c5410 ARCH_S3C5410 S3C5410 266
+zcp320a ARCH_ZCP320A ZCP320A 267
+i_box ARCH_I_BOX I_BOX 268
+stlc1502 ARCH_STLC1502 STLC1502 269
+siren ARCH_SIREN SIREN 270
+greenlake ARCH_GREENLAKE GREENLAKE 271
+argus ARCH_ARGUS ARGUS 272
+combadge SA1100_COMBADGE COMBADGE 273
+rokepxa ARCH_ROKEPXA ROKEPXA 274
cintegrator ARCH_CINTEGRATOR CINTEGRATOR 275
+guidea07 ARCH_GUIDEA07 GUIDEA07 276
+tat257 ARCH_TAT257 TAT257 277
+igp2425 ARCH_IGP2425 IGP2425 278
+bluegrama ARCH_BLUEGRAMMA BLUEGRAMMA 279
+ipod ARCH_IPOD IPOD 280
+adsbitsyx ARCH_ADSBITSYX ADSBITSYX 281
+trizeps2 ARCH_TRIZEPS2 TRIZEPS2 282
viper ARCH_VIPER VIPER 283
+adsbitsyplus SA1100_ADSBITSYPLUS ADSBITSYPLUS 284
+adsagc SA1100_ADSAGC ADSAGC 285
+stp7312 ARCH_STP7312 STP7312 286
+nx_phnx MACH_NX_PHNX NX_PHNX 287
+wep_ep250 ARCH_WEP_EP250 WEP_EP250 288
+inhandelf3 ARCH_INHANDELF3 INHANDELF3 289
adi_coyote ARCH_ADI_COYOTE ADI_COYOTE 290
+iyonix ARCH_IYONIX IYONIX 291
+damicam1 ARCH_DAMICAM_SA1110 DAMICAM_SA1110 292
+meg03 ARCH_MEG03 MEG03 293
+pxa_whitechapel ARCH_PXA_WHITECHAPEL PXA_WHITECHAPEL 294
+nwsc ARCH_NWSC NWSC 295
+nwlarm ARCH_NWLARM NWLARM 296
+ixp425_mguard ARCH_IXP425_MGUARD IXP425_MGUARD 297
+pxa_netdcu4 ARCH_PXA_NETDCU4 PXA_NETDCU4 298
ixdp2401 ARCH_IXDP2401 IXDP2401 299
ixdp2801 ARCH_IXDP2801 IXDP2801 300
+zodiac ARCH_ZODIAC ZODIAC 301
+armmodul ARCH_ARMMODUL ARMMODUL 302
+ketop SA1100_KETOP KETOP 303
+av7200 ARCH_AV7200 AV7200 304
+arch_ti925 ARCH_ARCH_TI925 ARCH_TI925 305
+acq200 ARCH_ACQ200 ACQ200 306
+pt_dafit SA1100_PT_DAFIT PT_DAFIT 307
+ihba ARCH_IHBA IHBA 308
+quinque ARCH_QUINQUE QUINQUE 309
+nimbraone ARCH_NIMBRAONE NIMBRAONE 310
+nimbra29x ARCH_NIMBRA29X NIMBRA29X 311
+nimbra210 ARCH_NIMBRA210 NIMBRA210 312
+hhp_d95xx ARCH_HHP_D95XX HHP_D95XX 313
+labarm ARCH_LABARM LABARM 314
+m825xx ARCH_M825XX M825XX 315
+m7100 SA1100_M7100 M7100 316
+nipc2 ARCH_NIPC2 NIPC2 317
+fu7202 ARCH_FU7202 FU7202 318
+adsagx ARCH_ADSAGX ADSAGX 319
+pxa_pooh ARCH_PXA_POOH PXA_POOH 320
+bandon ARCH_BANDON BANDON 321
+pcm7210 ARCH_PCM7210 PCM7210 322
+nms9200 ARCH_NMS9200 NMS9200 323
+logodl ARCH_LOGODL LOGODL 324
+m7140 SA1100_M7140 M7140 325
+korebot ARCH_KOREBOT KOREBOT 326
iq31244 ARCH_IQ31244 IQ31244 327
+koan393 SA1100_KOAN393 KOAN393 328
+inhandftip3 ARCH_INHANDFTIP3 INHANDFTIP3 329
+gonzo ARCH_GONZO GONZO 330
bast ARCH_BAST BAST 331
+scanpass ARCH_SCANPASS SCANPASS 332
+ep7312_pooh ARCH_EP7312_POOH EP7312_POOH 333
+ta7s ARCH_TA7S TA7S 334
+ta7v ARCH_TA7V TA7V 335
+icarus SA1100_ICARUS ICARUS 336
+h1900 ARCH_H1900 H1900 337
+gemini SA1100_GEMINI GEMINI 338
+axim ARCH_AXIM AXIM 339
+audiotron ARCH_AUDIOTRON AUDIOTRON 340
+h2200 ARCH_H2200 H2200 341
+loox600 ARCH_LOOX600 LOOX600 342
+niop ARCH_NIOP NIOP 343
+dm310 ARCH_DM310 DM310 344
+seedpxa_c2 ARCH_SEEDPXA_C2 SEEDPXA_C2 345
+ixp4xx_mguardpci ARCH_IXP4XX_MGUARD_PCI IXP4XX_MGUARD_PCI 346
h1940 ARCH_H1940 H1940 347
+scorpio ARCH_SCORPIO SCORPIO 348
+viva ARCH_VIVA VIVA 349
+pxa_xcard ARCH_PXA_XCARD PXA_XCARD 350
+csb335 ARCH_CSB335 CSB335 351
+ixrd425 ARCH_IXRD425 IXRD425 352
+iq80315 ARCH_IQ80315 IQ80315 353
+nmp7312 ARCH_NMP7312 NMP7312 354
+cx861xx ARCH_CX861XX CX861XX 355
enp2611 ARCH_ENP2611 ENP2611 356
+xda SA1100_XDA XDA 357
+csir_ims ARCH_CSIR_IMS CSIR_IMS 358
+ixp421_dnaeeth ARCH_IXP421_DNAEETH IXP421_DNAEETH 359
+pocketserv9200 ARCH_POCKETSERV9200 POCKETSERV9200 360
+toto ARCH_TOTO TOTO 361
s3c2440 ARCH_S3C2440 S3C2440 362
+ks8695p ARCH_KS8695P KS8695P 363
+se4000 ARCH_SE4000 SE4000 364
+quadriceps ARCH_QUADRICEPS QUADRICEPS 365
+bronco ARCH_BRONCO BRONCO 366
+esl_wireless_tab ARCH_ESL_WIRELESS_TAB ESL_WIRELESS_TAB 367
+esl_sofcomp ARCH_ESL_SOFCOMP ESL_SOFCOMP 368
+s5c7375 ARCH_S5C7375 S5C7375 369
+spearhead ARCH_SPEARHEAD SPEARHEAD 370
+pantera ARCH_PANTERA PANTERA 371
+prayoglite ARCH_PRAYOGLITE PRAYOGLITE 372
gumstix ARCH_GUMSTIX GUMSTIX 373
+rcube ARCH_RCUBE RCUBE 374
+rea_olv ARCH_REA_OLV REA_OLV 375
+pxa_iphone ARCH_PXA_IPHONE PXA_IPHONE 376
+s3c3410 ARCH_S3C3410 S3C3410 377
+espd_4510b ARCH_ESPD_4510B ESPD_4510B 378
+mp1x ARCH_MP1X MP1X 379
+at91rm9200tb ARCH_AT91RM9200TB AT91RM9200TB 380
+adsvgx ARCH_ADSVGX ADSVGX 381
omap_h2 MACH_OMAP_H2 OMAP_H2 382
+pelee ARCH_PELEE PELEE 383
e740 MACH_E740 E740 384
iq80331 ARCH_IQ80331 IQ80331 385
versatile_pb ARCH_VERSATILE_PB VERSATILE_PB 387
kev7a400 MACH_KEV7A400 KEV7A400 388
lpd7a400 MACH_LPD7A400 LPD7A400 389
lpd7a404 MACH_LPD7A404 LPD7A404 390
+fujitsu_camelot ARCH_FUJITSU_CAMELOT FUJITSU_CAMELOT 391
+janus2m ARCH_JANUS2M JANUS2M 392
+embtf MACH_EMBTF EMBTF 393
+hpm MACH_HPM HPM 394
+smdk2410tk MACH_SMDK2410TK SMDK2410TK 395
+smdk2410aj MACH_SMDK2410AJ SMDK2410AJ 396
+streetracer MACH_STREETRACER STREETRACER 397
+eframe MACH_EFRAME EFRAME 398
csb337 MACH_CSB337 CSB337 399
+pxa_lark MACH_PXA_LARK PXA_LARK 400
+pxa_pnp2110 MACH_PNP2110 PNP2110 401
+tcc72x MACH_TCC72X TCC72X 402
+altair MACH_ALTAIR ALTAIR 403
+kc3 MACH_KC3 KC3 404
+sinteftd MACH_SINTEFTD SINTEFTD 405
mainstone MACH_MAINSTONE MAINSTONE 406
+aday4x MACH_ADAY4X ADAY4X 407
+lite300 MACH_LITE300 LITE300 408
+s5c7376 MACH_S5C7376 S5C7376 409
+mt02 MACH_MT02 MT02 410
+mport3s MACH_MPORT3S MPORT3S 411
+ra_alpha MACH_RA_ALPHA RA_ALPHA 412
xcep MACH_XCEP XCEP 413
arcom_vulcan MACH_ARCOM_VULCAN ARCOM_VULCAN 414
+stargate MACH_STARGATE STARGATE 415
+armadilloj MACH_ARMADILLOJ ARMADILLOJ 416
+elroy_jack MACH_ELROY_JACK ELROY_JACK 417
+backend MACH_BACKEND BACKEND 418
+s5linbox MACH_S5LINBOX S5LINBOX 419
nomadik MACH_NOMADIK NOMADIK 420
+ia_cpu_9200 MACH_IA_CPU_9200 IA_CPU_9200 421
+at91_bja1 MACH_AT91_BJA1 AT91_BJA1 422
corgi MACH_CORGI CORGI 423
poodle MACH_POODLE POODLE 424
+ten MACH_TEN TEN 425
+roverp5p MACH_ROVERP5P ROVERP5P 426
+sc2700 MACH_SC2700 SC2700 427
+ex_eagle MACH_EX_EAGLE EX_EAGLE 428
+nx_pxa12 MACH_NX_PXA12 NX_PXA12 429
+nx_pxa5 MACH_NX_PXA5 NX_PXA5 430
+blackboard2 MACH_BLACKBOARD2 BLACKBOARD2 431
+i819 MACH_I819 I819 432
+ixmb995e MACH_IXMB995E IXMB995E 433
+skyrider MACH_SKYRIDER SKYRIDER 434
+skyhawk MACH_SKYHAWK SKYHAWK 435
+enterprise MACH_ENTERPRISE ENTERPRISE 436
+dep2410 MACH_DEP2410 DEP2410 437
armcore MACH_ARMCORE ARMCORE 438
+hobbit MACH_HOBBIT HOBBIT 439
+h7210 MACH_H7210 H7210 440
+pxa_netdcu5 MACH_PXA_NETDCU5 PXA_NETDCU5 441
+acc MACH_ACC ACC 442
+esl_sarva MACH_ESL_SARVA ESL_SARVA 443
+xm250 MACH_XM250 XM250 444
+t6tc1xb MACH_T6TC1XB T6TC1XB 445
+ess710 MACH_ESS710 ESS710 446
mx31ads MACH_MX31ADS MX31ADS 447
himalaya MACH_HIMALAYA HIMALAYA 448
+bolfenk MACH_BOLFENK BOLFENK 449
+at91rm9200kr MACH_AT91RM9200KR AT91RM9200KR 450
edb9312 MACH_EDB9312 EDB9312 451
omap_generic MACH_OMAP_GENERIC OMAP_GENERIC 452
+aximx3 MACH_AXIMX3 AXIMX3 453
+eb67xdip MACH_EB67XDIP EB67XDIP 454
+webtxs MACH_WEBTXS WEBTXS 455
+hawk MACH_HAWK HAWK 456
+ccat91sbc001 MACH_CCAT91SBC001 CCAT91SBC001 457
+expresso MACH_EXPRESSO EXPRESSO 458
+h4000 MACH_H4000 H4000 459
+dino MACH_DINO DINO 460
+ml675k MACH_ML675K ML675K 461
edb9301 MACH_EDB9301 EDB9301 462
edb9315 MACH_EDB9315 EDB9315 463
+reciva_tt MACH_RECIVA_TT RECIVA_TT 464
+cstcb01 MACH_CSTCB01 CSTCB01 465
+cstcb1 MACH_CSTCB1 CSTCB1 466
+shadwell MACH_SHADWELL SHADWELL 467
+goepel263 MACH_GOEPEL263 GOEPEL263 468
+acq100 MACH_ACQ100 ACQ100 469
+mx1fs2 MACH_MX1FS2 MX1FS2 470
+hiptop_g1 MACH_HIPTOP_G1 HIPTOP_G1 471
+sparky MACH_SPARKY SPARKY 472
+ns9750 MACH_NS9750 NS9750 473
+phoenix MACH_PHOENIX PHOENIX 474
vr1000 MACH_VR1000 VR1000 475
+deisterpxa MACH_DEISTERPXA DEISTERPXA 476
+bcm1160 MACH_BCM1160 BCM1160 477
+pcm022 MACH_PCM022 PCM022 478
+adsgcx MACH_ADSGCX ADSGCX 479
+dreadnaught MACH_DREADNAUGHT DREADNAUGHT 480
+dm320 MACH_DM320 DM320 481
+markov MACH_MARKOV MARKOV 482
+cos7a400 MACH_COS7A400 COS7A400 483
+milano MACH_MILANO MILANO 484
+ue9328 MACH_UE9328 UE9328 485
+uex255 MACH_UEX255 UEX255 486
+ue2410 MACH_UE2410 UE2410 487
+a620 MACH_A620 A620 488
+ocelot MACH_OCELOT OCELOT 489
+cheetah MACH_CHEETAH CHEETAH 490
omap_perseus2 MACH_OMAP_PERSEUS2 OMAP_PERSEUS2 491
+zvue MACH_ZVUE ZVUE 492
+roverp1 MACH_ROVERP1 ROVERP1 493
+asidial2 MACH_ASIDIAL2 ASIDIAL2 494
+s3c24a0 MACH_S3C24A0 S3C24A0 495
e800 MACH_E800 E800 496
e750 MACH_E750 E750 497
+s3c5500 MACH_S3C5500 S3C5500 498
+smdk5500 MACH_SMDK5500 SMDK5500 499
+signalsync MACH_SIGNALSYNC SIGNALSYNC 500
+nbc MACH_NBC NBC 501
+kodiak MACH_KODIAK KODIAK 502
+netbookpro MACH_NETBOOKPRO NETBOOKPRO 503
+hw90200 MACH_HW90200 HW90200 504
+condor MACH_CONDOR CONDOR 505
+cup MACH_CUP CUP 506
+kite MACH_KITE KITE 507
scb9328 MACH_SCB9328 SCB9328 508
omap_h3 MACH_OMAP_H3 OMAP_H3 509
omap_h4 MACH_OMAP_H4 OMAP_H4 510
+n10 MACH_N10 N10 511
+montejade MACH_MONTAJADE MONTAJADE 512
+sg560 MACH_SG560 SG560 513
+dp1000 MACH_DP1000 DP1000 514
omap_osk MACH_OMAP_OSK OMAP_OSK 515
+rg100v3 MACH_RG100V3 RG100V3 516
+mx2ads MACH_MX2ADS MX2ADS 517
+pxa_kilo MACH_PXA_KILO PXA_KILO 518
+ixp4xx_eagle MACH_IXP4XX_EAGLE IXP4XX_EAGLE 519
tosa MACH_TOSA TOSA 520
+mb2520f MACH_MB2520F MB2520F 521
+emc1000 MACH_EMC1000 EMC1000 522
+tidsc25 MACH_TIDSC25 TIDSC25 523
+akcpmxl MACH_AKCPMXL AKCPMXL 524
+av3xx MACH_AV3XX AV3XX 525
avila MACH_AVILA AVILA 526
+pxa_mpm10 MACH_PXA_MPM10 PXA_MPM10 527
+pxa_kyanite MACH_PXA_KYANITE PXA_KYANITE 528
+sgold MACH_SGOLD SGOLD 529
+oscar MACH_OSCAR OSCAR 530
+epxa4usb2 MACH_EPXA4USB2 EPXA4USB2 531
+xsengine MACH_XSENGINE XSENGINE 532
+ip600 MACH_IP600 IP600 533
+mcan2 MACH_MCAN2 MCAN2 534
+ddi_blueridge MACH_DDI_BLUERIDGE DDI_BLUERIDGE 535
+skyminder MACH_SKYMINDER SKYMINDER 536
+lpd79520 MACH_LPD79520 LPD79520 537
edb9302 MACH_EDB9302 EDB9302 538
+hw90340 MACH_HW90340 HW90340 539
+cip_box MACH_CIP_BOX CIP_BOX 540
+ivpn MACH_IVPN IVPN 541
+rsoc2 MACH_RSOC2 RSOC2 542
husky MACH_HUSKY HUSKY 543
+boxer MACH_BOXER BOXER 544
shepherd MACH_SHEPHERD SHEPHERD 545
+aml42800aa MACH_AML42800AA AML42800AA 546
+lpc2294 MACH_LPC2294 LPC2294 548
+switchgrass MACH_SWITCHGRASS SWITCHGRASS 549
+ens_cmu MACH_ENS_CMU ENS_CMU 550
+mm6_sdb MACH_MM6_SDB MM6_SDB 551
+saturn MACH_SATURN SATURN 552
+i30030evb MACH_I30030EVB I30030EVB 553
+mxc27530evb MACH_MXC27530EVB MXC27530EVB 554
+smdk2800 MACH_SMDK2800 SMDK2800 555
+mtwilson MACH_MTWILSON MTWILSON 556
+ziti MACH_ZITI ZITI 557
+grandfather MACH_GRANDFATHER GRANDFATHER 558
+tengine MACH_TENGINE TENGINE 559
+s3c2460 MACH_S3C2460 S3C2460 560
+pdm MACH_PDM PDM 561
h4700 MACH_H4700 H4700 562
+h6300 MACH_H6300 H6300 563
+rz1700 MACH_RZ1700 RZ1700 564
+a716 MACH_A716 A716 565
+estk2440a MACH_ESTK2440A ESTK2440A 566
+atwixp425 MACH_ATWIXP425 ATWIXP425 567
+csb336 MACH_CSB336 CSB336 568
+rirm2 MACH_RIRM2 RIRM2 569
+cx23518 MACH_CX23518 CX23518 570
+cx2351x MACH_CX2351X CX2351X 571
+computime MACH_COMPUTIME COMPUTIME 572
+izarus MACH_IZARUS IZARUS 573
+pxa_rts MACH_RTS RTS 574
+se5100 MACH_SE5100 SE5100 575
+s3c2510 MACH_S3C2510 S3C2510 576
+csb437tl MACH_CSB437TL CSB437TL 577
+slauson MACH_SLAUSON SLAUSON 578
+pearlriver MACH_PEARLRIVER PEARLRIVER 579
+tdc_p210 MACH_TDC_P210 TDC_P210 580
+sg580 MACH_SG580 SG580 581
+wrsbcarm7 MACH_WRSBCARM7 WRSBCARM7 582
+ipd MACH_IPD IPD 583
+pxa_dnp2110 MACH_PXA_DNP2110 PXA_DNP2110 584
+xaeniax MACH_XAENIAX XAENIAX 585
+somn4250 MACH_SOMN4250 SOMN4250 586
+pleb2 MACH_PLEB2 PLEB2 587
+cornwallis MACH_CORNWALLIS CORNWALLIS 588
+gurney_drv MACH_GURNEY_DRV GURNEY_DRV 589
+chaffee MACH_CHAFFEE CHAFFEE 590
+rms101 MACH_RMS101 RMS101 591
rx3715 MACH_RX3715 RX3715 592
+swift MACH_SWIFT SWIFT 593
+roverp7 MACH_ROVERP7 ROVERP7 594
+pr818s MACH_PR818S PR818S 595
+trxpro MACH_TRXPRO TRXPRO 596
nslu2 MACH_NSLU2 NSLU2 597
e400 MACH_E400 E400 598
+trab MACH_TRAB TRAB 599
+cmc_pu2 MACH_CMC_PU2 CMC_PU2 600
+fulcrum MACH_FULCRUM FULCRUM 601
+netgate42x MACH_NETGATE42X NETGATE42X 602
+str710 MACH_STR710 STR710 603
ixdpg425 MACH_IXDPG425 IXDPG425 604
+tomtomgo MACH_TOMTOMGO TOMTOMGO 605
versatile_ab MACH_VERSATILE_AB VERSATILE_AB 606
edb9307 MACH_EDB9307 EDB9307 607
+sg565 MACH_SG565 SG565 608
+lpd79524 MACH_LPD79524 LPD79524 609
+lpd79525 MACH_LPD79525 LPD79525 610
+rms100 MACH_RMS100 RMS100 611
kb9200 MACH_KB9200 KB9200 612
sx1 MACH_SX1 SX1 613
+hms39c7092 MACH_HMS39C7092 HMS39C7092 614
+armadillo MACH_ARMADILLO ARMADILLO 615
+ipcu MACH_IPCU IPCU 616
+loox720 MACH_LOOX720 LOOX720 617
ixdp465 MACH_IXDP465 IXDP465 618
ixdp2351 MACH_IXDP2351 IXDP2351 619
+adsvix MACH_ADSVIX ADSVIX 620
+dm270 MACH_DM270 DM270 621
+socltplus MACH_SOCLTPLUS SOCLTPLUS 622
+ecia MACH_ECIA ECIA 623
+cm4008 MACH_CM4008 CM4008 624
+p2001 MACH_P2001 P2001 625
+twister MACH_TWISTER TWISTER 626
+mudshark MACH_MUDSHARK MUDSHARK 627
+hb2 MACH_HB2 HB2 628
iq80332 MACH_IQ80332 IQ80332 629
+sendt MACH_SENDT SENDT 630
+mx2jazz MACH_MX2JAZZ MX2JAZZ 631
+multiio MACH_MULTIIO MULTIIO 632
+hrdisplay MACH_HRDISPLAY HRDISPLAY 633
+mxc27530ads MACH_MXC27530ADS MXC27530ADS 634
+trizeps3 MACH_TRIZEPS3 TRIZEPS3 635
+zefeerdza MACH_ZEFEERDZA ZEFEERDZA 636
+zefeerdzb MACH_ZEFEERDZB ZEFEERDZB 637
+zefeerdzg MACH_ZEFEERDZG ZEFEERDZG 638
+zefeerdzn MACH_ZEFEERDZN ZEFEERDZN 639
+zefeerdzq MACH_ZEFEERDZQ ZEFEERDZQ 640
gtwx5715 MACH_GTWX5715 GTWX5715 641
+astro_jack MACH_ASTRO_JACK ASTRO_JACK 643
+tip03 MACH_TIP03 TIP03 644
+a9200ec MACH_A9200EC A9200EC 645
+pnx0105 MACH_PNX0105 PNX0105 646
+adcpoecpu MACH_ADCPOECPU ADCPOECPU 647
csb637 MACH_CSB637 CSB637 648
+mb9200 MACH_MB9200 MB9200 650
+kulun MACH_KULUN KULUN 651
+snapper MACH_SNAPPER SNAPPER 652
+optima MACH_OPTIMA OPTIMA 653
+dlhsbc MACH_DLHSBC DLHSBC 654
+x30 MACH_X30 X30 655
n30 MACH_N30 N30 656
+manga_ks8695 MACH_MANGA_KS8695 MANGA_KS8695 657
+ajax MACH_AJAX AJAX 658
nec_mp900 MACH_NEC_MP900 NEC_MP900 659
+vvtk1000 MACH_VVTK1000 VVTK1000 661
kafa MACH_KAFA KAFA 662
+vvtk3000 MACH_VVTK3000 VVTK3000 663
+pimx1 MACH_PIMX1 PIMX1 664
+ollie MACH_OLLIE OLLIE 665
+skymax MACH_SKYMAX SKYMAX 666
+jazz MACH_JAZZ JAZZ 667
+tel_t3 MACH_TEL_T3 TEL_T3 668
+aisino_fcr255 MACH_AISINO_FCR255 AISINO_FCR255 669
+btweb MACH_BTWEB BTWEB 670
+dbg_lh79520 MACH_DBG_LH79520 DBG_LH79520 671
+cm41xx MACH_CM41XX CM41XX 672
ts72xx MACH_TS72XX TS72XX 673
+nggpxa MACH_NGGPXA NGGPXA 674
+csb535 MACH_CSB535 CSB535 675
+csb536 MACH_CSB536 CSB536 676
+pxa_trakpod MACH_PXA_TRAKPOD PXA_TRAKPOD 677
+praxis MACH_PRAXIS PRAXIS 678
+lh75411 MACH_LH75411 LH75411 679
otom MACH_OTOM OTOM 680
nexcoder_2440 MACH_NEXCODER_2440 NEXCODER_2440 681
+loox410 MACH_LOOX410 LOOX410 682
+westlake MACH_WESTLAKE WESTLAKE 683
+nsb MACH_NSB NSB 684
+esl_sarva_stn MACH_ESL_SARVA_STN ESL_SARVA_STN 685
+esl_sarva_tft MACH_ESL_SARVA_TFT ESL_SARVA_TFT 686
+esl_sarva_iad MACH_ESL_SARVA_IAD ESL_SARVA_IAD 687
+esl_sarva_acc MACH_ESL_SARVA_ACC ESL_SARVA_ACC 688
+typhoon MACH_TYPHOON TYPHOON 689
+cnav MACH_CNAV CNAV 690
+a730 MACH_A730 A730 691
+netstar MACH_NETSTAR NETSTAR 692
+supercon MACH_PHASEFALE_SUPERCON PHASEFALE_SUPERCON 693
+shiva1100 MACH_SHIVA1100 SHIVA1100 694
+etexsc MACH_ETEXSC ETEXSC 695
+ixdpg465 MACH_IXDPG465 IXDPG465 696
+a9m2410 MACH_A9M2410 A9M2410 697
+a9m2440 MACH_A9M2440 A9M2440 698
+a9m9750 MACH_A9M9750 A9M9750 699
+a9m9360 MACH_A9M9360 A9M9360 700
+unc90 MACH_UNC90 UNC90 701
eco920 MACH_ECO920 ECO920 702
+satview MACH_SATVIEW SATVIEW 703
roadrunner MACH_ROADRUNNER ROADRUNNER 704
at91rm9200ek MACH_AT91RM9200EK AT91RM9200EK 705
+gp32 MACH_GP32 GP32 706
+gem MACH_GEM GEM 707
+i858 MACH_I858 I858 708
+hx2750 MACH_HX2750 HX2750 709
+mxc91131evb MACH_MXC91131EVB MXC91131EVB 710
+p700 MACH_P700 P700 711
+cpe MACH_CPE CPE 712
spitz MACH_SPITZ SPITZ 713
+nimbra340 MACH_NIMBRA340 NIMBRA340 714
+lpc22xx MACH_LPC22XX LPC22XX 715
+omap_comet3 MACH_COMET3 COMET3 716
+omap_comet4 MACH_COMET4 COMET4 717
+csb625 MACH_CSB625 CSB625 718
+fortunet2 MACH_FORTUNET2 FORTUNET2 719
+s5h2200 MACH_S5H2200 S5H2200 720
+optorm920 MACH_OPTORM920 OPTORM920 721
+adsbitsyxb MACH_ADSBITSYXB ADSBITSYXB 722
adssphere MACH_ADSSPHERE ADSSPHERE 723
+adsportal MACH_ADSPORTAL ADSPORTAL 724
+ln2410sbc MACH_LN2410SBC LN2410SBC 725
+cb3rufc MACH_CB3RUFC CB3RUFC 726
+mp2usb MACH_MP2USB MP2USB 727
+ntnp425c MACH_NTNP425C NTNP425C 728
colibri MACH_COLIBRI COLIBRI 729
+pcm7220 MACH_PCM7220 PCM7220 730
gateway7001 MACH_GATEWAY7001 GATEWAY7001 731
pcm027 MACH_PCM027 PCM027 732
+cmpxa MACH_CMPXA CMPXA 733
anubis MACH_ANUBIS ANUBIS 734
+ite8152 MACH_ITE8152 ITE8152 735
+lpc3xxx MACH_LPC3XXX LPC3XXX 736
+puppeteer MACH_PUPPETEER PUPPETEER 737
+e570 MACH_E570 E570 739
+x50 MACH_X50 X50 740
+recon MACH_RECON RECON 741
+xboardgp8 MACH_XBOARDGP8 XBOARDGP8 742
+fpic2 MACH_FPIC2 FPIC2 743
akita MACH_AKITA AKITA 744
+a81 MACH_A81 A81 745
+svm_sc25x MACH_SVM_SC25X SVM_SC25X 746
+vt020 MACH_VADATECH020 VADATECH020 747
+tli MACH_TLI TLI 748
+edb9315lc MACH_EDB9315LC EDB9315LC 749
+passec MACH_PASSEC PASSEC 750
+ds_tiger MACH_DS_TIGER DS_TIGER 751
+e310 MACH_E310 E310 752
e330 MACH_E330 E330 753
+rt3000 MACH_RT3000 RT3000 754
nokia770 MACH_NOKIA770 NOKIA770 755
+pnx0106 MACH_PNX0106 PNX0106 756
+hx21xx MACH_HX21XX HX21XX 757
+faraday MACH_FARADAY FARADAY 758
+sbc9312 MACH_SBC9312 SBC9312 759
+batman MACH_BATMAN BATMAN 760
+jpd201 MACH_JPD201 JPD201 761
+mipsa MACH_MIPSA MIPSA 762
+kacom MACH_KACOM KACOM 763
+swarcocpu MACH_SWARCOCPU SWARCOCPU 764
+swarcodsl MACH_SWARCODSL SWARCODSL 765
+blueangel MACH_BLUEANGEL BLUEANGEL 766
+hairygrama MACH_HAIRYGRAMA HAIRYGRAMA 767
+banff MACH_BANFF BANFF 768
carmeva MACH_CARMEVA CARMEVA 769
+sam255 MACH_SAM255 SAM255 770
+ppm10 MACH_PPM10 PPM10 771
edb9315a MACH_EDB9315A EDB9315A 772
+sunset MACH_SUNSET SUNSET 773
stargate2 MACH_STARGATE2 STARGATE2 774
intelmote2 MACH_INTELMOTE2 INTELMOTE2 775
trizeps4 MACH_TRIZEPS4 TRIZEPS4 776
+mainstone2 MACH_MAINSTONE2 MAINSTONE2 777
+ez_ixp42x MACH_EZ_IXP42X EZ_IXP42X 778
+tapwave_zodiac MACH_TAPWAVE_ZODIAC TAPWAVE_ZODIAC 779
+universalmeter MACH_UNIVERSALMETER UNIVERSALMETER 780
+hicoarm9 MACH_HICOARM9 HICOARM9 781
pnx4008 MACH_PNX4008 PNX4008 782
+kws6000 MACH_KWS6000 KWS6000 783
+portux920t MACH_PORTUX920T PORTUX920T 784
+ez_x5 MACH_EZ_X5 EZ_X5 785
+omap_rudolph MACH_OMAP_RUDOLPH OMAP_RUDOLPH 786
cpuat91 MACH_CPUAT91 CPUAT91 787
+rea9200 MACH_REA9200 REA9200 788
+acts_pune_sa1110 MACH_ACTS_PUNE_SA1110 ACTS_PUNE_SA1110 789
+ixp425 MACH_IXP425 IXP425 790
+i30030ads MACH_I30030ADS I30030ADS 791
+perch MACH_PERCH PERCH 792
+eis05r1 MACH_EIS05R1 EIS05R1 793
+pepperpad MACH_PEPPERPAD PEPPERPAD 794
+sb3010 MACH_SB3010 SB3010 795
+rm9200 MACH_RM9200 RM9200 796
+dma03 MACH_DMA03 DMA03 797
+road_s101 MACH_ROAD_S101 ROAD_S101 798
iq81340sc MACH_IQ81340SC IQ81340SC 799
+iq_nextgen_b MACH_IQ_NEXTGEN_B IQ_NEXTGEN_B 800
iq81340mc MACH_IQ81340MC IQ81340MC 801
+iq_nextgen_d MACH_IQ_NEXTGEN_D IQ_NEXTGEN_D 802
+iq_nextgen_e MACH_IQ_NEXTGEN_E IQ_NEXTGEN_E 803
+mallow_at91 MACH_MALLOW_AT91 MALLOW_AT91 804
+cybertracker_i MACH_CYBERTRACKER_I CYBERTRACKER_I 805
+gesbc931x MACH_GESBC931X GESBC931X 806
+centipad MACH_CENTIPAD CENTIPAD 807
+armsoc MACH_ARMSOC ARMSOC 808
+se4200 MACH_SE4200 SE4200 809
+ems197a MACH_EMS197A EMS197A 810
micro9 MACH_MICRO9 MICRO9 811
micro9l MACH_MICRO9L MICRO9L 812
+uc5471dsp MACH_UC5471DSP UC5471DSP 813
+sj5471eng MACH_SJ5471ENG SJ5471ENG 814
+none MACH_CMPXA26X CMPXA26X 815
+nc1 MACH_NC NC 816
omap_palmte MACH_OMAP_PALMTE OMAP_PALMTE 817
+ajax52x MACH_AJAX52X AJAX52X 818
+siriustar MACH_SIRIUSTAR SIRIUSTAR 819
+iodata_hdlg MACH_IODATA_HDLG IODATA_HDLG 820
+at91rm9200utl MACH_AT91RM9200UTL AT91RM9200UTL 821
+biosafe MACH_BIOSAFE BIOSAFE 822
+mp1000 MACH_MP1000 MP1000 823
+parsy MACH_PARSY PARSY 824
+ccxp270 MACH_CCXP CCXP 825
+omap_gsample MACH_OMAP_GSAMPLE OMAP_GSAMPLE 826
realview_eb MACH_REALVIEW_EB REALVIEW_EB 827
+samoa MACH_SAMOA SAMOA 828
+palmt3 MACH_PALMT3 PALMT3 829
+i878 MACH_I878 I878 830
borzoi MACH_BORZOI BORZOI 831
+gecko MACH_GECKO GECKO 832
+ds101 MACH_DS101 DS101 833
+omap_palmtt2 MACH_OMAP_PALMTT2 OMAP_PALMTT2 834
palmld MACH_PALMLD PALMLD 835
+cc9c MACH_CC9C CC9C 836
+sbc1670 MACH_SBC1670 SBC1670 837
ixdp28x5 MACH_IXDP28X5 IXDP28X5 838
omap_palmtt MACH_OMAP_PALMTT OMAP_PALMTT 839
+ml696k MACH_ML696K ML696K 840
arcom_zeus MACH_ARCOM_ZEUS ARCOM_ZEUS 841
osiris MACH_OSIRIS OSIRIS 842
+maestro MACH_MAESTRO MAESTRO 843
palmte2 MACH_PALMTE2 PALMTE2 844
+ixbbm MACH_IXBBM IXBBM 845
mx27ads MACH_MX27ADS MX27ADS 846
+ax8004 MACH_AX8004 AX8004 847
at91sam9261ek MACH_AT91SAM9261EK AT91SAM9261EK 848
loft MACH_LOFT LOFT 849
+magpie MACH_MAGPIE MAGPIE 850
mx21ads MACH_MX21ADS MX21ADS 851
+mb87m3400 MACH_MB87M3400 MB87M3400 852
+mguard_delta MACH_MGUARD_DELTA MGUARD_DELTA 853
+davinci_dvdp MACH_DAVINCI_DVDP DAVINCI_DVDP 854
+htcuniversal MACH_HTCUNIVERSAL HTCUNIVERSAL 855
+tpad MACH_TPAD TPAD 856
+roverp3 MACH_ROVERP3 ROVERP3 857
+jornada928 MACH_JORNADA928 JORNADA928 858
+mv88fxx81 MACH_MV88FXX81 MV88FXX81 859
+stmp36xx MACH_STMP36XX STMP36XX 860
+sxni79524 MACH_SXNI79524 SXNI79524 861
ams_delta MACH_AMS_DELTA AMS_DELTA 862
+uranium MACH_URANIUM URANIUM 863
+ucon MACH_UCON UCON 864
nas100d MACH_NAS100D NAS100D 865
+l083 MACH_L083_1000 L083_1000 866
+ezx MACH_EZX EZX 867
+pnx5220 MACH_PNX5220 PNX5220 868
+butte MACH_BUTTE BUTTE 869
+srm2 MACH_SRM2 SRM2 870
+dsbr MACH_DSBR DSBR 871
+crystalball MACH_CRYSTALBALL CRYSTALBALL 872
+tinypxa27x MACH_TINYPXA27X TINYPXA27X 873
+herbie MACH_HERBIE HERBIE 874
magician MACH_MAGICIAN MAGICIAN 875
+cm4002 MACH_CM4002 CM4002 876
+b4 MACH_B4 B4 877
+maui MACH_MAUI MAUI 878
+cybertracker_g MACH_CYBERTRACKER_G CYBERTRACKER_G 879
nxdkn MACH_NXDKN NXDKN 880
+mio8390 MACH_MIO8390 MIO8390 881
+omi_board MACH_OMI_BOARD OMI_BOARD 882
+mx21civ MACH_MX21CIV MX21CIV 883
+mahi_cdac MACH_MAHI_CDAC MAHI_CDAC 884
palmtx MACH_PALMTX PALMTX 885
s3c2413 MACH_S3C2413 S3C2413 887
+samsys_ep0 MACH_SAMSYS_EP0 SAMSYS_EP0 888
+wg302v1 MACH_WG302V1 WG302V1 889
wg302v2 MACH_WG302V2 WG302V2 890
+eb42x MACH_EB42X EB42X 891
+iq331es MACH_IQ331ES IQ331ES 892
+cosydsp MACH_COSYDSP COSYDSP 893
+uplat7d_proto MACH_UPLAT7D UPLAT7D 894
+ptdavinci MACH_PTDAVINCI PTDAVINCI 895
+mbus MACH_MBUS MBUS 896
+nadia2vb MACH_NADIA2VB NADIA2VB 897
+r1000 MACH_R1000 R1000 898
+hw90250 MACH_HW90250 HW90250 899
omap_2430sdp MACH_OMAP_2430SDP OMAP_2430SDP 900
davinci_evm MACH_DAVINCI_EVM DAVINCI_EVM 901
+omap_tornado MACH_OMAP_TORNADO OMAP_TORNADO 902
+olocreek MACH_OLOCREEK OLOCREEK 903
palmz72 MACH_PALMZ72 PALMZ72 904
nxdb500 MACH_NXDB500 NXDB500 905
apf9328 MACH_APF9328 APF9328 906
+omap_wipoq MACH_OMAP_WIPOQ OMAP_WIPOQ 907
+omap_twip MACH_OMAP_TWIP OMAP_TWIP 908
+treo650 MACH_TREO650 TREO650 909
+acumen MACH_ACUMEN ACUMEN 910
+xp100 MACH_XP100 XP100 911
+fs2410 MACH_FS2410 FS2410 912
+pxa270_cerf MACH_PXA270_CERF PXA270_CERF 913
+sq2ftlpalm MACH_SQ2FTLPALM SQ2FTLPALM 914
+bsemserver MACH_BSEMSERVER BSEMSERVER 915
+netclient MACH_NETCLIENT NETCLIENT 916
palmt5 MACH_PALMT5 PALMT5 917
palmtc MACH_PALMTC PALMTC 918
omap_apollon MACH_OMAP_APOLLON OMAP_APOLLON 919
+mxc30030evb MACH_MXC30030EVB MXC30030EVB 920
+rea_cpu2 MACH_REA_2D REA_2D 921
+eti3e524 MACH_TI3E524 TI3E524 922
ateb9200 MACH_ATEB9200 ATEB9200 923
+auckland MACH_AUCKLAND AUCKLAND 924
+ak3220m MACH_AK3320M AK3320M 925
+duramax MACH_DURAMAX DURAMAX 926
n35 MACH_N35 N35 927
+pronghorn MACH_PRONGHORN PRONGHORN 928
+fundy MACH_FUNDY FUNDY 929
logicpd_pxa270 MACH_LOGICPD_PXA270 LOGICPD_PXA270 930
+cpu777 MACH_CPU777 CPU777 931
+simicon9201 MACH_SIMICON9201 SIMICON9201 932
+leap2_hpm MACH_LEAP2_HPM LEAP2_HPM 933
+cm922txa10 MACH_CM922TXA10 CM922TXA10 934
+sandgate MACH_PXA PXA 935
+sandgate2 MACH_SANDGATE2 SANDGATE2 936
+sandgate2g MACH_SANDGATE2G SANDGATE2G 937
+sandgate2p MACH_SANDGATE2P SANDGATE2P 938
+fred_jack MACH_FRED_JACK FRED_JACK 939
+ttg_color1 MACH_TTG_COLOR1 TTG_COLOR1 940
nxeb500hmi MACH_NXEB500HMI NXEB500HMI 941
+netdcu8 MACH_NETDCU8 NETDCU8 942
+ng_fvx538 MACH_NG_FVX538 NG_FVX538 944
+ng_fvs338 MACH_NG_FVS338 NG_FVS338 945
+pnx4103 MACH_PNX4103 PNX4103 946
+hesdb MACH_HESDB HESDB 947
+xsilo MACH_XSILO XSILO 948
espresso MACH_ESPRESSO ESPRESSO 949
+emlc MACH_EMLC EMLC 950
+sisteron MACH_SISTERON SISTERON 951
rx1950 MACH_RX1950 RX1950 952
+tsc_venus MACH_TSC_VENUS TSC_VENUS 953
+ds101j MACH_DS101J DS101J 954
+mxc30030ads MACH_MXC30030ADS MXC30030ADS 955
+fujitsu_wimaxsoc MACH_FUJITSU_WIMAXSOC FUJITSU_WIMAXSOC 956
+dualpcmodem MACH_DUALPCMODEM DUALPCMODEM 957
gesbc9312 MACH_GESBC9312 GESBC9312 958
+htcapache MACH_HTCAPACHE HTCAPACHE 959
+ixdp435 MACH_IXDP435 IXDP435 960
+catprovt100 MACH_CATPROVT100 CATPROVT100 961
+picotux1xx MACH_PICOTUX1XX PICOTUX1XX 962
picotux2xx MACH_PICOTUX2XX PICOTUX2XX 963
dsmg600 MACH_DSMG600 DSMG600 964
+empc2 MACH_EMPC2 EMPC2 965
+ventura MACH_VENTURA VENTURA 966
+phidget_sbc MACH_PHIDGET_SBC PHIDGET_SBC 967
+ij3k MACH_IJ3K IJ3K 968
+pisgah MACH_PISGAH PISGAH 969
omap_fsample MACH_OMAP_FSAMPLE OMAP_FSAMPLE 970
+sg720 MACH_SG720 SG720 971
+redfox MACH_REDFOX REDFOX 972
+mysh_ep9315_1 MACH_MYSH_EP9315_1 MYSH_EP9315_1 973
+tpf106 MACH_TPF106 TPF106 974
+at91rm9200kg MACH_AT91RM9200KG AT91RM9200KG 975
+rcmt2 MACH_SLEDB SLEDB 976
+ontrack MACH_ONTRACK ONTRACK 977
+pm1200 MACH_PM1200 PM1200 978
+ess24562 MACH_ESS24XXX ESS24XXX 979
+coremp7 MACH_COREMP7 COREMP7 980
+nexcoder_6446 MACH_NEXCODER_6446 NEXCODER_6446 981
+stvc8380 MACH_STVC8380 STVC8380 982
+teklynx MACH_TEKLYNX TEKLYNX 983
+carbonado MACH_CARBONADO CARBONADO 984
+sysmos_mp730 MACH_SYSMOS_MP730 SYSMOS_MP730 985
snapper_cl15 MACH_SNAPPER_CL15 SNAPPER_CL15 986
+pgigim MACH_PGIGIM PGIGIM 987
+ptx9160p2 MACH_PTX9160P2 PTX9160P2 988
+dcore1 MACH_DCORE1 DCORE1 989
+victorpxa MACH_VICTORPXA VICTORPXA 990
+mx2dtb MACH_MX2DTB MX2DTB 991
+pxa_irex_er0100 MACH_PXA_IREX_ER0100 PXA_IREX_ER0100 992
omap_palmz71 MACH_OMAP_PALMZ71 OMAP_PALMZ71 993
+bartec_deg MACH_BARTEC_DEG BARTEC_DEG 994
+hw50251 MACH_HW50251 HW50251 995
+ibox MACH_IBOX IBOX 996
+atlaslh7a404 MACH_ATLASLH7A404 ATLASLH7A404 997
+pt2026 MACH_PT2026 PT2026 998
+htcalpine MACH_HTCALPINE HTCALPINE 999
+bartec_vtu MACH_BARTEC_VTU BARTEC_VTU 1000
+vcoreii MACH_VCOREII VCOREII 1001
+pdnb3 MACH_PDNB3 PDNB3 1002
+htcbeetles MACH_HTCBEETLES HTCBEETLES 1003
+s3c6400 MACH_S3C6400 S3C6400 1004
+s3c2443 MACH_S3C2443 S3C2443 1005
+omap_ldk MACH_OMAP_LDK OMAP_LDK 1006
+smdk2460 MACH_SMDK2460 SMDK2460 1007
+smdk2440 MACH_SMDK2440 SMDK2440 1008
smdk2412 MACH_SMDK2412 SMDK2412 1009
+webbox MACH_WEBBOX WEBBOX 1010
+cwwndp MACH_CWWNDP CWWNDP 1011
+i839 MACH_DRAGON DRAGON 1012
+opendo_cpu_board MACH_OPENDO_CPU_BOARD OPENDO_CPU_BOARD 1013
+ccm2200 MACH_CCM2200 CCM2200 1014
+etwarm MACH_ETWARM ETWARM 1015
+m93030 MACH_M93030 M93030 1016
+cc7u MACH_CC7U CC7U 1017
+mtt_ranger MACH_MTT_RANGER MTT_RANGER 1018
+nexus MACH_NEXUS NEXUS 1019
+desman MACH_DESMAN DESMAN 1020
+bkde303 MACH_BKDE303 BKDE303 1021
smdk2413 MACH_SMDK2413 SMDK2413 1022
+aml_m7200 MACH_AML_M7200 AML_M7200 1023
aml_m5900 MACH_AML_M5900 AML_M5900 1024
+sg640 MACH_SG640 SG640 1025
+edg79524 MACH_EDG79524 EDG79524 1026
+ai2410 MACH_AI2410 AI2410 1027
+ixp465 MACH_IXP465 IXP465 1028
balloon3 MACH_BALLOON3 BALLOON3 1029
+heins MACH_HEINS HEINS 1030
+mpluseva MACH_MPLUSEVA MPLUSEVA 1031
+rt042 MACH_RT042 RT042 1032
+cwiem MACH_CWIEM CWIEM 1033
+cm_x270 MACH_CM_X270 CM_X270 1034
+cm_x255 MACH_CM_X255 CM_X255 1035
+esh_at91 MACH_ESH_AT91 ESH_AT91 1036
+sandgate3 MACH_SANDGATE3 SANDGATE3 1037
+primo MACH_PRIMO PRIMO 1038
+gemstone MACH_GEMSTONE GEMSTONE 1039
+pronghorn_metro MACH_PRONGHORNMETRO PRONGHORNMETRO 1040
+sidewinder MACH_SIDEWINDER SIDEWINDER 1041
+picomod1 MACH_PICOMOD1 PICOMOD1 1042
+sg590 MACH_SG590 SG590 1043
+akai9307 MACH_AKAI9307 AKAI9307 1044
+fontaine MACH_FONTAINE FONTAINE 1045
+wombat MACH_WOMBAT WOMBAT 1046
+acq300 MACH_ACQ300 ACQ300 1047
+mod272 MACH_MOD_270 MOD_270 1048
+vmc_vc0820 MACH_VC0820 VC0820 1049
+ani_aim MACH_ANI_AIM ANI_AIM 1050
+jellyfish MACH_JELLYFISH JELLYFISH 1051
+amanita MACH_AMANITA AMANITA 1052
+vlink MACH_VLINK VLINK 1053
+dexflex MACH_DEXFLEX DEXFLEX 1054
+eigen_ttq MACH_EIGEN_TTQ EIGEN_TTQ 1055
+arcom_titan MACH_ARCOM_TITAN ARCOM_TITAN 1056
+tabla MACH_TABLA TABLA 1057
+mdirac3 MACH_MDIRAC3 MDIRAC3 1058
+mrhfbp2 MACH_MRHFBP2 MRHFBP2 1059
+at91rm9200rb MACH_AT91RM9200RB AT91RM9200RB 1060
+ani_apm MACH_ANI_APM ANI_APM 1061
+ella1 MACH_ELLA1 ELLA1 1062
+inhand_pxa27x MACH_INHAND_PXA27X INHAND_PXA27X 1063
+inhand_pxa25x MACH_INHAND_PXA25X INHAND_PXA25X 1064
+empos_xm MACH_EMPOS_XM EMPOS_XM 1065
+empos MACH_EMPOS EMPOS 1066
+empos_tiny MACH_EMPOS_TINY EMPOS_TINY 1067
+empos_sm MACH_EMPOS_SM EMPOS_SM 1068
+egret MACH_EGRET EGRET 1069
+ostrich MACH_OSTRICH OSTRICH 1070
+n50 MACH_N50 N50 1071
ecbat91 MACH_ECBAT91 ECBAT91 1072
+stareast MACH_STAREAST STAREAST 1073
+dspg_dw MACH_DSPG_DW DSPG_DW 1074
onearm MACH_ONEARM ONEARM 1075
+mrg110_6 MACH_MRG110_6 MRG110_6 1076
+wrt300nv2 MACH_WRT300NV2 WRT300NV2 1077
+xm_bulverde MACH_XM_BULVERDE XM_BULVERDE 1078
+msm6100 MACH_MSM6100 MSM6100 1079
+eti_b1 MACH_ETI_B1 ETI_B1 1080
+za9l_series MACH_ZILOG_ZA9L ZILOG_ZA9L 1081
+bit2440 MACH_BIT2440 BIT2440 1082
+nbi MACH_NBI NBI 1083
smdk2443 MACH_SMDK2443 SMDK2443 1084
+vdavinci MACH_VDAVINCI VDAVINCI 1085
+atc6 MACH_ATC6 ATC6 1086
+multmdw MACH_MULTMDW MULTMDW 1087
+mba2440 MACH_MBA2440 MBA2440 1088
+ecsd MACH_ECSD ECSD 1089
+palmz31 MACH_PALMZ31 PALMZ31 1090
fsg MACH_FSG FSG 1091
+razor101 MACH_RAZOR101 RAZOR101 1092
+opera_tdm MACH_OPERA_TDM OPERA_TDM 1093
+comcerto MACH_COMCERTO COMCERTO 1094
+tb0319 MACH_TB0319 TB0319 1095
+kws8000 MACH_KWS8000 KWS8000 1096
+b2 MACH_B2 B2 1097
+lcl54 MACH_LCL54 LCL54 1098
at91sam9260ek MACH_AT91SAM9260EK AT91SAM9260EK 1099
glantank MACH_GLANTANK GLANTANK 1100
n2100 MACH_N2100 N2100 1101
+n4100 MACH_N4100 N4100 1102
+rsc4 MACH_VERTICAL_RSC4 VERTICAL_RSC4 1103
+sg8100 MACH_SG8100 SG8100 1104
+im42xx MACH_IM42XX IM42XX 1105
+ftxx MACH_FTXX FTXX 1106
+lwfusion MACH_LWFUSION LWFUSION 1107
qt2410 MACH_QT2410 QT2410 1108
kixrp435 MACH_KIXRP435 KIXRP435 1109
+ccw9c MACH_CCW9C CCW9C 1110
+dabhs MACH_DABHS DABHS 1111
+gzmx MACH_GZMX GZMX 1112
+ipnw100ap MACH_IPNW100AP IPNW100AP 1113
cc9p9360dev MACH_CC9P9360DEV CC9P9360DEV 1114
+cc9p9750dev MACH_CC9P9750DEV CC9P9750DEV 1115
+cc9p9360val MACH_CC9P9360VAL CC9P9360VAL 1116
+cc9p9750val MACH_CC9P9750VAL CC9P9750VAL 1117
+nx70v MACH_NX70V NX70V 1118
+at91rm9200df MACH_AT91RM9200DF AT91RM9200DF 1119
+se_pilot2 MACH_SE_PILOT2 SE_PILOT2 1120
+mtcn_t800 MACH_MTCN_T800 MTCN_T800 1121
+vcmx212 MACH_VCMX212 VCMX212 1122
+lynx MACH_LYNX LYNX 1123
+at91sam9260id MACH_AT91SAM9260ID AT91SAM9260ID 1124
+hw86052 MACH_HW86052 HW86052 1125
+pilz_pmi3 MACH_PILZ_PMI3 PILZ_PMI3 1126
edb9302a MACH_EDB9302A EDB9302A 1127
edb9307a MACH_EDB9307A EDB9307A 1128
+ct_dfs MACH_CT_DFS CT_DFS 1129
+pilz_pmi4 MACH_PILZ_PMI4 PILZ_PMI4 1130
+xceednp_ixp MACH_XCEEDNP_IXP XCEEDNP_IXP 1131
+smdk2442b MACH_SMDK2442B SMDK2442B 1132
+xnode MACH_XNODE XNODE 1133
+aidx270 MACH_AIDX270 AIDX270 1134
+rema MACH_REMA REMA 1135
+bps1000 MACH_BPS1000 BPS1000 1136
+hw90350 MACH_HW90350 HW90350 1137
omap_3430sdp MACH_OMAP_3430SDP OMAP_3430SDP 1138
+bluetouch MACH_BLUETOUCH BLUETOUCH 1139
vstms MACH_VSTMS VSTMS 1140
+xsbase270 MACH_XSBASE270 XSBASE270 1141
+at91sam9260ek_cn MACH_AT91SAM9260EK_CN AT91SAM9260EK_CN 1142
+adsturboxb MACH_ADSTURBOXB ADSTURBOXB 1143
+oti4110 MACH_OTI4110 OTI4110 1144
+hme_pxa MACH_HME_PXA HME_PXA 1145
+deisterdca MACH_DEISTERDCA DEISTERDCA 1146
+ces_ssem2 MACH_CES_SSEM2 CES_SSEM2 1147
+ces_mtr MACH_CES_MTR CES_MTR 1148
+tds_avng_sbc MACH_TDS_AVNG_SBC TDS_AVNG_SBC 1149
+everest MACH_EVEREST EVEREST 1150
+pnx4010 MACH_PNX4010 PNX4010 1151
+oxnas MACH_OXNAS OXNAS 1152
+fiori MACH_FIORI FIORI 1153
+ml1200 MACH_ML1200 ML1200 1154
+pecos MACH_PECOS PECOS 1155
+nb2xxx MACH_NB2XXX NB2XXX 1156
+hw6900 MACH_HW6900 HW6900 1157
+cdcs_quoll MACH_CDCS_QUOLL CDCS_QUOLL 1158
+quicksilver MACH_QUICKSILVER QUICKSILVER 1159
+uplat926 MACH_UPLAT926 UPLAT926 1160
+dep2410_dep2410 MACH_DEP2410_THOMAS DEP2410_THOMAS 1161
+dtk2410 MACH_DTK2410 DTK2410 1162
+chili MACH_CHILI CHILI 1163
+demeter MACH_DEMETER DEMETER 1164
+dionysus MACH_DIONYSUS DIONYSUS 1165
+as352x MACH_AS352X AS352X 1166
+service MACH_SERVICE SERVICE 1167
+cs_e9301 MACH_CS_E9301 CS_E9301 1168
micro9m MACH_MICRO9M MICRO9M 1169
+ia_mospck MACH_IA_MOSPCK IA_MOSPCK 1170
+ql201b MACH_QL201B QL201B 1171
+bbm MACH_BBM BBM 1174
+exxx MACH_EXXX EXXX 1175
+wma11b MACH_WMA11B WMA11B 1176
+pelco_atlas MACH_PELCO_ATLAS PELCO_ATLAS 1177
+g500 MACH_G500 G500 1178
bug MACH_BUG BUG 1179
+mx33ads MACH_MX33ADS MX33ADS 1180
+chub MACH_CHUB CHUB 1181
+neo1973_gta01 MACH_NEO1973_GTA01 NEO1973_GTA01 1182
+w90n740 MACH_W90N740 W90N740 1183
+medallion_sa2410 MACH_MEDALLION_SA2410 MEDALLION_SA2410 1184
+ia_cpu_9200_2 MACH_IA_CPU_9200_2 IA_CPU_9200_2 1185
+dimmrm9200 MACH_DIMMRM9200 DIMMRM9200 1186
+pm9261 MACH_PM9261 PM9261 1187
+ml7304 MACH_ML7304 ML7304 1189
+ucp250 MACH_UCP250 UCP250 1190
+intboard MACH_INTBOARD INTBOARD 1191
+gulfstream MACH_GULFSTREAM GULFSTREAM 1192
+labquest MACH_LABQUEST LABQUEST 1193
+vcmx313 MACH_VCMX313 VCMX313 1194
+urg200 MACH_URG200 URG200 1195
+cpux255lcdnet MACH_CPUX255LCDNET CPUX255LCDNET 1196
+netdcu9 MACH_NETDCU9 NETDCU9 1197
+netdcu10 MACH_NETDCU10 NETDCU10 1198
+dspg_dga MACH_DSPG_DGA DSPG_DGA 1199
+dspg_dvw MACH_DSPG_DVW DSPG_DVW 1200
+solos MACH_SOLOS SOLOS 1201
at91sam9263ek MACH_AT91SAM9263EK AT91SAM9263EK 1202
+osstbox MACH_OSSTBOX OSSTBOX 1203
+kbat9261 MACH_KBAT9261 KBAT9261 1204
+ct1100 MACH_CT1100 CT1100 1205
+akcppxa MACH_AKCPPXA AKCPPXA 1206
+ochaya1020 MACH_OCHAYA1020 OCHAYA1020 1207
+hitrack MACH_HITRACK HITRACK 1208
+syme1 MACH_SYME1 SYME1 1209
+syhl1 MACH_SYHL1 SYHL1 1210
+empca400 MACH_EMPCA400 EMPCA400 1211
em7210 MACH_EM7210 EM7210 1212
+htchermes MACH_HTCHERMES HTCHERMES 1213
+eti_c1 MACH_ETI_C1 ETI_C1 1214
+ac100 MACH_AC100 AC100 1216
+sneetch MACH_SNEETCH SNEETCH 1217
+studentmate MACH_STUDENTMATE STUDENTMATE 1218
+zir2410 MACH_ZIR2410 ZIR2410 1219
+zir2413 MACH_ZIR2413 ZIR2413 1220
+dlonip3 MACH_DLONIP3 DLONIP3 1221
+instream MACH_INSTREAM INSTREAM 1222
+ambarella MACH_AMBARELLA AMBARELLA 1223
+nevis MACH_NEVIS NEVIS 1224
+htc_trinity MACH_HTC_TRINITY HTC_TRINITY 1225
+ql202b MACH_QL202B QL202B 1226
vpac270 MACH_VPAC270 VPAC270 1227
+rd129 MACH_RD129 RD129 1228
+htcwizard MACH_HTCWIZARD HTCWIZARD 1229
treo680 MACH_TREO680 TREO680 1230
+tecon_tmezon MACH_TECON_TMEZON TECON_TMEZON 1231
zylonite MACH_ZYLONITE ZYLONITE 1233
+gene1270 MACH_GENE1270 GENE1270 1234
+zir2412 MACH_ZIR2412 ZIR2412 1235
mx31lite MACH_MX31LITE MX31LITE 1236
+t700wx MACH_T700WX T700WX 1237
+vf100 MACH_VF100 VF100 1238
+nsb2 MACH_NSB2 NSB2 1239
+nxhmi_bb MACH_NXHMI_BB NXHMI_BB 1240
+nxhmi_re MACH_NXHMI_RE NXHMI_RE 1241
+n4100pro MACH_N4100PRO N4100PRO 1242
+sam9260 MACH_SAM9260 SAM9260 1243
+omap_treo600 MACH_OMAP_TREO600 OMAP_TREO600 1244
+indy2410 MACH_INDY2410 INDY2410 1245
+nelt_a MACH_NELT_A NELT_A 1246
+n311 MACH_N311 N311 1248
+at91sam9260vgk MACH_AT91SAM9260VGK AT91SAM9260VGK 1249
+at91leppe MACH_AT91LEPPE AT91LEPPE 1250
+at91lepccn MACH_AT91LEPCCN AT91LEPCCN 1251
+apc7100 MACH_APC7100 APC7100 1252
+stargazer MACH_STARGAZER STARGAZER 1253
+sonata MACH_SONATA SONATA 1254
+schmoogie MACH_SCHMOOGIE SCHMOOGIE 1255
+aztool MACH_AZTOOL AZTOOL 1256
mioa701 MACH_MIOA701 MIOA701 1257
+sxni9260 MACH_SXNI9260 SXNI9260 1258
+mxc27520evb MACH_MXC27520EVB MXC27520EVB 1259
armadillo5x0 MACH_ARMADILLO5X0 ARMADILLO5X0 1260
+mb9260 MACH_MB9260 MB9260 1261
+mb9263 MACH_MB9263 MB9263 1262
+ipac9302 MACH_IPAC9302 IPAC9302 1263
cc9p9360js MACH_CC9P9360JS CC9P9360JS 1264
+gallium MACH_GALLIUM GALLIUM 1265
+msc2410 MACH_MSC2410 MSC2410 1266
+ghi270 MACH_GHI270 GHI270 1267
+davinci_leonardo MACH_DAVINCI_LEONARDO DAVINCI_LEONARDO 1268
+oiab MACH_OIAB OIAB 1269
smdk6400 MACH_SMDK6400 SMDK6400 1270
nokia_n800 MACH_NOKIA_N800 NOKIA_N800 1271
+greenphone MACH_GREENPHONE GREENPHONE 1272
+compex42x MACH_COMPEXWP18 COMPEXWP18 1273
+xmate MACH_XMATE XMATE 1274
+energizer MACH_ENERGIZER ENERGIZER 1275
+ime1 MACH_IME1 IME1 1276
+sweda_tms MACH_SWEDATMS SWEDATMS 1277
+ntnp435c MACH_NTNP435C NTNP435C 1278
+spectro2 MACH_SPECTRO2 SPECTRO2 1279
+h6039 MACH_H6039 H6039 1280
ep80219 MACH_EP80219 EP80219 1281
+samoa_ii MACH_SAMOA_II SAMOA_II 1282
+cwmxl MACH_CWMXL CWMXL 1283
+as9200 MACH_AS9200 AS9200 1284
+sfx1149 MACH_SFX1149 SFX1149 1285
+navi010 MACH_NAVI010 NAVI010 1286
+multmdp MACH_MULTMDP MULTMDP 1287
+scb9520 MACH_SCB9520 SCB9520 1288
+htcathena MACH_HTCATHENA HTCATHENA 1289
+xp179 MACH_XP179 XP179 1290
+h4300 MACH_H4300 H4300 1291
goramo_mlr MACH_GORAMO_MLR GORAMO_MLR 1292
+mxc30020evb MACH_MXC30020EVB MXC30020EVB 1293
+adsbitsyg5 MACH_ADSBITSYG5 ADSBITSYG5 1294
+adsportalplus MACH_ADSPORTALPLUS ADSPORTALPLUS 1295
+mmsp2plus MACH_MMSP2PLUS MMSP2PLUS 1296
em_x270 MACH_EM_X270 EM_X270 1297
+tpp302 MACH_TPP302 TPP302 1298
+tpp104 MACH_TPM104 TPM104 1299
+tpm102 MACH_TPM102 TPM102 1300
+tpm109 MACH_TPM109 TPM109 1301
+fbxo1 MACH_FBXO1 FBXO1 1302
+hxd8 MACH_HXD8 HXD8 1303
neo1973_gta02 MACH_NEO1973_GTA02 NEO1973_GTA02 1304
+emtest MACH_EMTEST EMTEST 1305
+ad6900 MACH_AD6900 AD6900 1306
+europa MACH_EUROPA EUROPA 1307
+metroconnect MACH_METROCONNECT METROCONNECT 1308
+ez_s2410 MACH_EZ_S2410 EZ_S2410 1309
+ez_s2440 MACH_EZ_S2440 EZ_S2440 1310
+ez_ep9312 MACH_EZ_EP9312 EZ_EP9312 1311
+ez_ep9315 MACH_EZ_EP9315 EZ_EP9315 1312
+ez_x7 MACH_EZ_X7 EZ_X7 1313
+godotdb MACH_GODOTDB GODOTDB 1314
+mistral MACH_MISTRAL MISTRAL 1315
+msm MACH_MSM MSM 1316
+ct5910 MACH_CT5910 CT5910 1317
+ct5912 MACH_CT5912 CT5912 1318
+argonst_mp MACH_HYNET_INE HYNET_INE 1319
+hynet_app MACH_HYNET_APP HYNET_APP 1320
+msm7200 MACH_MSM7200 MSM7200 1321
+msm7600 MACH_MSM7600 MSM7600 1322
+ceb255 MACH_CEB255 CEB255 1323
+ciel MACH_CIEL CIEL 1324
+slm5650 MACH_SLM5650 SLM5650 1325
at91sam9rlek MACH_AT91SAM9RLEK AT91SAM9RLEK 1326
+comtech_router MACH_COMTECH_ROUTER COMTECH_ROUTER 1327
+sbc2410x MACH_SBC2410X SBC2410X 1328
+at4x0bd MACH_AT4X0BD AT4X0BD 1329
+cbifr MACH_CBIFR CBIFR 1330
+arcom_quantum MACH_ARCOM_QUANTUM ARCOM_QUANTUM 1331
+matrix520 MACH_MATRIX520 MATRIX520 1332
+matrix510 MACH_MATRIX510 MATRIX510 1333
+matrix500 MACH_MATRIX500 MATRIX500 1334
+m501 MACH_M501 M501 1335
+aaeon1270 MACH_AAEON1270 AAEON1270 1336
+matrix500ev MACH_MATRIX500EV MATRIX500EV 1337
+pac500 MACH_PAC500 PAC500 1338
+pnx8181 MACH_PNX8181 PNX8181 1339
colibri320 MACH_COLIBRI320 COLIBRI320 1340
+aztoolbb MACH_AZTOOLBB AZTOOLBB 1341
+aztoolg2 MACH_AZTOOLG2 AZTOOLG2 1342
+dvlhost MACH_DVLHOST DVLHOST 1343
+zir9200 MACH_ZIR9200 ZIR9200 1344
+zir9260 MACH_ZIR9260 ZIR9260 1345
+cocopah MACH_COCOPAH COCOPAH 1346
+nds MACH_NDS NDS 1347
+rosencrantz MACH_ROSENCRANTZ ROSENCRANTZ 1348
+fttx_odsc MACH_FTTX_ODSC FTTX_ODSC 1349
+classe_r6904 MACH_CLASSE_R6904 CLASSE_R6904 1350
cam60 MACH_CAM60 CAM60 1351
+mxc30031ads MACH_MXC30031ADS MXC30031ADS 1352
+datacall MACH_DATACALL DATACALL 1353
at91eb01 MACH_AT91EB01 AT91EB01 1354
+rty MACH_RTY RTY 1355
+dwl2100 MACH_DWL2100 DWL2100 1356
+vinsi MACH_VINSI VINSI 1357
db88f5281 MACH_DB88F5281 DB88F5281 1358
csb726 MACH_CSB726 CSB726 1359
+tik27 MACH_TIK27 TIK27 1360
+mx_uc7420 MACH_MX_UC7420 MX_UC7420 1361
+rirm3 MACH_RIRM3 RIRM3 1362
+pelco_odyssey MACH_PELCO_ODYSSEY PELCO_ODYSSEY 1363
+adx_abox MACH_ADX_ABOX ADX_ABOX 1365
+adx_tpid MACH_ADX_TPID ADX_TPID 1366
+minicheck MACH_MINICHECK MINICHECK 1367
+idam MACH_IDAM IDAM 1368
+mario_mx MACH_MARIO_MX MARIO_MX 1369
+vi1888 MACH_VI1888 VI1888 1370
+zr4230 MACH_ZR4230 ZR4230 1371
+t1_ix_blue MACH_T1_IX_BLUE T1_IX_BLUE 1372
+syhq2 MACH_SYHQ2 SYHQ2 1373
+computime_r3 MACH_COMPUTIME_R3 COMPUTIME_R3 1374
+oratis MACH_ORATIS ORATIS 1375
+mikko MACH_MIKKO MIKKO 1376
+holon MACH_HOLON HOLON 1377
+olip8 MACH_OLIP8 OLIP8 1378
+ghi270hg MACH_GHI270HG GHI270HG 1379
davinci_dm6467_evm MACH_DAVINCI_DM6467_EVM DAVINCI_DM6467_EVM 1380
davinci_dm355_evm MACH_DAVINCI_DM355_EVM DAVINCI_DM355_EVM 1381
+blackriver MACH_BLACKRIVER BLACKRIVER 1383
+sandgate_wp MACH_SANDGATEWP SANDGATEWP 1384
+cdotbwsg MACH_CDOTBWSG CDOTBWSG 1385
+quark963 MACH_QUARK963 QUARK963 1386
+csb735 MACH_CSB735 CSB735 1387
littleton MACH_LITTLETON LITTLETON 1388
+mio_p550 MACH_MIO_P550 MIO_P550 1389
+motion2440 MACH_MOTION2440 MOTION2440 1390
+imm500 MACH_IMM500 IMM500 1391
+homematic MACH_HOMEMATIC HOMEMATIC 1392
+ermine MACH_ERMINE ERMINE 1393
+kb9202b MACH_KB9202B KB9202B 1394
+hs1xx MACH_HS1XX HS1XX 1395
+studentmate2440 MACH_STUDENTMATE2440 STUDENTMATE2440 1396
+arvoo_l1_z1 MACH_ARVOO_L1_Z1 ARVOO_L1_Z1 1397
+dep2410k MACH_DEP2410K DEP2410K 1398
+xxsvideo MACH_XXSVIDEO XXSVIDEO 1399
+im4004 MACH_IM4004 IM4004 1400
+ochaya1050 MACH_OCHAYA1050 OCHAYA1050 1401
+lep9261 MACH_LEP9261 LEP9261 1402
+svenmeb MACH_SVENMEB SVENMEB 1403
+fortunet2ne MACH_FORTUNET2NE FORTUNET2NE 1404
+nxhx MACH_NXHX NXHX 1406
realview_pb11mp MACH_REALVIEW_PB11MP REALVIEW_PB11MP 1407
+ids500 MACH_IDS500 IDS500 1408
+ors_n725 MACH_ORS_N725 ORS_N725 1409
+hsdarm MACH_HSDARM HSDARM 1410
+sha_pon003 MACH_SHA_PON003 SHA_PON003 1411
+sha_pon004 MACH_SHA_PON004 SHA_PON004 1412
+sha_pon007 MACH_SHA_PON007 SHA_PON007 1413
+sha_pon011 MACH_SHA_PON011 SHA_PON011 1414
+h6042 MACH_H6042 H6042 1415
+h6043 MACH_H6043 H6043 1416
+looxc550 MACH_LOOXC550 LOOXC550 1417
+cnty_titan MACH_CNTY_TITAN CNTY_TITAN 1418
+app3xx MACH_APP3XX APP3XX 1419
+sideoatsgrama MACH_SIDEOATSGRAMA SIDEOATSGRAMA 1420
+treo700p MACH_TREO700P TREO700P 1421
+treo700w MACH_TREO700W TREO700W 1422
+treo750 MACH_TREO750 TREO750 1423
+treo755p MACH_TREO755P TREO755P 1424
+ezreganut9200 MACH_EZREGANUT9200 EZREGANUT9200 1425
+sarge MACH_SARGE SARGE 1426
+a696 MACH_A696 A696 1427
+turtle1916 MACH_TURTLE TURTLE 1428
mx27_3ds MACH_MX27_3DS MX27_3DS 1430
+bishop MACH_BISHOP BISHOP 1431
+pxx MACH_PXX PXX 1432
+redwood MACH_REDWOOD REDWOOD 1433
+omap_2430dlp MACH_OMAP_2430DLP OMAP_2430DLP 1436
+omap_2430osk MACH_OMAP_2430OSK OMAP_2430OSK 1437
+sardine MACH_SARDINE SARDINE 1438
halibut MACH_HALIBUT HALIBUT 1439
trout MACH_TROUT TROUT 1440
+goldfish MACH_GOLDFISH GOLDFISH 1441
+gesbc2440 MACH_GESBC2440 GESBC2440 1442
+nomad MACH_NOMAD NOMAD 1443
+rosalind MACH_ROSALIND ROSALIND 1444
+cc9p9215 MACH_CC9P9215 CC9P9215 1445
+cc9p9210 MACH_CC9P9210 CC9P9210 1446
+cc9p9215js MACH_CC9P9215JS CC9P9215JS 1447
+cc9p9210js MACH_CC9P9210JS CC9P9210JS 1448
+nasffe MACH_NASFFE NASFFE 1449
+tn2x0bd MACH_TN2X0BD TN2X0BD 1450
+gwmpxa MACH_GWMPXA GWMPXA 1451
+exyplus MACH_EXYPLUS EXYPLUS 1452
+jadoo21 MACH_JADOO21 JADOO21 1453
+looxn560 MACH_LOOXN560 LOOXN560 1454
+bonsai MACH_BONSAI BONSAI 1455
+adsmilgato MACH_ADSMILGATO ADSMILGATO 1456
+gba MACH_GBA GBA 1457
+h6044 MACH_H6044 H6044 1458
+app MACH_APP APP 1459
tct_hammer MACH_TCT_HAMMER TCT_HAMMER 1460
herald MACH_HERALD HERALD 1461
+artemis MACH_ARTEMIS ARTEMIS 1462
+htctitan MACH_HTCTITAN HTCTITAN 1463
+qranium MACH_QRANIUM QRANIUM 1464
+adx_wsc2 MACH_ADX_WSC2 ADX_WSC2 1465
+adx_medcom MACH_ADX_MEDCOM ADX_MEDCOM 1466
+bboard MACH_BBOARD BBOARD 1467
+cambria MACH_CAMBRIA CAMBRIA 1468
+mt7xxx MACH_MT7XXX MT7XXX 1469
+matrix512 MACH_MATRIX512 MATRIX512 1470
+matrix522 MACH_MATRIX522 MATRIX522 1471
+ipac5010 MACH_IPAC5010 IPAC5010 1472
+sakura MACH_SAKURA SAKURA 1473
+grocx MACH_GROCX GROCX 1474
+pm9263 MACH_PM9263 PM9263 1475
sim_one MACH_SIM_ONE SIM_ONE 1476
+acq132 MACH_ACQ132 ACQ132 1477
+datr MACH_DATR DATR 1478
+actux1 MACH_ACTUX1 ACTUX1 1479
+actux2 MACH_ACTUX2 ACTUX2 1480
+actux3 MACH_ACTUX3 ACTUX3 1481
+flexit MACH_FLEXIT FLEXIT 1482
+bh2x0bd MACH_BH2X0BD BH2X0BD 1483
+atb2002 MACH_ATB2002 ATB2002 1484
+xenon MACH_XENON XENON 1485
+fm607 MACH_FM607 FM607 1486
+matrix514 MACH_MATRIX514 MATRIX514 1487
+matrix524 MACH_MATRIX524 MATRIX524 1488
+inpod MACH_INPOD INPOD 1489
jive MACH_JIVE JIVE 1490
+tll_mx21 MACH_TLL_MX21 TLL_MX21 1491
+sbc2800 MACH_SBC2800 SBC2800 1492
+cc7ucamry MACH_CC7UCAMRY CC7UCAMRY 1493
+ubisys_p9_sc15 MACH_UBISYS_P9_SC15 UBISYS_P9_SC15 1494
+ubisys_p9_ssc2d10 MACH_UBISYS_P9_SSC2D10 UBISYS_P9_SSC2D10 1495
+ubisys_p9_rcu3 MACH_UBISYS_P9_RCU3 UBISYS_P9_RCU3 1496
+aml_m8000 MACH_AML_M8000 AML_M8000 1497
+snapper_270 MACH_SNAPPER_270 SNAPPER_270 1498
+omap_bbx MACH_OMAP_BBX OMAP_BBX 1499
+ucn2410 MACH_UCN2410 UCN2410 1500
sam9_l9260 MACH_SAM9_L9260 SAM9_L9260 1501
+eti_c2 MACH_ETI_C2 ETI_C2 1502
+avalanche MACH_AVALANCHE AVALANCHE 1503
realview_pb1176 MACH_REALVIEW_PB1176 REALVIEW_PB1176 1504
+dp1500 MACH_DP1500 DP1500 1505
+apple_iphone MACH_APPLE_IPHONE APPLE_IPHONE 1506
yl9200 MACH_YL9200 YL9200 1507
rd88f5182 MACH_RD88F5182 RD88F5182 1508
kurobox_pro MACH_KUROBOX_PRO KUROBOX_PRO 1509
+se_poet MACH_SE_POET SE_POET 1510
mx31_3ds MACH_MX31_3DS MX31_3DS 1511
+r270 MACH_R270 R270 1512
+armour21 MACH_ARMOUR21 ARMOUR21 1513
+dt2 MACH_DT2 DT2 1514
+vt4 MACH_VT4 VT4 1515
+tyco320 MACH_TYCO320 TYCO320 1516
+adma MACH_ADMA ADMA 1517
+wp188 MACH_WP188 WP188 1518
+corsica MACH_CORSICA CORSICA 1519
+bigeye MACH_BIGEYE BIGEYE 1520
+tll5000 MACH_TLL5000 TLL5000 1522
+bebot MACH_BEBOT BEBOT 1523
qong MACH_QONG QONG 1524
+tcompact MACH_TCOMPACT TCOMPACT 1525
+puma5 MACH_PUMA5 PUMA5 1526
+elara MACH_ELARA ELARA 1527
+ellington MACH_ELLINGTON ELLINGTON 1528
+xda_atom MACH_XDA_ATOM XDA_ATOM 1529
+energizer2 MACH_ENERGIZER2 ENERGIZER2 1530
+odin MACH_ODIN ODIN 1531
+actux4 MACH_ACTUX4 ACTUX4 1532
+esl_omap MACH_ESL_OMAP ESL_OMAP 1533
omap2evm MACH_OMAP2EVM OMAP2EVM 1534
omap3evm MACH_OMAP3EVM OMAP3EVM 1535
+adx_pcu57 MACH_ADX_PCU57 ADX_PCU57 1536
+monaco MACH_MONACO MONACO 1537
+levante MACH_LEVANTE LEVANTE 1538
+tmxipx425 MACH_TMXIPX425 TMXIPX425 1539
+leep MACH_LEEP LEEP 1540
+raad MACH_RAAD RAAD 1541
dns323 MACH_DNS323 DNS323 1542
+ap1000 MACH_AP1000 AP1000 1543
+a9sam6432 MACH_A9SAM6432 A9SAM6432 1544
+shiny MACH_SHINY SHINY 1545
omap3_beagle MACH_OMAP3_BEAGLE OMAP3_BEAGLE 1546
+csr_bdb2 MACH_CSR_BDB2 CSR_BDB2 1547
nokia_n810 MACH_NOKIA_N810 NOKIA_N810 1548
+c270 MACH_C270 C270 1549
+sentry MACH_SENTRY SENTRY 1550
pcm038 MACH_PCM038 PCM038 1551
+anc300 MACH_ANC300 ANC300 1552
+htckaiser MACH_HTCKAISER HTCKAISER 1553
+sbat100 MACH_SBAT100 SBAT100 1554
+modunorm MACH_MODUNORM MODUNORM 1555
+pelos_twarm MACH_PELOS_TWARM PELOS_TWARM 1556
+flank MACH_FLANK FLANK 1557
+sirloin MACH_SIRLOIN SIRLOIN 1558
+brisket MACH_BRISKET BRISKET 1559
+chuck MACH_CHUCK CHUCK 1560
+otter MACH_OTTER OTTER 1561
+davinci_ldk MACH_DAVINCI_LDK DAVINCI_LDK 1562
+phreedom MACH_PHREEDOM PHREEDOM 1563
+sg310 MACH_SG310 SG310 1564
ts209 MACH_TS209 TS209 1565
at91cap9adk MACH_AT91CAP9ADK AT91CAP9ADK 1566
+tion9315 MACH_TION9315 TION9315 1567
+mast MACH_MAST MAST 1568
+pfw MACH_PFW PFW 1569
+yl_p2440 MACH_YL_P2440 YL_P2440 1570
+zsbc32 MACH_ZSBC32 ZSBC32 1571
+omap_pace2 MACH_OMAP_PACE2 OMAP_PACE2 1572
+imx_pace2 MACH_IMX_PACE2 IMX_PACE2 1573
mx31moboard MACH_MX31MOBOARD MX31MOBOARD 1574
+mx37_3ds MACH_MX37_3DS MX37_3DS 1575
+rcc MACH_RCC RCC 1576
+dmp MACH_ARM9 ARM9 1577
vision_ep9307 MACH_VISION_EP9307 VISION_EP9307 1578
+scly1000 MACH_SCLY1000 SCLY1000 1579
+fontel_ep MACH_FONTEL_EP FONTEL_EP 1580
+voiceblue3g MACH_VOICEBLUE3G VOICEBLUE3G 1581
+tt9200 MACH_TT9200 TT9200 1582
+digi2410 MACH_DIGI2410 DIGI2410 1583
terastation_pro2 MACH_TERASTATION_PRO2 TERASTATION_PRO2 1584
linkstation_pro MACH_LINKSTATION_PRO LINKSTATION_PRO 1585
+motorola_a780 MACH_MOTOROLA_A780 MOTOROLA_A780 1587
+motorola_e6 MACH_MOTOROLA_E6 MOTOROLA_E6 1588
+motorola_e2 MACH_MOTOROLA_E2 MOTOROLA_E2 1589
+motorola_e680 MACH_MOTOROLA_E680 MOTOROLA_E680 1590
+ur2410 MACH_UR2410 UR2410 1591
+tas9261 MACH_TAS9261 TAS9261 1592
+davinci_hermes_hd MACH_HERMES_HD HERMES_HD 1593
+davinci_perseo_hd MACH_PERSEO_HD PERSEO_HD 1594
+stargazer2 MACH_STARGAZER2 STARGAZER2 1595
e350 MACH_E350 E350 1596
+wpcm450 MACH_WPCM450 WPCM450 1597
+cartesio MACH_CARTESIO CARTESIO 1598
+toybox MACH_TOYBOX TOYBOX 1599
+tx27 MACH_TX27 TX27 1600
ts409 MACH_TS409 TS409 1601
+p300 MACH_P300 P300 1602
+xdacomet MACH_XDACOMET XDACOMET 1603
+dexflex2 MACH_DEXFLEX2 DEXFLEX2 1604
+ow MACH_OW OW 1605
+armebs3 MACH_ARMEBS3 ARMEBS3 1606
+u3 MACH_U3 U3 1607
+smdk2450 MACH_SMDK2450 SMDK2450 1608
rsi_ews MACH_RSI_EWS RSI_EWS 1609
+tnb MACH_TNB TNB 1610
+toepath MACH_TOEPATH TOEPATH 1611
+kb9263 MACH_KB9263 KB9263 1612
+mt7108 MACH_MT7108 MT7108 1613
+smtr2440 MACH_SMTR2440 SMTR2440 1614
+manao MACH_MANAO MANAO 1615
cm_x300 MACH_CM_X300 CM_X300 1616
+gulfstream_kp MACH_GULFSTREAM_KP GULFSTREAM_KP 1617
+lanreadyfn522 MACH_LANREADYFN522 LANREADYFN522 1618
+arma37 MACH_ARMA37 ARMA37 1619
+mendel MACH_MENDEL MENDEL 1620
+pelco_iliad MACH_PELCO_ILIAD PELCO_ILIAD 1621
+unit2p MACH_UNIT2P UNIT2P 1622
+inc20otter MACH_INC20OTTER INC20OTTER 1623
at91sam9g20ek MACH_AT91SAM9G20EK AT91SAM9G20EK 1624
+sc_ge2 MACH_STORCENTER STORCENTER 1625
smdk6410 MACH_SMDK6410 SMDK6410 1626
u300 MACH_U300 U300 1627
+u500 MACH_U500 U500 1628
+ds9260 MACH_DS9260 DS9260 1629
+riverrock MACH_RIVERROCK RIVERROCK 1630
+scibath MACH_SCIBATH SCIBATH 1631
+at91sam7se MACH_AT91SAM7SE512EK AT91SAM7SE512EK 1632
wrt350n_v2 MACH_WRT350N_V2 WRT350N_V2 1633
+multimedia MACH_MULTIMEDIA MULTIMEDIA 1634
+marvin MACH_MARVIN MARVIN 1635
+x500 MACH_X500 X500 1636
+awlug4lcu MACH_AWLUG4LCU AWLUG4LCU 1637
+palermoc MACH_PALERMOC PALERMOC 1638
omap_ldp MACH_OMAP_LDP OMAP_LDP 1639
+ip500 MACH_IP500 IP500 1640
+ase2 MACH_ASE2 ASE2 1642
+mx35evb MACH_MX35EVB MX35EVB 1643
+aml_m8050 MACH_AML_M8050 AML_M8050 1644
mx35_3ds MACH_MX35_3DS MX35_3DS 1645
+mars MACH_MARS MARS 1646
neuros_osd2 MACH_NEUROS_OSD2 NEUROS_OSD2 1647
+badger MACH_BADGER BADGER 1648
trizeps4wl MACH_TRIZEPS4WL TRIZEPS4WL 1649
+trizeps5 MACH_TRIZEPS5 TRIZEPS5 1650
+marlin MACH_MARLIN MARLIN 1651
ts78xx MACH_TS78XX TS78XX 1652
+hpipaq214 MACH_HPIPAQ214 HPIPAQ214 1653
+at572d940dcm MACH_AT572D940DCM AT572D940DCM 1654
+ne1board MACH_NE1BOARD NE1BOARD 1655
+zante MACH_ZANTE ZANTE 1656
sffsdr MACH_SFFSDR SFFSDR 1657
+tw2662 MACH_TW2662 TW2662 1658
+vf10xx MACH_VF10XX VF10XX 1659
+zoran43xx MACH_ZORAN43XX ZORAN43XX 1660
+sonix926 MACH_SONIX926 SONIX926 1661
+celestialsemi MACH_CELESTIALSEMI CELESTIALSEMI 1662
+cc9m2443js MACH_CC9M2443JS CC9M2443JS 1663
+tw5334 MACH_TW5334 TW5334 1664
+omap_htcartemis MACH_HTCARTEMIS HTCARTEMIS 1665
+nal_hlite MACH_NAL_HLITE NAL_HLITE 1666
+htcvogue MACH_HTCVOGUE HTCVOGUE 1667
+smartweb MACH_SMARTWEB SMARTWEB 1668
+mv86xx MACH_MV86XX MV86XX 1669
+mv87xx MACH_MV87XX MV87XX 1670
+songyoungho MACH_SONGYOUNGHO SONGYOUNGHO 1671
+younghotema MACH_YOUNGHOTEMA YOUNGHOTEMA 1672
pcm037 MACH_PCM037 PCM037 1673
+mmvp MACH_MMVP MMVP 1674
+mmap MACH_MMAP MMAP 1675
+ptid2410 MACH_PTID2410 PTID2410 1676
+james_926 MACH_JAMES_926 JAMES_926 1677
+fm6000 MACH_FM6000 FM6000 1678
db88f6281_bp MACH_DB88F6281_BP DB88F6281_BP 1680
rd88f6192_nas MACH_RD88F6192_NAS RD88F6192_NAS 1681
rd88f6281 MACH_RD88F6281 RD88F6281 1682
db78x00_bp MACH_DB78X00_BP DB78X00_BP 1683
smdk2416 MACH_SMDK2416 SMDK2416 1685
+oce_spider_si MACH_OCE_SPIDER_SI OCE_SPIDER_SI 1686
+oce_spider_sk MACH_OCE_SPIDER_SK OCE_SPIDER_SK 1687
+rovern6 MACH_ROVERN6 ROVERN6 1688
+pelco_evolution MACH_PELCO_EVOLUTION PELCO_EVOLUTION 1689
wbd111 MACH_WBD111 WBD111 1690
+elaracpe MACH_ELARACPE ELARACPE 1691
+mabv3 MACH_MABV3 MABV3 1692
mv2120 MACH_MV2120 MV2120 1693
+csb737 MACH_CSB737 CSB737 1695
mx51_3ds MACH_MX51_3DS MX51_3DS 1696
+g900 MACH_G900 G900 1697
+apf27 MACH_APF27 APF27 1698
+ggus2000 MACH_GGUS2000 GGUS2000 1699
+omap_2430_mimic MACH_OMAP_2430_MIMIC OMAP_2430_MIMIC 1700
imx27lite MACH_IMX27LITE IMX27LITE 1701
+almex MACH_ALMEX ALMEX 1702
+control MACH_CONTROL CONTROL 1703
+mba2410 MACH_MBA2410 MBA2410 1704
+volcano MACH_VOLCANO VOLCANO 1705
+zenith MACH_ZENITH ZENITH 1706
+muchip MACH_MUCHIP MUCHIP 1707
+magellan MACH_MAGELLAN MAGELLAN 1708
usb_a9260 MACH_USB_A9260 USB_A9260 1709
usb_a9263 MACH_USB_A9263 USB_A9263 1710
qil_a9260 MACH_QIL_A9260 QIL_A9260 1711
+cme9210 MACH_CME9210 CME9210 1712
+hczh4 MACH_HCZH4 HCZH4 1713
+spearbasic MACH_SPEARBASIC SPEARBASIC 1714
+dep2440 MACH_DEP2440 DEP2440 1715
+hdl_gxr MACH_HDL_GXR HDL_GXR 1716
+hdl_gt MACH_HDL_GT HDL_GT 1717
+hdl_4g MACH_HDL_4G HDL_4G 1718
+s3c6000 MACH_S3C6000 S3C6000 1719
+mmsp2_mdk MACH_MMSP2_MDK MMSP2_MDK 1720
+mpx220 MACH_MPX220 MPX220 1721
kzm_arm11_01 MACH_KZM_ARM11_01 KZM_ARM11_01 1722
+htc_polaris MACH_HTC_POLARIS HTC_POLARIS 1723
+htc_kaiser MACH_HTC_KAISER HTC_KAISER 1724
+lg_ks20 MACH_LG_KS20 LG_KS20 1725
+hhgps MACH_HHGPS HHGPS 1726
nokia_n810_wimax MACH_NOKIA_N810_WIMAX NOKIA_N810_WIMAX 1727
+insight MACH_INSIGHT INSIGHT 1728
sapphire MACH_SAPPHIRE SAPPHIRE 1729
+csb637xo MACH_CSB637XO CSB637XO 1730
+evisiong MACH_EVISIONG EVISIONG 1731
stmp37xx MACH_STMP37XX STMP37XX 1732
stmp378x MACH_STMP378X STMP378X 1733
+tnt MACH_TNT TNT 1734
+tbxt MACH_TBXT TBXT 1735
+playmate MACH_PLAYMATE PLAYMATE 1736
+pns10 MACH_PNS10 PNS10 1737
+eznavi MACH_EZNAVI EZNAVI 1738
+ps4000 MACH_PS4000 PS4000 1739
ezx_a780 MACH_EZX_A780 EZX_A780 1740
ezx_e680 MACH_EZX_E680 EZX_E680 1741
ezx_a1200 MACH_EZX_A1200 EZX_A1200 1742
ezx_e6 MACH_EZX_E6 EZX_E6 1743
ezx_e2 MACH_EZX_E2 EZX_E2 1744
ezx_a910 MACH_EZX_A910 EZX_A910 1745
+cwmx31 MACH_CWMX31 CWMX31 1746
+sl2312 MACH_SL2312 SL2312 1747
+blenny MACH_BLENNY BLENNY 1748
+ds107 MACH_DS107 DS107 1749
+dsx07 MACH_DSX07 DSX07 1750
+picocom1 MACH_PICOCOM1 PICOCOM1 1751
+lynx_wolverine MACH_LYNX_WOLVERINE LYNX_WOLVERINE 1752
+ubisys_p9_sc19 MACH_UBISYS_P9_SC19 UBISYS_P9_SC19 1753
+kratos_low MACH_KRATOS_LOW KRATOS_LOW 1754
+m700 MACH_M700 M700 1755
edmini_v2 MACH_EDMINI_V2 EDMINI_V2 1756
zipit2 MACH_ZIPIT2 ZIPIT2 1757
+hslfemtocell MACH_HSLFEMTOCELL HSLFEMTOCELL 1758
+daintree_at91 MACH_DAINTREE_AT91 DAINTREE_AT91 1759
+sg560usb MACH_SG560USB SG560USB 1760
omap3_pandora MACH_OMAP3_PANDORA OMAP3_PANDORA 1761
+usr8200 MACH_USR8200 USR8200 1762
+s1s65k MACH_S1S65K S1S65K 1763
+s2s65a MACH_S2S65A S2S65A 1764
+icore MACH_ICORE ICORE 1765
mss2 MACH_MSS2 MSS2 1766
+belmont MACH_BELMONT BELMONT 1767
+asusp525 MACH_ASUSP525 ASUSP525 1768
lb88rc8480 MACH_LB88RC8480 LB88RC8480 1769
+hipxa MACH_HIPXA HIPXA 1770
mx25_3ds MACH_MX25_3DS MX25_3DS 1771
+m800 MACH_M800 M800 1772
omap3530_lv_som MACH_OMAP3530_LV_SOM OMAP3530_LV_SOM 1773
+prima_evb MACH_PRIMA_EVB PRIMA_EVB 1774
+mx31bt1 MACH_MX31BT1 MX31BT1 1775
+atlas4_evb MACH_ATLAS4_EVB ATLAS4_EVB 1776
+mx31cicada MACH_MX31CICADA MX31CICADA 1777
+mi424wr MACH_MI424WR MI424WR 1778
+axs_ultrax MACH_AXS_ULTRAX AXS_ULTRAX 1779
+at572d940deb MACH_AT572D940DEB AT572D940DEB 1780
davinci_da830_evm MACH_DAVINCI_DA830_EVM DAVINCI_DA830_EVM 1781
+ep9302 MACH_EP9302 EP9302 1782
+at572d940hfek MACH_AT572D940HFEB AT572D940HFEB 1783
+cybook3 MACH_CYBOOK3 CYBOOK3 1784
+wdg002 MACH_WDG002 WDG002 1785
+sg560adsl MACH_SG560ADSL SG560ADSL 1786
+nextio_n2800_ica MACH_NEXTIO_N2800_ICA NEXTIO_N2800_ICA 1787
dove_db MACH_DOVE_DB DOVE_DB 1788
+vandihud MACH_VANDIHUD VANDIHUD 1790
+magx_e8 MACH_MAGX_E8 MAGX_E8 1791
+magx_z6 MACH_MAGX_Z6 MAGX_Z6 1792
+magx_v8 MACH_MAGX_V8 MAGX_V8 1793
+magx_u9 MACH_MAGX_U9 MAGX_U9 1794
+toughcf08 MACH_TOUGHCF08 TOUGHCF08 1795
+zw4400 MACH_ZW4400 ZW4400 1796
+marat91 MACH_MARAT91 MARAT91 1797
overo MACH_OVERO OVERO 1798
at2440evb MACH_AT2440EVB AT2440EVB 1799
neocore926 MACH_NEOCORE926 NEOCORE926 1800
wnr854t MACH_WNR854T WNR854T 1801
+imx27 MACH_IMX27 IMX27 1802
+moose_db MACH_MOOSE_DB MOOSE_DB 1803
+fab4 MACH_FAB4 FAB4 1804
+htcdiamond MACH_HTCDIAMOND HTCDIAMOND 1805
+fiona MACH_FIONA FIONA 1806
+mxc30030_x MACH_MXC30030_X MXC30030_X 1807
+bmp1000 MACH_BMP1000 BMP1000 1808
+logi9200 MACH_LOGI9200 LOGI9200 1809
+tqma31 MACH_TQMA31 TQMA31 1810
+ccw9p9215js MACH_CCW9P9215JS CCW9P9215JS 1811
rd88f5181l_ge MACH_RD88F5181L_GE RD88F5181L_GE 1812
+sifmain MACH_SIFMAIN SIFMAIN 1813
+sam9_l9261 MACH_SAM9_L9261 SAM9_L9261 1814
+cc9m2443 MACH_CC9M2443 CC9M2443 1815
+xaria300 MACH_XARIA300 XARIA300 1816
+it9200 MACH_IT9200 IT9200 1817
rd88f5181l_fxo MACH_RD88F5181L_FXO RD88F5181L_FXO 1818
+kriss_sensor MACH_KRISS_SENSOR KRISS_SENSOR 1819
+pilz_pmi5 MACH_PILZ_PMI5 PILZ_PMI5 1820
+jade MACH_JADE JADE 1821
+ks8695_softplc MACH_KS8695_SOFTPLC KS8695_SOFTPLC 1822
+gprisc3 MACH_GPRISC3 GPRISC3 1823
stamp9g20 MACH_STAMP9G20 STAMP9G20 1824
+smdk6430 MACH_SMDK6430 SMDK6430 1825
smdkc100 MACH_SMDKC100 SMDKC100 1826
tavorevb MACH_TAVOREVB TAVOREVB 1827
saar MACH_SAAR SAAR 1828
+deister_eyecam MACH_DEISTER_EYECAM DEISTER_EYECAM 1829
at91sam9m10g45ek MACH_AT91SAM9M10G45EK AT91SAM9M10G45EK 1830
+linkstation_produo MACH_LINKSTATION_PRODUO LINKSTATION_PRODUO 1831
+hit_b0 MACH_HIT_B0 HIT_B0 1832
+adx_rmu MACH_ADX_RMU ADX_RMU 1833
+xg_cpe_main MACH_XG_CPE_MAIN XG_CPE_MAIN 1834
+edb9407a MACH_EDB9407A EDB9407A 1835
+dtb9608 MACH_DTB9608 DTB9608 1836
+em104v1 MACH_EM104V1 EM104V1 1837
+demo MACH_DEMO DEMO 1838
+logi9260 MACH_LOGI9260 LOGI9260 1839
+mx31_exm32 MACH_MX31_EXM32 MX31_EXM32 1840
usb_a9g20 MACH_USB_A9G20 USB_A9G20 1841
+picproje2008 MACH_PICPROJE2008 PICPROJE2008 1842
+cs_e9315 MACH_CS_E9315 CS_E9315 1843
+qil_a9g20 MACH_QIL_A9G20 QIL_A9G20 1844
+sha_pon020 MACH_SHA_PON020 SHA_PON020 1845
+nad MACH_NAD NAD 1846
+sbc35_a9260 MACH_SBC35_A9260 SBC35_A9260 1847
+sbc35_a9g20 MACH_SBC35_A9G20 SBC35_A9G20 1848
+davinci_beginning MACH_DAVINCI_BEGINNING DAVINCI_BEGINNING 1849
+uwc MACH_UWC UWC 1850
mxlads MACH_MXLADS MXLADS 1851
+htcnike MACH_HTCNIKE HTCNIKE 1852
+deister_pxa270 MACH_DEISTER_PXA270 DEISTER_PXA270 1853
+cme9210js MACH_CME9210JS CME9210JS 1854
+cc9p9360 MACH_CC9P9360 CC9P9360 1855
+mocha MACH_MOCHA MOCHA 1856
+wapd170ag MACH_WAPD170AG WAPD170AG 1857
linkstation_mini MACH_LINKSTATION_MINI LINKSTATION_MINI 1858
afeb9260 MACH_AFEB9260 AFEB9260 1859
+w90x900 MACH_W90X900 W90X900 1860
+w90x700 MACH_W90X700 W90X700 1861
+kt300ip MACH_KT300IP KT300IP 1862
+kt300ip_g20 MACH_KT300IP_G20 KT300IP_G20 1863
+srcm MACH_SRCM SRCM 1864
+wlnx_9260 MACH_WLNX_9260 WLNX_9260 1865
+openmoko_gta03 MACH_OPENMOKO_GTA03 OPENMOKO_GTA03 1866
+osprey2 MACH_OSPREY2 OSPREY2 1867
+kbio9260 MACH_KBIO9260 KBIO9260 1868
+ginza MACH_GINZA GINZA 1869
+a636n MACH_A636N A636N 1870
imx27ipcam MACH_IMX27IPCAM IMX27IPCAM 1871
+nemoc MACH_NEMOC NEMOC 1872
+geneva MACH_GENEVA GENEVA 1873
+htcpharos MACH_HTCPHAROS HTCPHAROS 1874
+neonc MACH_NEONC NEONC 1875
+nas7100 MACH_NAS7100 NAS7100 1876
+teuphone MACH_TEUPHONE TEUPHONE 1877
+annax_eth2 MACH_ANNAX_ETH2 ANNAX_ETH2 1878
+csb733 MACH_CSB733 CSB733 1879
+bk3 MACH_BK3 BK3 1880
+omap_em32 MACH_OMAP_EM32 OMAP_EM32 1881
+et9261cp MACH_ET9261CP ET9261CP 1882
+jasperc MACH_JASPERC JASPERC 1883
+issi_arm9 MACH_ISSI_ARM9 ISSI_ARM9 1884
+ued MACH_UED UED 1885
+esiblade MACH_ESIBLADE ESIBLADE 1886
+eye02 MACH_EYE02 EYE02 1887
+imx27kbd MACH_IMX27KBD IMX27KBD 1888
+kixvp435 MACH_KIXVP435 KIXVP435 1890
+kixnp435 MACH_KIXNP435 KIXNP435 1891
+africa MACH_AFRICA AFRICA 1892
+nh233 MACH_NH233 NH233 1893
rd88f6183ap_ge MACH_RD88F6183AP_GE RD88F6183AP_GE 1894
+bcm4760 MACH_BCM4760 BCM4760 1895
+eddy_v2 MACH_EDDY_V2 EDDY_V2 1896
realview_pba8 MACH_REALVIEW_PBA8 REALVIEW_PBA8 1897
+hid_a7 MACH_HID_A7 HID_A7 1898
+hero MACH_HERO HERO 1899
+omap_poseidon MACH_OMAP_POSEIDON OMAP_POSEIDON 1900
realview_pbx MACH_REALVIEW_PBX REALVIEW_PBX 1901
micro9s MACH_MICRO9S MICRO9S 1902
+mako MACH_MAKO MAKO 1903
+xdaflame MACH_XDAFLAME XDAFLAME 1904
+phidget_sbc2 MACH_PHIDGET_SBC2 PHIDGET_SBC2 1905
+limestone MACH_LIMESTONE LIMESTONE 1906
+iprobe_c32 MACH_IPROBE_C32 IPROBE_C32 1907
rut100 MACH_RUT100 RUT100 1908
+asusp535 MACH_ASUSP535 ASUSP535 1909
+htcraphael MACH_HTCRAPHAEL HTCRAPHAEL 1910
+sygdg1 MACH_SYGDG1 SYGDG1 1911
+sygdg2 MACH_SYGDG2 SYGDG2 1912
+seoul MACH_SEOUL SEOUL 1913
+salerno MACH_SALERNO SALERNO 1914
+ucn_s3c64xx MACH_UCN_S3C64XX UCN_S3C64XX 1915
+msm7201a MACH_MSM7201A MSM7201A 1916
+lpr1 MACH_LPR1 LPR1 1917
+armadillo500fx MACH_ARMADILLO500FX ARMADILLO500FX 1918
g3evm MACH_G3EVM G3EVM 1919
+z3_dm355 MACH_Z3_DM355 Z3_DM355 1920
w90p910evb MACH_W90P910EVB W90P910EVB 1921
+w90p920evb MACH_W90P920EVB W90P920EVB 1922
w90p950evb MACH_W90P950EVB W90P950EVB 1923
w90n960evb MACH_W90N960EVB W90N960EVB 1924
+camhd MACH_CAMHD CAMHD 1925
+mvc100 MACH_MVC100 MVC100 1926
+electrum_200 MACH_ELECTRUM_200 ELECTRUM_200 1927
+htcjade MACH_HTCJADE HTCJADE 1928
+memphis MACH_MEMPHIS MEMPHIS 1929
+imx27sbc MACH_IMX27SBC IMX27SBC 1930
+lextar MACH_LEXTAR LEXTAR 1931
mv88f6281gtw_ge MACH_MV88F6281GTW_GE MV88F6281GTW_GE 1932
ncp MACH_NCP NCP 1933
+z32an_series MACH_Z32AN Z32AN 1934
+tmq_capd MACH_TMQ_CAPD TMQ_CAPD 1935
+omap3_wl MACH_OMAP3_WL OMAP3_WL 1936
+chumby MACH_CHUMBY CHUMBY 1937
+atsarm9 MACH_ATSARM9 ATSARM9 1938
davinci_dm365_evm MACH_DAVINCI_DM365_EVM DAVINCI_DM365_EVM 1939
+bahamas MACH_BAHAMAS BAHAMAS 1940
+das MACH_DAS DAS 1941
+minidas MACH_MINIDAS MINIDAS 1942
+vk1000 MACH_VK1000 VK1000 1943
centro MACH_CENTRO CENTRO 1944
+ctera_2bay MACH_CTERA_2BAY CTERA_2BAY 1945
+edgeconnect MACH_EDGECONNECT EDGECONNECT 1946
+nd27000 MACH_ND27000 ND27000 1947
+cobra MACH_GEMALTO_COBRA GEMALTO_COBRA 1948
+ingelabs_comet MACH_INGELABS_COMET INGELABS_COMET 1949
+pollux_wiz MACH_POLLUX_WIZ POLLUX_WIZ 1950
+blackstone MACH_BLACKSTONE BLACKSTONE 1951
+topaz MACH_TOPAZ TOPAZ 1952
+aixle MACH_AIXLE AIXLE 1953
+mw998 MACH_MW998 MW998 1954
nokia_rx51 MACH_NOKIA_RX51 NOKIA_RX51 1955
+vsc5605ev MACH_VSC5605EV VSC5605EV 1956
+nt98700dk MACH_NT98700DK NT98700DK 1957
+icontact MACH_ICONTACT ICONTACT 1958
+swarco_frcpu MACH_SWARCO_FRCPU SWARCO_FRCPU 1959
+swarco_scpu MACH_SWARCO_SCPU SWARCO_SCPU 1960
+bbox_p16 MACH_BBOX_P16 BBOX_P16 1961
+bstd MACH_BSTD BSTD 1962
+sbc2440ii MACH_SBC2440II SBC2440II 1963
+pcm034 MACH_PCM034 PCM034 1964
+neso MACH_NESO NESO 1965
+wlnx_9g20 MACH_WLNX_9G20 WLNX_9G20 1966
omap_zoom2 MACH_OMAP_ZOOM2 OMAP_ZOOM2 1967
+totemnova MACH_TOTEMNOVA TOTEMNOVA 1968
+c5000 MACH_C5000 C5000 1969
+unipo_at91sam9263 MACH_UNIPO_AT91SAM9263 UNIPO_AT91SAM9263 1970
+ethernut5 MACH_ETHERNUT5 ETHERNUT5 1971
+arm11 MACH_ARM11 ARM11 1972
cpuat9260 MACH_CPUAT9260 CPUAT9260 1973
+cpupxa255 MACH_CPUPXA255 CPUPXA255 1974
eukrea_cpuimx27 MACH_EUKREA_CPUIMX27 EUKREA_CPUIMX27 1975
+cheflux MACH_CHEFLUX CHEFLUX 1976
+eb_cpux9k2 MACH_EB_CPUX9K2 EB_CPUX9K2 1977
+opcotec MACH_OPCOTEC OPCOTEC 1978
+yt MACH_YT YT 1979
+motoq MACH_MOTOQ MOTOQ 1980
+bsb1 MACH_BSB1 BSB1 1981
acs5k MACH_ACS5K ACS5K 1982
+milan MACH_MILAN MILAN 1983
+quartzv2 MACH_QUARTZV2 QUARTZV2 1984
+rsvp MACH_RSVP RSVP 1985
+rmp200 MACH_RMP200 RMP200 1986
snapper_9260 MACH_SNAPPER_9260 SNAPPER_9260 1987
dsm320 MACH_DSM320 DSM320 1988
+adsgcm MACH_ADSGCM ADSGCM 1989
+ase2_400 MACH_ASE2_400 ASE2_400 1990
+pizza MACH_PIZZA PIZZA 1991
+spot_ngpl MACH_SPOT_NGPL SPOT_NGPL 1992
+armata MACH_ARMATA ARMATA 1993
exeda MACH_EXEDA EXEDA 1994
+mx31sf005 MACH_MX31SF005 MX31SF005 1995
+f5d8231_4_v2 MACH_F5D8231_4_V2 F5D8231_4_V2 1996
+q2440 MACH_Q2440 Q2440 1997
+qq2440 MACH_QQ2440 QQ2440 1998
mini2440 MACH_MINI2440 MINI2440 1999
colibri300 MACH_COLIBRI300 COLIBRI300 2000
+jades MACH_JADES JADES 2001
+spark MACH_SPARK SPARK 2002
+benzina MACH_BENZINA BENZINA 2003
+blaze MACH_BLAZE BLAZE 2004
linkstation_ls_hgl MACH_LINKSTATION_LS_HGL LINKSTATION_LS_HGL 2005
+htckovsky MACH_HTCKOVSKY HTCKOVSKY 2006
+sony_prs505 MACH_SONY_PRS505 SONY_PRS505 2007
+hanlin_v3 MACH_HANLIN_V3 HANLIN_V3 2008
+sapphira MACH_SAPPHIRA SAPPHIRA 2009
+dack_sda_01 MACH_DACK_SDA_01 DACK_SDA_01 2010
+armbox MACH_ARMBOX ARMBOX 2011
+harris_rvp MACH_HARRIS_RVP HARRIS_RVP 2012
+ribaldo MACH_RIBALDO RIBALDO 2013
+agora MACH_AGORA AGORA 2014
+omap3_mini MACH_OMAP3_MINI OMAP3_MINI 2015
+a9sam6432_b MACH_A9SAM6432_B A9SAM6432_B 2016
+usg2410 MACH_USG2410 USG2410 2017
+pc72052_i10_revb MACH_PC72052_I10_REVB PC72052_I10_REVB 2018
+mx35_exm32 MACH_MX35_EXM32 MX35_EXM32 2019
+topas910 MACH_TOPAS910 TOPAS910 2020
+hyena MACH_HYENA HYENA 2021
+pospax MACH_POSPAX POSPAX 2022
+hdl_gx MACH_HDL_GX HDL_GX 2023
+ctera_4bay MACH_CTERA_4BAY CTERA_4BAY 2024
+ctera_plug_c MACH_CTERA_PLUG_C CTERA_PLUG_C 2025
+crwea_plug_i MACH_CRWEA_PLUG_I CRWEA_PLUG_I 2026
+egauge2 MACH_EGAUGE2 EGAUGE2 2027
+didj MACH_DIDJ DIDJ 2028
+m_s3c2443 MACH_MEISTER MEISTER 2029
+htcblackstone MACH_HTCBLACKSTONE HTCBLACKSTONE 2030
cpuat9g20 MACH_CPUAT9G20 CPUAT9G20 2031
smdk6440 MACH_SMDK6440 SMDK6440 2032
+omap_35xx_mvp MACH_OMAP_35XX_MVP OMAP_35XX_MVP 2033
+ctera_plug_i MACH_CTERA_PLUG_I CTERA_PLUG_I 2034
+pvg610_100 MACH_PVG610 PVG610 2035
+hprw6815 MACH_HPRW6815 HPRW6815 2036
+omap3_oswald MACH_OMAP3_OSWALD OMAP3_OSWALD 2037
nas4220b MACH_NAS4220B NAS4220B 2038
+htcraphael_cdma MACH_HTCRAPHAEL_CDMA HTCRAPHAEL_CDMA 2039
+htcdiamond_cdma MACH_HTCDIAMOND_CDMA HTCDIAMOND_CDMA 2040
+scaler MACH_SCALER SCALER 2041
zylonite2 MACH_ZYLONITE2 ZYLONITE2 2042
aspenite MACH_ASPENITE ASPENITE 2043
+teton MACH_TETON TETON 2044
ttc_dkb MACH_TTC_DKB TTC_DKB 2045
+bishop2 MACH_BISHOP2 BISHOP2 2046
+ippv5 MACH_IPPV5 IPPV5 2047
+farm926 MACH_FARM926 FARM926 2048
+mmccpu MACH_MMCCPU MMCCPU 2049
+sgmsfl MACH_SGMSFL SGMSFL 2050
+tt8000 MACH_TT8000 TT8000 2051
+zrn4300lp MACH_ZRN4300LP ZRN4300LP 2052
+mptc MACH_MPTC MPTC 2053
+h6051 MACH_H6051 H6051 2054
+pvg610_101 MACH_PVG610_101 PVG610_101 2055
+stamp9261_pc_evb MACH_STAMP9261_PC_EVB STAMP9261_PC_EVB 2056
+pelco_odysseus MACH_PELCO_ODYSSEUS PELCO_ODYSSEUS 2057
+tny_a9260 MACH_TNY_A9260 TNY_A9260 2058
+tny_a9g20 MACH_TNY_A9G20 TNY_A9G20 2059
+aesop_mp2530f MACH_AESOP_MP2530F AESOP_MP2530F 2060
+dx900 MACH_DX900 DX900 2061
+cpodc2 MACH_CPODC2 CPODC2 2062
+tilt_8925 MACH_TILT_8925 TILT_8925 2063
+davinci_dm357_evm MACH_DAVINCI_DM357_EVM DAVINCI_DM357_EVM 2064
+swordfish MACH_SWORDFISH SWORDFISH 2065
+corvus MACH_CORVUS CORVUS 2066
+taurus MACH_TAURUS TAURUS 2067
+axm MACH_AXM AXM 2068
+axc MACH_AXC AXC 2069
+baby MACH_BABY BABY 2070
+mp200 MACH_MP200 MP200 2071
pcm043 MACH_PCM043 PCM043 2072
+hanlin_v3c MACH_HANLIN_V3C HANLIN_V3C 2073
+kbk9g20 MACH_KBK9G20 KBK9G20 2074
+adsturbog5 MACH_ADSTURBOG5 ADSTURBOG5 2075
+avenger_lite1 MACH_AVENGER_LITE1 AVENGER_LITE1 2076
+suc82x MACH_SUC SUC 2077
+at91sam7s256 MACH_AT91SAM7S256 AT91SAM7S256 2078
+mendoza MACH_MENDOZA MENDOZA 2079
+kira MACH_KIRA KIRA 2080
+mx1hbm MACH_MX1HBM MX1HBM 2081
+quatro43xx MACH_QUATRO43XX QUATRO43XX 2082
+quatro4230 MACH_QUATRO4230 QUATRO4230 2083
+nsb400 MACH_NSB400 NSB400 2084
+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
+socr MACH_SOCR SOCR 2090
+htcoxygen MACH_HTCOXYGEN HTCOXYGEN 2091
+heroc MACH_HEROC HEROC 2092
+zeno6800 MACH_ZENO6800 ZENO6800 2093
+sc2mcs MACH_SC2MCS SC2MCS 2094
+gene100 MACH_GENE100 GENE100 2095
+as353x MACH_AS353X AS353X 2096
sheevaplug MACH_SHEEVAPLUG SHEEVAPLUG 2097
+at91sam9g20 MACH_AT91SAM9G20 AT91SAM9G20 2098
+mv88f6192gtw_fe MACH_MV88F6192GTW_FE MV88F6192GTW_FE 2099
+cc9200 MACH_CC9200 CC9200 2100
+sm9200 MACH_SM9200 SM9200 2101
+tp9200 MACH_TP9200 TP9200 2102
+snapperdv MACH_SNAPPERDV SNAPPERDV 2103
avengers_lite MACH_AVENGERS_LITE AVENGERS_LITE 2104
+avengers_lite1 MACH_AVENGERS_LITE1 AVENGERS_LITE1 2105
+omap3axon MACH_OMAP3AXON OMAP3AXON 2106
+ma8xx MACH_MA8XX MA8XX 2107
+mp201ek MACH_MP201EK MP201EK 2108
+davinci_tux MACH_DAVINCI_TUX DAVINCI_TUX 2109
+mpa1600 MACH_MPA1600 MPA1600 2110
+pelco_troy MACH_PELCO_TROY PELCO_TROY 2111
+nsb667 MACH_NSB667 NSB667 2112
+rovers5_4mpix MACH_ROVERS5_4MPIX ROVERS5_4MPIX 2113
+twocom MACH_TWOCOM TWOCOM 2114
+ubisys_p9_rcu3r2 MACH_UBISYS_P9_RCU3R2 UBISYS_P9_RCU3R2 2115
+hero_espresso MACH_HERO_ESPRESSO HERO_ESPRESSO 2116
+afeusb MACH_AFEUSB AFEUSB 2117
+t830 MACH_T830 T830 2118
+spd8020_cc MACH_SPD8020_CC SPD8020_CC 2119
+om_3d7k MACH_OM_3D7K OM_3D7K 2120
+picocom2 MACH_PICOCOM2 PICOCOM2 2121
+uwg4mx27 MACH_UWG4MX27 UWG4MX27 2122
+uwg4mx31 MACH_UWG4MX31 UWG4MX31 2123
+cherry MACH_CHERRY CHERRY 2124
mx51_babbage MACH_MX51_BABBAGE MX51_BABBAGE 2125
+s3c2440turkiye MACH_S3C2440TURKIYE S3C2440TURKIYE 2126
tx37 MACH_TX37 TX37 2127
+sbc2800_9g20 MACH_SBC2800_9G20 SBC2800_9G20 2128
+benzglb MACH_BENZGLB BENZGLB 2129
+benztd MACH_BENZTD BENZTD 2130
+cartesio_plus MACH_CARTESIO_PLUS CARTESIO_PLUS 2131
+solrad_g20 MACH_SOLRAD_G20 SOLRAD_G20 2132
+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
dm355_leopard MACH_DM355_LEOPARD DM355_LEOPARD 2138
ts219 MACH_TS219 TS219 2139
+tny_a9263 MACH_TNY_A9263 TNY_A9263 2140
+apollo MACH_APOLLO APOLLO 2141
+at91cap9stk MACH_AT91CAP9STK AT91CAP9STK 2142
+spc300 MACH_SPC300 SPC300 2143
+eko MACH_EKO EKO 2144
+ccw9m2443 MACH_CCW9M2443 CCW9M2443 2145
+ccw9m2443js MACH_CCW9M2443JS CCW9M2443JS 2146
+m2m_router_device MACH_M2M_ROUTER_DEVICE M2M_ROUTER_DEVICE 2147
+str9104nas MACH_STAR9104NAS STAR9104NAS 2148
pca100 MACH_PCA100 PCA100 2149
+z3_dm365_mod_01 MACH_Z3_DM365_MOD_01 Z3_DM365_MOD_01 2150
+hipox MACH_HIPOX HIPOX 2151
+omap3_piteds MACH_OMAP3_PITEDS OMAP3_PITEDS 2152
+bm150r MACH_BM150R BM150R 2153
+tbone MACH_TBONE TBONE 2154
+merlin MACH_MERLIN MERLIN 2155
+falcon MACH_FALCON FALCON 2156
davinci_da850_evm MACH_DAVINCI_DA850_EVM DAVINCI_DA850_EVM 2157
+s5p6440 MACH_S5P6440 S5P6440 2158
at91sam9g10ek MACH_AT91SAM9G10EK AT91SAM9G10EK 2159
omap_4430sdp MACH_OMAP_4430SDP OMAP_4430SDP 2160
+lpc313x MACH_LPC313X LPC313X 2161
magx_zn5 MACH_MAGX_ZN5 MAGX_ZN5 2162
+magx_em30 MACH_MAGX_EM30 MAGX_EM30 2163
+magx_ve66 MACH_MAGX_VE66 MAGX_VE66 2164
+meesc MACH_MEESC MEESC 2165
+otc570 MACH_OTC570 OTC570 2166
+bcu2412 MACH_BCU2412 BCU2412 2167
+beacon MACH_BEACON BEACON 2168
+actia_tgw MACH_ACTIA_TGW ACTIA_TGW 2169
+e4430 MACH_E4430 E4430 2170
+ql300 MACH_QL300 QL300 2171
btmavb101 MACH_BTMAVB101 BTMAVB101 2172
btmawb101 MACH_BTMAWB101 BTMAWB101 2173
+sq201 MACH_SQ201 SQ201 2174
+quatro45xx MACH_QUATRO45XX QUATRO45XX 2175
+openpad MACH_OPENPAD OPENPAD 2176
tx25 MACH_TX25 TX25 2177
omap3_torpedo MACH_OMAP3_TORPEDO OMAP3_TORPEDO 2178
+htcraphael_k MACH_HTCRAPHAEL_K HTCRAPHAEL_K 2179
+lal43 MACH_LAL43 LAL43 2181
+htcraphael_cdma500 MACH_HTCRAPHAEL_CDMA500 HTCRAPHAEL_CDMA500 2182
anw6410 MACH_ANW6410 ANW6410 2183
+htcprophet MACH_HTCPROPHET HTCPROPHET 2185
+cfa_10022 MACH_CFA_10022 CFA_10022 2186
imx27_visstrim_m10 MACH_IMX27_VISSTRIM_M10 IMX27_VISSTRIM_M10 2187
+px2imx27 MACH_PX2IMX27 PX2IMX27 2188
+stm3210e_eval MACH_STM3210E_EVAL STM3210E_EVAL 2189
+dvs10 MACH_DVS10 DVS10 2190
portuxg20 MACH_PORTUXG20 PORTUXG20 2191
+arm_spv MACH_ARM_SPV ARM_SPV 2192
smdkc110 MACH_SMDKC110 SMDKC110 2193
+cabespresso MACH_CABESPRESSO CABESPRESSO 2194
+hmc800 MACH_HMC800 HMC800 2195
+sholes MACH_SHOLES SHOLES 2196
+btmxc31 MACH_BTMXC31 BTMXC31 2197
+dt501 MACH_DT501 DT501 2198
+ktx MACH_KTX KTX 2199
omap3517evm MACH_OMAP3517EVM OMAP3517EVM 2200
netspace_v2 MACH_NETSPACE_V2 NETSPACE_V2 2201
netspace_max_v2 MACH_NETSPACE_MAX_V2 NETSPACE_MAX_V2 2202
d2net_v2 MACH_D2NET_V2 D2NET_V2 2203
net2big_v2 MACH_NET2BIG_V2 NET2BIG_V2 2204
+net4big_v2 MACH_NET4BIG_V2 NET4BIG_V2 2205
net5big_v2 MACH_NET5BIG_V2 NET5BIG_V2 2206
+endb2443 MACH_ENDB2443 ENDB2443 2207
inetspace_v2 MACH_INETSPACE_V2 INETSPACE_V2 2208
+tros MACH_TROS TROS 2209
+pelco_homer MACH_PELCO_HOMER PELCO_HOMER 2210
+ofsp8 MACH_OFSP8 OFSP8 2211
at91sam9g45ekes MACH_AT91SAM9G45EKES AT91SAM9G45EKES 2212
+guf_cupid MACH_GUF_CUPID GUF_CUPID 2213
+eab1r MACH_EAB1R EAB1R 2214
+desirec MACH_DESIREC DESIREC 2215
+cordoba MACH_CORDOBA CORDOBA 2216
+irvine MACH_IRVINE IRVINE 2217
+sff772 MACH_SFF772 SFF772 2218
+pelco_milano MACH_PELCO_MILANO PELCO_MILANO 2219
pc7302 MACH_PC7302 PC7302 2220
+bip6000 MACH_BIP6000 BIP6000 2221
+silvermoon MACH_SILVERMOON SILVERMOON 2222
+vc0830 MACH_VC0830 VC0830 2223
+dt430 MACH_DT430 DT430 2224
+ji42pf MACH_JI42PF JI42PF 2225
+gnet_ksm MACH_GNET_KSM GNET_KSM 2226
+gnet_sgm MACH_GNET_SGM GNET_SGM 2227
+gnet_sgr MACH_GNET_SGR GNET_SGR 2228
+omap3_icetekevm MACH_OMAP3_ICETEKEVM OMAP3_ICETEKEVM 2229
+pnp MACH_PNP PNP 2230
+ctera_2bay_k MACH_CTERA_2BAY_K CTERA_2BAY_K 2231
+ctera_2bay_u MACH_CTERA_2BAY_U CTERA_2BAY_U 2232
+sas_c MACH_SAS_C SAS_C 2233
+vma2315 MACH_VMA2315 VMA2315 2234
+vcs MACH_VCS VCS 2235
spear600 MACH_SPEAR600 SPEAR600 2236
spear300 MACH_SPEAR300 SPEAR300 2237
+spear1300 MACH_SPEAR1300 SPEAR1300 2238
lilly1131 MACH_LILLY1131 LILLY1131 2239
+arvoo_ax301 MACH_ARVOO_AX301 ARVOO_AX301 2240
+mapphone MACH_MAPPHONE MAPPHONE 2241
+legend MACH_LEGEND LEGEND 2242
+salsa MACH_SALSA SALSA 2243
+lounge MACH_LOUNGE LOUNGE 2244
+vision MACH_VISION VISION 2245
+vmb20 MACH_VMB20 VMB20 2246
+hy2410 MACH_HY2410 HY2410 2247
+hy9315 MACH_HY9315 HY9315 2248
+bullwinkle MACH_BULLWINKLE BULLWINKLE 2249
+arm_ultimator2 MACH_ARM_ULTIMATOR2 ARM_ULTIMATOR2 2250
+vs_v210 MACH_VS_V210 VS_V210 2252
+vs_v212 MACH_VS_V212 VS_V212 2253
hmt MACH_HMT HMT 2254
+km_kirkwood MACH_KM_KIRKWOOD KM_KIRKWOOD 2255
+vesper MACH_VESPER VESPER 2256
+str9 MACH_STR9 STR9 2257
+omap3_wl_ff MACH_OMAP3_WL_FF OMAP3_WL_FF 2258
+simcom MACH_SIMCOM SIMCOM 2259
+mcwebio MACH_MCWEBIO MCWEBIO 2260
+omap3_phrazer MACH_OMAP3_PHRAZER OMAP3_PHRAZER 2261
+darwin MACH_DARWIN DARWIN 2262
+oratiscomu MACH_ORATISCOMU ORATISCOMU 2263
+rtsbc20 MACH_RTSBC20 RTSBC20 2264
+sgh_i780 MACH_I780 I780 2265
+gemini324 MACH_GEMINI324 GEMINI324 2266
+oratislan MACH_ORATISLAN ORATISLAN 2267
+oratisalog MACH_ORATISALOG ORATISALOG 2268
+oratismadi MACH_ORATISMADI ORATISMADI 2269
+oratisot16 MACH_ORATISOT16 ORATISOT16 2270
+oratisdesk MACH_ORATISDESK ORATISDESK 2271
vexpress MACH_VEXPRESS VEXPRESS 2272
+sintexo MACH_SINTEXO SINTEXO 2273
+cm3389 MACH_CM3389 CM3389 2274
+omap3_cio MACH_OMAP3_CIO OMAP3_CIO 2275
+sgh_i900 MACH_SGH_I900 SGH_I900 2276
+bst100 MACH_BST100 BST100 2277
+passion MACH_PASSION PASSION 2278
+indesign_at91sam MACH_INDESIGN_AT91SAM INDESIGN_AT91SAM 2279
+c4_badger MACH_C4_BADGER C4_BADGER 2280
+c4_viper MACH_C4_VIPER C4_VIPER 2281
d2net MACH_D2NET D2NET 2282
bigdisk MACH_BIGDISK BIGDISK 2283
+notalvision MACH_NOTALVISION NOTALVISION 2284
+omap3_kboc MACH_OMAP3_KBOC OMAP3_KBOC 2285
+cyclone MACH_CYCLONE CYCLONE 2286
+ninja MACH_NINJA NINJA 2287
at91sam9g20ek_2mmc MACH_AT91SAM9G20EK_2MMC AT91SAM9G20EK_2MMC 2288
bcmring MACH_BCMRING BCMRING 2289
+resol_dl2 MACH_RESOL_DL2 RESOL_DL2 2290
+ifosw MACH_IFOSW IFOSW 2291
+htcrhodium MACH_HTCRHODIUM HTCRHODIUM 2292
+htctopaz MACH_HTCTOPAZ HTCTOPAZ 2293
+matrix504 MACH_MATRIX504 MATRIX504 2294
+mrfsa MACH_MRFSA MRFSA 2295
+sc_p270 MACH_SC_P270 SC_P270 2296
+atlas5_evb MACH_ATLAS5_EVB ATLAS5_EVB 2297
+pelco_lobox MACH_PELCO_LOBOX PELCO_LOBOX 2298
+dilax_pcu200 MACH_DILAX_PCU200 DILAX_PCU200 2299
+leonardo MACH_LEONARDO LEONARDO 2300
+zoran_approach7 MACH_ZORAN_APPROACH7 ZORAN_APPROACH7 2301
dp6xx MACH_DP6XX DP6XX 2302
+bcm2153_vesper MACH_BCM2153_VESPER BCM2153_VESPER 2303
mahimahi MACH_MAHIMAHI MAHIMAHI 2304
+clickc MACH_CLICKC CLICKC 2305
+zb_gateway MACH_ZB_GATEWAY ZB_GATEWAY 2306
+tazcard MACH_TAZCARD TAZCARD 2307
+tazdev MACH_TAZDEV TAZDEV 2308
+annax_cb_arm MACH_ANNAX_CB_ARM ANNAX_CB_ARM 2309
+annax_dm3 MACH_ANNAX_DM3 ANNAX_DM3 2310
+cerebric MACH_CEREBRIC CEREBRIC 2311
+orca MACH_ORCA ORCA 2312
+pc9260 MACH_PC9260 PC9260 2313
+ems285a MACH_EMS285A EMS285A 2314
+gec2410 MACH_GEC2410 GEC2410 2315
+gec2440 MACH_GEC2440 GEC2440 2316
+mw903 MACH_ARCH_MW903 ARCH_MW903 2317
+mw2440 MACH_MW2440 MW2440 2318
+ecac2378 MACH_ECAC2378 ECAC2378 2319
+tazkiosk MACH_TAZKIOSK TAZKIOSK 2320
+whiterabbit_mch MACH_WHITERABBIT_MCH WHITERABBIT_MCH 2321
+sbox9263 MACH_SBOX9263 SBOX9263 2322
smdk6442 MACH_SMDK6442 SMDK6442 2324
openrd_base MACH_OPENRD_BASE OPENRD_BASE 2325
+incredible MACH_INCREDIBLE INCREDIBLE 2326
+incrediblec MACH_INCREDIBLEC INCREDIBLEC 2327
+heroct MACH_HEROCT HEROCT 2328
+mmnet1000 MACH_MMNET1000 MMNET1000 2329
devkit8000 MACH_DEVKIT8000 DEVKIT8000 2330
+devkit9000 MACH_DEVKIT9000 DEVKIT9000 2331
+mx31txtr MACH_MX31TXTR MX31TXTR 2332
+u380 MACH_U380 U380 2333
+oamp3_hualu MACH_HUALU_BOARD HUALU_BOARD 2334
+npcmx50 MACH_NPCMX50 NPCMX50 2335
mx51_efikamx MACH_MX51_EFIKAMX MX51_EFIKAMX 2336
+mx51_lange52 MACH_MX51_LANGE52 MX51_LANGE52 2337
+riom MACH_RIOM RIOM 2338
+comcas MACH_COMCAS COMCAS 2339
+wsi_mx27 MACH_WSI_MX27 WSI_MX27 2340
cm_t35 MACH_CM_T35 CM_T35 2341
net2big MACH_NET2BIG NET2BIG 2342
+motorola_a1600 MACH_MOTOROLA_A1600 MOTOROLA_A1600 2343
igep0020 MACH_IGEP0020 IGEP0020 2344
+igep0010 MACH_IGEP0010 IGEP0010 2345
+mv6281gtwge2 MACH_MV6281GTWGE2 MV6281GTWGE2 2346
+scat100 MACH_SCAT100 SCAT100 2347
+sanmina MACH_SANMINA SANMINA 2348
+momento MACH_MOMENTO MOMENTO 2349
+nuc9xx MACH_NUC9XX NUC9XX 2350
+nuc910evb MACH_NUC910EVB NUC910EVB 2351
+nuc920evb MACH_NUC920EVB NUC920EVB 2352
+nuc950evb MACH_NUC950EVB NUC950EVB 2353
+nuc945evb MACH_NUC945EVB NUC945EVB 2354
+nuc960evb MACH_NUC960EVB NUC960EVB 2355
nuc932evb MACH_NUC932EVB NUC932EVB 2356
+nuc900 MACH_NUC900 NUC900 2357
+sd1soc MACH_SD1SOC SD1SOC 2358
+ln2440bc MACH_LN2440BC LN2440BC 2359
+rsbc MACH_RSBC RSBC 2360
openrd_client MACH_OPENRD_CLIENT OPENRD_CLIENT 2361
+hpipaq11x MACH_HPIPAQ11X HPIPAQ11X 2362
+wayland MACH_WAYLAND WAYLAND 2363
+acnbsx102 MACH_ACNBSX102 ACNBSX102 2364
+hwat91 MACH_HWAT91 HWAT91 2365
+at91sam9263cs MACH_AT91SAM9263CS AT91SAM9263CS 2366
+csb732 MACH_CSB732 CSB732 2367
u8500 MACH_U8500 U8500 2368
+huqiu MACH_HUQIU HUQIU 2369
mx51_efikasb MACH_MX51_EFIKASB MX51_EFIKASB 2370
+pmt1g MACH_PMT1G PMT1G 2371
+htcelf MACH_HTCELF HTCELF 2372
+armadillo420 MACH_ARMADILLO420 ARMADILLO420 2373
+armadillo440 MACH_ARMADILLO440 ARMADILLO440 2374
+u_chip_dual_arm MACH_U_CHIP_DUAL_ARM U_CHIP_DUAL_ARM 2375
+csr_bdb3 MACH_CSR_BDB3 CSR_BDB3 2376
+dolby_cat1018 MACH_DOLBY_CAT1018 DOLBY_CAT1018 2377
+hy9307 MACH_HY9307 HY9307 2378
+aspire_easystore MACH_A_ES A_ES 2379
+davinci_irif MACH_DAVINCI_IRIF DAVINCI_IRIF 2380
+agama9263 MACH_AGAMA9263 AGAMA9263 2381
marvell_jasper MACH_MARVELL_JASPER MARVELL_JASPER 2382
flint MACH_FLINT FLINT 2383
tavorevb3 MACH_TAVOREVB3 TAVOREVB3 2384
+sch_m490 MACH_SCH_M490 SCH_M490 2386
+rbl01 MACH_RBL01 RBL01 2387
+omnifi MACH_OMNIFI OMNIFI 2388
+otavalo MACH_OTAVALO OTAVALO 2389
+htc_excalibur_s620 MACH_HTC_EXCALIBUR_S620 HTC_EXCALIBUR_S620 2391
+htc_opal MACH_HTC_OPAL HTC_OPAL 2392
touchbook MACH_TOUCHBOOK TOUCHBOOK 2393
+latte MACH_LATTE LATTE 2394
+xa200 MACH_XA200 XA200 2395
+nimrod MACH_NIMROD NIMROD 2396
+cc9p9215_3g MACH_CC9P9215_3G CC9P9215_3G 2397
+cc9p9215_3gjs MACH_CC9P9215_3GJS CC9P9215_3GJS 2398
+tk71 MACH_TK71 TK71 2399
+comham3525 MACH_COMHAM3525 COMHAM3525 2400
+mx31erebus MACH_MX31EREBUS MX31EREBUS 2401
+mcardmx27 MACH_MCARDMX27 MCARDMX27 2402
+paradise MACH_PARADISE PARADISE 2403
+tide MACH_TIDE TIDE 2404
+wzl2440 MACH_WZL2440 WZL2440 2405
+sdrdemo MACH_SDRDEMO SDRDEMO 2406
+ethercan2 MACH_ETHERCAN2 ETHERCAN2 2407
+ecmimg20 MACH_ECMIMG20 ECMIMG20 2408
+omap_dragon MACH_OMAP_DRAGON OMAP_DRAGON 2409
+halo MACH_HALO HALO 2410
+huangshan MACH_HUANGSHAN HUANGSHAN 2411
+vl_ma2sc MACH_VL_MA2SC VL_MA2SC 2412
raumfeld_rc MACH_RAUMFELD_RC RAUMFELD_RC 2413
raumfeld_connector MACH_RAUMFELD_CONNECTOR RAUMFELD_CONNECTOR 2414
raumfeld_speaker MACH_RAUMFELD_SPEAKER RAUMFELD_SPEAKER 2415
+multibus_master MACH_MULTIBUS_MASTER MULTIBUS_MASTER 2416
+multibus_pbk MACH_MULTIBUS_PBK MULTIBUS_PBK 2417
tnetv107x MACH_TNETV107X TNETV107X 2418
+snake MACH_SNAKE SNAKE 2419
+cwmx27 MACH_CWMX27 CWMX27 2420
+sch_m480 MACH_SCH_M480 SCH_M480 2421
+platypus MACH_PLATYPUS PLATYPUS 2422
+pss2 MACH_PSS2 PSS2 2423
+davinci_apm150 MACH_DAVINCI_APM150 DAVINCI_APM150 2424
+str9100 MACH_STR9100 STR9100 2425
+net5big MACH_NET5BIG NET5BIG 2426
+seabed9263 MACH_SEABED9263 SEABED9263 2427
mx51_m2id MACH_MX51_M2ID MX51_M2ID 2428
+octvocplus_eb MACH_OCTVOCPLUS_EB OCTVOCPLUS_EB 2429
+klk_firefox MACH_KLK_FIREFOX KLK_FIREFOX 2430
+klk_wirma_module MACH_KLK_WIRMA_MODULE KLK_WIRMA_MODULE 2431
+klk_wirma_mmi MACH_KLK_WIRMA_MMI KLK_WIRMA_MMI 2432
+supersonic MACH_SUPERSONIC SUPERSONIC 2433
+liberty MACH_LIBERTY LIBERTY 2434
+mh355 MACH_MH355 MH355 2435
+pc7802 MACH_PC7802 PC7802 2436
+gnet_sgc MACH_GNET_SGC GNET_SGC 2437
+einstein15 MACH_EINSTEIN15 EINSTEIN15 2438
+cmpd MACH_CMPD CMPD 2439
+davinci_hase1 MACH_DAVINCI_HASE1 DAVINCI_HASE1 2440
+lgeincitephone MACH_LGEINCITEPHONE LGEINCITEPHONE 2441
+ea313x MACH_EA313X EA313X 2442
+fwbd_39064 MACH_FWBD_39064 FWBD_39064 2443
+fwbd_390128 MACH_FWBD_390128 FWBD_390128 2444
+pelco_moe MACH_PELCO_MOE PELCO_MOE 2445
+minimix27 MACH_MINIMIX27 MINIMIX27 2446
+omap3_thunder MACH_OMAP3_THUNDER OMAP3_THUNDER 2447
+passionc MACH_PASSIONC PASSIONC 2448
+mx27amata MACH_MX27AMATA MX27AMATA 2449
+bgat1 MACH_BGAT1 BGAT1 2450
+buzz MACH_BUZZ BUZZ 2451
+mb9g20 MACH_MB9G20 MB9G20 2452
+yushan MACH_YUSHAN YUSHAN 2453
+lizard MACH_LIZARD LIZARD 2454
+omap3polycom MACH_OMAP3POLYCOM OMAP3POLYCOM 2455
smdkv210 MACH_SMDKV210 SMDKV210 2456
+bravo MACH_BRAVO BRAVO 2457
+siogentoo1 MACH_SIOGENTOO1 SIOGENTOO1 2458
+siogentoo2 MACH_SIOGENTOO2 SIOGENTOO2 2459
+sm3k MACH_SM3K SM3K 2460
+acer_tempo_f900 MACH_ACER_TEMPO_F900 ACER_TEMPO_F900 2461
+glittertind MACH_GLITTERTIND GLITTERTIND 2463
omap_zoom3 MACH_OMAP_ZOOM3 OMAP_ZOOM3 2464
omap_3630sdp MACH_OMAP_3630SDP OMAP_3630SDP 2465
+cybook2440 MACH_CYBOOK2440 CYBOOK2440 2466
+torino_s MACH_TORINO_S TORINO_S 2467
+havana MACH_HAVANA HAVANA 2468
+beaumont_11 MACH_BEAUMONT_11 BEAUMONT_11 2469
+vanguard MACH_VANGUARD VANGUARD 2470
+s5pc110_draco MACH_S5PC110_DRACO S5PC110_DRACO 2471
+cartesio_two MACH_CARTESIO_TWO CARTESIO_TWO 2472
+aster MACH_ASTER ASTER 2473
+voguesv210 MACH_VOGUESV210 VOGUESV210 2474
+acm500x MACH_ACM500X ACM500X 2475
+km9260 MACH_KM9260 KM9260 2476
+nideflexg1 MACH_NIDEFLEXG1 NIDEFLEXG1 2477
+ctera_plug_io MACH_CTERA_PLUG_IO CTERA_PLUG_IO 2478
smartq7 MACH_SMARTQ7 SMARTQ7 2479
+at91sam9g10ek2 MACH_AT91SAM9G10EK2 AT91SAM9G10EK2 2480
+asusp527 MACH_ASUSP527 ASUSP527 2481
+at91sam9g20mpm2 MACH_AT91SAM9G20MPM2 AT91SAM9G20MPM2 2482
+topasa900 MACH_TOPASA900 TOPASA900 2483
+electrum_100 MACH_ELECTRUM_100 ELECTRUM_100 2484
+mx51grb MACH_MX51GRB MX51GRB 2485
+xea300 MACH_XEA300 XEA300 2486
+htcstartrek MACH_HTCSTARTREK HTCSTARTREK 2487
+lima MACH_LIMA LIMA 2488
+csb740 MACH_CSB740 CSB740 2489
+usb_s8815 MACH_USB_S8815 USB_S8815 2490
watson_efm_plugin MACH_WATSON_EFM_PLUGIN WATSON_EFM_PLUGIN 2491
+milkyway MACH_MILKYWAY MILKYWAY 2492
g4evm MACH_G4EVM G4EVM 2493
+picomod6 MACH_PICOMOD6 PICOMOD6 2494
omapl138_hawkboard MACH_OMAPL138_HAWKBOARD OMAPL138_HAWKBOARD 2495
+ip6000 MACH_IP6000 IP6000 2496
+ip6010 MACH_IP6010 IP6010 2497
+utm400 MACH_UTM400 UTM400 2498
+omap3_zybex MACH_OMAP3_ZYBEX OMAP3_ZYBEX 2499
+wireless_space MACH_WIRELESS_SPACE WIRELESS_SPACE 2500
+sx560 MACH_SX560 SX560 2501
ts41x MACH_TS41X TS41X 2502
+elphel10373 MACH_ELPHEL10373 ELPHEL10373 2503
+rhobot MACH_RHOBOT RHOBOT 2504
+mx51_refresh MACH_MX51_REFRESH MX51_REFRESH 2505
+ls9260 MACH_LS9260 LS9260 2506
+shank MACH_SHANK SHANK 2507
+qsd8x50_st1 MACH_QSD8X50_ST1 QSD8X50_ST1 2508
+at91sam9m10ekes MACH_AT91SAM9M10EKES AT91SAM9M10EKES 2509
+hiram MACH_HIRAM HIRAM 2510
phy3250 MACH_PHY3250 PHY3250 2511
+ea3250 MACH_EA3250 EA3250 2512
+fdi3250 MACH_FDI3250 FDI3250 2513
+at91sam9263nit MACH_AT91SAM9263NIT AT91SAM9263NIT 2515
+ccmx51 MACH_CCMX51 CCMX51 2516
+ccmx51js MACH_CCMX51JS CCMX51JS 2517
+ccwmx51 MACH_CCWMX51 CCWMX51 2518
+ccwmx51js MACH_CCWMX51JS CCWMX51JS 2519
mini6410 MACH_MINI6410 MINI6410 2520
+tiny6410 MACH_TINY6410 TINY6410 2521
+nano6410 MACH_NANO6410 NANO6410 2522
+at572d940hfnldb MACH_AT572D940HFNLDB AT572D940HFNLDB 2523
+htcleo MACH_HTCLEO HTCLEO 2524
+avp13 MACH_AVP13 AVP13 2525
+xxsvideod MACH_XXSVIDEOD XXSVIDEOD 2526
+vpnext MACH_VPNEXT VPNEXT 2527
+swarco_itc3 MACH_SWARCO_ITC3 SWARCO_ITC3 2528
tx51 MACH_TX51 TX51 2529
+dolby_cat1021 MACH_DOLBY_CAT1021 DOLBY_CAT1021 2530
mx28evk MACH_MX28EVK MX28EVK 2531
+phoenix260 MACH_PHOENIX260 PHOENIX260 2532
+uvaca_stork MACH_UVACA_STORK UVACA_STORK 2533
smartq5 MACH_SMARTQ5 SMARTQ5 2534
+all3078 MACH_ALL3078 ALL3078 2535
+ctera_2bay_ds MACH_CTERA_2BAY_DS CTERA_2BAY_DS 2536
+siogentoo3 MACH_SIOGENTOO3 SIOGENTOO3 2537
+epb5000 MACH_EPB5000 EPB5000 2538
+hy9263 MACH_HY9263 HY9263 2539
+acer_tempo_m900 MACH_ACER_TEMPO_M900 ACER_TEMPO_M900 2540
+acer_tempo_dx650 MACH_ACER_TEMPO_DX900 ACER_TEMPO_DX900 2541
+acer_tempo_x960 MACH_ACER_TEMPO_X960 ACER_TEMPO_X960 2542
+acer_eten_v900 MACH_ACER_ETEN_V900 ACER_ETEN_V900 2543
+acer_eten_x900 MACH_ACER_ETEN_X900 ACER_ETEN_X900 2544
+bonnell MACH_BONNELL BONNELL 2545
+oht_mx27 MACH_OHT_MX27 OHT_MX27 2546
+htcquartz MACH_HTCQUARTZ HTCQUARTZ 2547
davinci_dm6467tevm MACH_DAVINCI_DM6467TEVM DAVINCI_DM6467TEVM 2548
+c3ax03 MACH_C3AX03 C3AX03 2549
mxt_td60 MACH_MXT_TD60 MXT_TD60 2550
+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
+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
+bcmring_sv MACH_BCMRING_SV BCMRING_SV 2559
+bcmring_sv_jazz MACH_BCMRING_SV_JAZZ BCMRING_SV_JAZZ 2560
+bcmring_tablet MACH_BCMRING_TABLET BCMRING_TABLET 2561
+bcmring_vp MACH_BCMRING_VP BCMRING_VP 2562
+bcmring_evm_seikor MACH_BCMRING_EVM_SEIKOR BCMRING_EVM_SEIKOR 2563
+bcmring_sp_wqvga MACH_BCMRING_SP_WQVGA BCMRING_SP_WQVGA 2564
+bcmring_custom MACH_BCMRING_CUSTOM BCMRING_CUSTOM 2565
+acer_s200 MACH_ACER_S200 ACER_S200 2566
+bt270 MACH_BT270 BT270 2567
+iseo MACH_ISEO ISEO 2568
+cezanne MACH_CEZANNE CEZANNE 2569
+lucca MACH_LUCCA LUCCA 2570
+supersmart MACH_SUPERSMART SUPERSMART 2571
+arm11_board MACH_CS_MISANO CS_MISANO 2572
+magnolia2 MACH_MAGNOLIA2 MAGNOLIA2 2573
+emxx MACH_EMXX EMXX 2574
+outlaw MACH_OUTLAW OUTLAW 2575
riot_bei2 MACH_RIOT_BEI2 RIOT_BEI2 2576
+riot_gx2 MACH_RIOT_VOX RIOT_VOX 2577
riot_x37 MACH_RIOT_X37 RIOT_X37 2578
+mega25mx MACH_MEGA25MX MEGA25MX 2579
+benzina2 MACH_BENZINA2 BENZINA2 2580
+ignite MACH_IGNITE IGNITE 2581
+foggia MACH_FOGGIA FOGGIA 2582
+arezzo MACH_AREZZO AREZZO 2583
+leica_skywalker MACH_LEICA_SKYWALKER LEICA_SKYWALKER 2584
+jacinto2_jamr MACH_JACINTO2_JAMR JACINTO2_JAMR 2585
+gts_nova MACH_GTS_NOVA GTS_NOVA 2586
+p3600 MACH_P3600 P3600 2587
+dlt2 MACH_DLT2 DLT2 2588
+df3120 MACH_DF3120 DF3120 2589
+ecucore_9g20 MACH_ECUCORE_9G20 ECUCORE_9G20 2590
+nautel_am35xx MACH_NAUTEL_LPC3240 NAUTEL_LPC3240 2591
+glacier MACH_GLACIER GLACIER 2592
+phrazer_bulldog MACH_PHRAZER_BULLDOG PHRAZER_BULLDOG 2593
+omap3_bulldog MACH_OMAP3_BULLDOG OMAP3_BULLDOG 2594
pca101 MACH_PCA101 PCA101 2595
+buzzc MACH_BUZZC BUZZC 2596
+sasie2 MACH_SASIE2 SASIE2 2597
+smartmeter_dl MACH_SMARTMETER_DL SMARTMETER_DL 2599
+wzl6410 MACH_WZL6410 WZL6410 2600
+wzl6410m MACH_WZL6410M WZL6410M 2601
+wzl6410f MACH_WZL6410F WZL6410F 2602
+wzl6410i MACH_WZL6410I WZL6410I 2603
+spacecom1 MACH_SPACECOM1 SPACECOM1 2604
+pingu920 MACH_PINGU920 PINGU920 2605
+bravoc MACH_BRAVOC BRAVOC 2606
+vdssw MACH_VDSSW VDSSW 2608
+romulus MACH_ROMULUS ROMULUS 2609
+omap_magic MACH_OMAP_MAGIC OMAP_MAGIC 2610
+eltd100 MACH_ELTD100 ELTD100 2611
capc7117 MACH_CAPC7117 CAPC7117 2612
+swan MACH_SWAN SWAN 2613
+veu MACH_VEU VEU 2614
+rm2 MACH_RM2 RM2 2615
+tt2100 MACH_TT2100 TT2100 2616
+venice MACH_VENICE VENICE 2617
+pc7323 MACH_PC7323 PC7323 2618
+masp MACH_MASP MASP 2619
+fujitsu_tvstbsoc0 MACH_FUJITSU_TVSTBSOC FUJITSU_TVSTBSOC 2620
+fujitsu_tvstbsoc1 MACH_FUJITSU_TVSTBSOC1 FUJITSU_TVSTBSOC1 2621
+lexikon MACH_LEXIKON LEXIKON 2622
+mini2440v2 MACH_MINI2440V2 MINI2440V2 2623
icontrol MACH_ICONTROL ICONTROL 2624
gplugd MACH_GPLUGD GPLUGD 2625
+qsd8x50a_st1_1 MACH_QSD8X50A_ST1_1 QSD8X50A_ST1_1 2626
qsd8x50a_st1_5 MACH_QSD8X50A_ST1_5 QSD8X50A_ST1_5 2627
+bee MACH_BEE BEE 2628
mx23evk MACH_MX23EVK MX23EVK 2629
ap4evb MACH_AP4EVB AP4EVB 2630
+stockholm MACH_STOCKHOLM STOCKHOLM 2631
+lpc_h3131 MACH_LPC_H3131 LPC_H3131 2632
+stingray MACH_STINGRAY STINGRAY 2633
+kraken MACH_KRAKEN KRAKEN 2634
+gw2388 MACH_GW2388 GW2388 2635
+jadecpu MACH_JADECPU JADECPU 2636
+carlisle MACH_CARLISLE CARLISLE 2637
+lux_sf9 MACH_LUX_SF9 LUX_SF9 2638
+nemid_tb MACH_NEMID_TB NEMID_TB 2639
+terrier MACH_TERRIER TERRIER 2640
+turbot MACH_TURBOT TURBOT 2641
+sanddab MACH_SANDDAB SANDDAB 2642
+mx35_cicada MACH_MX35_CICADA MX35_CICADA 2643
+ghi2703d MACH_GHI2703D GHI2703D 2644
+lux_sfx9 MACH_LUX_SFX9 LUX_SFX9 2645
+lux_sf9g MACH_LUX_SF9G LUX_SF9G 2646
+lux_edk9 MACH_LUX_EDK9 LUX_EDK9 2647
+hw90240 MACH_HW90240 HW90240 2648
+dm365_leopard MACH_DM365_LEOPARD DM365_LEOPARD 2649
mityomapl138 MACH_MITYOMAPL138 MITYOMAPL138 2650
+scat110 MACH_SCAT110 SCAT110 2651
+acer_a1 MACH_ACER_A1 ACER_A1 2652
+cmcontrol MACH_CMCONTROL CMCONTROL 2653
+pelco_lamar MACH_PELCO_LAMAR PELCO_LAMAR 2654
+rfp43 MACH_RFP43 RFP43 2655
+sk86r0301 MACH_SK86R0301 SK86R0301 2656
+ctpxa MACH_CTPXA CTPXA 2657
+epb_arm9_a MACH_EPB_ARM9_A EPB_ARM9_A 2658
guruplug MACH_GURUPLUG GURUPLUG 2659
spear310 MACH_SPEAR310 SPEAR310 2660
spear320 MACH_SPEAR320 SPEAR320 2661
+robotx MACH_ROBOTX ROBOTX 2662
+lsxhl MACH_LSXHL LSXHL 2663
+smartlite MACH_SMARTLITE SMARTLITE 2664
+cws2 MACH_CWS2 CWS2 2665
+m619 MACH_M619 M619 2666
+smartview MACH_SMARTVIEW SMARTVIEW 2667
+lsa_salsa MACH_LSA_SALSA LSA_SALSA 2668
+kizbox MACH_KIZBOX KIZBOX 2669
+htccharmer MACH_HTCCHARMER HTCCHARMER 2670
+guf_neso_lt MACH_GUF_NESO_LT GUF_NESO_LT 2671
+pm9g45 MACH_PM9G45 PM9G45 2672
+htcpanther MACH_HTCPANTHER HTCPANTHER 2673
+htcpanther_cdma MACH_HTCPANTHER_CDMA HTCPANTHER_CDMA 2674
+reb01 MACH_REB01 REB01 2675
aquila MACH_AQUILA AQUILA 2676
+spark_sls_hw2 MACH_SPARK_SLS_HW2 SPARK_SLS_HW2 2677
esata_sheevaplug MACH_ESATA_SHEEVAPLUG ESATA_SHEEVAPLUG 2678
msm7x30_surf MACH_MSM7X30_SURF MSM7X30_SURF 2679
+micro2440 MACH_MICRO2440 MICRO2440 2680
+am2440 MACH_AM2440 AM2440 2681
+tq2440 MACH_TQ2440 TQ2440 2682
ea2478devkit MACH_EA2478DEVKIT EA2478DEVKIT 2683
+ak880x MACH_AK880X AK880X 2684
+cobra3530 MACH_COBRA3530 COBRA3530 2685
+pmppb MACH_PMPPB PMPPB 2686
+u6715 MACH_U6715 U6715 2687
+axar1500_sender MACH_AXAR1500_SENDER AXAR1500_SENDER 2688
+g30_dvb MACH_G30_DVB G30_DVB 2689
+vc088x MACH_VC088X VC088X 2690
+mioa702 MACH_MIOA702 MIOA702 2691
+hpmin MACH_HPMIN HPMIN 2692
+ak880xak MACH_AK880XAK AK880XAK 2693
+arm926tomap850 MACH_ARM926TOMAP850 ARM926TOMAP850 2694
+lkevm MACH_LKEVM LKEVM 2695
+mw6410 MACH_MW6410 MW6410 2696
terastation_wxl MACH_TERASTATION_WXL TERASTATION_WXL 2697
+cpu8000e MACH_CPU8000E CPU8000E 2698
+tokyo MACH_TOKYO TOKYO 2700
+msm7201a_surf MACH_MSM7201A_SURF MSM7201A_SURF 2701
+msm7201a_ffa MACH_MSM7201A_FFA MSM7201A_FFA 2702
msm7x25_surf MACH_MSM7X25_SURF MSM7X25_SURF 2703
msm7x25_ffa MACH_MSM7X25_FFA MSM7X25_FFA 2704
msm7x27_surf MACH_MSM7X27_SURF MSM7X27_SURF 2705
msm7x27_ffa MACH_MSM7X27_FFA MSM7X27_FFA 2706
msm7x30_ffa MACH_MSM7X30_FFA MSM7X30_FFA 2707
qsd8x50_surf MACH_QSD8X50_SURF QSD8X50_SURF 2708
+qsd8x50_comet MACH_QSD8X50_COMET QSD8X50_COMET 2709
+qsd8x50_ffa MACH_QSD8X50_FFA QSD8X50_FFA 2710
+qsd8x50a_surf MACH_QSD8X50A_SURF QSD8X50A_SURF 2711
+qsd8x50a_ffa MACH_QSD8X50A_FFA QSD8X50A_FFA 2712
+adx_xgcp10 MACH_ADX_XGCP10 ADX_XGCP10 2713
+mcgwumts2a MACH_MCGWUMTS2A MCGWUMTS2A 2714
+mobikt MACH_MOBIKT MOBIKT 2715
mx53_evk MACH_MX53_EVK MX53_EVK 2716
igep0030 MACH_IGEP0030 IGEP0030 2717
+axell_h40_h50_ctrl MACH_AXELL_H40_H50_CTRL AXELL_H40_H50_CTRL 2718
+dtcommod MACH_DTCOMMOD DTCOMMOD 2719
+gould MACH_GOULD GOULD 2720
+siberia MACH_SIBERIA SIBERIA 2721
sbc3530 MACH_SBC3530 SBC3530 2722
+qarm MACH_QARM QARM 2723
+mips MACH_MIPS MIPS 2724
+mx27grb MACH_MX27GRB MX27GRB 2725
+sbc8100 MACH_SBC8100 SBC8100 2726
saarb MACH_SAARB SAARB 2727
+omap3mini MACH_OMAP3MINI OMAP3MINI 2728
+cnmbook7se MACH_CNMBOOK7SE CNMBOOK7SE 2729
+catan MACH_CATAN CATAN 2730
harmony MACH_HARMONY HARMONY 2731
+tonga MACH_TONGA TONGA 2732
+cybook_orizon MACH_CYBOOK_ORIZON CYBOOK_ORIZON 2733
+htcrhodiumcdma MACH_HTCRHODIUMCDMA HTCRHODIUMCDMA 2734
+epc_g45 MACH_EPC_G45 EPC_G45 2735
+epc_lpc3250 MACH_EPC_LPC3250 EPC_LPC3250 2736
+mxc91341evb MACH_MXC91341EVB MXC91341EVB 2737
+rtw1000 MACH_RTW1000 RTW1000 2738
+bobcat MACH_BOBCAT BOBCAT 2739
+trizeps6 MACH_TRIZEPS6 TRIZEPS6 2740
msm7x30_fluid MACH_MSM7X30_FLUID MSM7X30_FLUID 2741
+nedap9263 MACH_NEDAP9263 NEDAP9263 2742
+netgear_ms2110 MACH_NETGEAR_MS2110 NETGEAR_MS2110 2743
+bmx MACH_BMX BMX 2744
+netstream MACH_NETSTREAM NETSTREAM 2745
+vpnext_rcu MACH_VPNEXT_RCU VPNEXT_RCU 2746
+vpnext_mpu MACH_VPNEXT_MPU VPNEXT_MPU 2747
+bcmring_tablet_v1 MACH_BCMRING_TABLET_V1 BCMRING_TABLET_V1 2748
+sgarm10 MACH_SGARM10 SGARM10 2749
cm_t3517 MACH_CM_T3517 CM_T3517 2750
+dig297 MACH_OMAP3_CPS OMAP3_CPS 2751
+axar1500_receiver MACH_AXAR1500_RECEIVER AXAR1500_RECEIVER 2752
wbd222 MACH_WBD222 WBD222 2753
+mt65xx MACH_MT65XX MT65XX 2754
msm8x60_surf MACH_MSM8X60_SURF MSM8X60_SURF 2755
msm8x60_sim MACH_MSM8X60_SIM MSM8X60_SIM 2756
tcc8000_sdk MACH_TCC8000_SDK TCC8000_SDK 2758
nanos MACH_NANOS NANOS 2759
+stamp9g10 MACH_STAMP9G10 STAMP9G10 2760
stamp9g45 MACH_STAMP9G45 STAMP9G45 2761
+h6053 MACH_H6053 H6053 2762
+smint01 MACH_SMINT01 SMINT01 2763
+prtlvt2 MACH_PRTLVT2 PRTLVT2 2764
+ap420 MACH_AP420 AP420 2765
+davinci_dm365_fc MACH_DAVINCI_DM365_FC DAVINCI_DM365_FC 2767
+msm8x55_surf MACH_MSM8X55_SURF MSM8X55_SURF 2768
+msm8x55_ffa MACH_MSM8X55_FFA MSM8X55_FFA 2769
+esl_vamana MACH_ESL_VAMANA ESL_VAMANA 2770
+sbc35 MACH_SBC35 SBC35 2771
+mpx6446 MACH_MPX6446 MPX6446 2772
+oreo_controller MACH_OREO_CONTROLLER OREO_CONTROLLER 2773
+kopin_models MACH_KOPIN_MODELS KOPIN_MODELS 2774
+ttc_vision2 MACH_TTC_VISION2 TTC_VISION2 2775
cns3420vb MACH_CNS3420VB CNS3420VB 2776
+lpc_evo MACH_LPC2 LPC2 2777
+olympus MACH_OLYMPUS OLYMPUS 2778
+vortex MACH_VORTEX VORTEX 2779
+s5pc200 MACH_S5PC200 S5PC200 2780
+ecucore_9263 MACH_ECUCORE_9263 ECUCORE_9263 2781
+smdkc200 MACH_SMDKC200 SMDKC200 2782
+emsiso_sx27 MACH_EMSISO_SX27 EMSISO_SX27 2783
+apx_som9g45_ek MACH_APX_SOM9G45_EK APX_SOM9G45_EK 2784
+songshan MACH_SONGSHAN SONGSHAN 2785
+tianshan MACH_TIANSHAN TIANSHAN 2786
+vpx500 MACH_VPX500 VPX500 2787
+am3517sam MACH_AM3517SAM AM3517SAM 2788
+skat91_sim508 MACH_SKAT91_SIM508 SKAT91_SIM508 2789
+skat91_s3e MACH_SKAT91_S3E SKAT91_S3E 2790
omap4_panda MACH_OMAP4_PANDA OMAP4_PANDA 2791
+df7220 MACH_DF7220 DF7220 2792
+nemini MACH_NEMINI NEMINI 2793
+t8200 MACH_T8200 T8200 2794
+apf51 MACH_APF51 APF51 2795
+dr_rc_unit MACH_DR_RC_UNIT DR_RC_UNIT 2796
+bordeaux MACH_BORDEAUX BORDEAUX 2797
+catania_b MACH_CATANIA_B CATANIA_B 2798
+mx51_ocean MACH_MX51_OCEAN MX51_OCEAN 2799
ti8168evm MACH_TI8168EVM TI8168EVM 2800
+neocoreomap MACH_NEOCOREOMAP NEOCOREOMAP 2801
+withings_wbp MACH_WITHINGS_WBP WITHINGS_WBP 2802
+dbps MACH_DBPS DBPS 2803
+pcbfp0001 MACH_PCBFP0001 PCBFP0001 2805
+speedy MACH_SPEEDY SPEEDY 2806
+chrysaor MACH_CHRYSAOR CHRYSAOR 2807
+tango MACH_TANGO TANGO 2808
+synology_dsx11 MACH_SYNOLOGY_DSX11 SYNOLOGY_DSX11 2809
+hanlin_v3ext MACH_HANLIN_V3EXT HANLIN_V3EXT 2810
+hanlin_v5 MACH_HANLIN_V5 HANLIN_V5 2811
+hanlin_v3plus MACH_HANLIN_V3PLUS HANLIN_V3PLUS 2812
+iriver_story MACH_IRIVER_STORY IRIVER_STORY 2813
+irex_iliad MACH_IREX_ILIAD IREX_ILIAD 2814
+irex_dr1000 MACH_IREX_DR1000 IREX_DR1000 2815
teton_bga MACH_TETON_BGA TETON_BGA 2816
+snapper9g45 MACH_SNAPPER9G45 SNAPPER9G45 2817
+tam3517 MACH_TAM3517 TAM3517 2818
+pdc100 MACH_PDC100 PDC100 2819
eukrea_cpuimx25sd MACH_EUKREA_CPUIMX25SD EUKREA_CPUIMX25SD 2820
eukrea_cpuimx35sd MACH_EUKREA_CPUIMX35SD EUKREA_CPUIMX35SD 2821
eukrea_cpuimx51sd MACH_EUKREA_CPUIMX51SD EUKREA_CPUIMX51SD 2822
eukrea_cpuimx51 MACH_EUKREA_CPUIMX51 EUKREA_CPUIMX51 2823
+p565 MACH_P565 P565 2824
+acer_a4 MACH_ACER_A4 ACER_A4 2825
+davinci_dm368_bip MACH_DAVINCI_DM368_BIP DAVINCI_DM368_BIP 2826
+eshare MACH_ESHARE ESHARE 2827
+wlbargn MACH_WLBARGN WLBARGN 2829
+bm170 MACH_BM170 BM170 2830
+netspace_mini_v2 MACH_NETSPACE_MINI_V2 NETSPACE_MINI_V2 2831
+netspace_plug_v2 MACH_NETSPACE_PLUG_V2 NETSPACE_PLUG_V2 2832
+siemens_l1 MACH_SIEMENS_L1 SIEMENS_L1 2833
+elv_lcu1 MACH_ELV_LCU1 ELV_LCU1 2834
+mcu1 MACH_MCU1 MCU1 2835
+omap3_tao3530 MACH_OMAP3_TAO3530 OMAP3_TAO3530 2836
+omap3_pcutouch MACH_OMAP3_PCUTOUCH OMAP3_PCUTOUCH 2837
smdkc210 MACH_SMDKC210 SMDKC210 2838
-pca102 MACH_PCA102 PCA102 2843
+omap3_braillo MACH_OMAP3_BRAILLO OMAP3_BRAILLO 2839
+spyplug MACH_SPYPLUG SPYPLUG 2840
+ginger MACH_GINGER GINGER 2841
+tny_t3530 MACH_TNY_T3530 TNY_T3530 2842
+pcaal1 MACH_PCAAL1 PCAAL1 2843
+spade MACH_SPADE SPADE 2844
+mxc25_topaz MACH_MXC25_TOPAZ MXC25_TOPAZ 2845
t5325 MACH_T5325 T5325 2846
+gw2361 MACH_GW2361 GW2361 2847
+elog MACH_ELOG ELOG 2848
income MACH_INCOME INCOME 2849
+bcm589x MACH_BCM589X BCM589X 2850
+etna MACH_ETNA ETNA 2851
+hawks MACH_HAWKS HAWKS 2852
+meson MACH_MESON MESON 2853
+xsbase255 MACH_XSBASE255 XSBASE255 2854
+pvm2030 MACH_PVM2030 PVM2030 2855
+mioa502 MACH_MIOA502 MIOA502 2856
vvbox_sdorig2 MACH_VVBOX_SDORIG2 VVBOX_SDORIG2 2857
vvbox_sdlite2 MACH_VVBOX_SDLITE2 VVBOX_SDLITE2 2858
vvbox_sdpro4 MACH_VVBOX_SDPRO4 VVBOX_SDPRO4 2859
+htc_spv_m700 MACH_HTC_SPV_M700 HTC_SPV_M700 2860
mx257sx MACH_MX257SX MX257SX 2861
goni MACH_GONI GONI 2862
+msm8x55_svlte_ffa MACH_MSM8X55_SVLTE_FFA MSM8X55_SVLTE_FFA 2863
+msm8x55_svlte_surf MACH_MSM8X55_SVLTE_SURF MSM8X55_SVLTE_SURF 2864
+quickstep MACH_QUICKSTEP QUICKSTEP 2865
+dmw96 MACH_DMW96 DMW96 2866
+hammerhead MACH_HAMMERHEAD HAMMERHEAD 2867
+trident MACH_TRIDENT TRIDENT 2868
+lightning MACH_LIGHTNING LIGHTNING 2869
+iconnect MACH_ICONNECT ICONNECT 2870
+autobot MACH_AUTOBOT AUTOBOT 2871
+coconut MACH_COCONUT COCONUT 2872
+durian MACH_DURIAN DURIAN 2873
+cayenne MACH_CAYENNE CAYENNE 2874
+fuji MACH_FUJI FUJI 2875
+synology_6282 MACH_SYNOLOGY_6282 SYNOLOGY_6282 2876
+em1sy MACH_EM1SY EM1SY 2877
+m502 MACH_M502 M502 2878
+matrix518 MACH_MATRIX518 MATRIX518 2879
+tiny_gurnard MACH_TINY_GURNARD TINY_GURNARD 2880
+spear1310 MACH_SPEAR1310 SPEAR1310 2881
bv07 MACH_BV07 BV07 2882
+mxt_td61 MACH_MXT_TD61 MXT_TD61 2883
openrd_ultimate MACH_OPENRD_ULTIMATE OPENRD_ULTIMATE 2884
devixp MACH_DEVIXP DEVIXP 2885
miccpt MACH_MICCPT MICCPT 2886
mic256 MACH_MIC256 MIC256 2887
+as1167 MACH_AS1167 AS1167 2888
+omap3_ibiza MACH_OMAP3_IBIZA OMAP3_IBIZA 2889
u5500 MACH_U5500 U5500 2890
+davinci_picto MACH_DAVINCI_PICTO DAVINCI_PICTO 2891
+mecha MACH_MECHA MECHA 2892
+bubba3 MACH_BUBBA3 BUBBA3 2893
+pupitre MACH_PUPITRE PUPITRE 2894
+tegra_vogue MACH_TEGRA_VOGUE TEGRA_VOGUE 2896
+tegra_e1165 MACH_TEGRA_E1165 TEGRA_E1165 2897
+simplenet MACH_SIMPLENET SIMPLENET 2898
+ec4350tbm MACH_EC4350TBM EC4350TBM 2899
+pec_tc MACH_PEC_TC PEC_TC 2900
+pec_hc2 MACH_PEC_HC2 PEC_HC2 2901
+esl_mobilis_a MACH_ESL_MOBILIS_A ESL_MOBILIS_A 2902
+esl_mobilis_b MACH_ESL_MOBILIS_B ESL_MOBILIS_B 2903
+esl_wave_a MACH_ESL_WAVE_A ESL_WAVE_A 2904
+esl_wave_b MACH_ESL_WAVE_B ESL_WAVE_B 2905
+unisense_mmm MACH_UNISENSE_MMM UNISENSE_MMM 2906
+blueshark MACH_BLUESHARK BLUESHARK 2907
+e10 MACH_E10 E10 2908
+app3k_robin MACH_APP3K_ROBIN APP3K_ROBIN 2909
+pov15hd MACH_POV15HD POV15HD 2910
+stella MACH_STELLA STELLA 2911
linkstation_lschl MACH_LINKSTATION_LSCHL LINKSTATION_LSCHL 2913
+netwalker MACH_NETWALKER NETWALKER 2914
+acsx106 MACH_ACSX106 ACSX106 2915
+atlas5_c1 MACH_ATLAS5_C1 ATLAS5_C1 2916
+nsb3ast MACH_NSB3AST NSB3AST 2917
+gnet_slc MACH_GNET_SLC GNET_SLC 2918
+af4000 MACH_AF4000 AF4000 2919
+ark9431 MACH_ARK9431 ARK9431 2920
+fs_s5pc100 MACH_FS_S5PC100 FS_S5PC100 2921
+omap3505nova8 MACH_OMAP3505NOVA8 OMAP3505NOVA8 2922
+omap3621_edp1 MACH_OMAP3621_EDP1 OMAP3621_EDP1 2923
+oratisaes MACH_ORATISAES ORATISAES 2924
smdkv310 MACH_SMDKV310 SMDKV310 2925
+siemens_l0 MACH_SIEMENS_L0 SIEMENS_L0 2926
+ventana MACH_VENTANA VENTANA 2927
wm8505_7in_netbook MACH_WM8505_7IN_NETBOOK WM8505_7IN_NETBOOK 2928
+ec4350sdb MACH_EC4350SDB EC4350SDB 2929
+mimas MACH_MIMAS MIMAS 2930
+titan MACH_TITAN TITAN 2931
craneboard MACH_CRANEBOARD CRANEBOARD 2932
+es2440 MACH_ES2440 ES2440 2933
+najay_a9263 MACH_NAJAY_A9263 NAJAY_A9263 2934
+htctornado MACH_HTCTORNADO HTCTORNADO 2935
+dimm_mx257 MACH_DIMM_MX257 DIMM_MX257 2936
+jigen301 MACH_JIGEN JIGEN 2937
smdk6450 MACH_SMDK6450 SMDK6450 2938
+meno_qng MACH_MENO_QNG MENO_QNG 2939
+ns2416 MACH_NS2416 NS2416 2940
+rpc353 MACH_RPC353 RPC353 2941
+tq6410 MACH_TQ6410 TQ6410 2942
+sky6410 MACH_SKY6410 SKY6410 2943
+dynasty MACH_DYNASTY DYNASTY 2944
+vivo MACH_VIVO VIVO 2945
+bury_bl7582 MACH_BURY_BL7582 BURY_BL7582 2946
+bury_bps5270 MACH_BURY_BPS5270 BURY_BPS5270 2947
+basi MACH_BASI BASI 2948
+tn200 MACH_TN200 TN200 2949
+c2mmi MACH_C2MMI C2MMI 2950
+meson_6236m MACH_MESON_6236M MESON_6236M 2951
+meson_8626m MACH_MESON_8626M MESON_8626M 2952
+tube MACH_TUBE TUBE 2953
+messina MACH_MESSINA MESSINA 2954
+mx50_arm2 MACH_MX50_ARM2 MX50_ARM2 2955
+cetus9263 MACH_CETUS9263 CETUS9263 2956
brownstone MACH_BROWNSTONE BROWNSTONE 2957
+vmx25 MACH_VMX25 VMX25 2958
+vmx51 MACH_VMX51 VMX51 2959
+abacus MACH_ABACUS ABACUS 2960
+cm4745 MACH_CM4745 CM4745 2961
+oratislink MACH_ORATISLINK ORATISLINK 2962
+davinci_dm365_dvr MACH_DAVINCI_DM365_DVR DAVINCI_DM365_DVR 2963
+netviz MACH_NETVIZ NETVIZ 2964
flexibity MACH_FLEXIBITY FLEXIBITY 2965
+wlan_computer MACH_WLAN_COMPUTER WLAN_COMPUTER 2966
+lpc24xx MACH_LPC24XX LPC24XX 2967
+spica MACH_SPICA SPICA 2968
+gpsdisplay MACH_GPSDISPLAY GPSDISPLAY 2969
+bipnet MACH_BIPNET BIPNET 2970
+overo_ctu_inertial MACH_OVERO_CTU_INERTIAL OVERO_CTU_INERTIAL 2971
+davinci_dm355_mmm MACH_DAVINCI_DM355_MMM DAVINCI_DM355_MMM 2972
+pc9260_v2 MACH_PC9260_V2 PC9260_V2 2973
+ptx7545 MACH_PTX7545 PTX7545 2974
+tm_efdc MACH_TM_EFDC TM_EFDC 2975
+omap3_waldo1 MACH_OMAP3_WALDO1 OMAP3_WALDO1 2977
+flyer MACH_FLYER FLYER 2978
+tornado3240 MACH_TORNADO3240 TORNADO3240 2979
+soli_01 MACH_SOLI_01 SOLI_01 2980
+omapl138_europalc MACH_OMAPL138_EUROPALC OMAPL138_EUROPALC 2981
+helios_v1 MACH_HELIOS_V1 HELIOS_V1 2982
+netspace_lite_v2 MACH_NETSPACE_LITE_V2 NETSPACE_LITE_V2 2983
+ssc MACH_SSC SSC 2984
+premierwave_en MACH_PREMIERWAVE_EN PREMIERWAVE_EN 2985
+wasabi MACH_WASABI WASABI 2986
mx50_rdp MACH_MX50_RDP MX50_RDP 2988
universal_c210 MACH_UNIVERSAL_C210 UNIVERSAL_C210 2989
real6410 MACH_REAL6410 REAL6410 2990
+spx_sakura MACH_SPX_SAKURA SPX_SAKURA 2991
+ij3k_2440 MACH_IJ3K_2440 IJ3K_2440 2992
+omap3_bc10 MACH_OMAP3_BC10 OMAP3_BC10 2993
+thebe MACH_THEBE THEBE 2994
+rv082 MACH_RV082 RV082 2995
+armlguest MACH_ARMLGUEST ARMLGUEST 2996
+tjinc1000 MACH_TJINC1000 TJINC1000 2997
dockstar MACH_DOCKSTAR DOCKSTAR 2998
+ax8008 MACH_AX8008 AX8008 2999
+gnet_sgce MACH_GNET_SGCE GNET_SGCE 3000
+pxwnas_500_1000 MACH_PXWNAS_500_1000 PXWNAS_500_1000 3001
+ea20 MACH_EA20 EA20 3002
+awm2 MACH_AWM2 AWM2 3003
ti8148evm MACH_TI8148EVM TI8148EVM 3004
seaboard MACH_SEABOARD SEABOARD 3005
+linkstation_chlv2 MACH_LINKSTATION_CHLV2 LINKSTATION_CHLV2 3006
+tera_pro2_rack MACH_TERA_PRO2_RACK TERA_PRO2_RACK 3007
+rubys MACH_RUBYS RUBYS 3008
+aquarius MACH_AQUARIUS AQUARIUS 3009
mx53_ard MACH_MX53_ARD MX53_ARD 3010
mx53_smd MACH_MX53_SMD MX53_SMD 3011
+lswxl MACH_LSWXL LSWXL 3012
+dove_avng_v3 MACH_DOVE_AVNG_V3 DOVE_AVNG_V3 3013
+sdi_ess_9263 MACH_SDI_ESS_9263 SDI_ESS_9263 3014
+jocpu550 MACH_JOCPU550 JOCPU550 3015
msm8x60_rumi3 MACH_MSM8X60_RUMI3 MSM8X60_RUMI3 3016
msm8x60_ffa MACH_MSM8X60_FFA MSM8X60_FFA 3017
+yanomami MACH_YANOMAMI YANOMAMI 3018
+gta04 MACH_GTA04 GTA04 3019
cm_a510 MACH_CM_A510 CM_A510 3020
+omap3_rfs200 MACH_OMAP3_RFS200 OMAP3_RFS200 3021
+kx33xx MACH_KX33XX KX33XX 3022
+ptx7510 MACH_PTX7510 PTX7510 3023
+top9000 MACH_TOP9000 TOP9000 3024
+teenote MACH_TEENOTE TEENOTE 3025
+ts3 MACH_TS3 TS3 3026
+a0 MACH_A0 A0 3027
+fsm9xxx_surf MACH_FSM9XXX_SURF FSM9XXX_SURF 3028
+fsm9xxx_ffa MACH_FSM9XXX_FFA FSM9XXX_FFA 3029
+frrhwcdma60w MACH_FRRHWCDMA60W FRRHWCDMA60W 3030
+remus MACH_REMUS REMUS 3031
+at91cap7xdk MACH_AT91CAP7XDK AT91CAP7XDK 3032
+at91cap7stk MACH_AT91CAP7STK AT91CAP7STK 3033
+kt_sbc_sam9_1 MACH_KT_SBC_SAM9_1 KT_SBC_SAM9_1 3034
+armada_xp_db MACH_ARMADA_XP_DB ARMADA_XP_DB 3036
+spdm MACH_SPDM SPDM 3037
+gtib MACH_GTIB GTIB 3038
+dgm3240 MACH_DGM3240 DGM3240 3039
+htcmega MACH_HTCMEGA HTCMEGA 3041
+tricorder MACH_TRICORDER TRICORDER 3042
tx28 MACH_TX28 TX28 3043
+bstbrd MACH_BSTBRD BSTBRD 3044
+pwb3090 MACH_PWB3090 PWB3090 3045
+idea6410 MACH_IDEA6410 IDEA6410 3046
+qbc9263 MACH_QBC9263 QBC9263 3047
+borabora MACH_BORABORA BORABORA 3048
+valdez MACH_VALDEZ VALDEZ 3049
+ls9g20 MACH_LS9G20 LS9G20 3050
+mios_v1 MACH_MIOS_V1 MIOS_V1 3051
+s5pc110_crespo MACH_S5PC110_CRESPO S5PC110_CRESPO 3052
+controltek9g20 MACH_CONTROLTEK9G20 CONTROLTEK9G20 3053
+tin307 MACH_TIN307 TIN307 3054
+tin510 MACH_TIN510 TIN510 3055
+ep3505 MACH_EP3517 EP3517 3056
+bluecheese MACH_BLUECHEESE BLUECHEESE 3057
+tem3x30 MACH_TEM3X30 TEM3X30 3058
+harvest_desoto MACH_HARVEST_DESOTO HARVEST_DESOTO 3059
+msm8x60_qrdc MACH_MSM8X60_QRDC MSM8X60_QRDC 3060
+spear900 MACH_SPEAR900 SPEAR900 3061
pcontrol_g20 MACH_PCONTROL_G20 PCONTROL_G20 3062
+rdstor MACH_RDSTOR RDSTOR 3063
+usdloader MACH_USDLOADER USDLOADER 3064
+tsoploader MACH_TSOPLOADER TSOPLOADER 3065
+kronos MACH_KRONOS KRONOS 3066
+ffcore MACH_FFCORE FFCORE 3067
+mone MACH_MONE MONE 3068
+unit2s MACH_UNIT2S UNIT2S 3069
+acer_a5 MACH_ACER_A5 ACER_A5 3070
+etherpro_isp MACH_ETHERPRO_ISP ETHERPRO_ISP 3071
+stretchs7000 MACH_STRETCHS7000 STRETCHS7000 3072
+p87_smartsim MACH_P87_SMARTSIM P87_SMARTSIM 3073
+tulip MACH_TULIP TULIP 3074
+sunflower MACH_SUNFLOWER SUNFLOWER 3075
+rib MACH_RIB RIB 3076
+clod MACH_CLOD CLOD 3077
+rump MACH_RUMP RUMP 3078
+tenderloin MACH_TENDERLOIN TENDERLOIN 3079
+shortloin MACH_SHORTLOIN SHORTLOIN 3080
+antares MACH_ANTARES ANTARES 3082
+wb40n MACH_WB40N WB40N 3083
+herring MACH_HERRING HERRING 3084
+naxy400 MACH_NAXY400 NAXY400 3085
+naxy1200 MACH_NAXY1200 NAXY1200 3086
vpr200 MACH_VPR200 VPR200 3087
+bug20 MACH_BUG20 BUG20 3088
+goflexnet MACH_GOFLEXNET GOFLEXNET 3089
torbreck MACH_TORBRECK TORBRECK 3090
+saarb_mg1 MACH_SAARB_MG1 SAARB_MG1 3091
+callisto MACH_CALLISTO CALLISTO 3092
+multhsu MACH_MULTHSU MULTHSU 3093
+saluda MACH_SALUDA SALUDA 3094
+pemp_omap3_apollo MACH_PEMP_OMAP3_APOLLO PEMP_OMAP3_APOLLO 3095
+vc0718 MACH_VC0718 VC0718 3096
+mvblx MACH_MVBLX MVBLX 3097
+inhand_apeiron MACH_INHAND_APEIRON INHAND_APEIRON 3098
+inhand_fury MACH_INHAND_FURY INHAND_FURY 3099
+inhand_siren MACH_INHAND_SIREN INHAND_SIREN 3100
+hdnvp MACH_HDNVP HDNVP 3101
+softwinner MACH_SOFTWINNER SOFTWINNER 3102
prima2_evb MACH_PRIMA2_EVB PRIMA2_EVB 3103
+nas6210 MACH_NAS6210 NAS6210 3104
+unisdev MACH_UNISDEV UNISDEV 3105
+sbca11 MACH_SBCA11 SBCA11 3106
+saga MACH_SAGA SAGA 3107
+ns_k330 MACH_NS_K330 NS_K330 3108
+tanna MACH_TANNA TANNA 3109
+imate8502 MACH_IMATE8502 IMATE8502 3110
+aspen MACH_ASPEN ASPEN 3111
+daintree_cwac MACH_DAINTREE_CWAC DAINTREE_CWAC 3112
+zmx25 MACH_ZMX25 ZMX25 3113
+maple1 MACH_MAPLE1 MAPLE1 3114
+qsd8x72_surf MACH_QSD8X72_SURF QSD8X72_SURF 3115
+qsd8x72_ffa MACH_QSD8X72_FFA QSD8X72_FFA 3116
+abilene MACH_ABILENE ABILENE 3117
+eigen_ttr MACH_EIGEN_TTR EIGEN_TTR 3118
+iomega_ix2_200 MACH_IOMEGA_IX2_200 IOMEGA_IX2_200 3119
+coretec_vcx7400 MACH_CORETEC_VCX7400 CORETEC_VCX7400 3120
+santiago MACH_SANTIAGO SANTIAGO 3121
+mx257sol MACH_MX257SOL MX257SOL 3122
+strasbourg MACH_STRASBOURG STRASBOURG 3123
+msm8x60_fluid MACH_MSM8X60_FLUID MSM8X60_FLUID 3124
+smartqv5 MACH_SMARTQV5 SMARTQV5 3125
+smartqv3 MACH_SMARTQV3 SMARTQV3 3126
+smartqv7 MACH_SMARTQV7 SMARTQV7 3127
paz00 MACH_PAZ00 PAZ00 3128
acmenetusfoxg20 MACH_ACMENETUSFOXG20 ACMENETUSFOXG20 3129
+fwbd_0404 MACH_FWBD_0404 FWBD_0404 3131
+hdgu MACH_HDGU HDGU 3132
+pyramid MACH_PYRAMID PYRAMID 3133
+epiphan MACH_EPIPHAN EPIPHAN 3134
+omap_bender MACH_OMAP_BENDER OMAP_BENDER 3135
+gurnard MACH_GURNARD GURNARD 3136
+gtl_it5100 MACH_GTL_IT5100 GTL_IT5100 3137
+bcm2708 MACH_BCM2708 BCM2708 3138
+mx51_ggc MACH_MX51_GGC MX51_GGC 3139
+sharespace MACH_SHARESPACE SHARESPACE 3140
+haba_knx_explorer MACH_HABA_KNX_EXPLORER HABA_KNX_EXPLORER 3141
+simtec_kirkmod MACH_SIMTEC_KIRKMOD SIMTEC_KIRKMOD 3142
+crux MACH_CRUX CRUX 3143
+mx51_bravo MACH_MX51_BRAVO MX51_BRAVO 3144
+charon MACH_CHARON CHARON 3145
+picocom3 MACH_PICOCOM3 PICOCOM3 3146
+picocom4 MACH_PICOCOM4 PICOCOM4 3147
+serrano MACH_SERRANO SERRANO 3148
+doubleshot MACH_DOUBLESHOT DOUBLESHOT 3149
+evsy MACH_EVSY EVSY 3150
+huashan MACH_HUASHAN HUASHAN 3151
+lausanne MACH_LAUSANNE LAUSANNE 3152
+emerald MACH_EMERALD EMERALD 3153
+tqma35 MACH_TQMA35 TQMA35 3154
+marvel MACH_MARVEL MARVEL 3155
+manuae MACH_MANUAE MANUAE 3156
+chacha MACH_CHACHA CHACHA 3157
+lemon MACH_LEMON LEMON 3158
+csc MACH_CSC CSC 3159
+gira_knxip_router MACH_GIRA_KNXIP_ROUTER GIRA_KNXIP_ROUTER 3160
+t20 MACH_T20 T20 3161
+hdmini MACH_HDMINI HDMINI 3162
+sciphone_g2 MACH_SCIPHONE_G2 SCIPHONE_G2 3163
+express MACH_EXPRESS EXPRESS 3164
+express_kt MACH_EXPRESS_KT EXPRESS_KT 3165
+maximasp MACH_MAXIMASP MAXIMASP 3166
+nitrogen_imx51 MACH_NITROGEN_IMX51 NITROGEN_IMX51 3167
+nitrogen_imx53 MACH_NITROGEN_IMX53 NITROGEN_IMX53 3168
+sunfire MACH_SUNFIRE SUNFIRE 3169
+arowana MACH_AROWANA AROWANA 3170
+tegra_daytona MACH_TEGRA_DAYTONA TEGRA_DAYTONA 3171
+tegra_swordfish MACH_TEGRA_SWORDFISH TEGRA_SWORDFISH 3172
+edison MACH_EDISON EDISON 3173
+svp8500v1 MACH_SVP8500V1 SVP8500V1 3174
+svp8500v2 MACH_SVP8500V2 SVP8500V2 3175
+svp5500 MACH_SVP5500 SVP5500 3176
+b5500 MACH_B5500 B5500 3177
+s5500 MACH_S5500 S5500 3178
+icon MACH_ICON ICON 3179
+elephant MACH_ELEPHANT ELEPHANT 3180
+shooter MACH_SHOOTER SHOOTER 3182
+spade_lte MACH_SPADE_LTE SPADE_LTE 3183
+philhwani MACH_PHILHWANI PHILHWANI 3184
+gsncomm MACH_GSNCOMM GSNCOMM 3185
+strasbourg_a2 MACH_STRASBOURG_A2 STRASBOURG_A2 3186
+mmm MACH_MMM MMM 3187
+davinci_dm365_bv MACH_DAVINCI_DM365_BV DAVINCI_DM365_BV 3188
ag5evm MACH_AG5EVM AG5EVM 3189
+sc575plc MACH_SC575PLC SC575PLC 3190
+sc575hmi MACH_SC575IPC SC575IPC 3191
+omap3_tdm3730 MACH_OMAP3_TDM3730 OMAP3_TDM3730 3192
+top9000_eval MACH_TOP9000_EVAL TOP9000_EVAL 3194
+top9000_su MACH_TOP9000_SU TOP9000_SU 3195
+utm300 MACH_UTM300 UTM300 3196
tsunagi MACH_TSUNAGI TSUNAGI 3197
+ts75xx MACH_TS75XX TS75XX 3198
+ts47xx MACH_TS47XX TS47XX 3200
+da850_k5 MACH_DA850_K5 DA850_K5 3201
+ax502 MACH_AX502 AX502 3202
+igep0032 MACH_IGEP0032 IGEP0032 3203
+antero MACH_ANTERO ANTERO 3204
+synergy MACH_SYNERGY SYNERGY 3205
ics_if_voip MACH_ICS_IF_VOIP ICS_IF_VOIP 3206
wlf_cragg_6410 MACH_WLF_CRAGG_6410 WLF_CRAGG_6410 3207
+punica MACH_PUNICA PUNICA 3208
trimslice MACH_TRIMSLICE TRIMSLICE 3209
+mx27_wmultra MACH_MX27_WMULTRA MX27_WMULTRA 3210
mackerel MACH_MACKEREL MACKEREL 3211
+fa9x27 MACH_FA9X27 FA9X27 3213
+ns2816tb MACH_NS2816TB NS2816TB 3214
+ns2816_ntpad MACH_NS2816_NTPAD NS2816_NTPAD 3215
+ns2816_ntnb MACH_NS2816_NTNB NS2816_NTNB 3216
kaen MACH_KAEN KAEN 3217
+nv1000 MACH_NV1000 NV1000 3218
+nuc950ts MACH_NUC950TS NUC950TS 3219
nokia_rm680 MACH_NOKIA_RM680 NOKIA_RM680 3220
+ast2200 MACH_AST2200 AST2200 3221
+lead MACH_LEAD LEAD 3222
+unino1 MACH_UNINO1 UNINO1 3223
+greeco MACH_GREECO GREECO 3224
+verdi MACH_VERDI VERDI 3225
dm6446_adbox MACH_DM6446_ADBOX DM6446_ADBOX 3226
quad_salsa MACH_QUAD_SALSA QUAD_SALSA 3227
abb_gma_1_1 MACH_ABB_GMA_1_1 ABB_GMA_1_1 3228
@@ -603,7 +3251,9 @@ isc3 MACH_ISC3 ISC3 3291
rascal MACH_RASCAL RASCAL 3292
hrefv60 MACH_HREFV60 HREFV60 3293
tpt_2_0 MACH_TPT_2_0 TPT_2_0 3294
+pydtd MACH_PYRAMID_TD PYRAMID_TD 3295
splendor MACH_SPLENDOR SPLENDOR 3296
+guf_vincell MACH_GUF_PLANET GUF_PLANET 3297
msm8x60_qt MACH_MSM8X60_QT MSM8X60_QT 3298
htc_hd_mini MACH_HTC_HD_MINI HTC_HD_MINI 3299
athene MACH_ATHENE ATHENE 3300
@@ -614,6 +3264,7 @@ rfl109145_ssrv MACH_RFL109145_SSRV RFL109145_SSRV 3304
nmh MACH_NMH NMH 3305
wn802t MACH_WN802T WN802T 3306
dragonet MACH_DRAGONET DRAGONET 3307
+geneva_b4 MACH_GENEVA_B GENEVA_B 3308
at91sam9263desk16l MACH_AT91SAM9263DESK16L AT91SAM9263DESK16L 3309
bcmhana_sv MACH_BCMHANA_SV BCMHANA_SV 3310
bcmhana_tablet MACH_BCMHANA_TABLET BCMHANA_TABLET 3311
@@ -621,11 +3272,13 @@ koi MACH_KOI KOI 3312
ts4800 MACH_TS4800 TS4800 3313
tqma9263 MACH_TQMA9263 TQMA9263 3314
holiday MACH_HOLIDAY HOLIDAY 3315
+dma_6410 MACH_DMA6410 DMA6410 3316
pcats_overlay MACH_PCATS_OVERLAY PCATS_OVERLAY 3317
hwgw6410 MACH_HWGW6410 HWGW6410 3318
shenzhou MACH_SHENZHOU SHENZHOU 3319
cwme9210 MACH_CWME9210 CWME9210 3320
cwme9210js MACH_CWME9210JS CWME9210JS 3321
+pgs_v1 MACH_PGS_SITARA PGS_SITARA 3322
colibri_tegra2 MACH_COLIBRI_TEGRA2 COLIBRI_TEGRA2 3323
w21 MACH_W21 W21 3324
polysat1 MACH_POLYSAT1 POLYSAT1 3325
@@ -691,11 +3344,15 @@ viprinet MACH_VIPRINET VIPRINET 3385
bockw MACH_BOCKW BOCKW 3386
eva2000 MACH_EVA2000 EVA2000 3387
steelyard MACH_STEELYARD STEELYARD 3388
+ea2468devkit MACH_LPC2468OEM LPC2468OEM 3389
+sdh001 MACH_MACH_SDH001 MACH_SDH001 3390
+fe2478mblox MACH_LPC2478MICROBLOX LPC2478MICROBLOX 3391
nsslsboard MACH_NSSLSBOARD NSSLSBOARD 3392
geneva_b5 MACH_GENEVA_B5 GENEVA_B5 3393
spear1340 MACH_SPEAR1340 SPEAR1340 3394
rexmas MACH_REXMAS REXMAS 3395
msm8960_cdp MACH_MSM8960_CDP MSM8960_CDP 3396
+msm8960_mtp MACH_MSM8960_MDP MSM8960_MDP 3397
msm8960_fluid MACH_MSM8960_FLUID MSM8960_FLUID 3398
msm8960_apq MACH_MSM8960_APQ MSM8960_APQ 3399
helios_v2 MACH_HELIOS_V2 HELIOS_V2 3400
@@ -727,6 +3384,7 @@ gt_i5700 MACH_GT_I5700 GT_I5700 3425
ctera_plug_c2 MACH_CTERA_PLUG_C2 CTERA_PLUG_C2 3426
marvelct MACH_MARVELCT MARVELCT 3427
ag11005 MACH_AG11005 AG11005 3428
+omap_tabletblaze MACH_OMAP_BLAZE OMAP_BLAZE 3429
vangogh MACH_VANGOGH VANGOGH 3430
matrix505 MACH_MATRIX505 MATRIX505 3431
oce_nigma MACH_OCE_NIGMA OCE_NIGMA 3432
@@ -766,6 +3424,7 @@ h1600 MACH_H1600 H1600 3465
mini210 MACH_MINI210 MINI210 3466
mini8168 MACH_MINI8168 MINI8168 3467
pc7308 MACH_PC7308 PC7308 3468
+ge863pro3 MACH_GE863 GE863 3469
kmm2m01 MACH_KMM2M01 KMM2M01 3470
mx51erebus MACH_MX51EREBUS MX51EREBUS 3471
wm8650refboard MACH_WM8650REFBOARD WM8650REFBOARD 3472
@@ -802,6 +3461,7 @@ shooter_k MACH_SHOOTER_K SHOOTER_K 3502
nspire MACH_NSPIRE NSPIRE 3503
mickxx MACH_MICKXX MICKXX 3504
lxmb MACH_LXMB LXMB 3505
+tmdxscbp6618x MACH_TMDXSCBP6616X TMDXSCBP6616X 3506
adam MACH_ADAM ADAM 3507
b1004 MACH_B1004 B1004 3508
oboea MACH_OBOEA OBOEA 3509
@@ -879,6 +3539,7 @@ bct MACH_BCT BCT 3582
tuscan MACH_TUSCAN TUSCAN 3583
xbt_sam9g45 MACH_XBT_SAM9G45 XBT_SAM9G45 3584
enbw_cmc MACH_ENBW_CMC ENBW_CMC 3585
+msm8x60_dragon MACH_APQ8060_DRAGON APQ8060_DRAGON 3586
ch104mx257 MACH_CH104MX257 CH104MX257 3587
openpri MACH_OPENPRI OPENPRI 3588
am335xevm MACH_AM335XEVM AM335XEVM 3589
@@ -900,6 +3561,7 @@ cinema MACH_CINEMA CINEMA 3604
cinema_tea MACH_CINEMA_TEA CINEMA_TEA 3605
cinema_coffee MACH_CINEMA_COFFEE CINEMA_COFFEE 3606
cinema_juice MACH_CINEMA_JUICE CINEMA_JUICE 3607
+linux_pad MACH_THEPAD THEPAD 3608
mx53_mirage2 MACH_MX53_MIRAGE2 MX53_MIRAGE2 3609
mx53_efikasb MACH_MX53_EFIKASB MX53_EFIKASB 3610
stm_b2000 MACH_STM_B2000 STM_B2000 3612
@@ -965,6 +3627,7 @@ pia_am35x MACH_PIA_AM35X PIA_AM35X 3671
cedar MACH_CEDAR CEDAR 3672
picasso_e MACH_PICASSO_E PICASSO_E 3673
samsung_e60 MACH_SAMSUNG_E60 SAMSUNG_E60 3674
+msm9615_cdp MACH_MDM9615 MDM9615 3675
sdvr_mini MACH_SDVR_MINI SDVR_MINI 3676
omap3_ij3k MACH_OMAP3_IJ3K OMAP3_IJ3K 3677
modasmc1 MACH_MODASMC1 MODASMC1 3678
@@ -992,6 +3655,8 @@ fmc_uic MACH_FMC_UIC FMC_UIC 3699
fmc_dcm MACH_FMC_DCM FMC_DCM 3700
batwm MACH_BATWM BATWM 3701
atlas6cb MACH_ATLAS6CB ATLAS6CB 3702
+quattro_f MACH_QUATTROF QUATTROF 3703
+quattro_u MACH_QUATTROU QUATTROU 3704
blue MACH_BLUE BLUE 3705
colorado MACH_COLORADO COLORADO 3706
popc MACH_POPC POPC 3707
@@ -999,15 +3664,19 @@ promwad_jade MACH_PROMWAD_JADE PROMWAD_JADE 3708
amp MACH_AMP AMP 3709
gnet_amp MACH_GNET_AMP GNET_AMP 3710
toques MACH_TOQUES TOQUES 3711
+apx4devkit MACH_APX4DEVKIT APX4DEVKIT 3712
dct_storm MACH_DCT_STORM DCT_STORM 3713
+dm8168z3 MACH_Z3 Z3 3714
owl MACH_OWL OWL 3715
cogent_csb1741 MACH_COGENT_CSB1741 COGENT_CSB1741 3716
+omap3_kiko MACH_OMAP3 OMAP3 3717
adillustra610 MACH_ADILLUSTRA610 ADILLUSTRA610 3718
ecafe_na04 MACH_ECAFE_NA04 ECAFE_NA04 3719
popct MACH_POPCT POPCT 3720
omap3_helena MACH_OMAP3_HELENA OMAP3_HELENA 3721
ach MACH_ACH ACH 3722
module_dtb MACH_MODULE_DTB MODULE_DTB 3723
+ratebox MACH_RACKBOX RACKBOX 3724
oslo_elisabeth MACH_OSLO_ELISABETH OSLO_ELISABETH 3725
tt01 MACH_TT01 TT01 3726
msm8930_cdp MACH_MSM8930_CDP MSM8930_CDP 3727
@@ -1038,6 +3707,7 @@ ptip_murnau MACH_PTIP_MURNAU PTIP_MURNAU 3752
ptip_classic MACH_PTIP_CLASSIC PTIP_CLASSIC 3753
mx53grb MACH_MX53GRB MX53GRB 3754
gagarin MACH_GAGARIN GAGARIN 3755
+msm7627a_qrd1 MACH_MSM7X27A_QRD1 MSM7X27A_QRD1 3756
nas2big MACH_NAS2BIG NAS2BIG 3757
superfemto MACH_SUPERFEMTO SUPERFEMTO 3758
teufel MACH_TEUFEL TEUFEL 3759
@@ -1087,6 +3757,7 @@ ubisys_g1 MACH_UBISYS_G1 UBISYS_G1 3802
mx53_pf1 MACH_MX53_PF1 MX53_PF1 3803
asanti MACH_ASANTI ASANTI 3804
volta MACH_VOLTA VOLTA 3805
+potenza MACH_S5P6450 S5P6450 3806
knight MACH_KNIGHT KNIGHT 3807
beaglebone MACH_BEAGLEBONE BEAGLEBONE 3808
becker MACH_BECKER BECKER 3809
@@ -1148,6 +3819,7 @@ primou MACH_PRIMOU PRIMOU 3864
primoc MACH_PRIMOC PRIMOC 3865
primoct MACH_PRIMOCT PRIMOCT 3866
a9500 MACH_A9500 A9500 3867
+pue_td MACH_PULSE_TD PULSE_TD 3868
pluto MACH_PLUTO PLUTO 3869
acfx100 MACH_ACFX100 ACFX100 3870
msm8625_rumi3 MACH_MSM8625_RUMI3 MSM8625_RUMI3 3871
@@ -1161,6 +3833,8 @@ valente_wx MACH_VALENTE_WX VALENTE_WX 3878
huangshans MACH_HUANGSHANS HUANGSHANS 3879
bosphorus1 MACH_BOSPHORUS1 BOSPHORUS1 3880
prima MACH_PRIMA PRIMA 3881
+meson3_skt MACH_M3_SKT M3_SKT 3882
+meson3_ref MACH_M3_REF M3_REF 3883
evita_ulk MACH_EVITA_ULK EVITA_ULK 3884
merisc600 MACH_MERISC600 MERISC600 3885
dolak MACH_DOLAK DOLAK 3886
@@ -1169,3 +3843,113 @@ elite_ulk MACH_ELITE_ULK ELITE_ULK 3888
pov2 MACH_POV2 POV2 3889
ipod_touch_2g MACH_IPOD_TOUCH_2G IPOD_TOUCH_2G 3890
da850_pqab MACH_DA850_PQAB DA850_PQAB 3891
+fermi MACH_FERMI FERMI 3892
+ccardwmx28 MACH_CCARDWMX28 CCARDWMX28 3893
+ccardmx28 MACH_CCARDMX28 CCARDMX28 3894
+fs20_fcm2050 MACH_FS20_FCM2050 FS20_FCM2050 3895
+kinetis MACH_KINETIS KINETIS 3896
+kai MACH_KAI KAI 3897
+bcthb2 MACH_BCTHB2 BCTHB2 3898
+inels3_cu MACH_INELS3_CU INELS3_CU 3899
+da850_juniper MACH_JUNIPER JUNIPER 3900
+da850_apollo MACH_DA850_APOLLO DA850_APOLLO 3901
+tracnas MACH_TRACNAS TRACNAS 3902
+mityarm335x MACH_MITYARM335X MITYARM335X 3903
+xcgz7x MACH_XCGZ7X XCGZ7X 3904
+cubox MACH_CUBOX CUBOX 3905
+terminator MACH_TERMINATOR TERMINATOR 3906
+eye03 MACH_EYE03 EYE03 3907
+kota3 MACH_KOTA3 KOTA3 3908
+mx53_nitrogen_k MACH_MX5 MX5 3909
+pscpe MACH_PSCPE PSCPE 3910
+akt1100 MACH_AKT1100 AKT1100 3911
+pcaaxl2 MACH_PCAAXL2 PCAAXL2 3912
+primodd_ct MACH_PRIMODD_CT PRIMODD_CT 3913
+nsbc MACH_NSBC NSBC 3914
+meson2_skt MACH_MESON2_SKT MESON2_SKT 3915
+meson2_ref MACH_MESON2_REF MESON2_REF 3916
+ccardwmx28js MACH_CCARDWMX28JS CCARDWMX28JS 3917
+ccardmx28js MACH_CCARDMX28JS CCARDMX28JS 3918
+indico MACH_INDICO INDICO 3919
+msm8960dt MACH_MSM8960DT MSM8960DT 3920
+primods MACH_PRIMODS PRIMODS 3921
+beluga_m1388 MACH_BELUGA_M1388 BELUGA_M1388 3922
+primotd MACH_PRIMOTD PRIMOTD 3923
+varan_master MACH_VARAN_MASTER VARAN_MASTER 3924
+primodd MACH_PRIMODD PRIMODD 3925
+jetduo MACH_JETDUO JETDUO 3926
+mx53_umobo MACH_MX53_UMOBO MX53_UMOBO 3927
+trats MACH_TRATS TRATS 3928
+starcraft MACH_STARCRAFT STARCRAFT 3929
+qseven_tegra2 MACH_QSEVEN_TEGRA2 QSEVEN_TEGRA2 3930
+lichee_sun4i_devbd MACH_LICHEE_SUN4I_DEVBD LICHEE_SUN4I_DEVBD 3931
+movenow MACH_MOVENOW MOVENOW 3932
+golf_u MACH_GOLF_U GOLF_U 3933
+msm7627a_evb MACH_MSM7627A_EVB MSM7627A_EVB 3934
+rambo MACH_RAMBO RAMBO 3935
+golfu MACH_GOLFU GOLFU 3936
+mango310 MACH_MANGO310 MANGO310 3937
+dns343 MACH_DNS343 DNS343 3938
+var_som_om44 MACH_VAR_SOM_OM44 VAR_SOM_OM44 3939
+naon MACH_NAON NAON 3940
+vp4000 MACH_VP4000 VP4000 3941
+impcard MACH_IMPCARD IMPCARD 3942
+smoovcam MACH_SMOOVCAM SMOOVCAM 3943
+cobham3725 MACH_COBHAM3725 COBHAM3725 3944
+cobham3730 MACH_COBHAM3730 COBHAM3730 3945
+cobham3703 MACH_COBHAM3703 COBHAM3703 3946
+quetzal MACH_QUETZAL QUETZAL 3947
+apq8064_cdp MACH_APQ8064_CDP APQ8064_CDP 3948
+apq8064_mtp MACH_APQ8064_MTP APQ8064_MTP 3949
+apq8064_fluid MACH_APQ8064_FLUID APQ8064_FLUID 3950
+apq8064_liquid MACH_APQ8064_LIQUID APQ8064_LIQUID 3951
+mango210 MACH_MANGO210 MANGO210 3952
+mango100 MACH_MANGO100 MANGO100 3953
+mango24 MACH_MANGO24 MANGO24 3954
+mango64 MACH_MANGO64 MANGO64 3955
+nsa320 MACH_NSA320 NSA320 3956
+elv_ccu2 MACH_ELV_CCU2 ELV_CCU2 3957
+triton_x00 MACH_TRITON_X00 TRITON_X00 3958
+triton_1500_2000 MACH_TRITON_1500_2000 TRITON_1500_2000 3959
+pogoplugv4 MACH_POGOPLUGV4 POGOPLUGV4 3960
+venus_cl MACH_VENUS_CL VENUS_CL 3961
+vulcano_g20 MACH_VULCANO_G20 VULCANO_G20 3962
+sgs_i9100 MACH_SGS_I9100 SGS_I9100 3963
+stsv2 MACH_STSV2 STSV2 3964
+csb1724 MACH_CSB1724 CSB1724 3965
+omapl138_lcdk MACH_OMAPL138_LCDK OMAPL138_LCDK 3966
+jel_dd MACH_JEWEL_DD JEWEL_DD 3967
+pvd_mx25 MACH_PVD_MX25 PVD_MX25 3968
+meson6_skt MACH_MESON6_SKT MESON6_SKT 3969
+meson6_ref MACH_MESON6_REF MESON6_REF 3970
+pxm MACH_PXM PXM 3971
+stuttgart MACH_S3 S3 3972
+pogoplugv3 MACH_POGOPLUGV3 POGOPLUGV3 3973
+mlp89626 MACH_MLP89626 MLP89626 3974
+iomegahmndce MACH_IOMEGAHMNDCE IOMEGAHMNDCE 3975
+pogoplugv3pci MACH_POGOPLUGV3PCI POGOPLUGV3PCI 3976
+bntv250 MACH_BNTV250 BNTV250 3977
+mx53_qseven MACH_MX53_QSEVEN MX53_QSEVEN 3978
+gtl_it1100 MACH_GTL_IT1100 GTL_IT1100 3979
+mx6q_sabresd MACH_MX6Q_SABRESD MX6Q_SABRESD 3980
+mt4 MACH_MT4 MT4 3981
+jumbo_d MACH_JUMBO_D JUMBO_D 3982
+jumbo_i MACH_JUMBO_I JUMBO_I 3983
+fs20_dmp MACH_FS20_DMP FS20_DMP 3984
+dns320 MACH_DNS320 DNS320 3985
+mx28bacos MACH_MX28BACOS MX28BACOS 3986
+tl80 MACH_TL80 TL80 3987
+polatis_nic_1001 MACH_POLATIS_NIC_1001 POLATIS_NIC_1001 3988
+tely MACH_TELY TELY 3989
+u8520 MACH_U8520 U8520 3990
+manta MACH_MANTA MANTA 3991
+spear1340_lcad MACH_SPEAR_EM_S900 SPEAR_EM_S900 3992
+mpq8064_cdp MACH_MPQ8064_CDP MPQ8064_CDP 3993
+mpq8064_stb MACH_MPQ8064_STB MPQ8064_STB 3994
+mpq8064_dtv MACH_MPQ8064_DTV MPQ8064_DTV 3995
+dm368som MACH_DM368SOM DM368SOM 3996
+gprisb2 MACH_GPRISB2 GPRISB2 3997
+chammid MACH_CHAMMID CHAMMID 3998
+seoul2 MACH_SEOUL2 SEOUL2 3999
+omap4_nooktablet MACH_OMAP4_NOOKTABLET OMAP4_NOOKTABLET 4000
+aalto MACH_AALTO AALTO 4001
diff --git a/block/partitions/Kconfig b/block/partitions/Kconfig
index cb5f0a3f1b0..097be1934ee 100644
--- a/block/partitions/Kconfig
+++ b/block/partitions/Kconfig
@@ -68,6 +68,25 @@ config ACORN_PARTITION_RISCIX
of machines called RISCiX. If you say 'Y' here, Linux will be able
to read disks partitioned under RISCiX.
+config BLKDEV_PARTITION
+ bool "Blockdev commandline partition support" if PARTITION_ADVANCED
+ default n
+ help
+ Say Y if you like to setup partitions for block devices by reading
+ from the kernel command line (kernel boot arguments).
+
+ The format of the partitions on the command line:
+ blkdevparts=<blkdev-def>[;<blkdev-def>]
+ <blkdev-def> := <blkdev-id>:<partdef>[,<partdef>]
+ <partdef> := <size>[@<offset>]
+
+ <blkdev-id> := unique id used to map driver to blockdev name
+ <size> := size in numbers of sectors
+ <offset> := offset in sectors for partition to start at
+
+ Example:
+ blkdevparts=mmc0:1024@0,524288@1024;mmc1:8192@0,8192@8192
+
config OSF_PARTITION
bool "Alpha OSF partition support" if PARTITION_ADVANCED
default y if ALPHA
diff --git a/block/partitions/Makefile b/block/partitions/Makefile
index 03af8eac51d..48b216c53db 100644
--- a/block/partitions/Makefile
+++ b/block/partitions/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_BLOCK) := check.o
obj-$(CONFIG_ACORN_PARTITION) += acorn.o
obj-$(CONFIG_AMIGA_PARTITION) += amiga.o
obj-$(CONFIG_ATARI_PARTITION) += atari.o
+obj-$(CONFIG_BLKDEV_PARTITION) += blkdev_parts.o
obj-$(CONFIG_MAC_PARTITION) += mac.o
obj-$(CONFIG_LDM_PARTITION) += ldm.o
obj-$(CONFIG_MSDOS_PARTITION) += msdos.o
diff --git a/block/partitions/blkdev_parts.c b/block/partitions/blkdev_parts.c
new file mode 100755
index 00000000000..030565b7ce7
--- /dev/null
+++ b/block/partitions/blkdev_parts.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ulf Hansson <ulf.hansson@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Create partitions for block devices by reading from the kernel
+ * command line (kernel boot arguments).
+ *
+ */
+
+#include "check.h"
+#include "blkdev_parts.h"
+
+static char *cmdline;
+
+/*
+ * This is the handler for our kernel commandline parameter,
+ * called from main.c::checksetup().
+ * Note that we can not yet kmalloc() anything, so we only save
+ * the commandline for later processing.
+ */
+static int cmdline_setup(char *s)
+{
+ cmdline = s;
+ return 1;
+}
+__setup("blkdevparts=", cmdline_setup);
+
+/* Parse for a matching blkdev-id and return pointer to partdef */
+static char *parse_blkdev_id(char *blkdev_name)
+{
+ int blkdev_id_len;
+ char *p, *blkdev_id;
+
+ /* Start parsing for a matching blkdev-id */
+ p = blkdev_id = cmdline;
+ while (blkdev_id != NULL) {
+
+ /* Find the end of the blkdev-id string */
+ p = strchr(blkdev_id, ':');
+ if (p == NULL)
+ return NULL;
+
+ /* Check if we found a matching blkdev-id */
+ blkdev_id_len = p - blkdev_id;
+ if (strlen(blkdev_name) == blkdev_id_len) {
+ if (strncmp(blkdev_name, blkdev_id, blkdev_id_len) == 0)
+ return p;
+ }
+
+ /* Move to next blkdev-id string if there is one */
+ blkdev_id = strchr(p, ';');
+ if (blkdev_id != NULL)
+ blkdev_id++;
+ }
+ return NULL;
+}
+
+static int parse_partdef(char **part, struct parsed_partitions *state, int part_nbr)
+{
+ sector_t size, offset;
+ char *p = *part;
+
+ /* Skip the beginning "," or ":" */
+ p++;
+
+ /* Fetch and verify size from partdef */
+ size = simple_strtoull(p, &p, 10);
+ if ((size == 0) || (*p != '@'))
+ return 0;
+
+ /* Skip the "@" */
+ p++;
+
+ /* Fetch offset from partdef and check if there are more parts */
+ offset = simple_strtoull(p, &p, 10);
+ if (*p == ',')
+ *part = p;
+ else
+ *part = NULL;
+
+ /* Add partition to state */
+ put_partition(state, part_nbr, offset, size);
+ printk(KERN_INFO "\nPartition: size=%llu, offset=%llu\n",
+ (unsigned long long) size,
+ (unsigned long long) offset);
+ return 1;
+}
+
+static int parse_blkdev_parts(char *blkdev_name, struct parsed_partitions *state)
+{
+ char *partdef;
+ int part_nbr = 0;
+
+ /* Find partdef */
+ partdef = parse_blkdev_id(blkdev_name);
+
+ /* Add parts */
+ while (partdef != NULL) {
+ /* Find next part and add it to state */
+ part_nbr++;
+ if (!parse_partdef(&partdef, state, part_nbr))
+ return 0;
+ }
+ return part_nbr;
+}
+
+int blkdev_partition(struct parsed_partitions *state)
+{
+ char blkdev_name[BDEVNAME_SIZE];
+
+ /* Check if there are any partitions to handle */
+ if (cmdline == NULL)
+ return 0;
+
+ /* Get the name of the blockdevice we are operating upon */
+ if (bdevname(state->bdev, blkdev_name) == NULL) {
+ printk(KERN_WARNING "Could not get a blkdev name\n");
+ return 0;
+ }
+
+ /* Parse for partitions and add them to the state */
+ return parse_blkdev_parts(blkdev_name, state);
+}
+
diff --git a/block/partitions/blkdev_parts.h b/block/partitions/blkdev_parts.h
new file mode 100755
index 00000000000..16d2b571625
--- /dev/null
+++ b/block/partitions/blkdev_parts.h
@@ -0,0 +1,14 @@
+/*
+ *
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ulf Hansson <ulf.hansson@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Create partitions for block devices by reading from the kernel
+ * command line (kernel boot arguments).
+ *
+ */
+
+int blkdev_partition(struct parsed_partitions *state);
+
diff --git a/block/partitions/check.c b/block/partitions/check.c
index bc908672c97..3020c577c3b 100644
--- a/block/partitions/check.c
+++ b/block/partitions/check.c
@@ -22,6 +22,7 @@
#include "acorn.h"
#include "amiga.h"
#include "atari.h"
+#include "blkdev_parts.h"
#include "ldm.h"
#include "mac.h"
#include "msdos.h"
@@ -41,6 +42,9 @@ static int (*check_part[])(struct parsed_partitions *) = {
* Probe partition formats with tables at disk address 0
* that also have an ADFS boot block at 0xdc0.
*/
+#ifdef CONFIG_BLKDEV_PARTITION
+ blkdev_partition,
+#endif
#ifdef CONFIG_ACORN_PARTITION_ICS
adfspart_check_ICS,
#endif
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index cc273226dbd..e98838a060e 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -117,7 +117,7 @@ static int amba_legacy_resume(struct device *dev)
#ifdef CONFIG_SUSPEND
-static int amba_pm_suspend(struct device *dev)
+int amba_pm_suspend(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -135,7 +135,7 @@ static int amba_pm_suspend(struct device *dev)
return ret;
}
-static int amba_pm_resume(struct device *dev)
+int amba_pm_resume(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -162,7 +162,7 @@ static int amba_pm_resume(struct device *dev)
#ifdef CONFIG_HIBERNATE_CALLBACKS
-static int amba_pm_freeze(struct device *dev)
+int amba_pm_freeze(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -180,7 +180,7 @@ static int amba_pm_freeze(struct device *dev)
return ret;
}
-static int amba_pm_thaw(struct device *dev)
+int amba_pm_thaw(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -198,7 +198,7 @@ static int amba_pm_thaw(struct device *dev)
return ret;
}
-static int amba_pm_poweroff(struct device *dev)
+int amba_pm_poweroff(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -216,7 +216,7 @@ static int amba_pm_poweroff(struct device *dev)
return ret;
}
-static int amba_pm_restore(struct device *dev)
+int amba_pm_restore(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 5138927a416..6f86f8ca9b0 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -19,13 +19,27 @@ config DW_APB_TIMER
config CLKSRC_DBX500_PRCMU
bool "Clocksource PRCMU Timer"
depends on UX500_SOC_DB5500 || UX500_SOC_DB8500
- default y
+ default y if UX500_SOC_DB8500
help
Use the always on PRCMU Timer as clocksource
config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
- bool "Clocksource PRCMU Timer sched_clock"
- depends on (CLKSRC_DBX500_PRCMU && !NOMADIK_MTU_SCHED_CLOCK)
+ bool
+ depends on CLKSRC_DBX500_PRCMU
+ select HAVE_SCHED_CLOCK
+ help
+ Use the always on PRCMU Timer as sched_clock
+
+config CLKSRC_DB5500_MTIMER
+ bool "Clocksource MTIMER"
+ depends on UX500_SOC_DB5500
default y
help
+ Use the always on MTIMER as clocksource
+
+config CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+ bool
+ depends on CLKSRC_DB5500_MTIMER
+ select HAVE_SCHED_CLOCK
+ help
Use the always on PRCMU Timer as sched_clock
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 8d81a1d3265..9b10f6b7536 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
-obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o \ No newline at end of file
+obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
+obj-$(CONFIG_CLKSRC_DB5500_MTIMER) += db5500-mtimer.o
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index c26c369eb9e..dc71e432dc5 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -14,6 +14,9 @@
*/
#include <linux/clockchips.h>
#include <linux/clksrc-dbx500-prcmu.h>
+#ifdef CONFIG_BOOTTIME
+#include <linux/boottime.h>
+#endif
#include <asm/sched_clock.h>
@@ -68,6 +71,23 @@ static u32 notrace dbx500_prcmu_sched_clock_read(void)
#endif
+#ifdef CONFIG_BOOTTIME
+static unsigned long __init boottime_get_time(void)
+{
+ return div_s64(clocksource_cyc2ns(clocksource_dbx500_prcmu.read(
+ &clocksource_dbx500_prcmu),
+ clocksource_dbx500_prcmu.mult,
+ clocksource_dbx500_prcmu.shift),
+ 1000);
+}
+
+static struct boottime_timer __initdata boottime_timer = {
+ .init = NULL,
+ .get_time = boottime_get_time,
+ .finalize = NULL,
+};
+#endif
+
void __init clksrc_dbx500_prcmu_init(void __iomem *base)
{
clksrc_dbx500_timer_base = base;
@@ -90,4 +110,7 @@ void __init clksrc_dbx500_prcmu_init(void __iomem *base)
32, RATE_32K);
#endif
clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
+#ifdef CONFIG_BOOTTIME
+ boottime_activate(&boottime_timer);
+#endif
}
diff --git a/drivers/clocksource/db5500-mtimer.c b/drivers/clocksource/db5500-mtimer.c
new file mode 100644
index 00000000000..5e64da19e66
--- /dev/null
+++ b/drivers/clocksource/db5500-mtimer.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/clockchips.h>
+#include <linux/clksrc-db5500-mtimer.h>
+#include <linux/boottime.h>
+
+#include <asm/sched_clock.h>
+
+#define MTIMER_PRIMARY_COUNTER 0x18
+
+static void __iomem *db5500_mtimer_base;
+
+#ifdef CONFIG_CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+static DEFINE_CLOCK_DATA(cd);
+
+unsigned long long notrace sched_clock(void)
+{
+ u32 cyc;
+
+ if (unlikely(!db5500_mtimer_base))
+ return 0;
+
+ cyc = readl_relaxed(db5500_mtimer_base + MTIMER_PRIMARY_COUNTER);
+
+ return cyc_to_sched_clock(&cd, cyc, (u32)~0);
+}
+
+static void notrace db5500_mtimer_update_sched_clock(void)
+{
+ u32 cyc = readl_relaxed(db5500_mtimer_base + MTIMER_PRIMARY_COUNTER);
+ update_sched_clock(&cd, cyc, (u32)~0);
+}
+#endif
+
+#ifdef CONFIG_BOOTTIME
+static unsigned long __init boottime_get_time(void)
+{
+ return sched_clock();
+}
+
+static struct boottime_timer __initdata boottime_timer = {
+ .init = NULL,
+ .get_time = boottime_get_time,
+ .finalize = NULL,
+};
+#endif
+
+void __init db5500_mtimer_init(void __iomem *base)
+{
+ db5500_mtimer_base = base;
+
+ clocksource_mmio_init(base + MTIMER_PRIMARY_COUNTER, "mtimer", 32768,
+ 400, 32, clocksource_mmio_readl_up);
+
+#ifdef CONFIG_CLKSRC_DB5500_MTIMER_SCHED_CLOCK
+ init_sched_clock(&cd, db5500_mtimer_update_sched_clock,
+ 32, 32768);
+#endif
+ boottime_activate(&boottime_timer);
+}
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 9531fc2eda2..44aa7093235 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -39,7 +39,8 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
##################################################################################
# ARM SoC drivers
-obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
+obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o
+obj-$(CONFIG_UX500_SOC_DB5500) += dbx500-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 7f2f149ae40..4de1fff790f 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -376,6 +376,27 @@ show_one(scaling_cur_freq, cur);
static int __cpufreq_set_policy(struct cpufreq_policy *data,
struct cpufreq_policy *policy);
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max)
+{
+ int ret;
+ struct cpufreq_policy new_policy;
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+
+ ret = cpufreq_get_policy(&new_policy, cpu);
+ if (ret)
+ return -EINVAL;
+
+ new_policy.min = min;
+ new_policy.max = max;
+
+ ret = __cpufreq_set_policy(policy, &new_policy);
+ policy->user_policy.min = policy->min;
+ policy->user_policy.max = policy->max;
+
+ return ret;
+}
+EXPORT_SYMBOL(cpufreq_update_freq);
+
/**
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
*/
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 836e9b062e5..765256e4904 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -79,7 +79,6 @@ struct cpu_dbs_info_s {
cputime64_t prev_cpu_wall;
cputime64_t prev_cpu_nice;
struct cpufreq_policy *cur_policy;
- struct delayed_work work;
struct cpufreq_frequency_table *freq_table;
unsigned int freq_lo;
unsigned int freq_lo_jiffies;
@@ -95,8 +94,10 @@ struct cpu_dbs_info_s {
struct mutex timer_mutex;
};
static DEFINE_PER_CPU(struct cpu_dbs_info_s, od_cpu_dbs_info);
+static DEFINE_PER_CPU(struct delayed_work, ondemand_work);
static unsigned int dbs_enable; /* number of CPUs using this policy */
+static ktime_t time_stamp;
/*
* dbs_mutex protects dbs_enable in governor start/stop.
@@ -290,22 +291,23 @@ static void update_sampling_rate(unsigned int new_rate)
mutex_lock(&dbs_info->timer_mutex);
- if (!delayed_work_pending(&dbs_info->work)) {
+ if (!delayed_work_pending(&per_cpu(ondemand_work, cpu))) {
mutex_unlock(&dbs_info->timer_mutex);
continue;
}
next_sampling = jiffies + usecs_to_jiffies(new_rate);
- appointed_at = dbs_info->work.timer.expires;
+ appointed_at = per_cpu(ondemand_work, cpu).timer.expires;
if (time_before(next_sampling, appointed_at)) {
mutex_unlock(&dbs_info->timer_mutex);
- cancel_delayed_work_sync(&dbs_info->work);
+ cancel_delayed_work_sync(&per_cpu(ondemand_work, cpu));
mutex_lock(&dbs_info->timer_mutex);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work,
+ schedule_delayed_work_on(dbs_info->cpu,
+ &per_cpu(ondemand_work, cpu),
usecs_to_jiffies(new_rate));
}
@@ -449,6 +451,26 @@ static struct attribute_group dbs_attr_group = {
/************************** sysfs end ************************/
+static bool dbs_sw_coordinated_cpus(void)
+{
+ struct cpu_dbs_info_s *dbs_info;
+ struct cpufreq_policy *policy;
+ int i = 0;
+ int j;
+
+ dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ policy = dbs_info->cur_policy;
+
+ for_each_cpu(j, policy->cpus) {
+ i++;
+ }
+
+ if (i > 1)
+ return true; /* Dependant CPUs */
+ else
+ return false;
+}
+
static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
{
if (dbs_tuners_ins.powersave_bias)
@@ -463,7 +485,6 @@ static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
{
unsigned int max_load_freq;
-
struct cpufreq_policy *policy;
unsigned int j;
@@ -598,20 +619,42 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
static void do_dbs_timer(struct work_struct *work)
{
- struct cpu_dbs_info_s *dbs_info =
- container_of(work, struct cpu_dbs_info_s, work.work);
- unsigned int cpu = dbs_info->cpu;
- int sample_type = dbs_info->sample_type;
-
+ struct cpu_dbs_info_s *dbs_info;
+ unsigned int cpu = smp_processor_id();
+ int sample_type;
int delay;
+ bool sample = true;
+
+ /* If SW dependant CPUs, use CPU 0 as leader */
+ if (dbs_sw_coordinated_cpus()) {
+
+ ktime_t time_now;
+ s64 delta_us;
- mutex_lock(&dbs_info->timer_mutex);
+ dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ mutex_lock(&dbs_info->timer_mutex);
+
+ time_now = ktime_get();
+ delta_us = ktime_us_delta(time_now, time_stamp);
+
+ /* Do nothing if we recently have sampled */
+ if (delta_us < (s64)(dbs_tuners_ins.sampling_rate / 2))
+ sample = false;
+ else
+ time_stamp = time_now;
+ } else {
+ dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+ mutex_lock(&dbs_info->timer_mutex);
+ }
+
+ sample_type = dbs_info->sample_type;
/* Common NORMAL_SAMPLE setup */
dbs_info->sample_type = DBS_NORMAL_SAMPLE;
if (!dbs_tuners_ins.powersave_bias ||
sample_type == DBS_NORMAL_SAMPLE) {
- dbs_check_cpu(dbs_info);
+ if (sample)
+ dbs_check_cpu(dbs_info);
if (dbs_info->freq_lo) {
/* Setup timer for SUB_SAMPLE */
dbs_info->sample_type = DBS_SUB_SAMPLE;
@@ -627,15 +670,17 @@ static void do_dbs_timer(struct work_struct *work)
delay -= jiffies % delay;
}
} else {
- __cpufreq_driver_target(dbs_info->cur_policy,
- dbs_info->freq_lo, CPUFREQ_RELATION_H);
+ if (sample)
+ __cpufreq_driver_target(dbs_info->cur_policy,
+ dbs_info->freq_lo,
+ CPUFREQ_RELATION_H);
delay = dbs_info->freq_lo_jiffies;
}
- schedule_delayed_work_on(cpu, &dbs_info->work, delay);
+ schedule_delayed_work_on(cpu, &per_cpu(ondemand_work, cpu), delay);
mutex_unlock(&dbs_info->timer_mutex);
}
-static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
+static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info, int cpu)
{
/* We want all CPUs to do sampling nearly on same jiffy */
int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate);
@@ -644,13 +689,18 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info)
delay -= jiffies % delay;
dbs_info->sample_type = DBS_NORMAL_SAMPLE;
- INIT_DELAYED_WORK_DEFERRABLE(&dbs_info->work, do_dbs_timer);
- schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work, delay);
+ cancel_delayed_work_sync(&per_cpu(ondemand_work, cpu));
+ schedule_delayed_work_on(cpu, &per_cpu(ondemand_work, cpu), delay);
+}
+
+static inline void dbs_timer_exit(int cpu)
+{
+ cancel_delayed_work_sync(&per_cpu(ondemand_work, cpu));
}
-static inline void dbs_timer_exit(struct cpu_dbs_info_s *dbs_info)
+static void dbs_timer_exit_per_cpu(struct work_struct *dummy)
{
- cancel_delayed_work_sync(&dbs_info->work);
+ dbs_timer_exit(smp_processor_id());
}
/*
@@ -676,6 +726,42 @@ static int should_io_be_busy(void)
return 0;
}
+static int __cpuinit cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned long)hcpu;
+ struct device *cpu_dev;
+ struct cpu_dbs_info_s *dbs_info;
+
+ if (dbs_sw_coordinated_cpus())
+ dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ else
+ dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
+
+ cpu_dev = get_cpu_device(cpu);
+ if (cpu_dev) {
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ dbs_timer_init(dbs_info, cpu);
+ break;
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ dbs_timer_exit(cpu);
+ break;
+ case CPU_DOWN_FAILED:
+ case CPU_DOWN_FAILED_FROZEN:
+ dbs_timer_init(dbs_info, cpu);
+ break;
+ }
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __refdata ondemand_cpu_notifier = {
+ .notifier_call = cpu_callback,
+};
+
static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
unsigned int event)
{
@@ -704,9 +790,13 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
if (dbs_tuners_ins.ignore_nice)
j_dbs_info->prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
+ mutex_init(&j_dbs_info->timer_mutex);
+ INIT_DELAYED_WORK_DEFERRABLE(&per_cpu(ondemand_work, j),
+ do_dbs_timer);
+
+ j_dbs_info->rate_mult = 1;
}
this_dbs_info->cpu = cpu;
- this_dbs_info->rate_mult = 1;
ondemand_powersave_bias_init_cpu(cpu);
/*
* Start the timerschedule work, when this governor
@@ -736,21 +826,46 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
}
mutex_unlock(&dbs_mutex);
- mutex_init(&this_dbs_info->timer_mutex);
- dbs_timer_init(this_dbs_info);
+ /* If SW coordinated CPUs then register notifier */
+ if (dbs_sw_coordinated_cpus()) {
+ register_hotcpu_notifier(&ondemand_cpu_notifier);
+
+ for_each_cpu(j, policy->cpus) {
+ struct cpu_dbs_info_s *j_dbs_info;
+
+ j_dbs_info = &per_cpu(od_cpu_dbs_info, 0);
+ dbs_timer_init(j_dbs_info, j);
+ }
+
+ /* Initiate timer time stamp */
+ time_stamp = ktime_get();
+
+
+ } else
+ dbs_timer_init(this_dbs_info, cpu);
break;
case CPUFREQ_GOV_STOP:
- dbs_timer_exit(this_dbs_info);
+
+ dbs_timer_exit(cpu);
mutex_lock(&dbs_mutex);
mutex_destroy(&this_dbs_info->timer_mutex);
dbs_enable--;
mutex_unlock(&dbs_mutex);
- if (!dbs_enable)
+ if (!dbs_enable) {
sysfs_remove_group(cpufreq_global_kobject,
&dbs_attr_group);
+ if (dbs_sw_coordinated_cpus()) {
+ /*
+ * Make sure all pending timers/works are
+ * stopped.
+ */
+ schedule_on_each_cpu(dbs_timer_exit_per_cpu);
+ unregister_hotcpu_notifier(&ondemand_cpu_notifier);
+ }
+ }
break;
case CPUFREQ_GOV_LIMITS:
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
deleted file mode 100644
index 0bf1b8910ee..00000000000
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) STMicroelectronics 2009
- * Copyright (C) ST-Ericsson SA 2010
- *
- * License Terms: GNU General Public License v2
- * Author: Sundar Iyer <sundar.iyer@stericsson.com>
- * Author: Martin Persson <martin.persson@stericsson.com>
- * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
- *
- */
-#include <linux/kernel.h>
-#include <linux/cpufreq.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/mfd/dbx500-prcmu.h>
-#include <mach/id.h>
-
-static struct cpufreq_frequency_table freq_table[] = {
- [0] = {
- .index = 0,
- .frequency = 200000,
- },
- [1] = {
- .index = 1,
- .frequency = 400000,
- },
- [2] = {
- .index = 2,
- .frequency = 800000,
- },
- [3] = {
- /* Used for MAX_OPP, if available */
- .index = 3,
- .frequency = CPUFREQ_TABLE_END,
- },
- [4] = {
- .index = 4,
- .frequency = CPUFREQ_TABLE_END,
- },
-};
-
-static enum arm_opp idx2opp[] = {
- ARM_EXTCLK,
- ARM_50_OPP,
- ARM_100_OPP,
- ARM_MAX_OPP
-};
-
-static struct freq_attr *db8500_cpufreq_attr[] = {
- &cpufreq_freq_attr_scaling_available_freqs,
- NULL,
-};
-
-static int db8500_cpufreq_verify_speed(struct cpufreq_policy *policy)
-{
- return cpufreq_frequency_table_verify(policy, freq_table);
-}
-
-static int db8500_cpufreq_target(struct cpufreq_policy *policy,
- unsigned int target_freq,
- unsigned int relation)
-{
- struct cpufreq_freqs freqs;
- unsigned int idx;
-
- /* scale the target frequency to one of the extremes supported */
- if (target_freq < policy->cpuinfo.min_freq)
- target_freq = policy->cpuinfo.min_freq;
- if (target_freq > policy->cpuinfo.max_freq)
- target_freq = policy->cpuinfo.max_freq;
-
- /* Lookup the next frequency */
- if (cpufreq_frequency_table_target
- (policy, freq_table, target_freq, relation, &idx)) {
- return -EINVAL;
- }
-
- freqs.old = policy->cur;
- freqs.new = freq_table[idx].frequency;
-
- if (freqs.old == freqs.new)
- return 0;
-
- /* pre-change notification */
- for_each_cpu(freqs.cpu, policy->cpus)
- cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-
- /* request the PRCM unit for opp change */
- if (prcmu_set_arm_opp(idx2opp[idx])) {
- pr_err("db8500-cpufreq: Failed to set OPP level\n");
- return -EINVAL;
- }
-
- /* post change notification */
- for_each_cpu(freqs.cpu, policy->cpus)
- cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
-
- return 0;
-}
-
-static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
-{
- int i;
- /* request the prcm to get the current ARM opp */
- for (i = 0; prcmu_get_arm_opp() != idx2opp[i]; i++)
- ;
- return freq_table[i].frequency;
-}
-
-static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
-{
- int i, res;
-
- BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
-
- if (prcmu_has_arm_maxopp())
- freq_table[3].frequency = 1000000;
-
- pr_info("db8500-cpufreq : Available frequencies:\n");
- for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
- pr_info(" %d Mhz\n", freq_table[i].frequency/1000);
-
- /* get policy fields based on the table */
- res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
- if (!res)
- cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
- else {
- pr_err("db8500-cpufreq : Failed to read policy table\n");
- return res;
- }
-
- policy->min = policy->cpuinfo.min_freq;
- policy->max = policy->cpuinfo.max_freq;
- policy->cur = db8500_cpufreq_getspeed(policy->cpu);
- policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
-
- /*
- * FIXME : Need to take time measurement across the target()
- * function with no/some/all drivers in the notification
- * list.
- */
- policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
-
- /* policy sharing between dual CPUs */
- cpumask_copy(policy->cpus, cpu_present_mask);
-
- policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
-
- return 0;
-}
-
-static struct cpufreq_driver db8500_cpufreq_driver = {
- .flags = CPUFREQ_STICKY,
- .verify = db8500_cpufreq_verify_speed,
- .target = db8500_cpufreq_target,
- .get = db8500_cpufreq_getspeed,
- .init = db8500_cpufreq_init,
- .name = "DB8500",
- .attr = db8500_cpufreq_attr,
-};
-
-static int __init db8500_cpufreq_register(void)
-{
- if (!cpu_is_u8500v20_or_later())
- return -ENODEV;
-
- pr_info("cpufreq for DB8500 started\n");
- return cpufreq_register_driver(&db8500_cpufreq_driver);
-}
-device_initcall(db8500_cpufreq_register);
diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c
new file mode 100644
index 00000000000..a6f991e2fb6
--- /dev/null
+++ b/drivers/cpufreq/dbx500-cpufreq.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) STMicroelectronics 2009
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com>
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <mach/id.h>
+
+static struct cpufreq_frequency_table db8500_freq_table[] = {
+ [0] = {
+ .index = 0,
+ .frequency = 200000,
+ },
+ [1] = {
+ .index = 1,
+ .frequency = 400000,
+ },
+ [2] = {
+ .index = 2,
+ .frequency = 800000,
+ },
+ [3] = {
+ /* Used for MAX_OPP, if available */
+ .index = 3,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+ [4] = {
+ .index = 4,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+};
+
+static struct cpufreq_frequency_table db5500_freq_table[] = {
+ [0] = {
+ .index = 0,
+ .frequency = 200000,
+ },
+ [1] = {
+ .index = 1,
+ .frequency = 396500,
+ },
+ [2] = {
+ .index = 2,
+ .frequency = 793000,
+ },
+ [3] = {
+ .index = 3,
+ .frequency = CPUFREQ_TABLE_END,
+ },
+};
+
+static struct cpufreq_frequency_table *freq_table;
+static int freq_table_len;
+
+static enum arm_opp db8500_idx2opp[] = {
+ ARM_EXTCLK,
+ ARM_50_OPP,
+ ARM_100_OPP,
+ ARM_MAX_OPP
+};
+
+static enum arm_opp db5500_idx2opp[] = {
+ ARM_EXTCLK,
+ ARM_50_OPP,
+ ARM_100_OPP,
+};
+
+static enum arm_opp *idx2opp;
+
+static struct freq_attr *dbx500_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static int dbx500_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ return cpufreq_frequency_table_verify(policy, freq_table);
+}
+
+static int dbx500_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_freqs freqs;
+ unsigned int idx;
+
+ /* scale the target frequency to one of the extremes supported */
+ if (target_freq < policy->cpuinfo.min_freq)
+ target_freq = policy->cpuinfo.min_freq;
+ if (target_freq > policy->cpuinfo.max_freq)
+ target_freq = policy->cpuinfo.max_freq;
+
+ /* Lookup the next frequency */
+ if (cpufreq_frequency_table_target
+ (policy, freq_table, target_freq, relation, &idx)) {
+ return -EINVAL;
+ }
+
+ freqs.old = policy->cur;
+ freqs.new = freq_table[idx].frequency;
+
+ if (freqs.old == freqs.new)
+ return 0;
+
+ /* pre-change notification */
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ BUG_ON(idx >= freq_table_len);
+
+ /* request the PRCM unit for opp change */
+ if (prcmu_set_arm_opp(idx2opp[idx])) {
+ pr_err("ux500-cpufreq: Failed to set OPP level\n");
+ return -EINVAL;
+ }
+
+ /* post change notification */
+ for_each_cpu(freqs.cpu, policy->cpus)
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+ return 0;
+}
+
+static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu)
+{
+ int i;
+ enum arm_opp current_opp;
+
+ current_opp = prcmu_get_arm_opp();
+
+ /* request the prcm to get the current ARM opp */
+ for (i = 0; i < freq_table_len; i++) {
+ if (current_opp == idx2opp[i])
+ return freq_table[i].frequency;
+ }
+
+ pr_err("cpufreq: ERROR: unknown opp %d given from prcmufw!\n",
+ current_opp);
+ BUG_ON(1);
+
+ /*
+ * Better to return something that might be correct than
+ * errno or zero, since clk_get_rate() won't do well with an errno.
+ */
+ return freq_table[0].frequency;
+}
+
+static void __init dbx500_cpufreq_init_maxopp_freq(void)
+{
+ struct prcmu_fw_version *fw_version = prcmu_get_fw_version();
+
+ if ((fw_version == NULL) || !prcmu_has_arm_maxopp())
+ return;
+
+ switch (fw_version->project) {
+ case PRCMU_FW_PROJECT_U8500:
+ case PRCMU_FW_PROJECT_U9500:
+ case PRCMU_FW_PROJECT_U8420:
+ freq_table[3].frequency = 1000000;
+ break;
+ case PRCMU_FW_PROJECT_U8500_C2:
+ case PRCMU_FW_PROJECT_U9500_C2:
+ case PRCMU_FW_PROJECT_U8520:
+ freq_table[3].frequency = 1150000;
+ break;
+ default:
+ break;
+ }
+}
+
+static bool initialized;
+
+static void __init dbx500_cpufreq_early_init(void)
+{
+ if (cpu_is_u5500()) {
+ freq_table = db5500_freq_table;
+ idx2opp = db5500_idx2opp;
+ freq_table_len = ARRAY_SIZE(db5500_freq_table);
+ } else if (cpu_is_u8500()) {
+ freq_table = db8500_freq_table;
+ idx2opp = db8500_idx2opp;
+ dbx500_cpufreq_init_maxopp_freq();
+ freq_table_len = ARRAY_SIZE(db8500_freq_table);
+ if (!prcmu_has_arm_maxopp())
+ freq_table_len--;
+ } else {
+ ux500_unknown_soc();
+ }
+ initialized = true;
+}
+
+/*
+ * This is called from localtimer initialization, via the clk_get_rate() for
+ * the smp_twd clock. This is way before cpufreq is initialized.
+ */
+unsigned long dbx500_cpufreq_getfreq(void)
+{
+ if (!initialized)
+ dbx500_cpufreq_early_init();
+
+ return dbx500_cpufreq_getspeed(0) * 1000;
+}
+
+int dbx500_cpufreq_percent2freq(int percent)
+{
+ int op;
+ int i;
+
+ switch (percent) {
+ case 0:
+ /* Fall through */
+ case 25:
+ op = ARM_EXTCLK;
+ break;
+ case 50:
+ op = ARM_50_OPP;
+ break;
+ case 100:
+ op = ARM_100_OPP;
+ break;
+ case 125:
+ if (cpu_is_u8500() && prcmu_has_arm_maxopp())
+ op = ARM_MAX_OPP;
+ else
+ op = ARM_100_OPP;
+ break;
+ default:
+ pr_err("cpufreq-dbx500: Incorrect arm target value (%d).\n",
+ percent);;
+ return -EINVAL;
+ break;
+ }
+
+ for (i = 0; idx2opp[i] != op && i < freq_table_len; i++)
+ ;
+
+ if (freq_table[i].frequency == CPUFREQ_TABLE_END) {
+ pr_err("cpufreq-dbx500: Matching frequency does not exist!\n");
+ return -EINVAL;
+ }
+
+ return freq_table[i].frequency;
+}
+
+int dbx500_cpufreq_get_limits(int cpu, int r,
+ unsigned int *min, unsigned int *max)
+{
+ int freq;
+ int ret;
+ static int old_freq;
+ struct cpufreq_policy p;
+
+ freq = dbx500_cpufreq_percent2freq(r);
+
+ if (freq < 0)
+ return -EINVAL;
+
+ if (freq != old_freq)
+ pr_debug("cpufreq-dbx500: set min arm freq to %d\n",
+ freq);
+
+ (*min) = freq;
+
+ ret = cpufreq_get_policy(&p, cpu);
+ if (ret) {
+ pr_err("cpufreq-dbx500: Failed to get policy.\n");
+ return -EINVAL;
+ }
+
+ (*max) = p.max;
+ return 0;
+}
+
+static int __cpuinit dbx500_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int res;
+
+ /* get policy fields based on the table */
+ res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ if (!res)
+ cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+ else {
+ pr_err("dbx500-cpufreq : Failed to read policy table\n");
+ return res;
+ }
+
+ policy->min = policy->cpuinfo.min_freq;
+ policy->max = policy->cpuinfo.max_freq;
+ policy->cur = dbx500_cpufreq_getspeed(policy->cpu);
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ /*
+ * FIXME : Need to take time measurement across the target()
+ * function with no/some/all drivers in the notification
+ * list.
+ */
+ policy->cpuinfo.transition_latency = 20 * 1000; /* in ns */
+
+ /* policy sharing between dual CPUs */
+ cpumask_copy(policy->cpus, cpu_present_mask);
+
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+
+ return 0;
+}
+
+static struct cpufreq_driver dbx500_cpufreq_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = dbx500_cpufreq_verify_speed,
+ .target = dbx500_cpufreq_target,
+ .get = dbx500_cpufreq_getspeed,
+ .init = dbx500_cpufreq_init,
+ .name = "DBX500",
+ .attr = dbx500_cpufreq_attr,
+};
+
+static int __init dbx500_cpufreq_register(void)
+{
+ int i;
+
+ if (!initialized)
+ dbx500_cpufreq_early_init();
+
+ pr_info("dbx500-cpufreq : Available frequencies:\n");
+
+ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
+ pr_info(" %d Mhz\n", freq_table[i].frequency / 1000);
+
+ return cpufreq_register_driver(&dbx500_cpufreq_driver);
+}
+device_initcall(dbx500_cpufreq_register);
diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
index 2ed1ac3513f..0fe43c3f598 100644
--- a/drivers/dma/ste_dma40.c
+++ b/drivers/dma/ste_dma40.c
@@ -345,6 +345,7 @@ struct d40_base {
int irq;
int num_phy_chans;
int num_log_chans;
+ struct device_dma_parameters dma_parms;
struct dma_device dma_both;
struct dma_device dma_slave;
struct dma_device dma_memcpy;
@@ -2577,6 +2578,14 @@ static int d40_set_runtime_config(struct dma_chan *chan,
return -EINVAL;
}
+ if (src_maxburst > 16) {
+ src_maxburst = 16;
+ dst_maxburst = src_maxburst * src_addr_width / dst_addr_width;
+ } else if (dst_maxburst > 16) {
+ dst_maxburst = 16;
+ src_maxburst = dst_maxburst * dst_addr_width / src_addr_width;
+ }
+
ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
src_addr_width,
src_maxburst);
@@ -2639,6 +2648,56 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
return -ENXIO;
}
+dma_addr_t stedma40_get_src_addr(struct dma_chan *chan)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ dma_addr_t addr;
+
+ if (chan_is_physical(d40c))
+ addr = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SSPTR);
+ else {
+ unsigned long lower;
+ unsigned long upper;
+
+ /*
+ * There is a potential for overflow between the time the two
+ * halves of the pointer are read.
+ */
+ lower = d40c->lcpa->lcsp0 & D40_MEM_LCSP0_SPTR_MASK;
+ upper = d40c->lcpa->lcsp1 & D40_MEM_LCSP1_SPTR_MASK;
+
+ addr = upper | lower;
+ }
+
+ return addr;
+}
+EXPORT_SYMBOL(stedma40_get_src_addr);
+
+dma_addr_t stedma40_get_dst_addr(struct dma_chan *chan)
+{
+ struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
+ dma_addr_t addr;
+
+ if (chan_is_physical(d40c))
+ addr = readl(d40c->base->virtbase + D40_DREG_PCBASE +
+ d40c->phy_chan->num * D40_DREG_PCDELTA +
+ D40_CHAN_REG_SDPTR);
+ else {
+ unsigned long lower;
+ unsigned long upper;
+
+ lower = d40c->lcpa->lcsp2 & D40_MEM_LCSP2_DPTR_MASK;
+ upper = d40c->lcpa->lcsp3 & D40_MEM_LCSP3_DPTR_MASK;
+
+ addr = upper | lower;
+ }
+
+ return addr;
+}
+EXPORT_SYMBOL(stedma40_get_dst_addr);
+
/* Initialization functions */
static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
@@ -2773,8 +2832,6 @@ static int dma40_pm_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct d40_base *base = platform_get_drvdata(pdev);
int ret = 0;
- if (!pm_runtime_suspended(dev))
- return -EBUSY;
if (base->lcpa_regulator)
ret = regulator_disable(base->lcpa_regulator);
@@ -3358,6 +3415,13 @@ static int __init d40_probe(struct platform_device *pdev)
if (err)
goto failure;
+ base->dev->dma_parms = &base->dma_parms;
+ err = dma_set_max_seg_size(base->dev, 0xffff);
+ if (err) {
+ d40_err(&pdev->dev, "Failed to set dma max seg size\n");
+ goto failure;
+ }
+
d40_hw_init(base);
dev_info(base->dev, "initialized\n");
diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c
index cad9e1daedf..d47d1fa36b9 100644
--- a/drivers/dma/ste_dma40_ll.c
+++ b/drivers/dma/ste_dma40_ll.c
@@ -102,16 +102,18 @@ void d40_phy_cfg(struct stedma40_chan_cfg *cfg,
src |= cfg->src_info.data_width << D40_SREG_CFG_ESIZE_POS;
dst |= cfg->dst_info.data_width << D40_SREG_CFG_ESIZE_POS;
+ /* Set the priority bit to high for the physical channel */
+ if (cfg->high_priority) {
+ src |= 1 << D40_SREG_CFG_PRI_POS;
+ dst |= 1 << D40_SREG_CFG_PRI_POS;
+ }
+
} else {
/* Logical channel */
dst |= 1 << D40_SREG_CFG_LOG_GIM_POS;
src |= 1 << D40_SREG_CFG_LOG_GIM_POS;
}
- if (cfg->high_priority) {
- src |= 1 << D40_SREG_CFG_PRI_POS;
- dst |= 1 << D40_SREG_CFG_PRI_POS;
- }
if (cfg->src_info.big_endian)
src |= 1 << D40_SREG_CFG_LBE_POS;
@@ -331,10 +333,10 @@ void d40_log_lli_lcpa_write(struct d40_log_lli_full *lcpa,
{
d40_log_lli_link(lli_dst, lli_src, next, flags);
- writel(lli_src->lcsp02, &lcpa[0].lcsp0);
- writel(lli_src->lcsp13, &lcpa[0].lcsp1);
- writel(lli_dst->lcsp02, &lcpa[0].lcsp2);
- writel(lli_dst->lcsp13, &lcpa[0].lcsp3);
+ writel_relaxed(lli_src->lcsp02, &lcpa[0].lcsp0);
+ writel_relaxed(lli_src->lcsp13, &lcpa[0].lcsp1);
+ writel_relaxed(lli_dst->lcsp02, &lcpa[0].lcsp2);
+ writel_relaxed(lli_dst->lcsp13, &lcpa[0].lcsp3);
}
void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
@@ -344,10 +346,10 @@ void d40_log_lli_lcla_write(struct d40_log_lli *lcla,
{
d40_log_lli_link(lli_dst, lli_src, next, flags);
- writel(lli_src->lcsp02, &lcla[0].lcsp02);
- writel(lli_src->lcsp13, &lcla[0].lcsp13);
- writel(lli_dst->lcsp02, &lcla[1].lcsp02);
- writel(lli_dst->lcsp13, &lcla[1].lcsp13);
+ writel_relaxed(lli_src->lcsp02, &lcla[0].lcsp02);
+ writel_relaxed(lli_src->lcsp13, &lcla[0].lcsp13);
+ writel_relaxed(lli_dst->lcsp02, &lcla[1].lcsp02);
+ writel_relaxed(lli_dst->lcsp13, &lcla[1].lcsp13);
}
static void d40_log_fill_lli(struct d40_log_lli *lli,
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 51e8e5396e9..2f3395d6850 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -94,10 +94,13 @@
/* LCSP2 */
#define D40_MEM_LCSP2_ECNT_POS 16
+#define D40_MEM_LCSP2_DPTR_POS 0
#define D40_MEM_LCSP2_ECNT_MASK (0xFFFF << D40_MEM_LCSP2_ECNT_POS)
+#define D40_MEM_LCSP2_DPTR_MASK (0xFFFF << D40_MEM_LCSP2_DPTR_POS)
/* LCSP3 */
+#define D40_MEM_LCSP3_DPTR_POS 16
#define D40_MEM_LCSP3_DCFG_MST_POS 15
#define D40_MEM_LCSP3_DCFG_TIM_POS 14
#define D40_MEM_LCSP3_DCFG_EIM_POS 13
@@ -107,6 +110,7 @@
#define D40_MEM_LCSP3_DLOS_POS 1
#define D40_MEM_LCSP3_DTCP_POS 0
+#define D40_MEM_LCSP3_DPTR_MASK (0xFFFF << D40_MEM_LCSP3_DPTR_POS)
#define D40_MEM_LCSP3_DLOS_MASK (0x7F << D40_MEM_LCSP3_DLOS_POS)
#define D40_MEM_LCSP3_DTCP_MASK (0x1 << D40_MEM_LCSP3_DTCP_POS)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index e03653d6935..6226d0cd30a 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -504,7 +504,7 @@ config GPIO_JANZ_TTL
config GPIO_AB8500
bool "ST-Ericsson AB8500 Mixed Signal Circuit gpio functions"
- depends on AB8500_CORE && BROKEN
+ depends on AB8500_CORE
help
Select this to enable the AB8500 IC GPIO driver
diff --git a/drivers/gpio/gpio-ab8500.c b/drivers/gpio/gpio-ab8500.c
index 050c05d9189..ef75984486c 100644
--- a/drivers/gpio/gpio-ab8500.c
+++ b/drivers/gpio/gpio-ab8500.c
@@ -18,9 +18,16 @@
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
-#include <linux/mfd/ab8500.h>
#include <linux/mfd/abx500.h>
-#include <linux/mfd/ab8500/gpio.h>
+#include <linux/mfd/abx500/ab8500-gpio.h>
+
+
+/*
+ * The AB9540 GPIO support is an extended version of the
+ * AB8500 GPIO support. The AB9540 supports an additional
+ * (7th) register so that more GPIO may be configured and
+ * used.
+ */
/*
* GPIO registers offset
@@ -32,6 +39,7 @@
#define AB8500_GPIO_SEL4_REG 0x03
#define AB8500_GPIO_SEL5_REG 0x04
#define AB8500_GPIO_SEL6_REG 0x05
+#define AB9540_GPIO_SEL7_REG 0x06
#define AB8500_GPIO_DIR1_REG 0x10
#define AB8500_GPIO_DIR2_REG 0x11
@@ -39,6 +47,7 @@
#define AB8500_GPIO_DIR4_REG 0x13
#define AB8500_GPIO_DIR5_REG 0x14
#define AB8500_GPIO_DIR6_REG 0x15
+#define AB9540_GPIO_DIR7_REG 0x16
#define AB8500_GPIO_OUT1_REG 0x20
#define AB8500_GPIO_OUT2_REG 0x21
@@ -46,6 +55,7 @@
#define AB8500_GPIO_OUT4_REG 0x23
#define AB8500_GPIO_OUT5_REG 0x24
#define AB8500_GPIO_OUT6_REG 0x25
+#define AB9540_GPIO_OUT7_REG 0x26
#define AB8500_GPIO_PUD1_REG 0x30
#define AB8500_GPIO_PUD2_REG 0x31
@@ -53,6 +63,7 @@
#define AB8500_GPIO_PUD4_REG 0x33
#define AB8500_GPIO_PUD5_REG 0x34
#define AB8500_GPIO_PUD6_REG 0x35
+#define AB9540_GPIO_PUD7_REG 0x36
#define AB8500_GPIO_IN1_REG 0x40
#define AB8500_GPIO_IN2_REG 0x41
@@ -60,9 +71,13 @@
#define AB8500_GPIO_IN4_REG 0x43
#define AB8500_GPIO_IN5_REG 0x44
#define AB8500_GPIO_IN6_REG 0x45
-#define AB8500_GPIO_ALTFUN_REG 0x45
-#define ALTFUN_REG_INDEX 6
+#define AB9540_GPIO_IN7_REG 0x46
+#define AB8500_GPIO_ALTFUN_REG 0x50
+#define AB8500_ALTFUN_REG_INDEX 6
+#define AB9540_ALTFUN_REG_INDEX 7
#define AB8500_NUM_GPIO 42
+#define AB9540_NUM_GPIO 54
+#define AB8505_NUM_GPIO 53
#define AB8500_NUM_VIR_GPIO_IRQ 16
enum ab8500_gpio_action {
@@ -73,6 +88,11 @@ enum ab8500_gpio_action {
UNMASK
};
+struct ab8500_gpio_irq_cluster {
+ int start;
+ int end;
+};
+
struct ab8500_gpio {
struct gpio_chip chip;
struct ab8500 *parent;
@@ -82,7 +102,50 @@ struct ab8500_gpio {
enum ab8500_gpio_action irq_action;
u16 rising;
u16 falling;
+ struct ab8500_gpio_irq_cluster *irq_cluster;
+ int irq_cluster_size;
+};
+
+/*
+ * Only some GPIOs are interrupt capable, and they are
+ * organized in discontiguous clusters:
+ *
+ * GPIO6 to GPIO13
+ * GPIO24 and GPIO25
+ * GPIO36 to GPIO41
+ * GPIO50 to GPIO54 (AB9540 only)
+ */
+static struct ab8500_gpio_irq_cluster ab8500_irq_clusters[] = {
+ {.start = 5, .end = 12}, /* GPIO numbers start from 1 */
+ {.start = 23, .end = 24},
+ {.start = 35, .end = 40},
+};
+
+static struct ab8500_gpio_irq_cluster ab9540_irq_clusters[] = {
+ {.start = 5, .end = 12}, /* GPIO numbers start from 1 */
+ {.start = 23, .end = 24},
+ {.start = 35, .end = 40},
+ {.start = 49, .end = 53},
+};
+
+/*
+ * For AB8505 Only some GPIOs are interrupt capable, and they are
+ * organized in discontiguous clusters:
+ *
+ * GPIO10 to GPIO11
+ * GPIO13
+ * GPIO40 and GPIO41
+ * GPIO50
+ * GPIO52 to GPIO53
+ */
+static struct ab8500_gpio_irq_cluster ab8505_irq_clusters[] = {
+ {.start = 9, .end = 10}, /* GPIO numbers start from 1 */
+ {.start = 12, .end = 12},
+ {.start = 39, .end = 40},
+ {.start = 49, .end = 49},
+ {.start = 51, .end = 52},
};
+
/**
* to_ab8500_gpio() - get the pointer to ab8500_gpio
* @chip: Member of the structure ab8500_gpio
@@ -115,7 +178,7 @@ static int ab8500_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
u8 mask = 1 << (offset % 8);
- u8 reg = AB8500_GPIO_OUT1_REG + (offset / 8);
+ u8 reg = AB8500_GPIO_IN1_REG + (offset / 8);
int ret;
u8 data;
ret = abx500_get_register_interruptible(ab8500_gpio->dev, AB8500_MISC,
@@ -132,7 +195,7 @@ static void ab8500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
int ret;
/* Write the data */
- ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, 1);
+ ret = ab8500_gpio_set_bits(chip, AB8500_GPIO_OUT1_REG, offset, val);
if (ret < 0)
dev_err(ab8500_gpio->dev, "%s write failed\n", __func__);
}
@@ -162,28 +225,13 @@ static int ab8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
static int ab8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
- /*
- * Only some GPIOs are interrupt capable, and they are
- * organized in discontiguous clusters:
- *
- * GPIO6 to GPIO13
- * GPIO24 and GPIO25
- * GPIO36 to GPIO41
- */
- static struct ab8500_gpio_irq_cluster {
- int start;
- int end;
- } clusters[] = {
- {.start = 6, .end = 13},
- {.start = 24, .end = 25},
- {.start = 36, .end = 41},
- };
struct ab8500_gpio *ab8500_gpio = to_ab8500_gpio(chip);
int base = ab8500_gpio->irq_base;
int i;
- for (i = 0; i < ARRAY_SIZE(clusters); i++) {
- struct ab8500_gpio_irq_cluster *cluster = &clusters[i];
+ for (i = 0; i < ab8500_gpio->irq_cluster_size; i++) {
+ struct ab8500_gpio_irq_cluster *cluster =
+ &ab8500_gpio->irq_cluster[i];
if (offset >= cluster->start && offset <= cluster->end)
return base + offset - cluster->start;
@@ -207,7 +255,7 @@ static struct gpio_chip ab8500gpio_chip = {
static unsigned int irq_to_rising(unsigned int irq)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_get_chip_data(irq);
int offset = irq - ab8500_gpio->irq_base;
int new_irq = offset + AB8500_INT_GPIO6R
+ ab8500_gpio->parent->irq_base;
@@ -216,7 +264,7 @@ static unsigned int irq_to_rising(unsigned int irq)
static unsigned int irq_to_falling(unsigned int irq)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_get_chip_data(irq);
int offset = irq - ab8500_gpio->irq_base;
int new_irq = offset + AB8500_INT_GPIO6F
+ ab8500_gpio->parent->irq_base;
@@ -261,15 +309,16 @@ static irqreturn_t handle_falling(int irq, void *dev)
return IRQ_HANDLED;
}
-static void ab8500_gpio_irq_lock(unsigned int irq)
+static void ab8500_gpio_irq_lock(struct irq_data *data)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
mutex_lock(&ab8500_gpio->lock);
}
-static void ab8500_gpio_irq_sync_unlock(unsigned int irq)
+static void ab8500_gpio_irq_sync_unlock(struct irq_data *data)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
int offset = irq - ab8500_gpio->irq_base;
bool rising = ab8500_gpio->rising & BIT(offset);
bool falling = ab8500_gpio->falling & BIT(offset);
@@ -280,12 +329,12 @@ static void ab8500_gpio_irq_sync_unlock(unsigned int irq)
if (rising)
ret = request_threaded_irq(irq_to_rising(irq),
NULL, handle_rising,
- IRQF_TRIGGER_RISING,
+ IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND,
"ab8500-gpio-r", ab8500_gpio);
if (falling)
ret = request_threaded_irq(irq_to_falling(irq),
NULL, handle_falling,
- IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND,
"ab8500-gpio-f", ab8500_gpio);
break;
case SHUTDOWN:
@@ -316,21 +365,22 @@ static void ab8500_gpio_irq_sync_unlock(unsigned int irq)
}
-static void ab8500_gpio_irq_mask(unsigned int irq)
+static void ab8500_gpio_irq_mask(struct irq_data *data)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
ab8500_gpio->irq_action = MASK;
}
-static void ab8500_gpio_irq_unmask(unsigned int irq)
+static void ab8500_gpio_irq_unmask(struct irq_data *data)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
ab8500_gpio->irq_action = UNMASK;
}
-static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type)
+static int ab8500_gpio_irq_set_type(struct irq_data *data, unsigned int type)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
+ unsigned int irq = data->irq;
int offset = irq - ab8500_gpio->irq_base;
if (type == IRQ_TYPE_EDGE_BOTH) {
@@ -344,28 +394,28 @@ static int ab8500_gpio_irq_set_type(unsigned int irq, unsigned int type)
return 0;
}
-unsigned int ab8500_gpio_irq_startup(unsigned int irq)
+unsigned int ab8500_gpio_irq_startup(struct irq_data *data)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
ab8500_gpio->irq_action = STARTUP;
return 0;
}
-void ab8500_gpio_irq_shutdown(unsigned int irq)
+void ab8500_gpio_irq_shutdown(struct irq_data *data)
{
- struct ab8500_gpio *ab8500_gpio = get_irq_chip_data(irq);
+ struct ab8500_gpio *ab8500_gpio = irq_data_get_irq_chip_data(data);
ab8500_gpio->irq_action = SHUTDOWN;
}
static struct irq_chip ab8500_gpio_irq_chip = {
.name = "ab8500-gpio",
- .startup = ab8500_gpio_irq_startup,
- .shutdown = ab8500_gpio_irq_shutdown,
- .bus_lock = ab8500_gpio_irq_lock,
- .bus_sync_unlock = ab8500_gpio_irq_sync_unlock,
- .mask = ab8500_gpio_irq_mask,
- .unmask = ab8500_gpio_irq_unmask,
- .set_type = ab8500_gpio_irq_set_type,
+ .irq_startup = ab8500_gpio_irq_startup,
+ .irq_shutdown = ab8500_gpio_irq_shutdown,
+ .irq_bus_lock = ab8500_gpio_irq_lock,
+ .irq_bus_sync_unlock = ab8500_gpio_irq_sync_unlock,
+ .irq_mask = ab8500_gpio_irq_mask,
+ .irq_unmask = ab8500_gpio_irq_unmask,
+ .irq_set_type = ab8500_gpio_irq_set_type,
};
static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio)
@@ -374,14 +424,14 @@ static int ab8500_gpio_irq_init(struct ab8500_gpio *ab8500_gpio)
int irq;
for (irq = base; irq < base + AB8500_NUM_VIR_GPIO_IRQ ; irq++) {
- set_irq_chip_data(irq, ab8500_gpio);
- set_irq_chip_and_handler(irq, &ab8500_gpio_irq_chip,
+ irq_set_chip_data(irq, ab8500_gpio);
+ irq_set_chip_and_handler(irq, &ab8500_gpio_irq_chip,
handle_simple_irq);
- set_irq_nested_thread(irq, 1);
+ irq_set_nested_thread(irq, 1);
#ifdef CONFIG_ARM
set_irq_flags(irq, IRQF_VALID);
#else
- set_irq_noprobe(irq);
+ irq_set_noprobe(irq);
#endif
}
@@ -397,19 +447,22 @@ static void ab8500_gpio_irq_remove(struct ab8500_gpio *ab8500_gpio)
#ifdef CONFIG_ARM
set_irq_flags(irq, 0);
#endif
- set_irq_chip_and_handler(irq, NULL, NULL);
- set_irq_chip_data(irq, NULL);
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
}
}
static int __devinit ab8500_gpio_probe(struct platform_device *pdev)
{
+ struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent);
struct ab8500_platform_data *ab8500_pdata =
dev_get_platdata(pdev->dev.parent);
struct ab8500_gpio_platform_data *pdata;
struct ab8500_gpio *ab8500_gpio;
int ret;
int i;
+ int last_gpio_sel_reg;
+ int altfun_reg_index;
pdata = ab8500_pdata->gpio;
if (!pdata) {
@@ -425,10 +478,36 @@ static int __devinit ab8500_gpio_probe(struct platform_device *pdev)
ab8500_gpio->dev = &pdev->dev;
ab8500_gpio->parent = dev_get_drvdata(pdev->dev.parent);
ab8500_gpio->chip = ab8500gpio_chip;
- ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO;
ab8500_gpio->chip.dev = &pdev->dev;
ab8500_gpio->chip.base = pdata->gpio_base;
ab8500_gpio->irq_base = pdata->irq_base;
+
+ /* Configure GPIO Settings for specific AB devices */
+ if (cpu_is_u9540()) {
+ ab8500_gpio->chip.ngpio = AB9540_NUM_GPIO;
+ ab8500_gpio->irq_cluster = ab9540_irq_clusters;
+ ab8500_gpio->irq_cluster_size =
+ ARRAY_SIZE(ab9540_irq_clusters);
+ last_gpio_sel_reg = AB9540_GPIO_SEL7_REG;
+ altfun_reg_index = AB9540_ALTFUN_REG_INDEX;
+ } else {
+ if (is_ab8505(parent)) {
+ ab8500_gpio->chip.ngpio = AB8505_NUM_GPIO;
+ ab8500_gpio->irq_cluster = ab8505_irq_clusters;
+ ab8500_gpio->irq_cluster_size =
+ ARRAY_SIZE(ab8505_irq_clusters);
+ last_gpio_sel_reg = AB9540_GPIO_SEL7_REG;
+ altfun_reg_index = AB9540_ALTFUN_REG_INDEX;
+ } else {
+ ab8500_gpio->chip.ngpio = AB8500_NUM_GPIO;
+ ab8500_gpio->irq_cluster = ab8500_irq_clusters;
+ ab8500_gpio->irq_cluster_size =
+ ARRAY_SIZE(ab8500_irq_clusters);
+ last_gpio_sel_reg = AB8500_GPIO_SEL6_REG;
+ altfun_reg_index = AB8500_ALTFUN_REG_INDEX;
+ }
+ }
+
/* initialize the lock */
mutex_init(&ab8500_gpio->lock);
/*
@@ -437,16 +516,28 @@ static int __devinit ab8500_gpio_probe(struct platform_device *pdev)
* These values are for selecting the PINs as
* GPIO or alternate function
*/
- for (i = AB8500_GPIO_SEL1_REG; i <= AB8500_GPIO_SEL6_REG; i++) {
+ for (i = AB8500_GPIO_SEL1_REG; i <= last_gpio_sel_reg; i++) {
ret = abx500_set_register_interruptible(ab8500_gpio->dev,
AB8500_MISC, i,
pdata->config_reg[i]);
if (ret < 0)
goto out_free;
+
+ ret = abx500_set_register_interruptible(ab8500_gpio->dev,
+ AB8500_MISC, i + AB8500_GPIO_DIR1_REG,
+ pdata->config_direction[i]);
+ if (ret < 0)
+ goto out_free;
+
+ ret = abx500_set_register_interruptible(ab8500_gpio->dev,
+ AB8500_MISC, i + AB8500_GPIO_PUD1_REG,
+ pdata->config_pullups[i]);
+ if (ret < 0)
+ goto out_free;
}
ret = abx500_set_register_interruptible(ab8500_gpio->dev, AB8500_MISC,
AB8500_GPIO_ALTFUN_REG,
- pdata->config_reg[ALTFUN_REG_INDEX]);
+ pdata->config_reg[altfun_reg_index]);
if (ret < 0)
goto out_free;
@@ -493,6 +584,86 @@ static int __devexit ab8500_gpio_remove(struct platform_device *pdev)
return 0;
}
+int ab8500_config_pulldown(struct device *dev,
+ enum ab8500_pin gpio, bool enable)
+{
+ u8 offset = gpio - AB8500_PIN_GPIO1;
+ u8 pos = offset % 8;
+ u8 val = enable ? 0 : 1;
+ u8 reg = AB8500_GPIO_PUD1_REG + (offset / 8);
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(dev, "%s write failed\n", __func__);
+ return ret;
+}
+EXPORT_SYMBOL(ab8500_config_pulldown);
+
+/*
+ * ab8500_gpio_config_select()
+ *
+ * Configure functionality of pin, either specific use or GPIO.
+ * @dev: device pointer
+ * @gpio: gpio number
+ * @gpio_select: true if the pin should be used as GPIO
+ */
+int ab8500_gpio_config_select(struct device *dev,
+ enum ab8500_pin gpio, bool gpio_select)
+{
+ u8 offset = gpio - AB8500_PIN_GPIO1;
+ u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8);
+ u8 pos = offset % 8;
+ u8 val = gpio_select ? 1 : 0;
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(dev,
+ AB8500_MISC, reg, 1 << pos, val << pos);
+ if (ret < 0)
+ dev_err(dev, "%s write failed\n", __func__);
+
+ dev_vdbg(dev, "%s (bank, addr, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ __func__, AB8500_MISC, reg, 1 << pos, val << pos);
+
+ return ret;
+}
+
+/*
+ * ab8500_gpio_config_get_select()
+ *
+ * Read currently configured functionality, either specific use or GPIO.
+ * @dev: device pointer
+ * @gpio: gpio number
+ * @gpio_select: pointer to pin selection status
+ */
+int ab8500_gpio_config_get_select(struct device *dev,
+ enum ab8500_pin gpio, bool *gpio_select)
+{
+ u8 offset = gpio - AB8500_PIN_GPIO1;
+ u8 reg = AB8500_GPIO_SEL1_REG + (offset / 8);
+ u8 pos = offset % 8;
+ u8 val;
+ int ret;
+
+ ret = abx500_get_register_interruptible(dev,
+ AB8500_MISC, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "%s read failed\n", __func__);
+ return ret;
+ }
+
+ if (val & (1 << pos))
+ *gpio_select = true;
+ else
+ *gpio_select = false;
+
+ dev_vdbg(dev, "%s (bank, addr, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
+ __func__, AB8500_MISC, reg, 1 << pos, val);
+
+ return 0;
+}
+
static struct platform_driver ab8500_gpio_driver = {
.driver = {
.name = "ab8500-gpio",
diff --git a/drivers/gpio/gpio-nomadik.c b/drivers/gpio/gpio-nomadik.c
index 839624f9fe6..05547ed48ff 100644
--- a/drivers/gpio/gpio-nomadik.c
+++ b/drivers/gpio/gpio-nomadik.c
@@ -23,12 +23,11 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
+#include <linux/gpio/nomadik.h>
#include <asm/mach/irq.h>
#include <plat/pincfg.h>
-#include <plat/gpio-nomadik.h>
-#include <mach/hardware.h>
#include <asm/gpio.h>
/*
@@ -58,8 +57,11 @@ struct nmk_gpio_chip {
u32 real_wake;
u32 rwimsc;
u32 fwimsc;
+ u32 rimsc;
+ u32 fimsc;
u32 slpm;
u32 pull_up;
+ u32 lowemi;
};
static struct nmk_gpio_chip *
@@ -124,6 +126,24 @@ static void __nmk_gpio_set_pull(struct nmk_gpio_chip *nmk_chip,
}
}
+static void __nmk_gpio_set_lowemi(struct nmk_gpio_chip *nmk_chip,
+ unsigned offset, bool lowemi)
+{
+ u32 bit = BIT(offset);
+ bool enabled = nmk_chip->lowemi & bit;
+
+ if (lowemi == enabled)
+ return;
+
+ if (lowemi)
+ nmk_chip->lowemi |= bit;
+ else
+ nmk_chip->lowemi &= ~bit;
+
+ writel_relaxed(nmk_chip->lowemi,
+ nmk_chip->addr + NMK_GPIO_LOWEMI);
+}
+
static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip,
unsigned offset)
{
@@ -150,8 +170,8 @@ static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip,
unsigned offset, int gpio_mode,
bool glitch)
{
- u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
- u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC);
+ u32 rwimsc = nmk_chip->rwimsc;
+ u32 fwimsc = nmk_chip->fwimsc;
if (glitch && nmk_chip->set_ioforce) {
u32 bit = BIT(offset);
@@ -173,6 +193,36 @@ static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip,
}
}
+static void
+nmk_gpio_disable_lazy_irq(struct nmk_gpio_chip *nmk_chip, unsigned offset)
+{
+ u32 falling = nmk_chip->fimsc & BIT(offset);
+ u32 rising = nmk_chip->rimsc & BIT(offset);
+ int gpio = nmk_chip->chip.base + offset;
+ int irq = NOMADIK_GPIO_TO_IRQ(gpio);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ if (!rising && !falling)
+ return;
+
+ if (!d || !irqd_irq_disabled(d))
+ return;
+
+ if (rising) {
+ nmk_chip->rimsc &= ~BIT(offset);
+ writel_relaxed(nmk_chip->rimsc,
+ nmk_chip->addr + NMK_GPIO_RIMSC);
+ }
+
+ if (falling) {
+ nmk_chip->fimsc &= ~BIT(offset);
+ writel_relaxed(nmk_chip->fimsc,
+ nmk_chip->addr + NMK_GPIO_FIMSC);
+ }
+
+ dev_dbg(nmk_chip->chip.dev, "%d: clearing interrupt mask\n", gpio);
+}
+
static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
pin_cfg_t cfg, bool sleep, unsigned int *slpmregs)
{
@@ -238,6 +288,17 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
__nmk_gpio_set_pull(nmk_chip, offset, pull);
}
+ __nmk_gpio_set_lowemi(nmk_chip, offset, PIN_LOWEMI(cfg));
+
+ /*
+ * If the pin is switching to altfunc, and there was an interrupt
+ * installed on it which has been lazy disabled, actually mask the
+ * interrupt to prevent spurious interrupts that would occur while the
+ * pin is under control of the peripheral. Only SKE does this.
+ */
+ if (af != NMK_GPIO_ALT_GPIO)
+ nmk_gpio_disable_lazy_irq(nmk_chip, offset);
+
/*
* If we've backed up the SLPM registers (glitch workaround), modify
* the backups since they will be restored.
@@ -359,7 +420,7 @@ static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep)
/**
* nmk_config_pin - configure a pin's mux attributes
* @cfg: pin confguration
- *
+ * @sleep: Non-zero to apply the sleep mode configuration
* Configures a pin's mode (alternate function or GPIO), its pull up status,
* and its sleep mode based on the specified configuration. The @cfg is
* usually one of the SoC specific macros defined in mach/<soc>-pins.h. These
@@ -556,27 +617,38 @@ static void __nmk_gpio_irq_modify(struct nmk_gpio_chip *nmk_chip,
int gpio, enum nmk_gpio_irq_type which,
bool enable)
{
- u32 rimsc = which == WAKE ? NMK_GPIO_RWIMSC : NMK_GPIO_RIMSC;
- u32 fimsc = which == WAKE ? NMK_GPIO_FWIMSC : NMK_GPIO_FIMSC;
u32 bitmask = nmk_gpio_get_bitmask(gpio);
- u32 reg;
+ u32 *rimscval;
+ u32 *fimscval;
+ u32 rimscreg;
+ u32 fimscreg;
+
+ if (which == NORMAL) {
+ rimscreg = NMK_GPIO_RIMSC;
+ fimscreg = NMK_GPIO_FIMSC;
+ rimscval = &nmk_chip->rimsc;
+ fimscval = &nmk_chip->fimsc;
+ } else {
+ rimscreg = NMK_GPIO_RWIMSC;
+ fimscreg = NMK_GPIO_FWIMSC;
+ rimscval = &nmk_chip->rwimsc;
+ fimscval = &nmk_chip->fwimsc;
+ }
/* we must individually set/clear the two edges */
if (nmk_chip->edge_rising & bitmask) {
- reg = readl(nmk_chip->addr + rimsc);
if (enable)
- reg |= bitmask;
+ *rimscval |= bitmask;
else
- reg &= ~bitmask;
- writel(reg, nmk_chip->addr + rimsc);
+ *rimscval &= ~bitmask;
+ writel(*rimscval, nmk_chip->addr + rimscreg);
}
if (nmk_chip->edge_falling & bitmask) {
- reg = readl(nmk_chip->addr + fimsc);
if (enable)
- reg |= bitmask;
+ *fimscval |= bitmask;
else
- reg &= ~bitmask;
- writel(reg, nmk_chip->addr + fimsc);
+ *fimscval &= ~bitmask;
+ writel(*fimscval, nmk_chip->addr + fimscreg);
}
}
@@ -1008,9 +1080,6 @@ void nmk_gpio_wakeups_suspend(void)
clk_enable(chip->clk);
- chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC);
- chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC);
-
writel(chip->rwimsc & chip->real_wake,
chip->addr + NMK_GPIO_RWIMSC);
writel(chip->fwimsc & chip->real_wake,
@@ -1076,6 +1145,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
struct resource *res;
struct clk *clk;
int secondary_irq;
+ void __iomem *base;
int irq;
int ret;
@@ -1106,10 +1176,16 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
goto out;
}
+ base = ioremap(res->start, resource_size(res));
+ if (!base) {
+ ret = -ENOMEM;
+ goto out_release;
+ }
+
clk = clk_get(&dev->dev, NULL);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
- goto out_release;
+ goto out_unmap;
}
nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL);
@@ -1123,7 +1199,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
*/
nmk_chip->bank = dev->id;
nmk_chip->clk = clk;
- nmk_chip->addr = io_p2v(res->start);
+ nmk_chip->addr = base;
nmk_chip->chip = nmk_gpio_template;
nmk_chip->parent_irq = irq;
nmk_chip->secondary_parent_irq = secondary_irq;
@@ -1139,6 +1215,10 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
chip->dev = &dev->dev;
chip->owner = THIS_MODULE;
+ clk_enable(nmk_chip->clk);
+ nmk_chip->lowemi = readl_relaxed(nmk_chip->addr + NMK_GPIO_LOWEMI);
+ clk_disable(nmk_chip->clk);
+
ret = gpiochip_add(&nmk_chip->chip);
if (ret)
goto out_free;
@@ -1159,6 +1239,8 @@ out_free:
out_clk:
clk_disable(clk);
clk_put(clk);
+out_unmap:
+ iounmap(base);
out_release:
release_mem_region(res->start, resource_size(res));
out:
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 8deedc1b984..58b2a6c93c8 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -39,6 +39,44 @@ config HWMON_DEBUG_CHIP
comment "Native drivers"
+config SENSORS_AB8500
+ tristate "AB8500 thermal monitoring"
+ depends on AB8500_GPADC
+ default n
+ help
+ If you say yes here you get support for the thermal sensor part
+ of the AB8500 chip. The driver includes thermal management for
+ AB8500 die and two GPADC channels. The GPADC channel are preferably
+ used to access sensors outside the AB8500 chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called abx500-temp.
+
+config SENSORS_AB5500
+ tristate "AB5500 thermal monitoring"
+ depends on AB5500_GPADC
+ default n
+ help
+ If you say yes here you get support for the thermal sensor part
+ of the AB5500 chip. The driver includes thermal management for
+ AB5500 die, pcb and RF XTAL temperature.
+
+ This driver can also be built as a module. If so, the module
+ will be called abx500-temp.
+
+config SENSORS_DBX500
+ tristate "DBX500 thermal monitoring"
+ depends on MFD_DB8500_PRCMU || MFD_DB5500_PRCMU
+ default n
+ help
+ If you say yes here you get support for the thermal sensor part
+ of the DBX500 chip. The driver includes thermal management for
+ DBX500 die.
+
+ This driver can also be built as a module. If so, the module
+ will be called dbx500_temp.
+
+
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
depends on X86 && DMI && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 6d3f11f7181..1e893cbdb83 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -19,6 +19,9 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
+obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
+obj-$(CONFIG_SENSORS_AB5500) += abx500.o ab5500.o
+obj-$(CONFIG_SENSORS_DBX500) += dbx500.o
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
diff --git a/drivers/hwmon/ab5500.c b/drivers/hwmon/ab5500.c
new file mode 100644
index 00000000000..cafadeba51c
--- /dev/null
+++ b/drivers/hwmon/ab5500.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Martin Persson <martin.persson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ * Note:
+ *
+ * If/when the AB5500 thermal warning temperature is reached (threshold
+ * 125C cannot be changed by SW), an interrupt is set and the driver
+ * notifies user space via a sysfs event. If a shut down is not
+ * triggered by user space and temperature reaches beyond critical
+ * limit(130C) pm_power off is called.
+ *
+ * If/when AB5500 thermal shutdown temperature is reached a hardware
+ * shutdown of the AB5500 will occur.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+#include <linux/mfd/abx500/ab5500-bm.h>
+#include "abx500.h"
+#include <asm/mach-types.h>
+
+/* AB5500 driver monitors GPADC - XTAL_TEMP, PCB_TEMP,
+ * BTEMP_BALL, BAT_CTRL and DIE_TEMP
+ */
+#define NUM_MONITORED_SENSORS 5
+
+#define SHUTDOWN_AUTO_MIN_LIMIT -25
+#define SHUTDOWN_AUTO_MAX_LIMIT 130
+
+static int ab5500_output_convert(int val, u8 sensor)
+{
+ int res = val;
+ /* GPADC returns die temperature in Celsius
+ * convert it to millidegree celsius
+ */
+ if (sensor == DIE_TEMP)
+ res = val * 1000;
+
+ return res;
+}
+
+static int ab5500_read_sensor(struct abx500_temp *data, u8 sensor)
+{
+ int val;
+ /*
+ * Special treatment for BAT_CTRL node, since this
+ * temperature measurement is more complex than just
+ * an ADC readout
+ */
+ if (sensor == BAT_CTRL)
+ val = ab5500_btemp_get_batctrl_temp(data->ab5500_btemp);
+ else
+ val = ab5500_gpadc_convert(data->ab5500_gpadc, sensor);
+
+ if (val < 0)
+ return val;
+ else
+ return ab5500_output_convert(val, sensor);
+}
+
+static ssize_t ab5500_show_name(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "ab5500\n");
+}
+
+static ssize_t ab5500_show_label(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ char *name;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int index = attr->index;
+
+ /*
+ * Make sure these labels correspond to the attribute indexes
+ * used when calling SENSOR_DEVICE_ATRR.
+ * Temperature sensors outside ab8500 (read via GPADC) are marked
+ * with prefix ext_
+ */
+ switch (index) {
+ case 1:
+ name = "xtal_temp";
+ break;
+ case 2:
+ name = "pcb_temp";
+ break;
+ case 3:
+ name = "bat_temp";
+ break;
+ case 4:
+ name = "bat_ctrl";
+ break;
+ case 5:
+ name = "ab5500";
+ break;
+ default:
+ return -EINVAL;
+ }
+ return sprintf(buf, "%s\n", name);
+}
+
+static int temp_shutdown_trig(int mux)
+{
+ pm_power_off();
+ return 0;
+}
+
+static int ab5500_temp_shutdown_auto(struct abx500_temp *data)
+{
+ int ret;
+ struct adc_auto_input *auto_ip;
+
+ auto_ip = kzalloc(sizeof(struct adc_auto_input), GFP_KERNEL);
+ if (!auto_ip) {
+ dev_err(&data->pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ auto_ip->mux = DIE_TEMP;
+ auto_ip->freq = MS500;
+ /*
+ * As per product specification, voltage decreases as
+ * temperature increases. Hence the min and max values
+ * should be passed in reverse order.
+ */
+ auto_ip->min = SHUTDOWN_AUTO_MAX_LIMIT;
+ auto_ip->max = SHUTDOWN_AUTO_MIN_LIMIT;
+ auto_ip->auto_adc_callback = temp_shutdown_trig;
+ data->gpadc_auto = auto_ip;
+ ret = ab5500_gpadc_convert_auto(data->ab5500_gpadc,
+ data->gpadc_auto);
+ if (ret < 0)
+ kfree(auto_ip);
+
+ return ret;
+}
+
+static int ab5500_is_visible(struct attribute *attr, int n)
+{
+ return attr->mode;
+}
+
+static int ab5500_temp_irq_handler(int irq, struct abx500_temp *data)
+{
+ /*
+ * Make sure the magic numbers below corresponds to the node
+ * used for AB5500 thermal warning from HW.
+ */
+ mutex_lock(&data->lock);
+ data->crit_alarm[4] = 1;
+ mutex_unlock(&data->lock);
+ sysfs_notify(&data->pdev->dev.kobj, NULL, "temp5_crit_alarm");
+ dev_info(&data->pdev->dev, "ABX500 thermal warning,"
+ " power off system now!\n");
+ return 0;
+}
+
+int __init ab5500_hwmon_init(struct abx500_temp *data)
+{
+ int err;
+
+ data->ab5500_gpadc = ab5500_gpadc_get("ab5500-adc.0");
+ if (IS_ERR(data->ab5500_gpadc))
+ return PTR_ERR(data->ab5500_gpadc);
+
+ data->ab5500_btemp = ab5500_btemp_get();
+ if (IS_ERR(data->ab5500_btemp))
+ return PTR_ERR(data->ab5500_btemp);
+
+ err = ab5500_temp_shutdown_auto(data);
+ if (err < 0) {
+ dev_err(&data->pdev->dev, "Failed to register"
+ " auto trigger(%d)\n", err);
+ return err;
+ }
+
+ /*
+ * Setup HW defined data.
+ *
+ * Reference hardware (HREF):
+ *
+ * XTAL_TEMP, PCB_TEMP, BTEMP_BALL refer to millivolts and
+ * BAT_CTRL and DIE_TEMP refer to millidegrees
+ *
+ * Make sure indexes correspond to the attribute indexes
+ * used when calling SENSOR_DEVICE_ATRR
+ */
+ data->gpadc_addr[0] = XTAL_TEMP;
+ data->gpadc_addr[1] = PCB_TEMP;
+ data->gpadc_addr[2] = BTEMP_BALL;
+ data->gpadc_addr[3] = BAT_CTRL;
+ data->gpadc_addr[4] = DIE_TEMP;
+ data->monitored_sensors = NUM_MONITORED_SENSORS;
+
+ data->ops.read_sensor = ab5500_read_sensor;
+ data->ops.irq_handler = ab5500_temp_irq_handler;
+ data->ops.show_name = ab5500_show_name;
+ data->ops.show_label = ab5500_show_label;
+ data->ops.is_visible = ab5500_is_visible;
+
+ return 0;
+}
diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c
new file mode 100644
index 00000000000..65a0f381d01
--- /dev/null
+++ b/drivers/hwmon/ab8500.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Martin Persson <martin.persson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ * Note:
+ *
+ * If/when the AB8500 thermal warning temperature is reached (threshold
+ * cannot be changed by SW), an interrupt is set and the driver
+ * notifies user space via a sysfs event. If a shut down is not
+ * triggered by user space within a certain time frame,
+ * pm_power off is called.
+ *
+ * If/when AB8500 thermal shutdown temperature is reached a hardware
+ * shutdown of the AB8500 will occur.
+ */
+
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+#include <linux/mfd/abx500/ab8500-bm.h>
+#include "abx500.h"
+#include <asm/mach-types.h>
+
+#define DEFAULT_POWER_OFF_DELAY 10000
+
+/*
+ * The driver monitors GPADC - ADC_AUX1, ADC_AUX2, BTEMP_BALL
+ * and BAT_CTRL.
+ */
+#define NUM_MONITORED_SENSORS 4
+
+static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor)
+{
+ int val;
+ /*
+ * Special treatment for the BAT_CTRL node, since this
+ * temperature measurement is more complex than just
+ * an ADC readout
+ */
+ if (sensor == BAT_CTRL)
+ val = ab8500_btemp_get_batctrl_temp(data->ab8500_btemp);
+ else
+ val = ab8500_gpadc_convert(data->ab8500_gpadc, sensor);
+
+ return val;
+}
+
+static void ab8500_thermal_power_off(struct work_struct *work)
+{
+ struct abx500_temp *data = container_of(work, struct abx500_temp,
+ power_off_work.work);
+
+ dev_warn(&data->pdev->dev, "Power off due to AB8500 thermal warning\n");
+ pm_power_off();
+}
+
+static ssize_t ab8500_show_name(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "ab8500\n");
+}
+
+static ssize_t ab8500_show_label(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ char *name;
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int index = attr->index;
+
+ /*
+ * Make sure these labels correspond to the attribute indexes
+ * used when calling SENSOR_DEVICE_ATRR.
+ * Temperature sensors outside ab8500 (read via GPADC) are marked
+ * with prefix ext_
+ */
+ switch (index) {
+ case 1:
+ name = "ext_rtc_xtal";
+ break;
+ case 2:
+ name = "ext_db8500";
+ break;
+ case 3:
+ name = "bat_temp";
+ break;
+ case 4:
+ name = "bat_ctrl";
+ break;
+ case 5:
+ name = "ab8500";
+ break;
+ default:
+ return -EINVAL;
+ }
+ return sprintf(buf, "%s\n", name);
+}
+
+static int ab8500_is_visible(struct attribute *attr, int n)
+{
+ if (!strcmp(attr->name, "temp5_input") ||
+ !strcmp(attr->name, "temp5_min") ||
+ !strcmp(attr->name, "temp5_max") ||
+ !strcmp(attr->name, "temp5_max_hyst") ||
+ !strcmp(attr->name, "temp5_min_alarm") ||
+ !strcmp(attr->name, "temp5_max_alarm") ||
+ !strcmp(attr->name, "temp5_max_hyst_alarm"))
+ return 0;
+
+ return attr->mode;
+}
+
+static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data)
+{
+ unsigned long delay_in_jiffies;
+ /*
+ * Make sure the magic numbers below corresponds to the node
+ * used for AB8500 thermal warning from HW.
+ */
+ mutex_lock(&data->lock);
+ data->crit_alarm[4] = 1;
+ mutex_unlock(&data->lock);
+
+ hwmon_notify(data->crit_alarm[4], NULL);
+ sysfs_notify(&data->pdev->dev.kobj, NULL, "temp5_crit_alarm");
+ dev_info(&data->pdev->dev, "AB8500 thermal warning,"
+ " power off in %lu s\n", data->power_off_delay);
+ delay_in_jiffies = msecs_to_jiffies(data->power_off_delay);
+ schedule_delayed_work(&data->power_off_work, delay_in_jiffies);
+ return 0;
+}
+
+int __init ab8500_hwmon_init(struct abx500_temp *data)
+{
+ data->ab8500_gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ if (IS_ERR(data->ab8500_gpadc))
+ return PTR_ERR(data->ab8500_gpadc);
+
+ data->ab8500_btemp = ab8500_btemp_get();
+ if (IS_ERR(data->ab8500_btemp))
+ return PTR_ERR(data->ab8500_btemp);
+
+ INIT_DELAYED_WORK(&data->power_off_work, ab8500_thermal_power_off);
+
+ /*
+ * Setup HW defined data.
+ *
+ * Reference hardware (HREF):
+ *
+ * GPADC - ADC_AUX1, connected to NTC R2148 next to RTC_XTAL on HREF
+ * GPADC - ADC_AUX2, connected to NTC R2150 near DB8500 on HREF
+ * Hence, temp#_min/max/max_hyst refer to millivolts and not
+ * millidegrees
+ * This is not the case for BAT_CTRL where millidegrees is used
+ *
+ * HREF HW does not support reading AB8500 temperature. BUT an
+ * AB8500 IRQ will be launched if die crit temp limit is reached.
+ *
+ * Make sure indexes correspond to the attribute indexes
+ * used when calling SENSOR_DEVICE_ATRR
+ */
+ data->gpadc_addr[0] = ADC_AUX1;
+ data->gpadc_addr[1] = ADC_AUX2;
+ data->gpadc_addr[2] = BTEMP_BALL;
+ data->gpadc_addr[3] = BAT_CTRL;
+ data->gpadc_addr[4] = DIE_TEMP;
+ data->power_off_delay = DEFAULT_POWER_OFF_DELAY;
+ data->monitored_sensors = NUM_MONITORED_SENSORS;
+
+ data->ops.read_sensor = ab8500_read_sensor;
+ data->ops.irq_handler = ab8500_temp_irq_handler;
+ data->ops.show_name = ab8500_show_name;
+ data->ops.show_label = ab8500_show_label;
+ data->ops.is_visible = ab8500_is_visible;
+
+ return 0;
+}
diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c
new file mode 100644
index 00000000000..7aa9994c54a
--- /dev/null
+++ b/drivers/hwmon/abx500.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Martin Persson <martin.persson@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ * Note:
+ *
+ * ABX500 does not provide auto ADC, so to monitor the required
+ * temperatures, a periodic work is used. It is more important
+ * to not wake up the CPU than to perform this job, hence the use
+ * of a deferred delay.
+ *
+ * A deferred delay for thermal monitor is considered safe because:
+ * If the chip gets too hot during a sleep state it's most likely
+ * due to external factors, such as the surrounding temperature.
+ * I.e. no SW decisions will make any difference.
+ *
+ * If/when the ABX500 thermal warning temperature is reached (threshold
+ * cannot be changed by SW), an interrupt is set and the driver
+ * notifies user space via a sysfs event.
+ *
+ * If/when ABX500 thermal shutdown temperature is reached a hardware
+ * shutdown of the ABX500 will occur.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <asm/mach-types.h>
+
+#include "abx500.h"
+
+#define DEFAULT_MONITOR_DELAY 1000
+
+/*
+ * Thresholds are considered inactive if set to 0.
+ * To avoid confusion for user space applications,
+ * the temp monitor delay is set to 0 if all thresholds
+ * are 0.
+ */
+static bool find_active_thresholds(struct abx500_temp *data)
+{
+ int i;
+ for (i = 0; i < data->monitored_sensors; i++)
+ if (data->max[i] != 0 || data->max_hyst[i] != 0
+ || data->min[i] != 0)
+ return true;
+
+ dev_dbg(&data->pdev->dev, "No active thresholds,"
+ "cancel deferred job (if it exists)"
+ "and reset temp monitor delay\n");
+ cancel_delayed_work_sync(&data->work);
+ return false;
+}
+
+static inline void schedule_monitor(struct abx500_temp *data)
+{
+ unsigned long delay_in_jiffies;
+ delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
+ schedule_delayed_work(&data->work, delay_in_jiffies);
+}
+
+static inline void gpadc_monitor_exit(struct abx500_temp *data)
+{
+ cancel_delayed_work_sync(&data->work);
+}
+
+static void gpadc_monitor(struct work_struct *work)
+{
+ unsigned long delay_in_jiffies;
+ int val, i, ret;
+ /* Container for alarm node name */
+ char alarm_node[30];
+
+ bool updated_min_alarm = false;
+ bool updated_max_alarm = false;
+ bool updated_max_hyst_alarm = false;
+ struct abx500_temp *data = container_of(work, struct abx500_temp,
+ work.work);
+
+ for (i = 0; i < data->monitored_sensors; i++) {
+ /* Thresholds are considered inactive if set to 0 */
+ if (data->max[i] == 0 && data->max_hyst[i] == 0
+ && data->min[i] == 0)
+ continue;
+
+ val = data->ops.read_sensor(data, data->gpadc_addr[i]);
+ if (val < 0) {
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+ continue;
+ }
+
+ mutex_lock(&data->lock);
+ if (data->min[i] != 0) {
+ if (val < data->min[i]) {
+ if (data->min_alarm[i] == 0) {
+ data->min_alarm[i] = 1;
+ updated_min_alarm = true;
+ }
+ } else {
+ if (data->min_alarm[i] == 1) {
+ data->min_alarm[i] = 0;
+ updated_min_alarm = true;
+ }
+ }
+
+ }
+ if (data->max[i] != 0) {
+ if (val > data->max[i]) {
+ if (data->max_alarm[i] == 0) {
+ data->max_alarm[i] = 1;
+ updated_max_alarm = true;
+ }
+ } else {
+ if (data->max_alarm[i] == 1) {
+ data->max_alarm[i] = 0;
+ updated_max_alarm = true;
+ }
+ }
+
+ }
+ if (data->max_hyst[i] != 0) {
+ if (val > data->max_hyst[i]) {
+ if (data->max_hyst_alarm[i] == 0) {
+ data->max_hyst_alarm[i] = 1;
+ updated_max_hyst_alarm = true;
+ }
+ } else {
+ if (data->max_hyst_alarm[i] == 1) {
+ data->max_hyst_alarm[i] = 0;
+ updated_max_hyst_alarm = true;
+ }
+ }
+ }
+ mutex_unlock(&data->lock);
+
+ /* hwmon attr index starts at 1, thus "i+1" below */
+ if (updated_min_alarm) {
+ ret = snprintf(alarm_node, 16, "temp%d_min_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_alarm) {
+ ret = snprintf(alarm_node, 16, "temp%d_max_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ hwmon_notify(data->max_alarm[i], NULL);
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ if (updated_max_hyst_alarm) {
+ ret = snprintf(alarm_node, 21, "temp%d_max_hyst_alarm",
+ (i + 1));
+ if (ret < 0) {
+ dev_err(&data->pdev->dev,
+ "Unable to update alarm node (%d)",
+ ret);
+ break;
+ }
+ sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+ }
+ }
+ delay_in_jiffies = msecs_to_jiffies(data->gpadc_monitor_delay);
+ schedule_delayed_work(&data->work, delay_in_jiffies);
+}
+
+static ssize_t set_temp_monitor_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_s;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->gpadc_monitor_delay = delay_in_s * 1000;
+
+ if (find_active_thresholds(data))
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ int res;
+ unsigned long delay_in_s;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+
+ res = strict_strtoul(buf, 10, &delay_in_s);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ data->power_off_delay = delay_in_s * 1000;
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t show_temp_monitor_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->gpadc_monitor_delay) / 1000);
+}
+
+static ssize_t show_temp_power_off_delay(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ /* return time in s, not ms */
+ return sprintf(buf, "%lu\n", (data->power_off_delay) / 1000);
+}
+
+/* HWMON sysfs interface */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ /*
+ * To avoid confusion between sensor label and chip name, the function
+ * "show_label" is not used to return the chip name.
+ */
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ return data->ops.show_name(dev, devattr, buf);
+}
+
+static ssize_t show_label(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ return data->ops.show_label(dev, devattr, buf);
+}
+
+static ssize_t show_input(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ int val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ u8 gpadc_addr = data->gpadc_addr[attr->index - 1];
+
+ val = data->ops.read_sensor(data, gpadc_addr);
+ if (val < 0)
+ dev_err(&data->pdev->dev, "GPADC read failed\n");
+
+ return sprintf(buf, "%d\n", val);
+}
+
+/* set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->min_alarm[attr->index - 1] = 0;
+
+ data->min[attr->index - 1] = val;
+
+ if (val == 0)
+ (void) find_active_thresholds(data);
+ else
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->max_alarm[attr->index - 1] = 0;
+
+ data->max[attr->index - 1] = val;
+
+ if (val == 0)
+ (void) find_active_thresholds(data);
+ else
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_max_hyst(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ /*
+ * Threshold is considered inactive if set to 0
+ * hwmon attr index starts at 1, thus "attr->index-1" below
+ */
+ if (val == 0)
+ data->max_hyst_alarm[attr->index - 1] = 0;
+
+ data->max_hyst[attr->index - 1] = val;
+
+ if (val == 0)
+ (void) find_active_thresholds(data);
+ else
+ schedule_monitor(data);
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/*
+ * show functions (RO nodes)
+ */
+static ssize_t show_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->min[attr->index - 1]);
+}
+
+static ssize_t show_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max[attr->index - 1]);
+}
+
+static ssize_t show_max_hyst(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_hyst[attr->index - 1]);
+}
+
+/* Alarms */
+static ssize_t show_min_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->min_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_hyst_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->max_hyst_alarm[attr->index - 1]);
+}
+
+static ssize_t show_crit_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%ld\n", data->crit_alarm[attr->index - 1]);
+}
+
+static mode_t abx500_attrs_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct abx500_temp *data = dev_get_drvdata(dev);
+ return data->ops.is_visible(a, n);
+}
+
+static SENSOR_DEVICE_ATTR(temp_monitor_delay, S_IRUGO | S_IWUSR,
+ show_temp_monitor_delay, set_temp_monitor_delay, 0);
+static SENSOR_DEVICE_ATTR(temp_power_off_delay, S_IRUGO | S_IWUSR,
+ show_temp_power_off_delay,
+ set_temp_power_off_delay, 0);
+
+/* Chip name, required by hwmon*/
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+/* GPADC - SENSOR1 */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 1);
+
+/* GPADC - SENSOR2 */
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 2);
+
+/* GPADC - SENSOR3 */
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 3);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 3);
+
+/* GPADC - SENSOR4 */
+static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_label, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_input, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_min, set_min, 4);
+static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_max, set_max, 4);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 4);
+static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_min_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_max_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp4_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 4);
+
+/* GPADC - SENSOR5 */
+static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, show_label, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_input, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_min, S_IWUSR | S_IRUGO, show_min, set_min, 5);
+static SENSOR_DEVICE_ATTR(temp5_max, S_IWUSR | S_IRUGO, show_max, set_max, 5);
+static SENSOR_DEVICE_ATTR(temp5_max_hyst, S_IWUSR | S_IRUGO,
+ show_max_hyst, set_max_hyst, 5);
+static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_min_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_max_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_max_hyst_alarm, S_IRUGO,
+ show_max_hyst_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp5_crit_alarm, S_IRUGO,
+ show_crit_alarm, NULL, 5);
+
+struct attribute *abx500_temp_attributes[] = {
+ &sensor_dev_attr_name.dev_attr.attr,
+ &sensor_dev_attr_temp_monitor_delay.dev_attr.attr,
+ &sensor_dev_attr_temp_power_off_delay.dev_attr.attr,
+ /* GPADC SENSOR1 */
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR2 */
+ &sensor_dev_attr_temp2_label.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR3 */
+ &sensor_dev_attr_temp3_label.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR4 */
+ &sensor_dev_attr_temp4_label.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_min.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst_alarm.dev_attr.attr,
+ /* GPADC SENSOR5*/
+ &sensor_dev_attr_temp5_label.dev_attr.attr,
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_min.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group abx500_temp_group = {
+ .attrs = abx500_temp_attributes,
+ .is_visible = abx500_attrs_visible,
+};
+
+static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data)
+{
+ struct platform_device *pdev = irq_data;
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+ data->ops.irq_handler(irq, data);
+ return IRQ_HANDLED;
+}
+
+static int setup_irqs(struct platform_device *pdev)
+{
+ int ret;
+ int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM");
+
+ if (irq < 0)
+ dev_err(&pdev->dev, "Get irq by name failed\n");
+
+ ret = request_threaded_irq(irq, NULL, abx500_temp_irq_handler,
+ IRQF_NO_SUSPEND, "abx500-temp", pdev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret);
+
+ return ret;
+}
+
+static int __devinit abx500_temp_probe(struct platform_device *pdev)
+{
+ struct abx500_temp *data;
+ int err;
+
+ data = kzalloc(sizeof(struct abx500_temp), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->pdev = pdev;
+ mutex_init(&data->lock);
+
+ /* Chip specific initialization */
+ if (!machine_is_u5500())
+ err = ab8500_hwmon_init(data);
+ else
+ err = ab5500_hwmon_init(data);
+ if (err < 0) {
+ dev_err(&pdev->dev, "abx500 init failed");
+ goto exit;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit;
+ }
+
+ INIT_DELAYED_WORK_DEFERRABLE(&data->work, gpadc_monitor);
+ data->gpadc_monitor_delay = DEFAULT_MONITOR_DELAY;
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+ goto exit_platform_data;
+ }
+
+ err = setup_irqs(pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "irq setup failed (%d)\n", err);
+ goto exit_sysfs_group;
+ }
+ return 0;
+
+exit_sysfs_group:
+ sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+exit_platform_data:
+ hwmon_device_unregister(data->hwmon_dev);
+ platform_set_drvdata(pdev, NULL);
+exit:
+ kfree(data->gpadc_auto);
+ kfree(data);
+ return err;
+}
+
+static int __devexit abx500_temp_remove(struct platform_device *pdev)
+{
+ struct abx500_temp *data = platform_get_drvdata(pdev);
+
+ gpadc_monitor_exit(data);
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data->gpadc_auto);
+ kfree(data);
+ return 0;
+}
+
+/* No action required in suspend/resume, thus the lack of functions */
+static struct platform_driver abx500_temp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "abx500-temp",
+ },
+ .probe = abx500_temp_probe,
+ .remove = __devexit_p(abx500_temp_remove),
+};
+
+static int __init abx500_temp_init(void)
+{
+ return platform_driver_register(&abx500_temp_driver);
+}
+
+static void __exit abx500_temp_exit(void)
+{
+ platform_driver_unregister(&abx500_temp_driver);
+}
+
+MODULE_AUTHOR("Martin Persson <martin.persson@stericsson.com>");
+MODULE_DESCRIPTION("ABX500 temperature driver");
+MODULE_LICENSE("GPL");
+
+module_init(abx500_temp_init)
+module_exit(abx500_temp_exit)
diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h
new file mode 100644
index 00000000000..9fe28dac28f
--- /dev/null
+++ b/drivers/hwmon/abx500.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * License terms: GNU General Public License v2
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ */
+
+#ifndef _ABX500_H
+#define _ABX500_H
+
+#define NUM_SENSORS 5
+
+struct ab8500_gpadc;
+struct ab5500_gpadc;
+struct ab8500_btemp;
+struct ab5500_btemp;
+struct adc_auto_input;
+struct abx500_temp;
+
+/**
+ * struct abx500_temp_ops - abx500 chip specific ops
+ * @read_sensor: reads gpadc output
+ * @irq_handler: irq handler
+ * @show_name: hwmon device name
+ * @show_label: hwmon attribute label
+ * @is_visible: is attribute visible
+ */
+struct abx500_temp_ops {
+ int (*read_sensor)(struct abx500_temp *, u8);
+ int (*irq_handler)(int, struct abx500_temp *);
+ ssize_t (*show_name)(struct device *,
+ struct device_attribute *, char *);
+ ssize_t (*show_label) (struct device *,
+ struct device_attribute *, char *);
+ int (*is_visible)(struct attribute *, int);
+};
+
+/**
+ * struct abx500_temp - representation of temp mon device
+ * @pdev: platform device
+ * @hwmon_dev: hwmon device
+ * @ab8500_gpadc: gpadc interface for ab8500
+ * @ab5500_gpadc: gpadc interface for ab5500
+ * @btemp: battery temperature interface for ab8500
+ * @adc_auto_input: gpadc auto trigger
+ * @gpadc_addr: gpadc channel address
+ * @temp: sensor temperature input value
+ * @min: sensor temperature min value
+ * @max: sensor temperature max value
+ * @max_hyst: sensor temperature hysteresis value for max limit
+ * @crit: sensor temperature critical value
+ * @min_alarm: sensor temperature min alarm
+ * @max_alarm: sensor temperature max alarm
+ * @max_hyst_alarm: sensor temperature hysteresis alarm
+ * @crit_alarm: sensor temperature critical value alarm
+ * @work: delayed work scheduled to monitor temperature periodically
+ * @power_off_work: delayed work scheduled to power off the system
+ when critical temperature is reached
+ * @lock: mutex
+ * @gpadc_monitor_delay: delay between temperature readings in ms
+ * @power_off_delay: delay before power off in ms
+ * @monitored_sensors: number of monitored sensors
+ */
+struct abx500_temp {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ struct ab8500_gpadc *ab8500_gpadc;
+ struct ab5500_gpadc *ab5500_gpadc;
+ struct ab8500_btemp *ab8500_btemp;
+ struct ab5500_btemp *ab5500_btemp;
+ struct adc_auto_input *gpadc_auto;
+ struct abx500_temp_ops ops;
+ u8 gpadc_addr[NUM_SENSORS];
+ unsigned long temp[NUM_SENSORS];
+ unsigned long min[NUM_SENSORS];
+ unsigned long max[NUM_SENSORS];
+ unsigned long max_hyst[NUM_SENSORS];
+ unsigned long crit[NUM_SENSORS];
+ unsigned long min_alarm[NUM_SENSORS];
+ unsigned long max_alarm[NUM_SENSORS];
+ unsigned long max_hyst_alarm[NUM_SENSORS];
+ unsigned long crit_alarm[NUM_SENSORS];
+ struct delayed_work work;
+ struct delayed_work power_off_work;
+ struct mutex lock;
+ /* Delay (ms) between temperature readings */
+ unsigned long gpadc_monitor_delay;
+ /* Delay (ms) before power off */
+ unsigned long power_off_delay;
+ int monitored_sensors;
+};
+
+int ab8500_hwmon_init(struct abx500_temp *data) __init;
+int ab5500_hwmon_init(struct abx500_temp *data) __init;
+
+#endif /* _ABX500_H */
diff --git a/drivers/hwmon/dbx500.c b/drivers/hwmon/dbx500.c
new file mode 100644
index 00000000000..c034b48f8dd
--- /dev/null
+++ b/drivers/hwmon/dbx500.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010. All rights reserved.
+ * This code is ST-Ericsson proprietary and confidential.
+ * Any use of the code for whatever purpose is subject to
+ * specific written permission of ST-Ericsson SA.
+ *
+ * Author: WenHai Fang <wenhai.h.fang@stericsson.com> for
+ * ST-Ericsson.
+ * License terms: GNU Gereral Public License (GPL) version 2
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/hwmon.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+/*
+ * Default measure period to 0xFF x cycle32k
+ */
+#define DEFAULT_MEASURE_TIME 0xFF
+
+/*
+ * Default critical sensor temperature
+ */
+#define DEFAULT_CRITICAL_TEMP 85
+
+/* This driver monitors DB thermal*/
+#define NUM_SENSORS 1
+
+struct dbx500_temp {
+ struct platform_device *pdev;
+ struct device *hwmon_dev;
+ unsigned char min[NUM_SENSORS];
+ unsigned char max[NUM_SENSORS];
+ unsigned char crit[NUM_SENSORS];
+ unsigned char min_alarm[NUM_SENSORS];
+ unsigned char max_alarm[NUM_SENSORS];
+ unsigned short measure_time;
+ bool monitoring_active;
+ struct mutex lock;
+};
+
+static inline void start_temp_monitoring(struct dbx500_temp *data,
+ const int index)
+{
+ unsigned int i;
+
+ /* determine if there are any sensors worth monitoring */
+ for (i = 0; i < NUM_SENSORS; i++)
+ if (data->min[i] || data->max[i])
+ goto start_monitoring;
+
+ return;
+
+start_monitoring:
+ /* kick off the monitor job */
+ data->min_alarm[index] = 0;
+ data->max_alarm[index] = 0;
+
+ (void) prcmu_start_temp_sense(data->measure_time);
+ data->monitoring_active = true;
+}
+
+static inline void stop_temp_monitoring(struct dbx500_temp *data)
+{
+ if (data->monitoring_active) {
+ (void) prcmu_stop_temp_sense();
+ data->monitoring_active = false;
+ }
+}
+
+/* HWMON sysfs interface */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "dbx500\n");
+}
+
+static ssize_t show_label(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ return show_name(dev, devattr, buf);
+}
+
+/* set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ val &= 0xFF;
+ if (val > data->max[attr->index - 1])
+ val = data->max[attr->index - 1];
+
+ data->min[attr->index - 1] = val;
+
+ stop_temp_monitoring(data);
+
+ (void) prcmu_config_hotmon(data->min[attr->index - 1],
+ data->max[attr->index - 1]);
+
+ start_temp_monitoring(data, (attr->index - 1));
+
+ mutex_unlock(&data->lock);
+ return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ val &= 0xFF;
+ if (val < data->min[attr->index - 1])
+ val = data->min[attr->index - 1];
+
+ data->max[attr->index - 1] = val;
+
+ stop_temp_monitoring(data);
+
+ (void) prcmu_config_hotmon(data->min[attr->index - 1],
+ data->max[attr->index - 1]);
+
+ start_temp_monitoring(data, (attr->index - 1));
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+static ssize_t set_crit(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int res = strict_strtoul(buf, 10, &val);
+ if (res < 0)
+ return res;
+
+ mutex_lock(&data->lock);
+ val &= 0xFF;
+ data->crit[attr->index - 1] = val;
+ (void) prcmu_config_hotdog(data->crit[attr->index - 1]);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/*
+ * show functions (RO nodes)
+ * Notice that min/max/crit refer to degrees
+ */
+static ssize_t show_min(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->min[attr->index - 1]);
+}
+
+static ssize_t show_max(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->max[attr->index - 1]);
+}
+
+static ssize_t show_crit(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->crit[attr->index - 1]);
+}
+
+/* Alarms */
+static ssize_t show_min_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->min_alarm[attr->index - 1]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct dbx500_temp *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ /* hwmon attr index starts at 1, thus "attr->index-1" below */
+ return sprintf(buf, "%d\n", data->max_alarm[attr->index - 1]);
+}
+
+/* Chip name, required by hwmon*/
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+ show_crit, set_crit, 1);
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+
+static struct attribute *dbx500_temp_attributes[] = {
+ &sensor_dev_attr_name.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_label.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group dbx500_temp_group = {
+ .attrs = dbx500_temp_attributes,
+};
+
+static irqreturn_t prcmu_hotmon_low_irq_handler(int irq, void *irq_data)
+{
+ struct platform_device *pdev = irq_data;
+ struct dbx500_temp *data = platform_get_drvdata(pdev);
+
+ mutex_lock(&data->lock);
+ data->min_alarm[0] = 1;
+ mutex_unlock(&data->lock);
+
+ sysfs_notify(&pdev->dev.kobj, NULL, "temp1_min_alarm");
+ dev_dbg(&pdev->dev, "DBX500 thermal low warning\n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t prcmu_hotmon_high_irq_handler(int irq, void *irq_data)
+{
+ struct platform_device *pdev = irq_data;
+ struct dbx500_temp *data = platform_get_drvdata(pdev);
+
+ mutex_lock(&data->lock);
+ data->max_alarm[0] = 1;
+ mutex_unlock(&data->lock);
+
+ hwmon_notify(data->max_alarm[0], NULL);
+ sysfs_notify(&pdev->dev.kobj, NULL, "temp1_max_alarm");
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit dbx500_temp_probe(struct platform_device *pdev)
+{
+ struct dbx500_temp *data;
+ int err = 0, i;
+ int irq;
+
+ dev_dbg(&pdev->dev, "dbx500_temp: Function dbx500_temp_probe.\n");
+
+ data = kzalloc(sizeof(struct dbx500_temp), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed\n");
+ goto exit;
+ }
+
+ err = request_threaded_irq(irq, NULL,
+ prcmu_hotmon_low_irq_handler,
+ IRQF_NO_SUSPEND,
+ "dbx500_temp_low", pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "dbx500: Failed allocate HOTMON_LOW.\n");
+ goto exit;
+ } else {
+ dev_dbg(&pdev->dev, "dbx500: Succeed allocate HOTMON_LOW.\n");
+ }
+
+ irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed\n");
+ goto exit;
+ }
+
+ err = request_threaded_irq(irq, NULL,
+ prcmu_hotmon_high_irq_handler,
+ IRQF_NO_SUSPEND,
+ "dbx500_temp_high", pdev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "dbx500: Failed allocate HOTMON_HIGH.\n");
+ goto exit;
+ } else {
+ dev_dbg(&pdev->dev, "dbx500: Succeed allocate HOTMON_HIGH.\n");
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit;
+ }
+
+ for (i = 0; i < NUM_SENSORS; i++) {
+ data->min[i] = 0;
+ data->max[i] = 0;
+ data->crit[i] = DEFAULT_CRITICAL_TEMP;
+ data->min_alarm[i] = 0;
+ data->max_alarm[i] = 0;
+ }
+
+ mutex_init(&data->lock);
+
+ data->pdev = pdev;
+ data->measure_time = DEFAULT_MEASURE_TIME;
+ data->monitoring_active = false;
+
+ /* set PRCMU to disable platform when we get to the critical temp */
+ (void) prcmu_config_hotdog(DEFAULT_CRITICAL_TEMP);
+
+ platform_set_drvdata(pdev, data);
+
+ err = sysfs_create_group(&pdev->dev.kobj, &dbx500_temp_group);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+ goto exit_platform_data;
+ }
+
+ return 0;
+
+exit_platform_data:
+ platform_set_drvdata(pdev, NULL);
+exit:
+ kfree(data);
+ return err;
+}
+
+static int __devexit dbx500_temp_remove(struct platform_device *pdev)
+{
+ struct dbx500_temp *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &dbx500_temp_group);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+ return 0;
+}
+
+/* No action required in suspend/resume, thus the lack of functions */
+static struct platform_driver dbx500_temp_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dbx500_temp",
+ },
+ .probe = dbx500_temp_probe,
+ .remove = __devexit_p(dbx500_temp_remove),
+};
+
+static int __init dbx500_temp_init(void)
+{
+ return platform_driver_register(&dbx500_temp_driver);
+}
+
+static void __exit dbx500_temp_exit(void)
+{
+ platform_driver_unregister(&dbx500_temp_driver);
+}
+
+MODULE_AUTHOR("WenHai Fang <wenhai.h.fang@stericsson.com>");
+MODULE_DESCRIPTION("DBX500 temperature driver");
+MODULE_LICENSE("GPL");
+
+module_init(dbx500_temp_init)
+module_exit(dbx500_temp_exit)
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index c3c471ca202..8957bbac7a7 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -21,6 +21,7 @@
#include <linux/gfp.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
+#include <linux/notifier.h>
#define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@@ -29,6 +30,8 @@ static struct class *hwmon_class;
static DEFINE_IDA(hwmon_ida);
+static BLOCKING_NOTIFIER_HEAD(hwmon_notifier_list);
+
/**
* hwmon_device_register - register w/ hwmon
* @dev: the device to register
@@ -75,6 +78,24 @@ void hwmon_device_unregister(struct device *dev)
}
EXPORT_SYMBOL_GPL(hwmon_device_unregister);
+int hwmon_notifier_register(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&hwmon_notifier_list, nb);
+}
+EXPORT_SYMBOL(hwmon_notifier_register);
+
+int hwmon_notifier_unregister(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&hwmon_notifier_list, nb);
+}
+EXPORT_SYMBOL(hwmon_notifier_unregister);
+
+void hwmon_notify(unsigned long val, void *v)
+{
+ blocking_notifier_call_chain(&hwmon_notifier_list, val, v);
+}
+EXPORT_SYMBOL(hwmon_notify);
+
static void __init hwmon_pci_quirks(void)
{
#if defined CONFIG_X86 && defined CONFIG_PCI
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 5267ab93d55..9ddf2c97d26 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -431,7 +431,7 @@ static int read_i2c(struct nmk_i2c_dev *dev)
if (timeout == 0) {
/* Controller timed out */
- dev_err(&dev->pdev->dev, "read from slave 0x%x timed out\n",
+ dev_err(&dev->pdev->dev, "Read from Slave 0x%x timed out\n",
dev->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -518,7 +518,7 @@ static int write_i2c(struct nmk_i2c_dev *dev)
if (timeout == 0) {
/* Controller timed out */
- dev_err(&dev->pdev->dev, "write to slave 0x%x timed out\n",
+ dev_err(&dev->pdev->dev, "Write to slave 0x%x timed out\n",
dev->cli.slave_adr);
status = -ETIMEDOUT;
}
@@ -628,12 +628,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
dev->busy = true;
- if (dev->regulator)
- regulator_enable(dev->regulator);
pm_runtime_get_sync(&dev->pdev->dev);
- clk_enable(dev->clk);
-
status = init_hw(dev);
if (status)
goto out;
@@ -666,10 +662,8 @@ static int nmk_i2c_xfer(struct i2c_adapter *i2c_adap,
}
out:
- clk_disable(dev->clk);
- pm_runtime_put_sync(&dev->pdev->dev);
- if (dev->regulator)
- regulator_disable(dev->regulator);
+
+ pm_runtime_put(&dev->pdev->dev);
dev->busy = false;
@@ -859,9 +853,9 @@ static irqreturn_t i2c_irq_handler(int irq, void *arg)
#ifdef CONFIG_PM
-static int nmk_i2c_suspend(struct device *dev)
+
+static int nmk_i2c_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct platform_device *pdev = to_platform_device(dev);
struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
if (nmk_i2c->busy)
@@ -870,23 +864,53 @@ static int nmk_i2c_suspend(struct device *dev)
return 0;
}
-static int nmk_i2c_resume(struct device *dev)
+static int nmk_i2c_suspend_noirq(struct device *dev)
{
+ struct nmk_i2c_dev *nmk_i2c =
+ platform_get_drvdata(to_platform_device(dev));
+
+ if (nmk_i2c->busy)
+ return -EBUSY;
+
return 0;
}
+
#else
#define nmk_i2c_suspend NULL
-#define nmk_i2c_resume NULL
+#define nmk_i2c_suspend_noirq NULL
#endif
+static int nmk_i2c_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
+
+ clk_disable(nmk_i2c->clk);
+ if (nmk_i2c->regulator)
+ regulator_disable(nmk_i2c->regulator);
+ return 0;
+}
+
+static int nmk_i2c_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct nmk_i2c_dev *nmk_i2c = platform_get_drvdata(pdev);
+
+ if (nmk_i2c->regulator)
+ regulator_enable(nmk_i2c->regulator);
+ clk_enable(nmk_i2c->clk);
+ return 0;
+}
+
/*
* We use noirq so that we suspend late and resume before the wakeup interrupt
* to ensure that we do the !pm_runtime_suspended() check in resume before
* there has been a regular pm runtime resume (via pm_runtime_get_sync()).
*/
static const struct dev_pm_ops nmk_i2c_pm = {
- .suspend_noirq = nmk_i2c_suspend,
- .resume_noirq = nmk_i2c_resume,
+ SET_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend, nmk_i2c_runtime_resume,
+ NULL)
+ .suspend_noirq = nmk_i2c_suspend_noirq,
};
static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap)
@@ -1047,6 +1071,7 @@ static struct platform_driver nmk_i2c_driver = {
},
.probe = nmk_i2c_probe,
.remove = __devexit_p(nmk_i2c_remove),
+ .suspend = nmk_i2c_suspend,
};
static int __init nmk_i2c_init(void)
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ff4b8cfda58..5183a2d4fd5 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -50,6 +50,14 @@ config LEDS_LM3530
controlled manually or using PWM input or using ambient
light automatically.
+config LEDS_AB5500
+ tristate "HVLED driver for AB5500"
+ depends on AB5500_CORE
+ help
+ This option enables support for the HVLED in AB5500
+ multi function device. Currently Ab5500 v1.0 chip leds
+ are supported.
+
config LEDS_LOCOMO
tristate "LED Support for Locomo device"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 890481cb09f..59a569b376e 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
+obj-$(CONFIG_LEDS_AB5500) += leds-ab5500.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
diff --git a/drivers/leds/leds-ab5500.c b/drivers/leds/leds-ab5500.c
new file mode 100644
index 00000000000..294551b1962
--- /dev/null
+++ b/drivers/leds/leds-ab5500.c
@@ -0,0 +1,811 @@
+/*
+ * leds-ab5500.c - driver for High Voltage (HV) LED in ST-Ericsson AB5500 chip
+ *
+ * Copyright (C) 2011 ST-Ericsson SA.
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ */
+
+/*
+ * Driver for HVLED in ST-Ericsson AB5500 analog baseband controller
+ *
+ * This chip can drive upto 3 leds, of upto 40mA of led sink current.
+ * These leds can be programmed to blink between two intensities with
+ * fading delay of half, one or two seconds.
+ *
+ * Leds can be controlled via sysfs entries in
+ * "/sys/class/leds/< red | green | blue >"
+ *
+ * For each led,
+ *
+ * Modes of operation:
+ * - manual: echo 0 > fade_auto (default, no auto blinking)
+ * - auto: echo 1 > fade_auto
+ *
+ * Soft scaling delay between two intensities:
+ * - 1/2 sec: echo 1 > fade_delay
+ * - 1 sec: echo 2 > fade_delay
+ * - 2 sec: echo 3 > fade_delay
+ *
+ * Possible sequence of operation:
+ * - continuous glow: set brightness (brt)
+ * - blink between LED_OFF and LED_FULL:
+ * set fade delay -> set fade auto
+ * - blink between previous two brightness (only for LED-1):
+ * set brt1 -> set brt2 -> set fade auto
+ *
+ * Delay can be set in any step, its affect will be seen on switching mode.
+ *
+ * Note: Blink/Fade feature is supported in AB5500 v2 onwards
+ *
+ */
+
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/leds-ab5500.h>
+#include <linux/types.h>
+
+#include <mach/hardware.h>
+
+#define AB5500LED_NAME "ab5500-leds"
+#define AB5500_LED_MAX 0x03
+
+/* Register offsets */
+#define AB5500_LED_REG_ENABLE 0x03
+#define AB5500_LED_FADE_CTRL 0x0D
+
+/* LED-0 Register Addr. Offsets */
+#define AB5500_LED0_PWM_DUTY 0x01
+#define AB5500_LED0_PWMFREQ 0x02
+#define AB5500_LED0_SINKCTL 0x0A
+#define AB5500_LED0_FADE_HI 0x11
+#define AB5500_LED0_FADE_LO 0x17
+
+/* LED-1 Register Addr. Offsets */
+#define AB5500_LED1_PWM_DUTY 0x05
+#define AB5500_LED1_PWMFREQ 0x06
+#define AB5500_LED1_SINKCTL 0x0B
+#define AB5500_LED1_FADE_HI 0x13
+#define AB5500_LED1_FADE_LO 0x19
+
+/* LED-2 Register Addr. Offsets */
+#define AB5500_LED2_PWM_DUTY 0x08
+#define AB5500_LED2_PWMFREQ 0x09
+#define AB5500_LED2_SINKCTL 0x0C
+#define AB5500_LED2_FADE_HI 0x15
+#define AB5500_LED2_FADE_LO 0x1B
+
+/* led-0/1/2 enable bit */
+#define AB5500_LED_ENABLE_MASK 0x04
+
+/* led intensity */
+#define AB5500_LED_INTENSITY_OFF 0x0
+#define AB5500_LED_INTENSITY_MAX 0x3FF
+#define AB5500_LED_INTENSITY_STEP (AB5500_LED_INTENSITY_MAX/LED_FULL)
+
+/* pwm frequency */
+#define AB5500_LED_PWMFREQ_MAX 0x0F /* 373.39 @sysclk=26MHz */
+#define AB5500_LED_PWMFREQ_SHIFT 4
+
+/* LED sink current control */
+#define AB5500_LED_SINKCURR_MAX 0x0F /* 40mA MAX */
+#define AB5500_LED_SINKCURR_SHIFT 4
+
+/* fade Control shift and masks */
+#define AB5500_FADE_DELAY_SHIFT 0x00
+#define AB5500_FADE_MODE_MASK 0x80
+#define AB5500_FADE_DELAY_MASK 0x03
+#define AB5500_FADE_START_MASK 0x04
+#define AB5500_FADE_ON_MASK 0x70
+#define AB5500_LED_FADE_ENABLE(ledid) (0x40 >> (ledid))
+
+struct ab5500_led {
+ u8 id;
+ u8 max_current;
+ u16 brt_val;
+ u16 fade_hi;
+ u16 fade_lo;
+ bool led_on;
+ struct led_classdev led_cdev;
+ struct work_struct led_work;
+};
+
+struct ab5500_hvleds {
+ struct mutex lock;
+ struct device *dev;
+ struct ab5500_hvleds_platform_data *pdata;
+ struct ab5500_led leds[AB5500_HVLEDS_MAX];
+ bool hw_fade;
+ bool fade_auto;
+ enum ab5500_fade_delay fade_delay;
+};
+
+static u8 ab5500_led_pwmduty_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_PWM_DUTY,
+ AB5500_LED1_PWM_DUTY,
+ AB5500_LED2_PWM_DUTY,
+};
+
+static u8 ab5500_led_pwmfreq_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_PWMFREQ,
+ AB5500_LED1_PWMFREQ,
+ AB5500_LED2_PWMFREQ,
+};
+
+static u8 ab5500_led_sinkctl_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_SINKCTL,
+ AB5500_LED1_SINKCTL,
+ AB5500_LED2_SINKCTL
+};
+
+static u8 ab5500_led_fade_hi_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_FADE_HI,
+ AB5500_LED1_FADE_HI,
+ AB5500_LED2_FADE_HI,
+};
+
+static u8 ab5500_led_fade_lo_reg[AB5500_LED_MAX] = {
+ AB5500_LED0_FADE_LO,
+ AB5500_LED1_FADE_LO,
+ AB5500_LED2_FADE_LO,
+};
+
+#define to_led(_x) container_of(_x, struct ab5500_led, _x)
+
+static inline struct ab5500_hvleds *led_to_hvleds(struct ab5500_led *led)
+{
+ return container_of(led, struct ab5500_hvleds, leds[led->id]);
+}
+
+static int ab5500_led_enable(struct ab5500_hvleds *hvleds,
+ unsigned int led_id)
+{
+ int ret;
+
+ ret = abx500_mask_and_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id],
+ AB5500_LED_ENABLE_MASK,
+ AB5500_LED_ENABLE_MASK);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmduty_reg[led_id], ret);
+
+ return ret;
+
+}
+
+static int ab5500_led_start_manual(struct ab5500_hvleds *hvleds)
+{
+ int ret;
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_mask_and_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_FADE_CTRL, AB5500_FADE_START_MASK,
+ AB5500_FADE_START_MASK);
+ if (ret < 0)
+ dev_err(hvleds->dev, "update reg 0x%x failed - %d\n",
+ AB5500_LED_FADE_CTRL, ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_disable(struct ab5500_hvleds *hvleds,
+ unsigned int led_id)
+{
+ int ret;
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id] - 1, 0);
+ ret |= abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id], 0);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmduty_reg[led_id], ret);
+
+ return ret;
+}
+
+static int ab5500_led_pwmduty_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, u16 val)
+{
+ int ret;
+ u8 val_lsb = val & 0xFF;
+ u8 val_msb = (val & 0x300) >> 8;
+
+ mutex_lock(&hvleds->lock);
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val = %d\n"
+ "reg[%d] w val = %d\n",
+ ab5500_led_pwmduty_reg[led_id] - 1, val_lsb,
+ ab5500_led_pwmduty_reg[led_id], val_msb);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id] - 1, val_lsb);
+ ret |= abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmduty_reg[led_id], val_msb);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmduty_reg[led_id], ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_pwmfreq_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, u8 val)
+{
+ int ret;
+
+ val = (val & 0x0F) << AB5500_LED_PWMFREQ_SHIFT;
+
+ mutex_lock(&hvleds->lock);
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val=%d\n",
+ ab5500_led_pwmfreq_reg[led_id], val);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_pwmfreq_reg[led_id], val);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_pwmfreq_reg[led_id], ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_sinkctl_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, u8 val)
+{
+ int ret;
+
+ if (val > AB5500_LED_SINKCURR_MAX)
+ val = AB5500_LED_SINKCURR_MAX;
+
+ val = (val << AB5500_LED_SINKCURR_SHIFT);
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val=%d\n",
+ ab5500_led_sinkctl_reg[led_id], val);
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_sinkctl_reg[led_id], val);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ ab5500_led_sinkctl_reg[led_id], ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_fade_write(struct ab5500_hvleds *hvleds,
+ unsigned int led_id, bool on, u16 val)
+{
+ int ret;
+ int val_lsb = val & 0xFF;
+ int val_msb = (val & 0x300) >> 8;
+ u8 *fade_reg;
+
+ if (on)
+ fade_reg = ab5500_led_fade_hi_reg;
+ else
+ fade_reg = ab5500_led_fade_lo_reg;
+
+ dev_dbg(hvleds->dev, "ab5500-leds: reg[%d] w val = %d\n"
+ "reg[%d] w val = %d\n",
+ fade_reg[led_id] - 1, val_lsb,
+ fade_reg[led_id], val_msb);
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ fade_reg[led_id] - 1, val_lsb);
+ ret |= abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ fade_reg[led_id], val_msb);
+ if (ret < 0)
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ fade_reg[led_id], ret);
+
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static int ab5500_led_sinkctl_read(struct ab5500_hvleds *hvleds,
+ unsigned int led_id)
+{
+ int ret;
+ u8 val;
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_get_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ ab5500_led_sinkctl_reg[led_id], &val);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] r failed: %d\n",
+ ab5500_led_sinkctl_reg[led_id], ret);
+ mutex_unlock(&hvleds->lock);
+ return ret;
+ }
+
+ val = (val & 0xF0) >> AB5500_LED_SINKCURR_SHIFT;
+
+ mutex_unlock(&hvleds->lock);
+
+ return val;
+}
+
+static void ab5500_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brt_val)
+{
+ struct ab5500_led *led = to_led(led_cdev);
+
+ /* adjust LED_FULL to 10bit range */
+ brt_val &= LED_FULL;
+ led->brt_val = brt_val * AB5500_LED_INTENSITY_STEP;
+
+ schedule_work(&led->led_work);
+}
+
+static void ab5500_led_work(struct work_struct *led_work)
+{
+ struct ab5500_led *led = to_led(led_work);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (led->led_on == true) {
+ ab5500_led_pwmduty_write(hvleds, led->id, led->brt_val);
+ if (hvleds->hw_fade && led->brt_val) {
+ ab5500_led_enable(hvleds, led->id);
+ ab5500_led_start_manual(hvleds);
+ }
+ }
+}
+
+static ssize_t ab5500_led_show_current(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int led_curr = 0;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ led_curr = ab5500_led_sinkctl_read(hvleds, led->id);
+
+ if (led_curr < 0)
+ return led_curr;
+
+ return sprintf(buf, "%d\n", led_curr);
+}
+
+static ssize_t ab5500_led_store_current(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ int ret;
+ unsigned long led_curr;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (strict_strtoul(buf, 0, &led_curr))
+ return -EINVAL;
+
+ if (led_curr > led->max_current)
+ led_curr = led->max_current;
+
+ ret = ab5500_led_sinkctl_write(hvleds, led->id, led_curr);
+ if (ret < 0)
+ return ret;
+
+ return len;
+}
+
+static ssize_t ab5500_led_store_fade_auto(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ int ret;
+ u8 fade_ctrl = 0;
+ unsigned long fade_auto;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (strict_strtoul(buf, 0, &fade_auto))
+ return -EINVAL;
+
+ if (fade_auto > 1) {
+ dev_err(hvleds->dev, "invalid mode\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&hvleds->lock);
+
+ ret = abx500_get_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_FADE_CTRL, &fade_ctrl);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_FADE_CTRL, ret);
+ goto unlock_and_return;
+ }
+
+ /* manual mode */
+ if (fade_auto == false) {
+ fade_ctrl &= ~(AB5500_LED_FADE_ENABLE(led->id));
+ if (!(fade_ctrl & AB5500_FADE_ON_MASK))
+ fade_ctrl = 0;
+
+ ret = ab5500_led_disable(hvleds, led->id);
+ if (ret < 0)
+ goto unlock_and_return;
+ } else {
+ /* set led auto enable bit */
+ fade_ctrl |= AB5500_FADE_MODE_MASK;
+ fade_ctrl |= AB5500_LED_FADE_ENABLE(led->id);
+
+ /* set fade delay */
+ fade_ctrl &= ~AB5500_FADE_DELAY_MASK;
+ fade_ctrl |= hvleds->fade_delay << AB5500_FADE_DELAY_SHIFT;
+
+ /* set fade start manual */
+ fade_ctrl |= AB5500_FADE_START_MASK;
+
+ /* enble corresponding led */
+ ret = ab5500_led_enable(hvleds, led->id);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ }
+
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_FADE_CTRL, fade_ctrl);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_FADE_CTRL, ret);
+ goto unlock_and_return;
+ }
+
+ hvleds->fade_auto = fade_auto;
+
+ ret = len;
+
+unlock_and_return:
+ mutex_unlock(&hvleds->lock);
+
+ return ret;
+}
+
+static ssize_t ab5500_led_show_fade_auto(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ return sprintf(buf, "%d\n", hvleds->fade_auto);
+}
+
+static ssize_t ab5500_led_store_fade_delay(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ unsigned long fade_delay;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct ab5500_led *led = to_led(led_cdev);
+ struct ab5500_hvleds *hvleds = led_to_hvleds(led);
+
+ if (strict_strtoul(buf, 0, &fade_delay))
+ return -EINVAL;
+
+ if (fade_delay > AB5500_FADE_DELAY_TWOSEC) {
+ dev_err(hvleds->dev, "invalid mode\n");
+ return -EINVAL;
+ }
+
+ hvleds->fade_delay = fade_delay;
+
+ return len;
+}
+
+/* led class device attributes */
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO,
+ ab5500_led_show_current, ab5500_led_store_current);
+static DEVICE_ATTR(fade_auto, S_IRUGO | S_IWUGO,
+ ab5500_led_show_fade_auto, ab5500_led_store_fade_auto);
+static DEVICE_ATTR(fade_delay, S_IRUGO | S_IWUGO,
+ NULL, ab5500_led_store_fade_delay);
+
+static int ab5500_led_init_registers(struct ab5500_hvleds *hvleds)
+{
+ int ret = 0;
+ unsigned int led_id;
+
+ /* fade - manual : dur mid : pwm duty mid */
+ if (!hvleds->hw_fade) {
+ ret = abx500_set_register_interruptible(
+ hvleds->dev, AB5500_BANK_LED,
+ AB5500_LED_REG_ENABLE, true);
+ if (ret < 0) {
+ dev_err(hvleds->dev, "reg[%d] w failed: %d\n",
+ AB5500_LED_REG_ENABLE, ret);
+ return ret;
+ }
+ }
+
+ for (led_id = 0; led_id < AB5500_HVLEDS_MAX; led_id++) {
+ if (hvleds->leds[led_id].led_on == false)
+ continue;
+
+ ret = ab5500_led_sinkctl_write(
+ hvleds, led_id,
+ hvleds->leds[led_id].max_current);
+ if (ret < 0)
+ return ret;
+
+ if (hvleds->hw_fade) {
+ ret = ab5500_led_pwmfreq_write(
+ hvleds, led_id,
+ AB5500_LED_PWMFREQ_MAX / 2);
+ if (ret < 0)
+ return ret;
+
+ /* fade high intensity */
+ ret = ab5500_led_fade_write(
+ hvleds, led_id, true,
+ hvleds->leds[led_id].fade_hi);
+ if (ret < 0)
+ return ret;
+
+ /* fade low intensity */
+ ret = ab5500_led_fade_write(
+ hvleds, led_id, false,
+ hvleds->leds[led_id].fade_lo);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* init led off */
+ ret |= ab5500_led_pwmduty_write(
+ hvleds, led_id, AB5500_LED_INTENSITY_OFF);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ab5500_led_register_leds(struct device *dev,
+ struct ab5500_hvleds_platform_data *pdata,
+ struct ab5500_hvleds *hvleds)
+{
+ int i_led;
+ int ret = 0;
+ struct ab5500_led_conf *pled;
+ struct ab5500_led *led;
+
+ hvleds->dev = dev;
+ hvleds->pdata = pdata;
+
+ if (abx500_get_chip_id(dev) == AB5500_2_0)
+ hvleds->hw_fade = true;
+ else
+ hvleds->hw_fade = false;
+
+ for (i_led = 0; i_led < AB5500_HVLEDS_MAX; i_led++) {
+ pled = &pdata->leds[i_led];
+ led = &hvleds->leds[i_led];
+
+ INIT_WORK(&led->led_work, ab5500_led_work);
+
+ led->id = pled->led_id;
+ led->max_current = pled->max_current;
+ led->led_on = pled->led_on;
+ led->led_cdev.name = pled->name;
+ led->led_cdev.brightness_set = ab5500_led_brightness_set;
+
+ /* Provide interface only for enabled LEDs */
+ if (led->led_on == false)
+ continue;
+
+ if (hvleds->hw_fade) {
+ led->fade_hi = (pled->fade_hi & LED_FULL);
+ led->fade_hi *= AB5500_LED_INTENSITY_STEP;
+ led->fade_lo = (pled->fade_lo & LED_FULL);
+ led->fade_lo *= AB5500_LED_INTENSITY_STEP;
+ }
+
+ ret = led_classdev_register(dev, &led->led_cdev);
+ if (ret < 0) {
+ dev_err(dev, "Register led class failed: %d\n", ret);
+ goto bailout1;
+ }
+
+ ret = device_create_file(led->led_cdev.dev,
+ &dev_attr_led_current);
+ if (ret < 0) {
+ dev_err(dev, "sysfs device creation failed: %d\n", ret);
+ goto bailout2;
+ }
+
+ if (hvleds->hw_fade) {
+ ret = device_create_file(led->led_cdev.dev,
+ &dev_attr_fade_auto);
+ if (ret < 0) {
+ dev_err(dev, "sysfs device "
+ "creation failed: %d\n", ret);
+ goto bailout3;
+ }
+
+ ret = device_create_file(led->led_cdev.dev,
+ &dev_attr_fade_delay);
+ if (ret < 0) {
+ dev_err(dev, "sysfs device "
+ "creation failed: %d\n", ret);
+ goto bailout4;
+ }
+ }
+ }
+
+ return ret;
+ for (; i_led >= 0; i_led--) {
+ if (hvleds->leds[i_led].led_on == false)
+ continue;
+
+ if (hvleds->hw_fade) {
+ device_remove_file(hvleds->leds[i_led].led_cdev.dev,
+ &dev_attr_fade_delay);
+bailout4:
+ device_remove_file(hvleds->leds[i_led].led_cdev.dev,
+ &dev_attr_fade_auto);
+ }
+bailout3:
+ device_remove_file(hvleds->leds[i_led].led_cdev.dev,
+ &dev_attr_led_current);
+bailout2:
+ led_classdev_unregister(&hvleds->leds[i_led].led_cdev);
+bailout1:
+ cancel_work_sync(&hvleds->leds[i_led].led_work);
+ }
+ return ret;
+}
+
+static int __devinit ab5500_hvleds_probe(struct platform_device *pdev)
+{
+ struct ab5500_hvleds_platform_data *pdata = pdev->dev.platform_data;
+ struct ab5500_hvleds *hvleds = NULL;
+ int ret = 0, i;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data required\n");
+ ret = -ENODEV;
+ goto err_out;
+ }
+
+ hvleds = kzalloc(sizeof(struct ab5500_hvleds), GFP_KERNEL);
+ if (hvleds == NULL) {
+ ret = -ENOMEM;
+ goto err_out;
+ }
+
+ mutex_init(&hvleds->lock);
+
+ /* init leds data and register led_classdev */
+ ret = ab5500_led_register_leds(&pdev->dev, pdata, hvleds);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "leds registration failed\n");
+ goto err_out;
+ }
+
+ /* init device registers and set initial led current */
+ ret = ab5500_led_init_registers(hvleds);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "reg init failed: %d\n", ret);
+ goto err_reg_init;
+ }
+
+ if (hvleds->hw_fade)
+ dev_info(&pdev->dev, "v2 enabled\n");
+ else
+ dev_info(&pdev->dev, "v1 enabled\n");
+
+ return ret;
+
+err_reg_init:
+ for (i = 0; i < AB5500_HVLEDS_MAX; i++) {
+ struct ab5500_led *led = &hvleds->leds[i];
+
+ if (led->led_on == false)
+ continue;
+
+ device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ if (hvleds->hw_fade) {
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_auto);
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_delay);
+ }
+ led_classdev_unregister(&led->led_cdev);
+ cancel_work_sync(&led->led_work);
+ }
+err_out:
+ kfree(hvleds);
+ return ret;
+}
+
+static int __devexit ab5500_hvleds_remove(struct platform_device *pdev)
+{
+ struct ab5500_hvleds *hvleds = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < AB5500_HVLEDS_MAX; i++) {
+ struct ab5500_led *led = &hvleds->leds[i];
+
+ if (led->led_on == false)
+ continue;
+
+ device_remove_file(led->led_cdev.dev, &dev_attr_led_current);
+ if (hvleds->hw_fade) {
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_auto);
+ device_remove_file(led->led_cdev.dev,
+ &dev_attr_fade_delay);
+ }
+ led_classdev_unregister(&led->led_cdev);
+ cancel_work_sync(&led->led_work);
+ }
+ kfree(hvleds);
+ return 0;
+}
+
+static struct platform_driver ab5500_hvleds_driver = {
+ .driver = {
+ .name = AB5500LED_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_hvleds_probe,
+ .remove = __devexit_p(ab5500_hvleds_remove),
+};
+
+static int __init ab5500_hvleds_module_init(void)
+{
+ return platform_driver_register(&ab5500_hvleds_driver);
+}
+
+static void __exit ab5500_hvleds_module_exit(void)
+{
+ platform_driver_unregister(&ab5500_hvleds_driver);
+}
+
+module_init(ab5500_hvleds_module_init);
+module_exit(ab5500_hvleds_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");
+MODULE_DESCRIPTION("Driver for AB5500 HVLED");
+
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index 968fd5fef4f..41aed6c89ab 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
+#include <linux/gpio.h>
#define LM3530_LED_DEV "lcd-backlight"
#define LM3530_NAME "lm3530-led"
@@ -101,6 +102,7 @@ static struct lm3530_mode_map mode_map[] = {
* @mode: mode of operation - manual, ALS, PWM
* @regulator: regulator
* @brighness: previous brightness value
+ * @hw_en_gpio: GPIO line for LM3530 HWEN
* @enable: regulator is enabled
*/
struct lm3530_data {
@@ -110,6 +112,7 @@ struct lm3530_data {
enum lm3530_mode mode;
struct regulator *regulator;
enum led_brightness brightness;
+ int hw_en_gpio;
bool enable;
};
@@ -151,7 +154,7 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
u8 als_imp_sel = 0;
u8 brightness;
u8 reg_val[LM3530_REG_MAX];
- u8 zones[LM3530_ALS_ZB_MAX];
+ u8 zones[LM3530_ALS_ZB_MAX] = {0};
u32 als_vmin, als_vmax, als_vstep;
struct lm3530_platform_data *pdata = drvdata->pdata;
struct i2c_client *client = drvdata->client;
@@ -230,6 +233,8 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
if (!drvdata->enable) {
+ if (drvdata->hw_en_gpio != LM3530_NO_HWEN_GPIO)
+ gpio_set_value(drvdata->hw_en_gpio, 1);
ret = regulator_enable(drvdata->regulator);
if (ret) {
dev_err(&drvdata->client->dev,
@@ -294,6 +299,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
if (err)
dev_err(&drvdata->client->dev,
"Disable regulator failed\n");
+ if (drvdata->hw_en_gpio != LM3530_NO_HWEN_GPIO)
+ gpio_set_value(drvdata->hw_en_gpio, 0);
drvdata->enable = false;
}
break;
@@ -397,6 +404,7 @@ static int __devinit lm3530_probe(struct i2c_client *client,
drvdata->client = client;
drvdata->pdata = pdata;
drvdata->brightness = LED_OFF;
+ drvdata->hw_en_gpio = pdata->hw_en_gpio;
drvdata->enable = false;
drvdata->led_dev.name = LM3530_LED_DEV;
drvdata->led_dev.brightness_set = lm3530_brightness_set;
@@ -404,6 +412,15 @@ static int __devinit lm3530_probe(struct i2c_client *client,
i2c_set_clientdata(client, drvdata);
+ if (gpio_is_valid(drvdata->hw_en_gpio)) {
+ err = gpio_request_one(drvdata->hw_en_gpio, GPIOF_OUT_INIT_HIGH,
+ "lm3530_hw_en");
+ if (err < 0) {
+ dev_err(&client->dev, "lm3530 hw_en gpio failed: %d\n", err);
+ goto err_gpio_request;
+ }
+ }
+
drvdata->regulator = regulator_get(&client->dev, "vin");
if (IS_ERR(drvdata->regulator)) {
dev_err(&client->dev, "regulator get failed\n");
@@ -443,6 +460,10 @@ err_class_register:
err_reg_init:
regulator_put(drvdata->regulator);
err_regulator_get:
+ if (gpio_is_valid(drvdata->hw_en_gpio))
+ gpio_free(drvdata->hw_en_gpio);
+err_gpio_request:
+ i2c_set_clientdata(client, NULL);
kfree(drvdata);
err_out:
return err;
@@ -457,6 +478,8 @@ static int __devexit lm3530_remove(struct i2c_client *client)
if (drvdata->enable)
regulator_disable(drvdata->regulator);
regulator_put(drvdata->regulator);
+ if (gpio_is_valid(drvdata->hw_en_gpio))
+ gpio_free(drvdata->hw_en_gpio);
led_classdev_unregister(&drvdata->led_dev);
kfree(drvdata);
return 0;
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index 410a723b869..2f667071278 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -355,7 +355,12 @@ static int lp5521_do_store_load(struct lp5521_engine *engine,
while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
/* separate sscanfs because length is working only for %s */
ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
- if (ret != 2)
+ /*
+ * Execution of a %n directive does not always
+ * increment the assignment count returned at
+ * completion of execution.so ret need not be 2
+ */
+ if ((ret != 1) && (ret != 2))
goto fail;
ret = sscanf(c, "%2x", &cmd);
if (ret != 1)
@@ -787,6 +792,7 @@ static int __devinit lp5521_probe(struct i2c_client *client,
ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
if (buf != LP5521_REG_R_CURR_DEFAULT) {
dev_err(&client->dev, "error in resetting chip\n");
+ ret = -EIO;
goto fail2;
}
usleep_range(10000, 20000);
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index 3ed92f34bd4..9c5f7136b37 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -27,9 +27,60 @@ struct led_pwm_data {
struct led_classdev cdev;
struct pwm_device *pwm;
unsigned int active_low;
+ unsigned int lth_brightness;
unsigned int period;
+ unsigned int dutycycle_steps;
+ unsigned int period_steps;
};
+static int led_pwm_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on, unsigned long *delay_off)
+{
+ struct led_pwm_data *led_dat =
+ container_of(led_cdev, struct led_pwm_data, cdev);
+ int dutycycle_ms, period_sec;
+ int dutycycle, period;
+ /*
+ * If both the delays are zero set some sensible delay
+ */
+ if (*delay_on == 0 && *delay_off == 0) {
+ *delay_on = 500;
+ *delay_off = 500;
+ }
+ /*
+ * calculate the duty cycle from on and off time
+ */
+ dutycycle_ms = ((*delay_on * 1000)/(*delay_on + *delay_off));
+ /*
+ * convert calculated value to write into the PWM out register
+ */
+ if (led_dat->dutycycle_steps)
+ dutycycle = ((dutycycle_ms * led_dat->dutycycle_steps)/1000);
+ else
+ dutycycle = (dutycycle_ms/1000);
+ /*
+ * calculate period from on and off time(msec)
+ */
+ period_sec = ((*delay_on + *delay_off)/1000);
+ /*
+ * convert calculated value to write into the PWM out register
+ */
+ if (led_dat->period_steps) {
+ if ((*delay_on + *delay_off) == 500)
+ period = led_dat->period_steps;
+ else
+ period = led_dat->period_steps - period_sec;
+ }
+ else
+ period = period_sec;
+ /*
+ * configure the PWM registers and enable blink functionality
+ */
+ pwm_config_blink(led_dat->pwm, dutycycle, period);
+ pwm_blink_ctrl(led_dat->pwm, 1);
+ return 0;
+}
+
static void led_pwm_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@@ -42,7 +93,10 @@ static void led_pwm_set(struct led_classdev *led_cdev,
pwm_config(led_dat->pwm, 0, period);
pwm_disable(led_dat->pwm);
} else {
- pwm_config(led_dat->pwm, brightness * period / max, period);
+ brightness = led_dat->lth_brightness + (brightness *
+ (led_dat->period - led_dat->lth_brightness) / max);
+ pwm_config(led_dat->pwm, brightness, led_dat->period);
+
pwm_enable(led_dat->pwm);
}
}
@@ -79,8 +133,13 @@ static int led_pwm_probe(struct platform_device *pdev)
led_dat->cdev.default_trigger = cur_led->default_trigger;
led_dat->active_low = cur_led->active_low;
led_dat->period = cur_led->pwm_period_ns;
+ led_dat->lth_brightness = cur_led->lth_brightness *
+ (cur_led->pwm_period_ns / cur_led->max_brightness);
+ led_dat->dutycycle_steps = cur_led->dutycycle_steps;
+ led_dat->period_steps = cur_led->period_steps;
led_dat->cdev.brightness_set = led_pwm_set;
led_dat->cdev.brightness = LED_OFF;
+ led_dat->cdev.blink_set = led_pwm_blink_set;
led_dat->cdev.max_brightness = cur_led->max_brightness;
led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e44386fa9..825673af5f3 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -331,6 +331,17 @@ config MFD_TC3589X
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_TC35892
+ bool "Support Toshiba TC35892"
+ depends on I2C=y && GENERIC_HARDIRQS
+ select MFD_CORE
+ help
+ Support for the Toshiba TC35892 I/O Expander.
+
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config MFD_TMIO
bool
default n
@@ -359,6 +370,27 @@ config MFD_TC6393XB
help
Support for Toshiba Mobile IO Controller TC6393XB
+config AB5500_CORE
+ bool "ST-Ericsson AB5500 Mixed Signal Circuit core functions"
+ select MFD_CORE
+ depends on GENERIC_HARDIRQS && ABX500_CORE
+ help
+ Select this to enable the AB5500 Mixed Signal IC core
+ functionality. This connects to a AB5500 chip on the I2C bus via
+ the Power and Reset Management Unit (PRCMU). It exposes a number
+ of symbols needed for dependent devices to read and write
+ registers and subscribe to events from this multi-functional IC.
+ This is needed to use other features of the AB5500 such as
+ battery-backed RTC, charging control, Regulators, LEDs, vibrator,
+ system power and temperature, power management and ALSA sound.
+
+config AB5500_GPADC
+ bool "AB5500 GPADC driver"
+ depends on AB5500_CORE
+ default y
+ help
+ AB5500 GPADC driver used to convert battery/usb voltage.
+
config PMIC_DA903X
bool "Dialog Semiconductor DA9030/DA9034 PMIC Support"
depends on I2C=y
@@ -678,7 +710,7 @@ config AB8500_CORE
config AB8500_I2C_CORE
bool "AB8500 register access via PRCMU I2C"
- depends on AB8500_CORE && MFD_DB8500_PRCMU
+ depends on AB8500_CORE
default y
help
This enables register access to the AB8500 chip via PRCMU I2C.
@@ -686,6 +718,14 @@ config AB8500_I2C_CORE
the I2C bus is connected to the Power Reset
and Mangagement Unit, PRCMU.
+config AB8500_DENC
+ bool "AB8500_DENC driver support(CVBS)"
+ depends on AB8500_CORE
+ help
+ Select this option to add driver support for analog TV out through
+ AB8500.
+
+
config AB8500_DEBUG
bool "Enable debug info via debugfs"
depends on AB8500_CORE && DEBUG_FS
@@ -696,10 +736,10 @@ config AB8500_DEBUG
config AB8500_GPADC
bool "AB8500 GPADC driver"
- depends on AB8500_CORE && REGULATOR_AB8500
+ depends on AB8500_CORE
default y
help
- AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage
+ AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage.
config MFD_DB8500_PRCMU
bool "ST-Ericsson DB8500 Power Reset Control Management Unit"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 05fa538c5ef..cfed0402931 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -2,6 +2,7 @@
# Makefile for multifunction miscellaneous devices
#
+obj-$(CONFIG_AB5500_CORE) += ab5500-core.o ab5500-power.o
88pm860x-objs := 88pm860x-core.o 88pm860x-i2c.o
obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o
obj-$(CONFIG_MFD_SM501) += sm501.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_MFD_STMPE) += stmpe.o
obj-$(CONFIG_STMPE_I2C) += stmpe-i2c.o
obj-$(CONFIG_STMPE_SPI) += stmpe-spi.o
obj-$(CONFIG_MFD_TC3589X) += tc3589x.o
+obj-$(CONFIG_MFD_TC35892) += tc35892.o
obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
@@ -91,11 +93,13 @@ obj-$(CONFIG_AB5500_CORE) += ab5500-core.o
obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o
obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o
obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o
+obj-$(CONFIG_AB8500_DENC) += ab8500-denc.o
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_MFD_DB8500_PRCMU) += db8500-prcmu.o
# ab8500-i2c need to come after db8500-prcmu (which provides the channel)
obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o
obj-$(CONFIG_MFD_DB5500_PRCMU) += db5500-prcmu.o
+obj-$(CONFIG_AB5500_GPADC) += ab5500-gpadc.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c
index 54d0fe40845..e8ae5d945e4 100644
--- a/drivers/mfd/ab5500-core.c
+++ b/drivers/mfd/ab5500-core.c
@@ -991,6 +991,74 @@ static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = {
},
},
},
+ [AB5500_DEVID_TEMPMON] = {
+ .name = "abx500-temp",
+ .id = AB5500_DEVID_TEMPMON,
+ .num_resources = 1,
+ .resources = (struct resource[]) {
+ {
+ .name = "ABX500_TEMP_WARM",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 2),
+ .end = AB5500_IRQ(2, 2),
+ },
+ },
+ },
+ [AB5500_DEVID_ACCDET] = {
+ .name = "ab5500-acc-det",
+ .id = AB5500_DEVID_ACCDET,
+ .num_resources = 8,
+ .resources = (struct resource[]) {
+ {
+ .name = "acc_detedt22db_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 7),
+ .end = AB5500_IRQ(2, 7),
+ },
+ {
+ .name = "acc_detedt21db_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 6),
+ .end = AB5500_IRQ(2, 6),
+ },
+ {
+ .name = "acc_detedt21db_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(2, 5),
+ .end = AB5500_IRQ(2, 5),
+ },
+ {
+ .name = "acc_detedt3db_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(3, 4),
+ .end = AB5500_IRQ(3, 4),
+ },
+ {
+ .name = "acc_detedt3db_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(3, 3),
+ .end = AB5500_IRQ(3, 3),
+ },
+ {
+ .name = "acc_detedt1db_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(3, 2),
+ .end = AB5500_IRQ(3, 2),
+ },
+ {
+ .name = "acc_detedt1db_rising",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(3, 1),
+ .end = AB5500_IRQ(3, 1),
+ },
+ {
+ .name = "acc_detedt22db_falling",
+ .flags = IORESOURCE_IRQ,
+ .start = AB5500_IRQ(3, 0),
+ .end = AB5500_IRQ(3, 0),
+ },
+ },
+ },
};
/*
@@ -1301,6 +1369,10 @@ static const struct ab_family_id ids[] __initdata = {
.id = AB5500_1_1,
.name = "1.1"
},
+ {
+ .id = AB5500_2_0,
+ .name = "2.0"
+ },
/* Terminator */
{
.id = 0x00,
diff --git a/drivers/mfd/ab5500-gpadc.c b/drivers/mfd/ab5500-gpadc.c
new file mode 100644
index 00000000000..fe05ffbadfd
--- /dev/null
+++ b/drivers/mfd/ab5500-gpadc.c
@@ -0,0 +1,1256 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Vijaya Kumar K <vijay.kilari@stericsson.com>
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+
+/*
+ * Manual mode ADC registers
+ */
+#define AB5500_GPADC_MANUAL_STAT_REG 0x1F
+#define AB5500_GPADC_MANDATAL_REG 0x21
+#define AB5500_GPADC_MANDATAH_REG 0x20
+#define AB5500_GPADC_MANUAL_MUX_CTRL 0x22
+#define AB5500_GPADC_MANUAL_MODE_CTRL 0x23
+#define AB5500_GPADC_MANUAL_MODE_CTRL2 0x24
+/*
+ * Auto/Polling mode ADC registers
+ */
+#define AB5500_GPADC_AUTO_VBAT_MAX 0x26
+#define AB5500_GPADC_AUTO_VBAT_MIN_TXON 0x27
+#define AB5500_GPADC_AUTO_VBAT_MIN_NOTX 0x28
+#define AB5500_GPADC_AUTO_VBAT_AVGH 0x29
+#define AB5500_GPADC_AUTO_VBAT_AVGL 0x2A
+#define AB5500_GPADC_AUTO_ICHAR_MAX 0x2B
+#define AB5500_GPADC_AUTO_ICHAR_MIN 0x2C
+#define AB5500_GPADC_AUTO_ICHAR_AVG 0x2D
+#define AB5500_GPADC_AUTO_CTRL2 0x2F
+#define AB5500_GPADC_AUTO_CTRL1 0x30
+#define AB5500_GPADC_AUTO_PWR_CTRL 0x31
+#define AB5500_GPADC_AUTO_TRIG_VBAT_MIN_TXON 0x32
+#define AB5500_GPADC_AUTO_TRIG_VBAT_MIN_NOTX 0x33
+#define AB5500_GPADC_AUTO_TRIG_ADOUT0_CTRL 0x34
+#define AB5500_GPADC_AUTO_TRIG_ADOUT1_CTRL 0x35
+#define AB5500_GPADC_AUTO_TRIG0_MUX_CTRL 0x37
+#define AB5500_GPADC_AUTO_XTALTEMP_CTRL 0x57
+#define AB5500_GPADC_KELVIN_CTRL 0xFE
+
+/* gpadc constants */
+#define AB5500_INT_ADC_TRIG0 0x0
+#define AB5500_INT_ADC_TRIG1 0x1
+#define AB5500_INT_ADC_TRIG2 0x2
+#define AB5500_INT_ADC_TRIG3 0x3
+#define AB5500_INT_ADC_TRIG4 0x4
+#define AB5500_INT_ADC_TRIG5 0x5
+#define AB5500_INT_ADC_TRIG6 0x6
+#define AB5500_INT_ADC_TRIG7 0x7
+
+#define AB5500_GPADC_AUTO_TRIG_INDEX AB5500_GPADC_AUTO_TRIG0_MUX_CTRL
+#define GPADC_MANUAL_READY 0x01
+#define GPADC_MANUAL_ADOUT0_MASK 0x30
+#define GPADC_MANUAL_ADOUT1_MASK 0xC0
+#define GPADC_MANUAL_ADOUT0_ON 0x10
+#define GPADC_MANUAL_ADOUT1_ON 0x40
+#define MUX_SCALE_GPADC0_MASK 0x08
+#define MUX_SCALE_VBAT_MASK 0x02
+#define MUX_SCALE_45 0x02
+#define MUX_SCALE_BDATA_MASK 0x01
+#define MUX_SCALE_BDATA27 0x00
+#define MUX_SCALE_BDATA18 0x01
+#define MUX_SCALE_ACCDET2_MASK 0x01
+#define MUX_SCALE_ACCDET3_MASK 0x02
+#define GPADC0_SCALE_VOL27 0x00
+#define GPADC0_SCALE_VOL18 0x01
+#define ACCDET2_SCALE_VOL27 0x00
+#define ACCDET3_SCALE_VOL27 0x00
+#define TRIGX_FREQ_MASK 0x07
+#define AUTO_VBAT_MASK 0x10
+#define AUTO_VBAT_ON 0x10
+#define TRIG_VBAT_TXON_ARM_MASK 0x08
+#define TRIG_VBAT_NOTX_ARM_MASK 0x04
+#define TRIGX_ARM_MASK 0x20
+#define TRIGX_ARM 0x20
+#define TRIGX_MUX_SELECT 0x1F
+#define ADC_CAL_OFF_MASK 0x04
+#define ADC_ON_MODE_MASK 0x03
+#define ADC_CAL_ON 0x00
+#define ADC_FULLPWR 0x03
+#define ADC_XTAL_FORCE_MASK 0x80
+#define ADC_XTAL_FORCE_EN 0x80
+#define ADC_XTAL_FORCE_DI 0x00
+#define ADOUT0 0x01
+#define ADOUT1 0x02
+#define MIN_INDEX 0x02
+#define MAX_INDEX 0x03
+#define CTRL_INDEX 0x01
+#define KELVIN_SCALE_VOL45 0x00
+
+/* GPADC constants from AB5500 spec */
+#define GPADC0_MIN 0
+#define GPADC0_MAX 1800
+#define BTEMP_MIN 0
+#define BTEMP_MAX 1800
+#define BDATA_MIN 0
+#define BDATA_MAX 2750
+#define PCBTEMP_MIN 0
+#define PCBTEMP_MAX 1800
+#define XTALTEMP_MIN 0
+#define XTALTEMP_MAX 1800
+#define DIETEMP_MIN 0
+#define DIETEMP_MAX 1800
+#define VBUS_I_MIN 0
+#define VBUS_I_MAX 1600
+#define VBUS_V_MIN 0
+#define VBUS_V_MAX 20000
+#define ACCDET2_MIN 0
+#define ACCDET2_MAX 2500
+#define ACCDET3_MIN 0
+#define ACCDET3_MAX 2500
+#define VBAT_MIN 2300
+#define VBAT_MAX 4500
+#define BKBAT_MIN 0
+#define BKBAT_MAX 2750
+#define USBID_MIN 0
+#define USBID_MAX 1800
+#define KELVIN_MIN 0
+#define KELVIN_MAX 4500
+#define VIBRA_MIN 0
+#define VIBRA_MAX 4500
+
+/* This is used for calibration */
+#define ADC_RESOLUTION 1023
+#define AUTO_ADC_RESOLUTION 255
+
+/* ADOUT prestart time */
+#define ADOUT0_TRIGX_PRESTART 0x18
+
+enum adc_auto_channels {
+ ADC_INPUT_TRIG0 = 0,
+ ADC_INPUT_TRIG1,
+ ADC_INPUT_TRIG2,
+ ADC_INPUT_TRIG3,
+ ADC_INPUT_TRIG4,
+ ADC_INPUT_TRIG5,
+ ADC_INPUT_TRIG6,
+ ADC_INPUT_TRIG7,
+ ADC_INPUT_VBAT_TXOFF,
+ ADC_INPUT_VBAT_TXON,
+ N_AUTO_TRIGGER
+};
+
+/**
+ * struct adc_auto_trigger - AB5500 GPADC auto trigger
+ * @adc_mux Mux input
+ * @flag Status of trigger
+ * @freq Frequency of conversion
+ * @adout Adout to pull
+ * @trig_min trigger minimum value
+ * @trig_max trigger maximum value
+ * @auto_adc_callback notification callback
+ */
+struct adc_auto_trigger {
+ u8 auto_mux;
+ u8 flag;
+ u8 freq;
+ u8 adout;
+ u8 trig_min;
+ u8 trig_max;
+ int (*auto_callb)(int mux);
+};
+
+/**
+ * struct ab5500_btemp_interrupts - ab5500 interrupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab5500_adc_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+/**
+ * struct ab5500_gpadc - AB5500 GPADC device information
+ * @chip_id ABB chip id
+ * @dev: pointer to the struct device
+ * @node: a list of AB5500 GPADCs, hence prepared for
+ reentrance
+ * @ab5500_gpadc_complete: pointer to the struct completion, to indicate
+ * the completion of gpadc conversion
+ * @ab5500_gpadc_lock: structure of type mutex
+ * @regu: pointer to the struct regulator
+ * @irq: interrupt number that is used by gpadc
+ * @cal_data array of ADC calibration data structs
+ * @auto_trig auto trigger channel
+ * @gpadc_trigX_work work items for trigger channels
+ */
+struct ab5500_gpadc {
+ u8 chip_id;
+ struct device *dev;
+ struct list_head node;
+ struct mutex ab5500_gpadc_lock;
+ struct regulator *regu;
+ int irq;
+ int prev_bdata;
+ spinlock_t gpadc_auto_lock;
+ struct adc_auto_trigger adc_trig[N_AUTO_TRIGGER];
+ struct workqueue_struct *gpadc_wq;
+ struct work_struct gpadc_trig0_work;
+ struct work_struct gpadc_trig1_work;
+ struct work_struct gpadc_trig2_work;
+ struct work_struct gpadc_trig3_work;
+ struct work_struct gpadc_trig4_work;
+ struct work_struct gpadc_trig5_work;
+ struct work_struct gpadc_trig6_work;
+ struct work_struct gpadc_trig7_work;
+ struct work_struct gpadc_trig_vbat_txon_work;
+ struct work_struct gpadc_trig_vbat_txoff_work;
+};
+
+static LIST_HEAD(ab5500_gpadc_list);
+
+struct adc_data {
+ u8 mux;
+ int min;
+ int max;
+ int adout;
+};
+
+#define ADC_DATA(_id, _mux, _min, _max, _adout) \
+ [_id] = { \
+ .mux = _mux, \
+ .min = _min, \
+ .max = _max, \
+ .adout = _adout \
+ }
+
+struct adc_data adc_tab[] = {
+ ADC_DATA(GPADC0_V, 0x00, GPADC0_MIN, GPADC0_MAX, 0),
+ ADC_DATA(BTEMP_BALL, 0x0D, BTEMP_MIN, BTEMP_MAX, ADOUT0),
+ ADC_DATA(BAT_CTRL, 0x0D, BDATA_MIN, BDATA_MAX, 0),
+ ADC_DATA(MAIN_BAT_V, 0x0C, VBAT_MIN, VBAT_MAX, 0),
+ ADC_DATA(MAIN_BAT_V_TXON, 0x0C, VBAT_MIN, VBAT_MAX, 0),
+ ADC_DATA(VBUS_V, 0x10, VBUS_V_MIN, VBUS_V_MAX, 0),
+ ADC_DATA(USB_CHARGER_C, 0x0A, VBUS_I_MIN, VBUS_I_MAX, 0),
+ ADC_DATA(BK_BAT_V, 0x07, BKBAT_MIN, BKBAT_MAX, 0),
+ ADC_DATA(DIE_TEMP, 0x0F, DIETEMP_MIN, DIETEMP_MAX, ADOUT0),
+ ADC_DATA(PCB_TEMP, 0x13, PCBTEMP_MIN, PCBTEMP_MAX, ADOUT0),
+ ADC_DATA(XTAL_TEMP, 0x06, XTALTEMP_MIN, XTALTEMP_MAX, ADOUT0),
+ ADC_DATA(USB_ID, 0x1A, USBID_MIN, USBID_MAX, 0),
+ ADC_DATA(ACC_DETECT2, 0x18, ACCDET2_MIN, ACCDET2_MAX, 0),
+ ADC_DATA(ACC_DETECT3, 0x19, ACCDET3_MIN, ACCDET3_MAX, 0),
+ ADC_DATA(MAIN_BAT_V_TRIG_MIN, 0x0C, VBAT_MIN, VBAT_MAX, 0),
+ ADC_DATA(MAIN_BAT_V_TXON_TRIG_MIN, 0x0C, VBAT_MIN, VBAT_MAX, 0),
+ ADC_DATA(VIBRA_KELVIN, 0x16, VIBRA_MIN, VIBRA_MAX, 0),
+};
+
+/**
+ * ab5500_gpadc_get() - returns a reference to the primary AB5500 GPADC
+ * (i.e. the first GPADC in the instance list)
+ */
+struct ab5500_gpadc *ab5500_gpadc_get(const char *name)
+{
+ struct ab5500_gpadc *gpadc;
+ list_for_each_entry(gpadc, &ab5500_gpadc_list, node) {
+ if (!strcmp(name, dev_name(gpadc->dev)))
+ return gpadc;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL(ab5500_gpadc_get);
+
+#define CONV(min, max, x)\
+ ((min) + ((((max)-(min))*(x))/ADC_RESOLUTION))
+
+static int ab5500_gpadc_ad_to_voltage(struct ab5500_gpadc *gpadc,
+ u8 in, u16 ad_val)
+{
+ int res;
+
+ switch (in) {
+ case VIBRA_KELVIN:
+ case GPADC0_V:
+ case PCB_TEMP:
+ case BTEMP_BALL:
+ case MAIN_BAT_V:
+ case MAIN_BAT_V_TXON:
+ case ACC_DETECT2:
+ case ACC_DETECT3:
+ case VBUS_V:
+ case USB_CHARGER_C:
+ case BK_BAT_V:
+ case XTAL_TEMP:
+ case USB_ID:
+ case BAT_CTRL:
+ res = CONV(adc_tab[in].min, adc_tab[in].max, ad_val);
+ break;
+ case DIE_TEMP:
+ /*
+ * From the AB5500 product specification
+ * T(deg cel) = 27 - ((ADCode - 709)/2.4213)
+ * 27 + 709/2.4213 - ADCode/2.4213
+ * 320 - (ADCode/2.4213)
+ */
+ res = 320 - (((unsigned long)ad_val * 10000) / 24213);
+ break;
+ default:
+ dev_err(gpadc->dev,
+ "unknown channel, not possible to convert\n");
+ res = -EINVAL;
+ break;
+ }
+ return res;
+}
+
+/**
+ * ab5500_gpadc_convert() - gpadc conversion
+ * @input: analog input to be converted to digital data
+ *
+ * This function converts the selected analog i/p to digital
+ * data.
+ */
+int ab5500_gpadc_convert(struct ab5500_gpadc *gpadc, u8 input)
+{
+ int result, ret = -EINVAL;
+ u16 data = 0;
+ u8 looplimit = 0;
+ u8 status = 0;
+ u8 low_data, high_data, adout_mask, adout_val;
+
+ if (!gpadc)
+ return -ENODEV;
+
+ mutex_lock(&gpadc->ab5500_gpadc_lock);
+
+ switch (input) {
+ case MAIN_BAT_V:
+ case MAIN_BAT_V_TXON:
+ /*
+ * The value of mux scale volatage depends
+ * on the type of battery
+ * for LI-ion use MUX_SCALE_35 => 2.3-3.5V
+ * for LiFePo4 use MUX_SCALE_45 => 2.3-4.5V
+ * Check type of battery from platform data TODO ???
+ */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ MUX_SCALE_VBAT_MASK, MUX_SCALE_45);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: failed to read status\n");
+ goto out;
+ }
+ break;
+ case BTEMP_BALL:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ MUX_SCALE_BDATA_MASK, MUX_SCALE_BDATA18);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set mux scale\n");
+ goto out;
+ }
+ break;
+ case BAT_CTRL:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ MUX_SCALE_BDATA_MASK, MUX_SCALE_BDATA27);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set mux scale\n");
+ goto out;
+ }
+ break;
+ case XTAL_TEMP:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_AUTO_XTALTEMP_CTRL,
+ ADC_XTAL_FORCE_MASK, ADC_XTAL_FORCE_EN);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set xtaltemp\n");
+ goto out;
+ }
+ break;
+ case GPADC0_V:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ MUX_SCALE_GPADC0_MASK, GPADC0_SCALE_VOL18);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set gpadc0\n");
+ goto out;
+ }
+ break;
+ case ACC_DETECT2:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL2,
+ MUX_SCALE_ACCDET2_MASK, ACCDET2_SCALE_VOL27);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set accdet2\n");
+ goto out;
+ }
+ break;
+ case ACC_DETECT3:
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL2,
+ MUX_SCALE_ACCDET3_MASK, ACCDET3_SCALE_VOL27);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set accdet3\n");
+ goto out;
+ }
+ break;
+ case VIBRA_KELVIN:
+ ret = abx500_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_KELVIN_CTRL,
+ KELVIN_SCALE_VOL45);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set kelv scale\n");
+ goto out;
+ }
+ break;
+ case USB_CHARGER_C:
+ case VBUS_V:
+ case BK_BAT_V:
+ case USB_ID:
+ case PCB_TEMP:
+ case DIE_TEMP:
+ break;
+ default:
+ dev_err(gpadc->dev, "gpadc: Wrong adc\n");
+ goto out;
+ break;
+ }
+ if (adc_tab[input].adout) {
+ adout_mask = adc_tab[input].adout == ADOUT0 ?
+ GPADC_MANUAL_ADOUT0_MASK : GPADC_MANUAL_ADOUT1_MASK;
+ adout_val = adc_tab[input].adout == ADOUT0 ?
+ GPADC_MANUAL_ADOUT0_ON : GPADC_MANUAL_ADOUT1_ON;
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ adout_mask, adout_val);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to set ADOUT\n");
+ goto out;
+ }
+ /* delay required to ramp up voltage on BDATA node */
+ usleep_range(10000, 12000);
+ }
+ ret = abx500_set_register_interruptible(gpadc->dev, AB5500_BANK_ADC,
+ AB5500_GPADC_MANUAL_MUX_CTRL, adc_tab[input].mux);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc: fail to trigger manual conv\n");
+ goto out;
+ }
+ /* wait for completion of conversion */
+ looplimit = 0;
+ do {
+ msleep(1);
+ ret = abx500_get_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_STAT_REG,
+ &status);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: failed to read status\n");
+ goto out;
+ }
+ if (status & GPADC_MANUAL_READY)
+ break;
+ } while (++looplimit < 2);
+ if (looplimit >= 2) {
+ dev_err(gpadc->dev, "timeout:failed to complete conversion\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Disable ADOUT for measurement
+ */
+ if (adc_tab[input].adout) {
+ adout_mask = adc_tab[input].adout == ADOUT0 ?
+ GPADC_MANUAL_ADOUT0_MASK : GPADC_MANUAL_ADOUT1_MASK;
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ adout_mask, 0x0);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to disable ADOUT\n");
+ goto out;
+ }
+ }
+ /*
+ * Disable XTAL TEMP
+ */
+ if (input == XTAL_TEMP) {
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_AUTO_XTALTEMP_CTRL,
+ ADC_XTAL_FORCE_MASK, ADC_XTAL_FORCE_DI);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc: fail to disable xtaltemp\n");
+ goto out;
+ }
+ }
+ /* Read the converted RAW data */
+ ret = abx500_get_register_interruptible(gpadc->dev, AB5500_BANK_ADC,
+ AB5500_GPADC_MANDATAL_REG, &low_data);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: read low data failed\n");
+ goto out;
+ }
+
+ ret = abx500_get_register_interruptible(gpadc->dev, AB5500_BANK_ADC,
+ AB5500_GPADC_MANDATAH_REG, &high_data);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: read high data failed\n");
+ goto out;
+ }
+
+ data = (high_data << 2) | (low_data >> 6);
+ if (input == BAT_CTRL || input == BTEMP_BALL) {
+ /*
+ * TODO: Re-check with h/w team
+ * discard null or value < 5, as there is some error
+ * in conversion
+ */
+ if (data < 5)
+ data = gpadc->prev_bdata;
+ else
+ gpadc->prev_bdata = data;
+ }
+ result = ab5500_gpadc_ad_to_voltage(gpadc, input, data);
+
+ mutex_unlock(&gpadc->ab5500_gpadc_lock);
+ return result;
+
+out:
+ mutex_unlock(&gpadc->ab5500_gpadc_lock);
+ dev_err(gpadc->dev,
+ "gpadc: Failed to AD convert channel %d\n", input);
+ return ret;
+}
+EXPORT_SYMBOL(ab5500_gpadc_convert);
+
+/**
+ * ab5500_gpadc_program_auto() - gpadc conversion auto conversion
+ * @trig_index: Generic trigger channel for conversion
+ *
+ * This function program the auto trigger channel
+ */
+static int ab5500_gpadc_program_auto(struct ab5500_gpadc *gpadc, int trig)
+{
+ int ret;
+ u8 adout;
+#define MIN_INDEX 0x02
+#define MAX_INDEX 0x03
+#define CTRL_INDEX 0x01
+
+ ret = abx500_set_register_interruptible(gpadc->dev, AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_TRIG_INDEX + (trig << 2) + MIN_INDEX,
+ gpadc->adc_trig[trig].trig_min);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to program min\n");
+ return ret;
+ }
+ ret = abx500_set_register_interruptible(gpadc->dev, AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_TRIG_INDEX + (trig << 2) + MAX_INDEX,
+ gpadc->adc_trig[trig].trig_max);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to program max\n");
+ return ret;
+ }
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_AUTO_TRIG_INDEX + (trig << 2),
+ TRIGX_MUX_SELECT, gpadc->adc_trig[trig].auto_mux);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to select mux\n");
+ return ret;
+ }
+ if (gpadc->adc_trig[trig].adout) {
+ adout = gpadc->adc_trig[trig].adout == ADOUT0 ?
+ gpadc->adc_trig[trig].adout << 6 :
+ gpadc->adc_trig[trig].adout << 5;
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_TRIG_INDEX + (trig << 2) + CTRL_INDEX,
+ adout, adout);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to program adout\n");
+ return ret;
+ }
+ }
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_TRIG_INDEX + (trig << 2) + CTRL_INDEX,
+ TRIGX_FREQ_MASK, gpadc->adc_trig[trig].freq);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to program freq\n");
+ return ret;
+ }
+ return ret;
+
+}
+
+#define TRIG_V(trigval, min, max) \
+ ((((trigval) - (min)) * AUTO_ADC_RESOLUTION) / ((max) - (min)))
+
+static int ab5500_gpadc_vbat_auto_conf(struct ab5500_gpadc *gpadc,
+ struct adc_auto_input *in)
+{
+ int trig_min, ret;
+ u8 trig_reg, trig_arm;
+
+ /* Scale mux voltage */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC,
+ AB5500_GPADC_MANUAL_MODE_CTRL,
+ MUX_SCALE_VBAT_MASK, MUX_SCALE_45);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: failed to set vbat scale\n");
+ return ret;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_CTRL1,
+ AUTO_VBAT_MASK, AUTO_VBAT_ON);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: failed to set vbat on\n");
+ return ret;
+ }
+
+ trig_min = TRIG_V(in->min, adc_tab[in->mux].min, adc_tab[in->mux].max);
+
+ if (in->mux == MAIN_BAT_V_TRIG_MIN) {
+ trig_reg = AB5500_GPADC_AUTO_TRIG_VBAT_MIN_NOTX;
+ trig_arm = TRIG_VBAT_NOTX_ARM_MASK;
+ } else {
+ trig_reg = AB5500_GPADC_AUTO_TRIG_VBAT_MIN_TXON;
+ trig_arm = TRIG_VBAT_TXON_ARM_MASK;
+ }
+ ret = abx500_set_register_interruptible(gpadc->dev, AB5500_BANK_ADC,
+ trig_reg, trig_min);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to program vbat min\n");
+ return ret;
+ }
+ /*
+ * arm the trigger
+ */
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_AUTO_CTRL1, trig_arm, trig_arm);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: failed to trig vbat\n");
+ return ret;
+ }
+ return ret;
+}
+/**
+ * ab5500_gpadc_convert_auto() - gpadc conversion
+ * @auto_input: input trigger for conversion
+ *
+ * This function converts the selected channel from
+ * analog to digital data in auto mode
+ */
+
+int ab5500_gpadc_convert_auto(struct ab5500_gpadc *gpadc,
+ struct adc_auto_input *in)
+{
+ int ret, trig;
+ unsigned long flags;
+
+ if (!gpadc)
+ return -ENODEV;
+ mutex_lock(&gpadc->ab5500_gpadc_lock);
+
+ if (in->mux == MAIN_BAT_V_TXON_TRIG_MIN) {
+ spin_lock_irqsave(&gpadc->gpadc_auto_lock, flags);
+ if (gpadc->adc_trig[ADC_INPUT_VBAT_TXON].flag == true) {
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+ ret = -EBUSY;
+ dev_err(gpadc->dev, "gpadc: Auto vbat txon busy");
+ goto out;
+ }
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+
+ ret = ab5500_gpadc_vbat_auto_conf(gpadc, in);
+ if (ret < 0)
+ goto out;
+
+ gpadc->adc_trig[ADC_INPUT_VBAT_TXON].auto_mux = in->mux;
+ gpadc->adc_trig[ADC_INPUT_VBAT_TXON].auto_callb =
+ in->auto_adc_callback;
+ spin_lock_irqsave(&gpadc->gpadc_auto_lock, flags);
+ gpadc->adc_trig[ADC_INPUT_VBAT_TXON].flag = true;
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+ } else if (in->mux == MAIN_BAT_V_TRIG_MIN) {
+
+ spin_lock_irqsave(&gpadc->gpadc_auto_lock, flags);
+ if (gpadc->adc_trig[ADC_INPUT_VBAT_TXOFF].flag == true) {
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+ ret = -EBUSY;
+ dev_err(gpadc->dev, "gpadc: Auto vbat busy");
+ goto out;
+ }
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+
+ ret = ab5500_gpadc_vbat_auto_conf(gpadc, in);
+ if (ret < 0)
+ goto out;
+
+ gpadc->adc_trig[ADC_INPUT_VBAT_TXOFF].auto_mux = in->mux;
+ gpadc->adc_trig[ADC_INPUT_VBAT_TXOFF].auto_callb =
+ in->auto_adc_callback;
+ spin_lock_irqsave(&gpadc->gpadc_auto_lock, flags);
+ gpadc->adc_trig[ADC_INPUT_VBAT_TXOFF].flag = true;
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+ } else {
+ /*
+ * check if free trigger is available
+ */
+ trig = ADC_INPUT_TRIG0;
+ spin_lock_irqsave(&gpadc->gpadc_auto_lock, flags);
+ while (gpadc->adc_trig[trig].flag == true &&
+ trig <= ADC_INPUT_TRIG7)
+ trig++;
+
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+ if (trig > ADC_INPUT_TRIG7) {
+ ret = -EBUSY;
+ dev_err(gpadc->dev, "gpadc: no free channel\n");
+ goto out;
+ }
+ switch (in->mux) {
+ case MAIN_BAT_V:
+ /*
+ * The value of mux scale volatage depends
+ * on the type of battery
+ * for LI-ion use MUX_SCALE_35 => 2.3-3.5V
+ * for LiFePo4 use MUX_SCALE_45 => 2.3-4.5V
+ * Check type of battery from platform data TODO ???
+ */
+ ret = abx500_mask_and_set_register_interruptible(
+ gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_MANUAL_MODE_CTRL,
+ MUX_SCALE_VBAT_MASK, MUX_SCALE_45);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc: failed to read status\n");
+ goto out;
+ }
+
+ case BTEMP_BALL:
+ ret = abx500_set_register_interruptible(
+ gpadc->dev, AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_TRIG_ADOUT0_CTRL,
+ ADOUT0_TRIGX_PRESTART);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc: failed to set prestart\n");
+ goto out;
+ }
+
+ case ACC_DETECT2:
+ case ACC_DETECT3:
+ case VBUS_V:
+ case USB_CHARGER_C:
+ case BK_BAT_V:
+ case PCB_TEMP:
+ case USB_ID:
+ case BAT_CTRL:
+ gpadc->adc_trig[trig].trig_min =
+ (u8)TRIG_V(in->min, adc_tab[in->mux].min,
+ adc_tab[in->mux].max);
+ gpadc->adc_trig[trig].trig_max =
+ (u8)TRIG_V(in->max, adc_tab[in->mux].min,
+ adc_tab[in->mux].max);
+ gpadc->adc_trig[trig].adout =
+ adc_tab[in->mux].adout;
+ break;
+ case DIE_TEMP:
+ /*
+ * From the AB5500 product specification
+ * T(deg_cel) = 27 - (ADCode - 709)/2.4213)
+ * ADCode = 709 + (2.4213 * (27 - T))
+ * Auto trigger min/max level is of 8bit precision.
+ * Hence use AB5500_GPADC_MANDATAH_REG value
+ * obtained by 2 bit right shift of ADCode.
+ */
+ gpadc->adc_trig[trig].trig_min =
+ (709 + ((24213 * (27 - in->min))/10000))>>2;
+ gpadc->adc_trig[trig].trig_max =
+ (709 + ((24213 * (27 - in->max))/10000))>>2;
+ gpadc->adc_trig[trig].adout =
+ adc_tab[in->mux].adout;
+ break;
+ default:
+ dev_err(gpadc->dev, "Unknow GPADC request\n");
+ break;
+ }
+ gpadc->adc_trig[trig].freq = in->freq;
+ gpadc->adc_trig[trig].auto_mux =
+ adc_tab[in->mux].mux;
+ gpadc->adc_trig[trig].auto_callb = in->auto_adc_callback;
+
+ ret = ab5500_gpadc_program_auto(gpadc, trig);
+ if (ret < 0) {
+ dev_err(gpadc->dev,
+ "gpadc: fail to program auto ch\n");
+ goto out;
+ }
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC,
+ AB5500_GPADC_AUTO_TRIG_INDEX + (trig * 4),
+ TRIGX_ARM_MASK, TRIGX_ARM);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: fail to trigger\n");
+ goto out;
+ }
+ spin_lock_irqsave(&gpadc->gpadc_auto_lock, flags);
+ gpadc->adc_trig[trig].flag = true;
+ spin_unlock_irqrestore(&gpadc->gpadc_auto_lock, flags);
+ }
+out:
+ mutex_unlock(&gpadc->ab5500_gpadc_lock);
+ return ret;
+
+}
+EXPORT_SYMBOL(ab5500_gpadc_convert_auto);
+
+/* sysfs interface for GPADC0 */
+static ssize_t ab5500_gpadc0_get(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int voltage;
+ struct ab5500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ voltage = ab5500_gpadc_convert(gpadc, GPADC0_V);
+
+ return sprintf(buf, "%d\n", voltage);
+}
+static DEVICE_ATTR(adc0volt, 0644, ab5500_gpadc0_get, NULL);
+
+static void ab5500_gpadc_trigx_work(struct ab5500_gpadc *gp, int trig)
+{
+ unsigned long flags;
+ if (gp->adc_trig[trig].auto_callb != NULL) {
+ gp->adc_trig[trig].auto_callb(gp->adc_trig[trig].auto_mux);
+ spin_lock_irqsave(&gp->gpadc_auto_lock, flags);
+ gp->adc_trig[trig].flag = false;
+ spin_unlock_irqrestore(&gp->gpadc_auto_lock, flags);
+ } else {
+ dev_err(gp->dev, "Unknown trig for %d\n", trig);
+ }
+}
+/**
+ * ab5500_gpadc_trig0_work() - work item for trig0 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 0 auto conversion.
+ */
+static void ab5500_gpadc_trig0_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig0_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG0);
+}
+
+/**
+ * ab5500_gpadc_trig1_work() - work item for trig1 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig1 auto conversion.
+ */
+static void ab5500_gpadc_trig1_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig1_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG1);
+}
+
+/**
+ * ab5500_gpadc_trig2_work() - work item for trig2 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 2 auto conversion.
+ */
+static void ab5500_gpadc_trig2_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig2_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG2);
+}
+
+/**
+ * ab5500_gpadc_trig3_work() - work item for trig3 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 3 auto conversion.
+ */
+static void ab5500_gpadc_trig3_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig3_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG3);
+}
+
+/**
+ * ab5500_gpadc_trig4_work() - work item for trig4 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 4 auto conversion.
+ */
+static void ab5500_gpadc_trig4_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig4_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG4);
+}
+
+/**
+ * ab5500_gpadc_trig5_work() - work item for trig5 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 5 auto conversion.
+ */
+static void ab5500_gpadc_trig5_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig5_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG5);
+}
+
+/**
+ * ab5500_gpadc_trig6_work() - work item for trig6 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 6 auto conversion.
+ */
+static void ab5500_gpadc_trig6_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig6_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG6);
+}
+
+/**
+ * ab5500_gpadc_trig7_work() - work item for trig7 auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for trig 7 auto conversion.
+ */
+static void ab5500_gpadc_trig7_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig7_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_TRIG7);
+}
+
+/**
+ * ab5500_gpadc_vbat_txon_work() - work item for vbat_txon trigger auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for vbat_txon trigger auto adc.
+ */
+static void ab5500_gpadc_vbat_txon_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig_vbat_txon_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_VBAT_TXON);
+}
+
+/**
+ * ab5500_gpadc_vbat_txoff_work() - work item for vbat_txoff trigger auto adc
+ * @irq: irq number
+ * @work: work pointer
+ *
+ * This is a work handler for vbat_txoff trigger auto adc.
+ */
+static void ab5500_gpadc_vbat_txoff_work(struct work_struct *work)
+{
+ struct ab5500_gpadc *gpadc = container_of(work,
+ struct ab5500_gpadc, gpadc_trig_vbat_txoff_work);
+ ab5500_gpadc_trigx_work(gpadc, ADC_INPUT_VBAT_TXOFF);
+}
+
+/**
+ * ab5500_adc_trigx_handler() - isr for auto gpadc conversion trigger
+ * @irq: irq number
+ * @data: pointer to the data passed during request irq
+ *
+ * This is a interrupt service routine for auto gpadc conversion.
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_adc_trigx_handler(int irq, void *_gpadc)
+{
+ struct ab5500_platform_data *plat;
+ struct ab5500_gpadc *gpadc = _gpadc;
+ int dev_irq;
+
+ plat = dev_get_platdata(gpadc->dev->parent);
+ dev_irq = irq - plat->irq.base;
+
+ switch (dev_irq) {
+ case AB5500_INT_ADC_TRIG0:
+ dev_dbg(gpadc->dev, "Trigger 0 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig0_work);
+ break;
+ case AB5500_INT_ADC_TRIG1:
+ dev_dbg(gpadc->dev, "Trigger 1 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig1_work);
+ break;
+ case AB5500_INT_ADC_TRIG2:
+ dev_dbg(gpadc->dev, "Trigger 2 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig2_work);
+ break;
+ case AB5500_INT_ADC_TRIG3:
+ dev_dbg(gpadc->dev, "Trigger 3 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig3_work);
+ break;
+ case AB5500_INT_ADC_TRIG4:
+ dev_dbg(gpadc->dev, "Trigger 4 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig4_work);
+ break;
+ case AB5500_INT_ADC_TRIG5:
+ dev_dbg(gpadc->dev, "Trigger 5 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig5_work);
+ break;
+ case AB5500_INT_ADC_TRIG6:
+ dev_dbg(gpadc->dev, "Trigger 6 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig6_work);
+ break;
+ case AB5500_INT_ADC_TRIG7:
+ dev_dbg(gpadc->dev, "Trigger 7 received\n");
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig7_work);
+ break;
+ default:
+ dev_dbg(gpadc->dev, "unknown trigx handler input\n");
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_adc_vbat_txon_handler() - isr for auto vbat_txon conversion trigger
+ * @irq: irq number
+ * @data: pointer to the data passed during request irq
+ *
+ * This is a interrupt service routine for auto vbat_txon conversion
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_adc_vbat_txon_handler(int irq, void *_gpadc)
+{
+ struct ab5500_gpadc *gpadc = _gpadc;
+
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig_vbat_txon_work);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_adc_vbat_txoff_handler() - isr for auto vbat_txoff conversion trigger
+ * @irq: irq number
+ * @data: pointer to the data passed during request irq
+ *
+ * This is a interrupt service routine for auto vbat_txoff conversion
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_adc_vbat_txoff_handler(int irq, void *_gpadc)
+{
+ struct ab5500_gpadc *gpadc = _gpadc;
+
+ queue_work(gpadc->gpadc_wq, &gpadc->gpadc_trig_vbat_txoff_work);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_gpadc_configuration() - function for gpadc conversion
+ * @irq: irq number
+ * @data: pointer to the data passed during request irq
+ *
+ * This function configures the gpadc
+ */
+static int ab5500_gpadc_configuration(struct ab5500_gpadc *gpadc)
+{
+ int ret;
+ ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
+ AB5500_BANK_ADC, AB5500_GPADC_AUTO_CTRL2,
+ ADC_CAL_OFF_MASK | ADC_ON_MODE_MASK,
+ ADC_CAL_ON | ADC_FULLPWR);
+ return ret;
+}
+
+/* ab5500 btemp driver interrupts and their respective isr */
+static struct ab5500_adc_interrupts ab5500_adc_irq[] = {
+ {"TRIGGER-0", ab5500_adc_trigx_handler},
+ {"TRIGGER-1", ab5500_adc_trigx_handler},
+ {"TRIGGER-2", ab5500_adc_trigx_handler},
+ {"TRIGGER-3", ab5500_adc_trigx_handler},
+ {"TRIGGER-4", ab5500_adc_trigx_handler},
+ {"TRIGGER-5", ab5500_adc_trigx_handler},
+ {"TRIGGER-6", ab5500_adc_trigx_handler},
+ {"TRIGGER-7", ab5500_adc_trigx_handler},
+ {"TRIGGER-VBAT-TXON", ab5500_adc_vbat_txon_handler},
+ {"TRIGGER-VBAT", ab5500_adc_vbat_txoff_handler},
+};
+
+static int __devinit ab5500_gpadc_probe(struct platform_device *pdev)
+{
+ int ret, irq, i, j;
+ struct ab5500_gpadc *gpadc;
+
+ gpadc = kzalloc(sizeof(struct ab5500_gpadc), GFP_KERNEL);
+ if (!gpadc) {
+ dev_err(&pdev->dev, "Error: No memory\n");
+ return -ENOMEM;
+ }
+ gpadc->dev = &pdev->dev;
+ mutex_init(&gpadc->ab5500_gpadc_lock);
+ spin_lock_init(&gpadc->gpadc_auto_lock);
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab5500_adc_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab5500_adc_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab5500_adc_irq[i].isr,
+ IRQF_NO_SUSPEND,
+ ab5500_adc_irq[i].name, gpadc);
+
+ if (ret) {
+ dev_err(gpadc->dev, "failed to request %s IRQ %d: %d\n"
+ , ab5500_adc_irq[i].name, irq, ret);
+ goto fail_irq;
+ }
+ dev_dbg(gpadc->dev, "Requested %s IRQ %d: %d\n",
+ ab5500_adc_irq[i].name, irq, ret);
+ }
+
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(gpadc->dev);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "failed to get chip ID\n");
+ goto fail_irq;
+ }
+ gpadc->chip_id = (u8) ret;
+
+ /* Create a work queue for gpadc auto */
+ gpadc->gpadc_wq =
+ create_singlethread_workqueue("ab5500_gpadc_wq");
+ if (gpadc->gpadc_wq == NULL) {
+ dev_err(gpadc->dev, "failed to create work queue\n");
+ goto fail_irq;
+ }
+
+ INIT_WORK(&gpadc->gpadc_trig0_work, ab5500_gpadc_trig0_work);
+ INIT_WORK(&gpadc->gpadc_trig1_work, ab5500_gpadc_trig1_work);
+ INIT_WORK(&gpadc->gpadc_trig2_work, ab5500_gpadc_trig2_work);
+ INIT_WORK(&gpadc->gpadc_trig3_work, ab5500_gpadc_trig3_work);
+ INIT_WORK(&gpadc->gpadc_trig4_work, ab5500_gpadc_trig4_work);
+ INIT_WORK(&gpadc->gpadc_trig5_work, ab5500_gpadc_trig5_work);
+ INIT_WORK(&gpadc->gpadc_trig6_work, ab5500_gpadc_trig6_work);
+ INIT_WORK(&gpadc->gpadc_trig7_work, ab5500_gpadc_trig7_work);
+ INIT_WORK(&gpadc->gpadc_trig_vbat_txon_work,
+ ab5500_gpadc_vbat_txon_work);
+ INIT_WORK(&gpadc->gpadc_trig_vbat_txoff_work,
+ ab5500_gpadc_vbat_txoff_work);
+
+ for (j = 0; j < N_AUTO_TRIGGER; j++)
+ gpadc->adc_trig[j].flag = false;
+
+ ret = ab5500_gpadc_configuration(gpadc);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "gpadc: configuration failed\n");
+ goto free_wq;
+ }
+
+ ret = device_create_file(gpadc->dev, &dev_attr_adc0volt);
+ if (ret < 0) {
+ dev_err(gpadc->dev, "File device creation failed: %d\n", ret);
+ ret = -ENODEV;
+ goto fail_sysfs;
+ }
+ list_add_tail(&gpadc->node, &ab5500_gpadc_list);
+
+ platform_set_drvdata(pdev, gpadc);
+
+ return 0;
+fail_sysfs:
+free_wq:
+ destroy_workqueue(gpadc->gpadc_wq);
+fail_irq:
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab5500_adc_irq[i].name);
+ free_irq(irq, gpadc);
+ }
+ kfree(gpadc);
+ gpadc = NULL;
+ return ret;
+}
+
+static int __devexit ab5500_gpadc_remove(struct platform_device *pdev)
+{
+ int i, irq;
+ struct ab5500_gpadc *gpadc = platform_get_drvdata(pdev);
+
+ device_remove_file(gpadc->dev, &dev_attr_adc0volt);
+
+ /* remove this gpadc entry from the list */
+ list_del(&gpadc->node);
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab5500_adc_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab5500_adc_irq[i].name);
+ free_irq(irq, gpadc);
+ }
+ /* Flush work */
+ flush_workqueue(gpadc->gpadc_wq);
+
+ /* Delete the work queue */
+ destroy_workqueue(gpadc->gpadc_wq);
+
+ kfree(gpadc);
+ gpadc = NULL;
+ return 0;
+}
+
+static struct platform_driver ab5500_gpadc_driver = {
+ .probe = ab5500_gpadc_probe,
+ .remove = __devexit_p(ab5500_gpadc_remove),
+ .driver = {
+ .name = "ab5500-adc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab5500_gpadc_init(void)
+{
+ return platform_driver_register(&ab5500_gpadc_driver);
+}
+
+static void __exit ab5500_gpadc_exit(void)
+{
+ platform_driver_unregister(&ab5500_gpadc_driver);
+}
+
+subsys_initcall_sync(ab5500_gpadc_init);
+module_exit(ab5500_gpadc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Vijaya Kumar K");
+MODULE_ALIAS("platform:ab5500_adc");
+MODULE_DESCRIPTION("AB5500 GPADC driver");
diff --git a/drivers/mfd/ab5500-power.c b/drivers/mfd/ab5500-power.c
new file mode 100644
index 00000000000..9474c32809b
--- /dev/null
+++ b/drivers/mfd/ab5500-power.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+
+static struct device *dev;
+
+/* STARTUP */
+#define AB5500_SYSPOR_CONTROL 0x30
+
+/* VINT IO I2C CLOCK */
+#define AB5500_RTC_VINT 0x01
+
+int ab5500_clock_rtc_enable(int num, bool enable)
+{
+ /* RTC_CLK{0,1,2} are bits {4,3,2}, active low */
+ u8 mask = BIT(4 - num);
+ u8 value = enable ? 0 : mask;
+
+ /* Don't allow RTC_CLK0 to be controlled. */
+ if (num < 1 || num > 2)
+ return -EINVAL;
+
+ if (!dev)
+ return -EAGAIN;
+
+ return abx500_mask_and_set(dev, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ AB5500_RTC_VINT, mask, value);
+}
+
+static void ab5500_power_off(void)
+{
+ sigset_t old;
+ sigset_t all;
+
+ sigfillset(&all);
+
+ if (!sigprocmask(SIG_BLOCK, &all, &old)) {
+ /* Clear dbb_on */
+ int ret = abx500_set(dev, AB5500_BANK_STARTUP,
+ AB5500_SYSPOR_CONTROL, 0);
+ WARN_ON(ret);
+ }
+}
+
+static int __devinit ab5500_power_probe(struct platform_device *pdev)
+{
+ struct ab5500_platform_data *plat = dev_get_platdata(pdev->dev.parent);
+
+ dev = &pdev->dev;
+
+ if (plat->pm_power_off)
+ pm_power_off = ab5500_power_off;
+
+ return 0;
+}
+
+static int __devexit ab5500_power_remove(struct platform_device *pdev)
+{
+ struct ab5500_platform_data *plat = dev_get_platdata(pdev->dev.parent);
+
+ if (plat->pm_power_off)
+ pm_power_off = NULL;
+ dev = NULL;
+
+ return 0;
+}
+
+static struct platform_driver ab5500_power_driver = {
+ .driver = {
+ .name = "ab5500-power",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_power_probe,
+ .remove = __devexit_p(ab5500_power_remove),
+};
+
+static int __init ab8500_sysctrl_init(void)
+{
+ return platform_driver_register(&ab5500_power_driver);
+}
+
+subsys_initcall(ab8500_sysctrl_init);
+
+MODULE_DESCRIPTION("AB5500 power driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c
index 1f08704f7ae..8137ac22816 100644
--- a/drivers/mfd/ab8500-core.c
+++ b/drivers/mfd/ab8500-core.c
@@ -100,6 +100,9 @@
#define AB9540_MODEM_CTRL2_REG 0x23
#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT BIT(2)
+static bool no_bm; /* No battery management */
+module_param(no_bm, bool, S_IRUGO);
+
/*
* Map interrupt numbers to the LATCH and MASK register offsets, Interrupt
* numbers are indexed into this array with (num / 8). The interupts are
@@ -257,6 +260,7 @@ static struct abx500_ops ab8500_ops = {
.mask_and_set_register = ab8500_mask_and_set_register,
.event_registers_startup_state_get = NULL,
.startup_irq_enabled = NULL,
+ .dump_all_banks = ab8500_dump_all_banks,
};
static void ab8500_irq_lock(struct irq_data *data)
@@ -354,6 +358,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
int line = i * 8 + bit;
handle_nested_irq(ab8500->irq_base + line);
+ ab8500_debug_register_interrupt(line);
value &= ~(1 << bit);
} while (value);
}
@@ -746,7 +751,7 @@ static struct resource __devinitdata ab8500_usb_resources[] = {
static struct resource __devinitdata ab8500_temp_resources[] = {
{
- .name = "AB8500_TEMP_WARM",
+ .name = "ABX500_TEMP_WARM",
.start = AB8500_INT_TEMP_WARM,
.end = AB8500_INT_TEMP_WARM,
.flags = IORESOURCE_IRQ,
@@ -768,6 +773,9 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
.name = "ab8500-regulator",
},
{
+ .name = "ab8500-regulator-debug",
+ },
+ {
.name = "ab8500-gpadc",
.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),
.resources = ab8500_gpadc_resources,
@@ -778,26 +786,6 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
.resources = ab8500_rtc_resources,
},
{
- .name = "ab8500-charger",
- .num_resources = ARRAY_SIZE(ab8500_charger_resources),
- .resources = ab8500_charger_resources,
- },
- {
- .name = "ab8500-btemp",
- .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
- .resources = ab8500_btemp_resources,
- },
- {
- .name = "ab8500-fg",
- .num_resources = ARRAY_SIZE(ab8500_fg_resources),
- .resources = ab8500_fg_resources,
- },
- {
- .name = "ab8500-chargalg",
- .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
- .resources = ab8500_chargalg_resources,
- },
- {
.name = "ab8500-acc-det",
.num_resources = ARRAY_SIZE(ab8500_av_acc_detect_resources),
.resources = ab8500_av_acc_detect_resources,
@@ -815,20 +803,12 @@ static struct mfd_cell __devinitdata abx500_common_devs[] = {
.name = "ab8500-pwm",
.id = 1,
},
- {
- .name = "ab8500-pwm",
- .id = 2,
- },
- {
- .name = "ab8500-pwm",
- .id = 3,
- },
{ .name = "ab8500-leds", },
{
.name = "ab8500-denc",
},
{
- .name = "ab8500-temp",
+ .name = "abx500-temp",
.num_resources = ARRAY_SIZE(ab8500_temp_resources),
.resources = ab8500_temp_resources,
},
@@ -860,6 +840,29 @@ static struct mfd_cell __devinitdata ab9540_devs[] = {
},
};
+static struct mfd_cell __devinitdata ab8500_bm_devs[] = {
+ {
+ .name = "ab8500-charger",
+ .num_resources = ARRAY_SIZE(ab8500_charger_resources),
+ .resources = ab8500_charger_resources,
+ },
+ {
+ .name = "ab8500-btemp",
+ .num_resources = ARRAY_SIZE(ab8500_btemp_resources),
+ .resources = ab8500_btemp_resources,
+ },
+ {
+ .name = "ab8500-fg",
+ .num_resources = ARRAY_SIZE(ab8500_fg_resources),
+ .resources = ab8500_fg_resources,
+ },
+ {
+ .name = "ab8500-chargalg",
+ .num_resources = ARRAY_SIZE(ab8500_chargalg_resources),
+ .resources = ab8500_chargalg_resources,
+ },
+};
+
static ssize_t show_chip_id(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1130,6 +1133,15 @@ int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)
if (ret)
goto out_freeirq;
+ if (!no_bm) {
+ /* Add battery management devices */
+ ret = mfd_add_devices(ab8500->dev, 0, ab8500_bm_devs,
+ ARRAY_SIZE(ab8500_bm_devs), NULL,
+ ab8500->irq_base);
+ if (ret)
+ dev_err(ab8500->dev, "error adding bm devices\n");
+ }
+
if (is_ab9540(ab8500))
ret = sysfs_create_group(&ab8500->dev->kobj,
&ab9540_attr_group);
diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c
index 9a0211aa889..7b912afd664 100644
--- a/drivers/mfd/ab8500-debugfs.c
+++ b/drivers/mfd/ab8500-debugfs.c
@@ -4,6 +4,72 @@
* Author: Mattias Wallin <mattias.wallin@stericsson.com> for ST-Ericsson.
* License Terms: GNU General Public License v2
*/
+/*
+ * AB8500 register access
+ * ======================
+ *
+ * read:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # cat <debugfs>/ab8500/register-value
+ *
+ * write:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # echo ADDR > <debugfs>/ab8500/register-address
+ * # echo VALUE > <debugfs>/ab8500/register-value
+ *
+ * read all registers from a bank:
+ * # echo BANK > <debugfs>/ab8500/register-bank
+ * # cat <debugfs>/ab8500/all-bank-register
+ *
+ * BANK target AB8500 register bank
+ * ADDR target AB8500 register address
+ * VALUE decimal or 0x-prefixed hexadecimal
+ *
+ *
+ * User Space notification on AB8500 IRQ
+ * =====================================
+ *
+ * Allows user space entity to be notified when target AB8500 IRQ occurs.
+ * When subscribed, a sysfs entry is created in ab8500.i2c platform device.
+ * One can pool this file to get target IRQ occurence information.
+ *
+ * subscribe to an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-subscribe
+ *
+ * unsubscribe from an AB8500 IRQ:
+ * # echo IRQ > <debugfs>/ab8500/irq-unsubscribe
+ *
+ *
+ * AB8500 register formated read/write access
+ * ==========================================
+ *
+ * Read: read data, data>>SHIFT, data&=MASK, output data
+ * [0xABCDEF98] shift=12 mask=0xFFF => 0x00000CDE
+ * Write: read data, data &= ~(MASK<<SHIFT), data |= (VALUE<<SHIFT), write data
+ * [0xABCDEF98] shift=12 mask=0xFFF value=0x123 => [0xAB123F98]
+ *
+ * Usage:
+ * # echo "CMD [OPTIONS] BANK ADRESS [VALUE]" > $debugfs/ab8500/hwreg
+ *
+ * CMD read read access
+ * write write access
+ *
+ * BANK target reg bank
+ * ADDRESS target reg address
+ * VALUE (write) value to be updated
+ *
+ * OPTIONS
+ * -d|-dec (read) output in decimal
+ * -h|-hexa (read) output in 0x-hexa (default)
+ * -l|-w|-b 32bit (default), 16bit or 8bit reg access
+ * -m|-mask MASK 0x-hexa mask (default 0xFFFFFFFF)
+ * -s|-shift SHIFT bit shift value (read:left, write:right)
+ * -o|-offset OFFSET address offset to add to ADDRESS value
+ *
+ * Warning: bit shift operation is applied to bit-mask.
+ * Warning: bit shift direction depends on read or right command.
+ */
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@@ -11,13 +77,29 @@
#include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
#include <linux/mfd/abx500.h>
-#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpadc.h>
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/string.h>
+#include <linux/ctype.h>
+#endif
static u32 debug_bank;
static u32 debug_address;
+static int irq_first;
+static int irq_last;
+static u32 *irq_count;
+static int num_irqs;
+
+static struct device_attribute **dev_attr;
+static char **event_name;
+
/**
* struct ab8500_reg_range
* @first: the first address of the range
@@ -42,15 +124,35 @@ struct ab8500_i2c_ranges {
const struct ab8500_reg_range *range;
};
+/* hwreg- "mask" and "shift" entries ressources */
+struct hwreg_cfg {
+ u32 bank; /* target bank */
+ u32 addr; /* target address */
+ uint fmt; /* format */
+ uint mask; /* read/write mask, applied before any bit shift */
+ int shift; /* bit shift (read:right shift, write:left shift */
+};
+/* fmt bit #0: 0=hexa, 1=dec */
+#define REG_FMT_DEC(c) ((c)->fmt & 0x1)
+#define REG_FMT_HEX(c) (!REG_FMT_DEC(c))
+
+static struct hwreg_cfg hwreg_cfg = {
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+};
+
#define AB8500_NAME_STRING "ab8500"
-#define AB8500_NUM_BANKS 22
+#define AB8500_ADC_NAME_STRING "gpadc"
+#define AB8500_NUM_BANKS 24
#define AB8500_REV_REG 0x80
static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
[0x0] = {
.num_ranges = 0,
- .range = 0,
+ .range = NULL,
},
[AB8500_SYS_CTRL1_BLOCK] = {
.num_ranges = 3,
@@ -215,7 +317,7 @@ static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
[AB8500_CHARGER] = {
- .num_ranges = 8,
+ .num_ranges = 9,
.range = (struct ab8500_reg_range[]) {
{
.first = 0x00,
@@ -249,6 +351,10 @@ static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
.first = 0xC0,
.last = 0xC2,
},
+ {
+ .first = 0xf5,
+ .last = 0xf6,
+ },
},
},
[AB8500_GAS_GAUGE] = {
@@ -268,6 +374,24 @@ static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
},
},
+ [AB8500_DEVELOPMENT] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x00,
+ .last = 0x00,
+ },
+ },
+ },
+ [AB8500_DEBUG] = {
+ .num_ranges = 1,
+ .range = (struct ab8500_reg_range[]) {
+ {
+ .first = 0x05,
+ .last = 0x07,
+ },
+ },
+ },
[AB8500_AUDIO] = {
.num_ranges = 1,
.range = (struct ab8500_reg_range[]) {
@@ -354,15 +478,30 @@ static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = {
},
};
-static int ab8500_registers_print(struct seq_file *s, void *p)
+static irqreturn_t ab8500_debug_handler(int irq, void *data)
{
- struct device *dev = s->private;
- unsigned int i;
- u32 bank = debug_bank;
+ char buf[16];
+ struct kobject *kobj = (struct kobject *)data;
+ unsigned int irq_abb = irq - irq_first;
- seq_printf(s, AB8500_NAME_STRING " register values:\n");
+ if (irq_abb < num_irqs)
+ irq_count[irq_abb]++;
+ /*
+ * This makes it possible to use poll for events (POLLPRI | POLLERR)
+ * from userspace on sysfs file named <irq-nr>
+ */
+ sprintf(buf, "%d", irq);
+ sysfs_notify(kobj, NULL, buf);
+
+ return IRQ_HANDLED;
+}
+
+/* Prints to seq_file or log_buf */
+static int ab8500_registers_print(struct device *dev, u32 bank,
+ struct seq_file *s)
+{
+ unsigned int i;
- seq_printf(s, " bank %u:\n", bank);
for (i = 0; i < debug_ranges[bank].num_ranges; i++) {
u32 reg;
@@ -379,22 +518,42 @@ static int ab8500_registers_print(struct seq_file *s, void *p)
return err;
}
- err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank,
- reg, value);
- if (err < 0) {
- dev_err(dev, "seq_printf overflow\n");
- /* Error is not returned here since
- * the output is wanted in any case */
- return 0;
+ if (s) {
+ err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n",
+ bank, reg, value);
+ if (err < 0) {
+ dev_err(dev,
+ "seq_printf overflow bank=%d reg=%d\n",
+ bank, reg);
+ /* Error is not returned here since
+ * the output is wanted in any case */
+ return 0;
+ }
+ } else {
+ printk(KERN_INFO" [%u/0x%02X]: 0x%02X\n", bank,
+ reg, value);
}
}
}
return 0;
}
+static int ab8500_print_bank_registers(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ u32 bank = debug_bank;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ seq_printf(s, " bank %u:\n", bank);
+
+ ab8500_registers_print(dev, bank, s);
+ return 0;
+}
+
static int ab8500_registers_open(struct inode *inode, struct file *file)
{
- return single_open(file, ab8500_registers_print, inode->i_private);
+ return single_open(file, ab8500_print_bank_registers, inode->i_private);
}
static const struct file_operations ab8500_registers_fops = {
@@ -405,6 +564,64 @@ static const struct file_operations ab8500_registers_fops = {
.owner = THIS_MODULE,
};
+static int ab8500_print_all_banks(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ unsigned int i;
+ int err;
+
+ seq_printf(s, AB8500_NAME_STRING " register values:\n");
+
+ for (i = 1; i < AB8500_NUM_BANKS; i++) {
+ err = seq_printf(s, " bank %u:\n", i);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow, bank=%d\n", i);
+
+ ab8500_registers_print(dev, i, s);
+ }
+ return 0;
+}
+
+/* Dump registers to kernel log */
+void ab8500_dump_all_banks(struct device *dev)
+{
+ unsigned int i;
+
+ printk(KERN_INFO"ab8500 register values:\n");
+
+ for (i = 1; i < AB8500_NUM_BANKS; i++) {
+ printk(KERN_INFO" bank %u:\n", i);
+ ab8500_registers_print(dev, i, NULL);
+ }
+}
+
+static int ab8500_all_banks_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *s;
+ int err;
+
+ err = single_open(file, ab8500_print_all_banks, inode->i_private);
+ if (!err) {
+ /* Default buf size in seq_read is not enough */
+ s = (struct seq_file *)file->private_data;
+ s->size = (PAGE_SIZE * 2);
+ s->buf = kmalloc(s->size, GFP_KERNEL);
+ if (!s->buf) {
+ single_release(inode, file);
+ err = -ENOMEM;
+ }
+ }
+ return err;
+}
+
+static const struct file_operations ab8500_all_banks_fops = {
+ .open = ab8500_all_banks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static int ab8500_bank_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", debug_bank);
@@ -515,10 +732,761 @@ static ssize_t ab8500_val_write(struct file *file,
printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__);
return -EINVAL;
}
+ return count;
+}
+
+/*
+ * Interrupt status
+ */
+static u32 num_interrupts[AB8500_MAX_NR_IRQS];
+static int num_interrupt_lines;
+
+void ab8500_debug_register_interrupt(int line)
+{
+ if (line < num_interrupt_lines)
+ num_interrupts[line]++;
+}
+
+static int ab8500_interrupts_print(struct seq_file *s, void *p)
+{
+ int line;
+
+ seq_printf(s, "irq: number of\n");
+
+ for (line = 0; line < num_interrupt_lines; line++)
+ seq_printf(s, "%3i: %6i\n", line, num_interrupts[line]);
+
+ return 0;
+}
+
+static int ab8500_interrupts_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_interrupts_print, inode->i_private);
+}
+
+/*
+ * - HWREG DB8500 formated routines
+ */
+static int ab8500_hwreg_print(struct seq_file *s, void *d)
+{
+ struct device *dev = s->private;
+ int ret;
+ u8 regvalue;
+
+ ret = abx500_get_register_interruptible(dev,
+ (u8)hwreg_cfg.bank, (u8)hwreg_cfg.addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (hwreg_cfg.shift >= 0)
+ regvalue >>= hwreg_cfg.shift;
+ else
+ regvalue <<= -hwreg_cfg.shift;
+ regvalue &= hwreg_cfg.mask;
+
+ if (REG_FMT_DEC(&hwreg_cfg))
+ seq_printf(s, "%d\n", regvalue);
+ else
+ seq_printf(s, "0x%02X\n", regvalue);
+ return 0;
+}
+
+static int ab8500_hwreg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_hwreg_print, inode->i_private);
+}
+
+static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
+{
+ int bat_ctrl_raw;
+ int bat_ctrl_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL);
+ bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ BAT_CTRL, bat_ctrl_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ bat_ctrl_convert, bat_ctrl_raw);
+}
+
+static int ab8500_gpadc_bat_ctrl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_bat_ctrl_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bat_ctrl_fops = {
+ .open = ab8500_gpadc_bat_ctrl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
+{
+ int btemp_ball_raw;
+ int btemp_ball_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL);
+ btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
+ btemp_ball_raw);
+
+ return seq_printf(s,
+ "%d,0x%X\n", btemp_ball_convert, btemp_ball_raw);
+}
+
+static int ab8500_gpadc_btemp_ball_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_btemp_ball_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_btemp_ball_fops = {
+ .open = ab8500_gpadc_btemp_ball_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
+{
+ int main_charger_v_raw;
+ int main_charger_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V);
+ main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ MAIN_CHARGER_V, main_charger_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_charger_v_convert, main_charger_v_raw);
+}
+
+static int ab8500_gpadc_main_charger_v_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_charger_v_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_v_fops = {
+ .open = ab8500_gpadc_main_charger_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
+{
+ int acc_detect1_raw;
+ int acc_detect1_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1);
+ acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
+ acc_detect1_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ acc_detect1_convert, acc_detect1_raw);
+}
+
+static int ab8500_gpadc_acc_detect1_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_acc_detect1_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_acc_detect1_fops = {
+ .open = ab8500_gpadc_acc_detect1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
+{
+ int acc_detect2_raw;
+ int acc_detect2_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2);
+ acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ ACC_DETECT2, acc_detect2_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ acc_detect2_convert, acc_detect2_raw);
+}
+
+static int ab8500_gpadc_acc_detect2_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_acc_detect2_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_acc_detect2_fops = {
+ .open = ab8500_gpadc_acc_detect2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
+{
+ int aux1_raw;
+ int aux1_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1);
+ aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
+ aux1_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ aux1_convert, aux1_raw);
+}
+
+static int ab8500_gpadc_aux1_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_aux1_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux1_fops = {
+ .open = ab8500_gpadc_aux1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
+{
+ int aux2_raw;
+ int aux2_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2);
+ aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
+ aux2_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ aux2_convert, aux2_raw);
+}
+
+static int ab8500_gpadc_aux2_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_aux2_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_aux2_fops = {
+ .open = ab8500_gpadc_aux2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
+{
+ int main_bat_v_raw;
+ int main_bat_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V);
+ main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
+ main_bat_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_bat_v_convert, main_bat_v_raw);
+}
+
+static int ab8500_gpadc_main_bat_v_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_bat_v_fops = {
+ .open = ab8500_gpadc_main_bat_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
+{
+ int vbus_v_raw;
+ int vbus_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V);
+ vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
+ vbus_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ vbus_v_convert, vbus_v_raw);
+}
+
+static int ab8500_gpadc_vbus_v_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_vbus_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_vbus_v_fops = {
+ .open = ab8500_gpadc_vbus_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
+{
+ int main_charger_c_raw;
+ int main_charger_c_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C);
+ main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ MAIN_CHARGER_C, main_charger_c_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ main_charger_c_convert, main_charger_c_raw);
+}
+
+static int ab8500_gpadc_main_charger_c_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_main_charger_c_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_main_charger_c_fops = {
+ .open = ab8500_gpadc_main_charger_c_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
+{
+ int usb_charger_c_raw;
+ int usb_charger_c_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C);
+ usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ USB_CHARGER_C, usb_charger_c_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ usb_charger_c_convert, usb_charger_c_raw);
+}
+
+static int ab8500_gpadc_usb_charger_c_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_gpadc_usb_charger_c_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_usb_charger_c_fops = {
+ .open = ab8500_gpadc_usb_charger_c_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
+{
+ int bk_bat_v_raw;
+ int bk_bat_v_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V);
+ bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
+ BK_BAT_V, bk_bat_v_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ bk_bat_v_convert, bk_bat_v_raw);
+}
+
+static int ab8500_gpadc_bk_bat_v_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_bk_bat_v_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_bk_bat_v_fops = {
+ .open = ab8500_gpadc_bk_bat_v_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
+{
+ int die_temp_raw;
+ int die_temp_convert;
+ struct ab8500_gpadc *gpadc;
+
+ gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP);
+ die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
+ die_temp_raw);
+
+ return seq_printf(s, "%d,0x%X\n",
+ die_temp_convert, die_temp_raw);
+}
+
+static int ab8500_gpadc_die_temp_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_gpadc_die_temp_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_gpadc_die_temp_fops = {
+ .open = ab8500_gpadc_die_temp_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * return length of an ASCII numerical value, 0 is string is not a
+ * numerical value.
+ * string shall start at value 1st char.
+ * string can be tailed with \0 or space or newline chars only.
+ * value can be decimal or hexadecimal (prefixed 0x or 0X).
+ */
+static int strval_len(char *b)
+{
+ char *s = b;
+ if ((*s == '0') && ((*(s+1) == 'x') || (*(s+1) == 'X'))) {
+ s += 2;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isxdigit(*s))
+ return 0;
+ }
+ } else {
+ if (*s == '-')
+ s++;
+ for (; *s && (*s != ' ') && (*s != '\n'); s++) {
+ if (!isdigit(*s))
+ return 0;
+ }
+ }
+ return (int) (s-b);
+}
+
+/*
+ * parse hwreg input data.
+ * update global hwreg_cfg only if input data syntax is ok.
+ */
+static ssize_t hwreg_common_write(char *b, struct hwreg_cfg *cfg,
+ struct device *dev)
+{
+ uint write, val = 0;
+ struct hwreg_cfg loc = {
+ .bank = 0, /* default: invalid phys addr */
+ .addr = 0, /* default: invalid phys addr */
+ .fmt = 0, /* default: 32bit access, hex output */
+ .mask = 0xFFFFFFFF, /* default: no mask */
+ .shift = 0, /* default: no bit shift */
+ };
+
+ /* read or write ? */
+ if (!strncmp(b, "read ", 5)) {
+ write = 0;
+ b += 5;
+ } else if (!strncmp(b, "write ", 6)) {
+ write = 1;
+ b += 6;
+ } else
+ return -EINVAL;
+
+ /* OPTIONS -l|-w|-b -s -m -o */
+ while ((*b == ' ') || (*b == '-')) {
+ if (*(b-1) != ' ') {
+ b++;
+ continue;
+ }
+ if ((!strncmp(b, "-d ", 3)) ||
+ (!strncmp(b, "-dec ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt |= (1<<0);
+ } else if ((!strncmp(b, "-h ", 3)) ||
+ (!strncmp(b, "-hex ", 5))) {
+ b += (*(b+2) == ' ') ? 3 : 5;
+ loc.fmt &= ~(1<<0);
+ } else if ((!strncmp(b, "-m ", 3)) ||
+ (!strncmp(b, "-mask ", 6))) {
+ b += (*(b+2) == ' ') ? 3 : 6;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.mask = simple_strtoul(b, &b, 0);
+ } else if ((!strncmp(b, "-s ", 3)) ||
+ (!strncmp(b, "-shift ", 7))) {
+ b += (*(b+2) == ' ') ? 3 : 7;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.shift = simple_strtol(b, &b, 0);
+ } else {
+ return -EINVAL;
+ }
+ }
+ /* get arg BANK and ADDRESS */
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.bank = simple_strtoul(b, &b, 0);
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ loc.addr = simple_strtoul(b, &b, 0);
+
+ if (write) {
+ while (*b == ' ')
+ b++;
+ if (strval_len(b) == 0)
+ return -EINVAL;
+ val = simple_strtoul(b, &b, 0);
+ }
+
+ /* args are ok, update target cfg (mainly for read) */
+ *cfg = loc;
+
+#ifdef ABB_HWREG_DEBUG
+ pr_warn("HWREG request: %s, %s, addr=0x%08X, mask=0x%X, shift=%d"
+ "value=0x%X\n", (write) ? "write" : "read",
+ REG_FMT_DEC(cfg) ? "decimal" : "hexa",
+ cfg->addr, cfg->mask, cfg->shift, val);
+#endif
+
+ if (write) {
+ u8 regvalue;
+ int ret = abx500_get_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, &regvalue);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ if (cfg->shift >= 0) {
+ regvalue &= ~(cfg->mask << (cfg->shift));
+ val = (val & cfg->mask) << (cfg->shift);
+ } else {
+ regvalue &= ~(cfg->mask >> (-cfg->shift));
+ val = (val & cfg->mask) >> (-cfg->shift);
+ }
+ val = val | regvalue;
+
+ ret = abx500_set_register_interruptible(dev,
+ (u8)cfg->bank, (u8)cfg->addr, (u8)val);
+ if (ret < 0) {
+ pr_err("abx500_set_reg failed %d, %d", ret, __LINE__);
+ return -EINVAL;
+ }
+
+ }
+ return 0;
+}
+
+static ssize_t ab8500_hwreg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[128];
+ int buf_size, ret;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* get args and process */
+ ret = hwreg_common_write(buf, &hwreg_cfg, dev);
+ return (ret) ? ret : buf_size;
+}
+
+/*
+ * - irq subscribe/unsubscribe stuff
+ */
+static int ab8500_subscribe_unsubscribe_print(struct seq_file *s, void *p)
+{
+ seq_printf(s, "%d\n", irq_first);
+
+ return 0;
+}
+
+static int ab8500_subscribe_unsubscribe_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_subscribe_unsubscribe_print,
+ inode->i_private);
+}
+
+/*
+ * Userspace should use poll() on this file. When an event occur
+ * the blocking poll will be released.
+ */
+static ssize_t show_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long name;
+ unsigned int irq_index;
+ int err;
+
+ err = strict_strtoul(attr->attr.name, 0, &name);
+ if (err)
+ return err;
+
+ irq_index = name - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+ else
+ return sprintf(buf, "%u\n", irq_count[irq_index]);
+}
+
+static ssize_t ab8500_subscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+
+ /*
+ * This will create a sysfs file named <irq-nr> which userspace can
+ * use to select or poll and get the AB8500 events
+ */
+ dev_attr[irq_index] = kmalloc(sizeof(struct device_attribute),
+ GFP_KERNEL);
+ event_name[irq_index] = kmalloc(buf_size, GFP_KERNEL);
+ sprintf(event_name[irq_index], "%lu", user_val);
+ dev_attr[irq_index]->show = show_irq;
+ dev_attr[irq_index]->store = NULL;
+ dev_attr[irq_index]->attr.name = event_name[irq_index];
+ dev_attr[irq_index]->attr.mode = S_IRUGO;
+ err = sysfs_create_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ if (err < 0) {
+ printk(KERN_ERR "sysfs_create_file failed %d\n", err);
+ return err;
+ }
+
+ err = request_threaded_irq(user_val, NULL, ab8500_debug_handler,
+ IRQF_SHARED | IRQF_NO_SUSPEND, "ab8500-debug", &dev->kobj);
+ if (err < 0) {
+ printk(KERN_ERR "request_threaded_irq failed %d, %lu\n",
+ err, user_val);
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+ return err;
+ }
+
+ return buf_size;
+}
+
+static ssize_t ab8500_unsubscribe_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct device *dev = ((struct seq_file *)(file->private_data))->private;
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+ unsigned int irq_index;
+
+ /* Get userspace string and assure termination */
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+ if (user_val < irq_first) {
+ dev_err(dev, "debugfs error input < %d\n", irq_first);
+ return -EINVAL;
+ }
+ if (user_val > irq_last) {
+ dev_err(dev, "debugfs error input > %d\n", irq_last);
+ return -EINVAL;
+ }
+
+ irq_index = user_val - irq_first;
+ if (irq_index >= num_irqs)
+ return -EINVAL;
+
+ /* Set irq count to 0 when unsubscribe */
+ irq_count[irq_index] = 0;
+
+ if (dev_attr[irq_index])
+ sysfs_remove_file(&dev->kobj, &dev_attr[irq_index]->attr);
+
+
+ free_irq(user_val, &dev->kobj);
+ kfree(event_name[irq_index]);
+ kfree(dev_attr[irq_index]);
return count;
}
+/*
+ * - several deubgfs nodes fops
+ */
+
static const struct file_operations ab8500_bank_fops = {
.open = ab8500_bank_open,
.write = ab8500_bank_write,
@@ -546,64 +1514,231 @@ static const struct file_operations ab8500_val_fops = {
.owner = THIS_MODULE,
};
+static const struct file_operations ab8500_interrupts_fops = {
+ .open = ab8500_interrupts_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_subscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_subscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_unsubscribe_fops = {
+ .open = ab8500_subscribe_unsubscribe_open,
+ .write = ab8500_unsubscribe_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations ab8500_hwreg_fops = {
+ .open = ab8500_hwreg_open,
+ .write = ab8500_hwreg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static struct dentry *ab8500_dir;
-static struct dentry *ab8500_reg_file;
-static struct dentry *ab8500_bank_file;
-static struct dentry *ab8500_address_file;
-static struct dentry *ab8500_val_file;
+static struct dentry *ab8500_gpadc_dir;
static int __devinit ab8500_debug_probe(struct platform_device *plf)
{
+ struct dentry *file;
+ int ret = -ENOMEM;
+ struct ab8500 *ab8500;
debug_bank = AB8500_MISC;
debug_address = AB8500_REV_REG & 0x00FF;
+ ab8500 = dev_get_drvdata(plf->dev.parent);
+ num_irqs = ab8500->mask_size;
+
+ irq_count = kzalloc(sizeof(*irq_count)*num_irqs, GFP_KERNEL);
+ if (!irq_count)
+ return -ENOMEM;
+
+ dev_attr = kzalloc(sizeof(*dev_attr)*num_irqs,GFP_KERNEL);
+ if (!dev_attr)
+ goto out_freeirq_count;
+
+ event_name = kzalloc(sizeof(*event_name)*num_irqs, GFP_KERNEL);
+ if (!event_name)
+ goto out_freedev_attr;
+
+ irq_first = platform_get_irq_byname(plf, "IRQ_FIRST");
+ if (irq_first < 0) {
+ dev_err(&plf->dev, "First irq not found, err %d\n",
+ irq_first);
+ ret = irq_first;
+ goto out_freeevent_name;
+ }
+
+ irq_last = platform_get_irq_byname(plf, "IRQ_LAST");
+ if (irq_last < 0) {
+ dev_err(&plf->dev, "Last irq not found, err %d\n",
+ irq_last);
+ ret = irq_last;
+ goto out_freeevent_name;
+ }
+
ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL);
if (!ab8500_dir)
- goto exit_no_debugfs;
+ goto err;
+
+ ab8500_gpadc_dir = debugfs_create_dir(AB8500_ADC_NAME_STRING,
+ ab8500_dir);
+ if (!ab8500_gpadc_dir)
+ goto err;
+
+ file = debugfs_create_file("all-bank-registers", S_IRUGO,
+ ab8500_dir, &plf->dev, &ab8500_registers_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("all-banks", S_IRUGO,
+ ab8500_dir, &plf->dev, &ab8500_all_banks_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-bank", (S_IRUGO | S_IWUGO),
+ ab8500_dir, &plf->dev, &ab8500_bank_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-address", (S_IRUGO | S_IWUGO),
+ ab8500_dir, &plf->dev, &ab8500_address_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("register-value", (S_IRUGO | S_IWUGO),
+ ab8500_dir, &plf->dev, &ab8500_val_fops);
+ if (!file)
+ goto err;
- ab8500_reg_file = debugfs_create_file("all-bank-registers",
- S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops);
- if (!ab8500_reg_file)
- goto exit_destroy_dir;
+ if (is_ab8500(ab8500))
+ num_interrupt_lines = AB8500_NR_IRQS;
+ else if (is_ab8505(ab8500))
+ num_interrupt_lines = AB8505_NR_IRQS;
+ else if (is_ab9540(ab8500))
+ num_interrupt_lines = AB9540_NR_IRQS;
- ab8500_bank_file = debugfs_create_file("register-bank",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops);
- if (!ab8500_bank_file)
- goto exit_destroy_reg;
+ file = debugfs_create_file("interrupts", (S_IRUGO),
+ ab8500_dir, &plf->dev, &ab8500_interrupts_fops);
+ if (!file)
+ goto err;
- ab8500_address_file = debugfs_create_file("register-address",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev,
- &ab8500_address_fops);
- if (!ab8500_address_file)
- goto exit_destroy_bank;
+ file = debugfs_create_file("irq-subscribe", (S_IRUGO | S_IWUGO),
+ ab8500_dir, &plf->dev, &ab8500_subscribe_fops);
+ if (!file)
+ goto err;
- ab8500_val_file = debugfs_create_file("register-value",
- (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops);
- if (!ab8500_val_file)
- goto exit_destroy_address;
+ file = debugfs_create_file("irq-unsubscribe", (S_IRUGO | S_IWUGO),
+ ab8500_dir, &plf->dev, &ab8500_unsubscribe_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("hwreg", (S_IRUGO | S_IWUGO),
+ ab8500_dir, &plf->dev, &ab8500_hwreg_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("bat_ctrl", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bat_ctrl_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("btemp_ball", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_btemp_ball_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_charger_v", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("acc_detect1", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect1_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("acc_detect2", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_acc_detect2_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("adc_aux1", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux1_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("adc_aux2", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_aux2_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_bat_v", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_bat_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("vbus_v", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_vbus_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("main_charger_c", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_main_charger_c_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("usb_charger_c", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_usb_charger_c_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("bk_bat_v", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_bk_bat_v_fops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("die_temp", (S_IRUGO | S_IWUGO),
+ ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_die_temp_fops);
+ if (!file)
+ goto err;
return 0;
-exit_destroy_address:
- debugfs_remove(ab8500_address_file);
-exit_destroy_bank:
- debugfs_remove(ab8500_bank_file);
-exit_destroy_reg:
- debugfs_remove(ab8500_reg_file);
-exit_destroy_dir:
- debugfs_remove(ab8500_dir);
-exit_no_debugfs:
+err:
+ if (ab8500_dir)
+ debugfs_remove_recursive(ab8500_dir);
dev_err(&plf->dev, "failed to create debugfs entries.\n");
- return -ENOMEM;
+out_freeevent_name:
+ kfree(event_name);
+out_freedev_attr:
+ kfree(dev_attr);
+out_freeirq_count:
+ kfree(irq_count);
+
+ return ret;
}
static int __devexit ab8500_debug_remove(struct platform_device *plf)
{
- debugfs_remove(ab8500_val_file);
- debugfs_remove(ab8500_address_file);
- debugfs_remove(ab8500_bank_file);
- debugfs_remove(ab8500_reg_file);
- debugfs_remove(ab8500_dir);
+ debugfs_remove_recursive(ab8500_dir);
+ kfree(event_name);
+ kfree(dev_attr);
+ kfree(irq_count);
return 0;
}
diff --git a/drivers/mfd/ab8500-denc.c b/drivers/mfd/ab8500-denc.c
new file mode 100644
index 00000000000..17efee62110
--- /dev/null
+++ b/drivers/mfd/ab8500-denc.c
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2010
+ *
+ * ST-Ericsson AB8500 DENC base driver
+ *
+ * Author: Marcel Tunnissen <marcel.tuennissen@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/ab8500/denc-regs.h>
+#include <linux/mfd/ab8500/denc.h>
+
+#define AB8500_NAME "ab8500"
+#define AB8500_DENC_NAME "ab8500_denc"
+
+struct device_usage {
+ struct list_head list;
+ struct platform_device *pdev;
+ bool taken;
+};
+static LIST_HEAD(device_list);
+
+/* To get rid of the extra bank parameter: */
+#define AB8500_REG_BANK_NR(__reg) ((0xff00 & (__reg)) >> 8)
+static inline u8 ab8500_rreg(struct device *dev, u32 reg)
+{
+ u8 val;
+ if (abx500_get_register_interruptible(dev, AB8500_REG_BANK_NR(reg),
+ reg, &val) < 0)
+ return 0;
+ else
+ return val;
+}
+
+static inline int ab8500_wreg(struct device *dev, u32 reg, u8 val)
+{
+ return abx500_set_register_interruptible(dev, AB8500_REG_BANK_NR(reg),
+ reg, val);
+}
+
+/* Only use in the macro below: */
+static inline int _ab8500_wreg_fld(struct device *dev, u32 reg, u8 val,
+ u8 mask, u8 shift)
+{
+ int ret;
+ u8 org_val;
+
+ ret = abx500_get_register_interruptible(dev, AB8500_REG_BANK_NR(reg),
+ reg, &org_val);
+ if (ret < 0)
+ return ret;
+ else
+ ab8500_wreg(dev, reg,
+ (org_val & ~mask) | ((val << shift) & mask));
+ return 0;
+}
+
+#define ab8500_wr_fld(__d, __reg, __fld, __val) \
+ _ab8500_wreg_fld(__d, __reg, __val, __reg##_##__fld##_MASK, \
+ __reg##_##__fld##_SHIFT)
+
+#define ab8500_set_fld(__cur_val, __reg, __fld, __val) \
+ (((__cur_val) & ~__reg##_##__fld##_MASK) | \
+ (((__val) << __reg##_##__fld##_SHIFT) & __reg##_##__fld##_MASK))
+
+#define AB8500_DENC_TRACE(__pd) dev_dbg(&(__pd)->dev, "%s\n", __func__)
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_ab8500_denc_dir;
+static struct dentry *debugfs_ab8500_dump_regs_file;
+static void ab8500_denc_conf_ddr(struct platform_device *pdev);
+static int debugfs_ab8500_open_file(struct inode *inode, struct file *file);
+static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
+ size_t count, loff_t *f_pos);
+
+static const struct file_operations debugfs_ab8500_dump_regs_fops = {
+ .owner = THIS_MODULE,
+ .open = debugfs_ab8500_open_file,
+ .read = debugfs_ab8500_dump_regs,
+};
+#endif /* CONFIG_DEBUG_FS */
+
+static int __devinit ab8500_denc_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ab8500_platform_data *ab8500_pdata =
+ dev_get_platdata(pdev->dev.parent);
+ struct ab8500_denc_platform_data *pdata;
+ struct device_usage *device_data;
+
+ AB8500_DENC_TRACE(pdev);
+
+ if (ab8500_pdata == NULL) {
+ dev_err(&pdev->dev, "AB8500 platform data missing\n");
+ return -EINVAL;
+ }
+
+ pdata = ab8500_pdata->denc;
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "Denc platform data missing\n");
+ return -EINVAL;
+ }
+
+ device_data = kzalloc(sizeof(struct device_usage), GFP_KERNEL);
+ if (!device_data) {
+ dev_err(&pdev->dev, "Failed to allocate device data\n");
+ return -ENOMEM;
+ }
+ device_data->pdev = pdev;
+ list_add_tail(&device_data->list, &device_list);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_ab8500_denc_dir = debugfs_create_dir(pdev->name, NULL);
+ debugfs_ab8500_dump_regs_file = debugfs_create_file(
+ "dumpregs", S_IRUGO,
+ debugfs_ab8500_denc_dir, &pdev->dev,
+ &debugfs_ab8500_dump_regs_fops
+ );
+#endif /* CONFIG_DEBUG_FS */
+ return ret;
+}
+
+static int __devexit ab8500_denc_remove(struct platform_device *pdev)
+{
+ struct list_head *element;
+ struct device_usage *device_data;
+
+ AB8500_DENC_TRACE(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(debugfs_ab8500_dump_regs_file);
+ debugfs_remove(debugfs_ab8500_denc_dir);
+#endif /* CONFIG_DEBUG_FS */
+
+ list_for_each(element, &device_list) {
+ device_data = list_entry(element, struct device_usage, list);
+ if (device_data->pdev == pdev) {
+ list_del(element);
+ kzfree(device_data);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver ab8500_denc_driver = {
+ .probe = ab8500_denc_probe,
+ .remove = ab8500_denc_remove,
+ .driver = {
+ .name = "ab8500-denc",
+ },
+};
+
+static void setup_27mhz(struct platform_device *pdev, bool enable)
+{
+ u8 data = ab8500_rreg(&pdev->dev, AB8500_SYS_ULP_CLK_CONF);
+
+ AB8500_DENC_TRACE(pdev);
+ /* TODO: check if this field needs to be set */
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_PD_ENA,
+ true);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_BUF_ENA,
+ enable);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, TVOUT_CLK_INV,
+ false);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, TVOUT_CLK_DE_IN,
+ false);
+ data = ab8500_set_fld(data, AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_STRE,
+ 1);
+ ab8500_wreg(&pdev->dev, AB8500_SYS_ULP_CLK_CONF, data);
+
+ data = ab8500_rreg(&pdev->dev, AB8500_SYS_CLK_CTRL);
+ data = ab8500_set_fld(data, AB8500_SYS_CLK_CTRL, TVOUT_CLK_VALID,
+ enable);
+ data = ab8500_set_fld(data, AB8500_SYS_CLK_CTRL, TVOUT_PLL_ENA,
+ enable);
+ ab8500_wreg(&pdev->dev, AB8500_SYS_CLK_CTRL, data);
+}
+
+static u32 map_tv_std(enum ab8500_denc_TV_std std)
+{
+ switch (std) {
+ case TV_STD_PAL_BDGHI:
+ return AB8500_DENC_CONF0_STD_PAL_BDGHI;
+ case TV_STD_PAL_N:
+ return AB8500_DENC_CONF0_STD_PAL_N;
+ case TV_STD_PAL_M:
+ return AB8500_DENC_CONF0_STD_PAL_M;
+ case TV_STD_NTSC_M:
+ return AB8500_DENC_CONF0_STD_NTSC_M;
+ default:
+ return 0;
+ }
+}
+
+static u32 map_cr_filter(enum ab8500_denc_cr_filter_bandwidth bw)
+{
+ switch (bw) {
+ case TV_CR_NTSC_LOW_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_1MHZ;
+ case TV_CR_PAL_LOW_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_3MHZ;
+ case TV_CR_NTSC_HIGH_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_6MHZ;
+ case TV_CR_PAL_HIGH_DEF_FILTER:
+ return AB8500_DENC_CONF1_FLT_1_9MHZ;
+ default:
+ return 0;
+ }
+}
+
+static u32 map_phase_rst_mode(enum ab8500_denc_phase_reset_mode mode)
+{
+ switch (mode) {
+ case TV_PHASE_RST_MOD_DISABLE:
+ return AB8500_DENC_CONF8_PH_RST_MODE_DISABLED;
+ case TV_PHASE_RST_MOD_FROM_PHASE_BUF:
+ return AB8500_DENC_CONF8_PH_RST_MODE_UPDATE_FROM_PHASE_BUF;
+ case TV_PHASE_RST_MOD_FROM_INC_DFS:
+ return AB8500_DENC_CONF8_PH_RST_MODE_UPDATE_FROM_INC_DFS;
+ case TV_PHASE_RST_MOD_RST:
+ return AB8500_DENC_CONF8_PH_RST_MODE_RESET;
+ default:
+ return 0;
+ }
+}
+
+static u32 map_plug_time(enum ab8500_denc_plug_time time)
+{
+ switch (time) {
+ case TV_PLUG_TIME_0_5S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_0_5S;
+ case TV_PLUG_TIME_1S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_1S;
+ case TV_PLUG_TIME_1_5S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_1_5S;
+ case TV_PLUG_TIME_2S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_2S;
+ case TV_PLUG_TIME_2_5S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_2_5S;
+ case TV_PLUG_TIME_3S:
+ return AB8500_TVOUT_CTRL_PLUG_TV_TIME_3S;
+ default:
+ return 0;
+ }
+}
+
+struct platform_device *ab8500_denc_get_device(void)
+{
+ struct list_head *element;
+ struct device_usage *device_data;
+
+ pr_debug("%s\n", __func__);
+ list_for_each(element, &device_list) {
+ device_data = list_entry(element, struct device_usage, list);
+ if (!device_data->taken) {
+ device_data->taken = true;
+ return device_data->pdev;
+ }
+ }
+ return NULL;
+}
+EXPORT_SYMBOL(ab8500_denc_get_device);
+
+void ab8500_denc_put_device(struct platform_device *pdev)
+{
+ struct list_head *element;
+ struct device_usage *device_data;
+
+ AB8500_DENC_TRACE(pdev);
+ list_for_each(element, &device_list) {
+ device_data = list_entry(element, struct device_usage, list);
+ if (device_data->pdev == pdev)
+ device_data->taken = false;
+ }
+}
+EXPORT_SYMBOL(ab8500_denc_put_device);
+
+void ab8500_denc_reset(struct platform_device *pdev, bool hard)
+{
+ AB8500_DENC_TRACE(pdev);
+ if (hard) {
+ u8 data = ab8500_rreg(&pdev->dev, AB8500_CTRL3);
+ /* reset start */
+ ab8500_wreg(&pdev->dev, AB8500_CTRL3,
+ ab8500_set_fld(data, AB8500_CTRL3, RESET_DENC_N, 0)
+ );
+ /* reset done */
+ ab8500_wreg(&pdev->dev, AB8500_CTRL3,
+ ab8500_set_fld(data, AB8500_CTRL3, RESET_DENC_N, 1)
+ );
+ } else {
+ ab8500_wr_fld(&pdev->dev, AB8500_DENC_CONF6, SOFT_RESET, 1);
+ mdelay(10);
+ }
+}
+EXPORT_SYMBOL(ab8500_denc_reset);
+
+void ab8500_denc_power_up(struct platform_device *pdev)
+{
+ setup_27mhz(pdev, true);
+}
+EXPORT_SYMBOL(ab8500_denc_power_up);
+
+void ab8500_denc_power_down(struct platform_device *pdev)
+{
+ setup_27mhz(pdev, false);
+}
+EXPORT_SYMBOL(ab8500_denc_power_down);
+
+void ab8500_denc_conf(struct platform_device *pdev,
+ struct ab8500_denc_conf *conf)
+{
+ u8 data;
+
+ AB8500_DENC_TRACE(pdev);
+
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF0,
+ AB8500_VAL2REG(AB8500_DENC_CONF0, STD, map_tv_std(conf->TV_std))
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF0, SYNC,
+ conf->test_pattern ? AB8500_DENC_CONF0_SYNC_AUTO_TEST :
+ AB8500_DENC_CONF0_SYNC_F_BASED_SLAVE
+ )
+ );
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF1,
+ AB8500_VAL2REG(AB8500_DENC_CONF1, BLK_LI,
+ !conf->partial_blanking)
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF1, FLT,
+ map_cr_filter(conf->cr_filter))
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF1, CO_KI, conf->suppress_col)
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF1, SETUP_MAIN,
+ conf->black_level_setup)
+ /* TODO: handle cc field: set to 0 now */
+ );
+
+ data = ab8500_rreg(&pdev->dev, AB8500_DENC_CONF2);
+ data = ab8500_set_fld(data, AB8500_DENC_CONF2, N_INTRL,
+ conf->progressive);
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF2, data);
+
+ ab8500_wreg(&pdev->dev, AB8500_DENC_CONF8,
+ AB8500_VAL2REG(AB8500_DENC_CONF8, PH_RST_MODE,
+ map_phase_rst_mode(conf->phase_reset_mode))
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF8, VAL_422_MUX,
+ conf->act_output)
+ |
+ AB8500_VAL2REG(AB8500_DENC_CONF8, BLK_ALL,
+ conf->blank_all)
+ );
+ data = ab8500_rreg(&pdev->dev, AB8500_TVOUT_CTRL);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, DAC_CTRL0,
+ conf->dac_enable);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, DAC_CTRL1,
+ conf->act_dc_output);
+ ab8500_wreg(&pdev->dev, AB8500_TVOUT_CTRL, data);
+
+ /* no support for DDR in early versions */
+ if (AB8500_REG2VAL(AB8500_REV, FULL_MASK,
+ ab8500_rreg(&pdev->dev, AB8500_REV)) > 0)
+ ab8500_denc_conf_ddr(pdev);
+}
+EXPORT_SYMBOL(ab8500_denc_conf);
+
+void ab8500_denc_conf_plug_detect(struct platform_device *pdev,
+ bool enable, bool load_RC,
+ enum ab8500_denc_plug_time time)
+{
+ u8 data;
+
+ AB8500_DENC_TRACE(pdev);
+ data = ab8500_rreg(&pdev->dev, AB8500_TVOUT_CTRL);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, TV_PLUG_ON, enable);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, TV_LOAD_RC, load_RC);
+ data = ab8500_set_fld(data, AB8500_TVOUT_CTRL, PLUG_TV_TIME,
+ map_plug_time(time));
+ ab8500_wreg(&pdev->dev, AB8500_TVOUT_CTRL, data);
+}
+EXPORT_SYMBOL(ab8500_denc_conf_plug_detect);
+
+void ab8500_denc_mask_int_plug_det(struct platform_device *pdev, bool plug,
+ bool unplug)
+{
+ u8 data = ab8500_rreg(&pdev->dev, AB8500_IT_MASK1);
+
+ AB8500_DENC_TRACE(pdev);
+ data = ab8500_set_fld(data, AB8500_IT_MASK1, PLUG_TV_DET, plug);
+ data = ab8500_set_fld(data, AB8500_IT_MASK1, UNPLUG_TV_DET, unplug);
+ ab8500_wreg(&pdev->dev, AB8500_IT_MASK1, data);
+}
+EXPORT_SYMBOL(ab8500_denc_mask_int_plug_det);
+
+static void ab8500_denc_conf_ddr(struct platform_device *pdev)
+{
+ struct ab8500_platform_data *core_pdata;
+ struct ab8500_denc_platform_data *denc_pdata;
+
+ AB8500_DENC_TRACE(pdev);
+ core_pdata = dev_get_platdata(pdev->dev.parent);
+ denc_pdata = core_pdata->denc;
+ ab8500_wreg(&pdev->dev, AB8500_TVOUT_CTRL2,
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL2,
+ DENC_DDR, denc_pdata->ddr_enable) |
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL2, SWAP_DDR_DATA_IN,
+ denc_pdata->ddr_little_endian));
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int debugfs_ab8500_open_file(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUG_BUF_SIZE 900
+
+#define AB8500_GPIO_DIR5 0x1014
+#define AB8500_GPIO_DIR5_35_SHIFT 2
+#define AB8500_GPIO_DIR5_35_MASK (1 << AB8500_GPIO_DIR5_35_SHIFT)
+#define AB8500_GPIO_OUT5 0x1024
+#define AB8500_GPIO_OUT5_35_SHIFT 2
+#define AB8500_GPIO_OUT5_35_MASK (1 << AB8500_GPIO_OUT5_35_SHIFT)
+#define AB8500_GPIO_OUT5_35_VIDEO 0
+#define AB8500_GPIO_OUT5_35_AUDIO 1
+#define AB8500_GPIO_NPUD5 0x1034
+#define AB8500_GPIO_NPUD5_35_SHIFT 2
+#define AB8500_GPIO_NPUD5_35_MASK (1 << AB8500_GPIO_NPUD5_35_SHIFT)
+#define AB8500_GPIO_NPUD5_35_ACTIVE 0
+#define AB8500_GPIO_NPUD5_35_INACTIVE 1
+
+static ssize_t debugfs_ab8500_dump_regs(struct file *file, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int ret = 0;
+ size_t data_size = 0;
+ char buffer[DEBUG_BUF_SIZE];
+ struct device *dev = file->private_data;
+
+ data_size += sprintf(buffer + data_size,
+ "AB8500 DENC registers:\n"
+ "------Regulators etc ----------\n"
+ "CTRL3 : 0x%04x = 0x%02x\n"
+ "SYSULPCLK_CONF: 0x%04x = 0x%02x\n"
+ "SYSCLK_CTRL : 0x%04x = 0x%02x\n"
+ "REGU_MISC1 : 0x%04x = 0x%02x\n"
+ "VAUX12_REGU : 0x%04x = 0x%02x\n"
+ "VAUX1_SEL1 : 0x%04x = 0x%02x\n"
+ "------TVout only --------------\n"
+ "DENC_CONF0 : 0x%04x = 0x%02x\n"
+ "DENC_CONF1 : 0x%04x = 0x%02x\n"
+ "DENC_CONF2 : 0x%04x = 0x%02x\n"
+ "DENC_CONF6 : 0x%04x = 0x%02x\n"
+ "DENC_CONF8 : 0x%04x = 0x%02x\n"
+ "TVOUT_CTRL : 0x%04x = 0x%02x\n"
+ "TVOUT_CTRL2 : 0x%04x = 0x%02x\n"
+ "IT_MASK1 : 0x%04x = 0x%02x\n"
+ "------AV connector-------------\n"
+ "GPIO_DIR5 : 0x%04x = 0x%02x\n"
+ "GPIO_OUT5 : 0x%04x = 0x%02x\n"
+ "GPIO_NPUD5 : 0x%04x = 0x%02x\n"
+ ,
+ AB8500_CTRL3, ab8500_rreg(dev, AB8500_CTRL3),
+ AB8500_SYS_ULP_CLK_CONF, ab8500_rreg(dev,
+ AB8500_SYS_ULP_CLK_CONF),
+ AB8500_SYS_CLK_CTRL, ab8500_rreg(dev, AB8500_SYS_CLK_CTRL),
+ AB8500_REGU_MISC1, ab8500_rreg(dev, AB8500_REGU_MISC1),
+ AB8500_VAUX12_REGU, ab8500_rreg(dev, AB8500_VAUX12_REGU),
+ AB8500_VAUX1_SEL, ab8500_rreg(dev, AB8500_VAUX1_SEL),
+ AB8500_DENC_CONF0, ab8500_rreg(dev, AB8500_DENC_CONF0),
+ AB8500_DENC_CONF1, ab8500_rreg(dev, AB8500_DENC_CONF1),
+ AB8500_DENC_CONF2, ab8500_rreg(dev, AB8500_DENC_CONF2),
+ AB8500_DENC_CONF6, ab8500_rreg(dev, AB8500_DENC_CONF6),
+ AB8500_DENC_CONF8, ab8500_rreg(dev, AB8500_DENC_CONF8),
+ AB8500_TVOUT_CTRL, ab8500_rreg(dev, AB8500_TVOUT_CTRL),
+ AB8500_TVOUT_CTRL2, ab8500_rreg(dev, AB8500_TVOUT_CTRL2),
+ AB8500_IT_MASK1, ab8500_rreg(dev, AB8500_IT_MASK1),
+ AB8500_GPIO_DIR5, ab8500_rreg(dev, AB8500_GPIO_DIR5),
+ AB8500_GPIO_OUT5, ab8500_rreg(dev, AB8500_GPIO_OUT5),
+ AB8500_GPIO_NPUD5, ab8500_rreg(dev, AB8500_GPIO_NPUD5)
+ );
+ if (data_size >= DEBUG_BUF_SIZE) {
+ printk(KERN_EMERG "AB8500 DENC: Buffer overrun\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* check if read done */
+ if (*f_pos > data_size)
+ goto out;
+
+ if (*f_pos + count > data_size)
+ count = data_size - *f_pos;
+
+ if (copy_to_user(buf, buffer + *f_pos, count))
+ ret = -EINVAL;
+ *f_pos += count;
+ ret = count;
+out:
+ return ret;
+}
+#endif /* CONFIG_DEBUG_FS */
+
+/* Module init */
+static int __init ab8500_denc_init(void)
+{
+ return platform_driver_register(&ab8500_denc_driver);
+}
+module_init(ab8500_denc_init);
+
+static void __exit ab8500_denc_exit(void)
+{
+ platform_driver_unregister(&ab8500_denc_driver);
+}
+module_exit(ab8500_denc_exit);
+
+MODULE_AUTHOR("Marcel Tunnissen <marcel.tuennissen@stericsson.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 DENC driver");
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index c39fc716e1d..d06f4826619 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -12,6 +12,7 @@
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
@@ -82,6 +83,11 @@
/* This is used to not lose precision when dividing to get gain and offset */
#define CALIB_SCALE 1000
+/* Time in ms before disabling regulator */
+#define GPADC_AUDOSUSPEND_DELAY 1
+
+#define CONVERSION_TIME 500 /* ms */
+
enum cal_channels {
ADC_INPUT_VMAIN = 0,
ADC_INPUT_BTEMP,
@@ -102,10 +108,10 @@ struct adc_cal_data {
/**
* struct ab8500_gpadc - AB8500 GPADC device information
- * @chip_id ABB chip id
* @dev: pointer to the struct device
* @node: a list of AB8500 GPADCs, hence prepared for
reentrance
+ * @parent: pointer to the struct ab8500
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
* the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex
@@ -114,9 +120,9 @@ struct adc_cal_data {
* @cal_data array of ADC calibration data structs
*/
struct ab8500_gpadc {
- u8 chip_id;
struct device *dev;
struct list_head node;
+ struct ab8500 *parent;
struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock;
struct regulator *regu;
@@ -282,8 +288,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
return -ENODEV;
mutex_lock(&gpadc->ab8500_gpadc_lock);
+
/* Enable VTVout LDO this is required for GPADC */
- regulator_enable(gpadc->regu);
+ pm_runtime_get_sync(gpadc->dev);
/* Check if ADC is not busy, lock and proceed */
do {
@@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
EN_BUF | EN_ICHAR);
break;
case BTEMP_BALL:
- if (gpadc->chip_id >= AB8500_CUT3P0) {
+ if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
/* Turn on btemp pull-up on ABB 3.0 */
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
@@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
* Delay might be needed for ABB8500 cut 3.0, if not, remove
* when hardware will be availible
*/
- msleep(1);
+ mdelay(1);
break;
}
/* Intentional fallthrough */
@@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out;
}
/* wait for completion of conversion */
- if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
+ if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
+ msecs_to_jiffies(CONVERSION_TIME))) {
dev_err(gpadc->dev,
"timeout: didn't receive GPADC conversion interrupt\n");
ret = -EINVAL;
@@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
goto out;
}
- /* Disable VTVout LDO this is required for GPADC */
- regulator_disable(gpadc->regu);
+
+ pm_runtime_mark_last_busy(gpadc->dev);
+ pm_runtime_put_autosuspend(gpadc->dev);
+
mutex_unlock(&gpadc->ab8500_gpadc_lock);
return (high_data << 8) | low_data;
@@ -412,7 +422,9 @@ out:
*/
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
- regulator_disable(gpadc->regu);
+
+ pm_runtime_put(gpadc->dev);
+
mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
@@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
gpadc->cal_data[ADC_INPUT_VBAT].offset);
}
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ regulator_disable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_runtime_resume(struct device *dev)
+{
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
+
+ regulator_enable(gpadc->regu);
+ return 0;
+}
+
+static int ab8500_gpadc_runtime_idle(struct device *dev)
+{
+ pm_runtime_suspend(dev);
+ return 0;
+}
+
static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -591,6 +625,7 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
}
gpadc->dev = &pdev->dev;
+ gpadc->parent = dev_get_drvdata(pdev->dev.parent);
mutex_init(&gpadc->ab8500_gpadc_lock);
/* Initialize completion used to notify completion of conversion */
@@ -606,14 +641,6 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
goto fail;
}
- /* Get Chip ID of the ABB ASIC */
- ret = abx500_get_chip_id(gpadc->dev);
- if (ret < 0) {
- dev_err(gpadc->dev, "failed to get chip ID\n");
- goto fail_irq;
- }
- gpadc->chip_id = (u8) ret;
-
/* VTVout LDO used to power up ab8500-GPADC */
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
if (IS_ERR(gpadc->regu)) {
@@ -621,6 +648,16 @@ static int __devinit ab8500_gpadc_probe(struct platform_device *pdev)
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
goto fail_irq;
}
+
+ platform_set_drvdata(pdev, gpadc);
+
+ regulator_enable(gpadc->regu);
+
+ pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(gpadc->dev);
+ pm_runtime_set_active(gpadc->dev);
+ pm_runtime_enable(gpadc->dev);
+
ab8500_gpadc_read_calibration_data(gpadc);
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
dev_dbg(gpadc->dev, "probe success\n");
@@ -641,19 +678,34 @@ static int __devexit ab8500_gpadc_remove(struct platform_device *pdev)
list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */
free_irq(gpadc->irq, gpadc);
- /* disable VTVout LDO that is being used by GPADC */
- regulator_put(gpadc->regu);
+
+ pm_runtime_get_sync(gpadc->dev);
+ pm_runtime_disable(gpadc->dev);
+
+ regulator_disable(gpadc->regu);
+
+ pm_runtime_set_suspended(gpadc->dev);
+
+ pm_runtime_put_noidle(gpadc->dev);
+
kfree(gpadc);
gpadc = NULL;
return 0;
}
+static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
+ SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
+ ab8500_gpadc_runtime_resume,
+ ab8500_gpadc_runtime_idle)
+};
+
static struct platform_driver ab8500_gpadc_driver = {
.probe = ab8500_gpadc_probe,
.remove = __devexit_p(ab8500_gpadc_remove),
.driver = {
.name = "ab8500-gpadc",
.owner = THIS_MODULE,
+ .pm = &ab8500_gpadc_pm_ops,
},
};
diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c
index b83045f102b..5ee90fd125e 100644
--- a/drivers/mfd/ab8500-i2c.c
+++ b/drivers/mfd/ab8500-i2c.c
@@ -13,6 +13,7 @@
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/dbx500-prcmu.h>
+
static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)
{
int ret;
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index c28d4eb1eff..d5865d41514 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -7,12 +7,114 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/reboot.h>
+#include <linux/signal.h>
+#include <linux/power_supply.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/time.h>
+#include <linux/hwmon.h>
static struct device *sysctrl_dev;
+void ab8500_power_off(void)
+{
+ struct ab8500_platform_data *plat;
+ struct timespec ts;
+ sigset_t old;
+ sigset_t all;
+ static char *pss[] = {"ab8500_ac", "ab8500_usb"};
+ int i;
+ bool charger_present = false;
+ union power_supply_propval val;
+ struct power_supply *psy;
+ int ret;
+
+ /*
+ * If we have a charger connected and we're powering off,
+ * reboot into charge-only mode.
+ */
+
+ for (i = 0; i < ARRAY_SIZE(pss); i++) {
+ psy = power_supply_get_by_name(pss[i]);
+ if (!psy)
+ continue;
+
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
+
+ if (!ret && val.intval) {
+ charger_present = true;
+ break;
+ }
+ }
+
+ if (!charger_present)
+ goto shutdown;
+
+ /* Check if battery is known */
+ psy = power_supply_get_by_name("ab8500_btemp");
+ if (psy) {
+ ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY,
+ &val);
+ if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
+ printk(KERN_INFO
+ "Charger \"%s\" is connected with known battery."
+ " Rebooting.\n",
+ pss[i]);
+ machine_restart("charging");
+ }
+ }
+
+shutdown:
+ sigfillset(&all);
+
+ plat = dev_get_platdata(sysctrl_dev->parent);
+ getnstimeofday(&ts);
+ if (!sigprocmask(SIG_BLOCK, &all, &old)) {
+ if (ts.tv_sec == 0 ||
+ (ts.tv_sec - plat->thermal_set_time_sec >
+ plat->thermal_time_out))
+ plat->thermal_power_off_pending = false;
+ if (!plat->thermal_power_off_pending) {
+ (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
+ AB8500_STW4500CTRL1_SWOFF |
+ AB8500_STW4500CTRL1_SWRESET4500N);
+ (void)sigprocmask(SIG_SETMASK, &old, NULL);
+ } else {
+ (void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
+ AB8500_STW4500CTRL1_THDB8500SWOFF |
+ AB8500_STW4500CTRL1_SWRESET4500N);
+ (void)sigprocmask(SIG_SETMASK, &old, NULL);
+ }
+ }
+}
+
+static int ab8500_notifier_call(struct notifier_block *this,
+ unsigned long val, void *data)
+{
+ struct ab8500_platform_data *plat;
+ static struct timespec ts;
+ if (sysctrl_dev == NULL)
+ return -EAGAIN;
+
+ plat = dev_get_platdata(sysctrl_dev->parent);
+ if (val) {
+ getnstimeofday(&ts);
+ plat->thermal_set_time_sec = ts.tv_sec;
+ plat->thermal_power_off_pending = true;
+ } else {
+ plat->thermal_set_time_sec = 0;
+ plat->thermal_power_off_pending = false;
+ }
+ return 0;
+}
+
+static struct notifier_block ab8500_notifier = {
+ .notifier_call = ab8500_notifier_call,
+};
+
static inline bool valid_bank(u8 bank)
{
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
@@ -33,6 +135,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
return abx500_get_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), value);
}
+EXPORT_SYMBOL(ab8500_sysctrl_read);
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
{
@@ -48,10 +151,42 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
(u8)(reg & 0xFF), mask, value);
}
+EXPORT_SYMBOL(ab8500_sysctrl_write);
static int __devinit ab8500_sysctrl_probe(struct platform_device *pdev)
{
+ struct ab8500_platform_data *plat;
+ struct ab8500_sysctrl_platform_data *pdata;
+
sysctrl_dev = &pdev->dev;
+ plat = dev_get_platdata(pdev->dev.parent);
+ if (plat->pm_power_off)
+ pm_power_off = ab8500_power_off;
+ hwmon_notifier_register(&ab8500_notifier);
+
+ pdata = plat->sysctrl;
+
+ if (pdata) {
+ int ret;
+ int i;
+ int j;
+ for (i = AB8500_SYSCLKREQ1RFCLKBUF;
+ i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
+ j = i - AB8500_SYSCLKREQ1RFCLKBUF;
+ ret = ab8500_sysctrl_write(i, 0xff,
+ pdata->initial_req_buf_config[j]);
+ dev_dbg(&pdev->dev,
+ "Setting SysClkReq%dRfClkBuf 0x%X\n",
+ j + 1,
+ pdata->initial_req_buf_config[j]);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "unable to set sysClkReq%dRfClkBuf: "
+ "%d\n", j + 1, ret);
+ }
+ }
+ }
+
return 0;
}
diff --git a/drivers/mfd/abx500-core.c b/drivers/mfd/abx500-core.c
index 7ce65f49480..9818afba251 100644
--- a/drivers/mfd/abx500-core.c
+++ b/drivers/mfd/abx500-core.c
@@ -153,6 +153,22 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
}
EXPORT_SYMBOL(abx500_startup_irq_enabled);
+void abx500_dump_all_banks(void)
+{
+ struct abx500_ops *ops;
+ struct device dummy_child = {0};
+ struct abx500_device_entry *dev_entry;
+
+ list_for_each_entry(dev_entry, &abx500_list, list) {
+ dummy_child.parent = dev_entry->dev;
+ ops = &dev_entry->ops;
+
+ if ((ops != NULL) && (ops->dump_all_banks != NULL))
+ ops->dump_all_banks(&dummy_child);
+ }
+}
+EXPORT_SYMBOL(abx500_dump_all_banks);
+
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
MODULE_DESCRIPTION("ABX500 core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h
new file mode 100644
index 00000000000..0428b5e95ae
--- /dev/null
+++ b/drivers/mfd/db5500-prcmu-regs.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __MACH_PRCMU_REGS_DB5500_H
+#define __MACH_PRCMU_REGS_DB5500_H
+
+#define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end))
+
+#define PRCM_TCR 0x1C8
+#define PRCM_TCR_TENSEL_MASK BITS(0, 7)
+#define PRCM_TCR_STOP_TIMERS BIT(16)
+#define PRCM_TCR_DOZE_MODE BIT(17)
+
+/* PRCMU HW semaphore */
+#define PRCM_SEM 0x400
+#define PRCM_SEM_PRCM_SEM BIT(0)
+
+#define DB5500_PRCM_ACLK_MGT 0x004
+#define DB5500_PRCM_SVACLK_MGT 0x008
+#define DB5500_PRCM_SIACLK_MGT 0x00C
+#define DB5500_PRCM_SGACLK_MGT 0x014
+#define DB5500_PRCM_UARTCLK_MGT 0x018
+#define DB5500_PRCM_MSP02CLK_MGT 0x01C
+#define DB5500_PRCM_I2CCLK_MGT 0x020
+#define DB5500_PRCM_SDMMCCLK_MGT 0x024
+#define DB5500_PRCM_PER1CLK_MGT 0x02C
+#define DB5500_PRCM_PER2CLK_MGT 0x030
+#define DB5500_PRCM_PER3CLK_MGT 0x034
+#define DB5500_PRCM_PER5CLK_MGT 0x038
+#define DB5500_PRCM_PER6CLK_MGT 0x03C
+#define DB5500_PRCM_IRDACLK_MGT 0x040
+#define DB5500_PRCM_PWMCLK_MGT 0x044
+#define DB5500_PRCM_SPARE1CLK_MGT 0x048
+#define DB5500_PRCM_IRRCCLK_MGT 0x04C
+#define DB5500_PRCM_HDMICLK_MGT 0x058
+#define DB5500_PRCM_APEATCLK_MGT 0x05C
+#define DB5500_PRCM_APETRACECLK_MGT 0x060
+#define DB5500_PRCM_MCDECLK_MGT 0x064
+#define DB5500_PRCM_DSIALTCLK_MGT 0x06C
+#define DB5500_PRCM_DMACLK_MGT 0x074
+#define DB5500_PRCM_B2R2CLK_MGT 0x078
+#define DB5500_PRCM_TVCLK_MGT 0x07C
+#define DB5500_PRCM_RNGCLK_MGT 0x284
+
+#define PRCM_CLK_MGT_CLKPLLDIV_MASK BITS(0, 4)
+#define PRCM_CLK_MGT_CLKPLLDIV_SHIFT 0
+#define PRCM_CLK_MGT_CLKPLLSW_MASK BITS(5, 7)
+#define PRCM_CLK_MGT_CLKEN BIT(8)
+
+#define PRCM_ARM_IT1_CLEAR 0x48C
+#define PRCM_ARM_IT1_VAL 0x494
+
+/* CPU mailbox registers */
+#define PRCM_MBOX_CPU_VAL 0x0FC
+#define PRCM_MBOX_CPU_SET 0x100
+
+/* System reset register */
+#define PRCM_APE_SOFTRST 0x228
+
+/* PRCMU clock/PLL/reset registers */
+#define PRCM_PLLDSI_FREQ 0x500
+#define PRCM_PLLDSI_ENABLE 0x504
+#define PRCM_PLLDSI_LOCKP 0x508
+#define PRCM_DSI_PLLOUT_SEL 0x530
+#define PRCM_DSITVCLK_DIV 0x52C
+#define PRCM_APE_RESETN_SET 0x1E4
+#define PRCM_APE_RESETN_CLR 0x1E8
+
+/* CLKOUTx SEL0 settings */
+#define CLKOUT_SEL0_REF_CLK 0x01 /* 0b 0001 */
+#define CLKOUT_SEL0_RTC_CLK0 0x02 /* 0b 0010 */
+#define CLKOUT_SEL0_ULP_CLK 0x04 /* 0b 0100 */
+#define CLKOUT_SEL0_SEL_CLK 0x08 /* 0b 1000 */
+
+/* CLKOUTx SEL settings */
+#define CLKOUT_SEL_STATIC0 0x0001 /* 0b 00 0000 0001 */
+#define CLKOUT_SEL_REFCLK 0x0002 /* 0b 00 0000 0010 */
+#define CLKOUT_SEL_ULPCLK 0x0004 /* 0b 00 0000 0100 */
+#define CLKOUT_SEL_ARMCLK 0x0008 /* 0b 00 0000 1000 */
+#define CLKOUT_SEL_SYSACC0CLK 0x0010 /* 0b 00 0001 0000 */
+#define CLKOUT_SEL_SOC0PLLCLK 0x0020 /* 0b 00 0010 0000 */
+#define CLKOUT_SEL_SOC1PLLCLK 0x0040 /* 0b 00 0100 0000 */
+#define CLKOUT_SEL_DDRPLLCLK 0x0080 /* 0b 00 1000 0000 */
+#define CLKOUT_SEL_TVCLK 0x0100 /* 0b 01 0000 0000 */
+#define CLKOUT_SEL_IRDACLK 0x0200 /* 0b 10 0000 0000 */
+
+/* CLKOUTx dividers */
+#define CLKOUT_DIV_2 0x00 /* 0b 000 */
+#define CLKOUT_DIV_4 0x01 /* 0b 001 */
+#define CLKOUT_DIV_8 0x02 /* 0b 010 */
+#define CLKOUT_DIV_16 0x03 /* 0b 011 */
+#define CLKOUT_DIV_32 0x04 /* 0b 100 */
+#define CLKOUT_DIV_64 0x05 /* 0b 101 */
+/* Values 0x06 and 0x07 will also set the CLKOUTx divider to 64. */
+
+/* PRCM_CLKOCR CLKOUTx Control registers */
+#define PRCM_CLKOCR 0x1CC
+#define PRCM_CLKOCR_CLKOUT0_SEL0_SHIFT 0
+#define PRCM_CLKOCR_CLKOUT0_SEL0_MASK BITS(0, 3)
+#define PRCM_CLKOCR_CLKOUT0_SEL_SHIFT 4
+#define PRCM_CLKOCR_CLKOUT0_SEL_MASK BITS(4, 13)
+#define PRCM_CLKOCR_CLKOUT1_SEL0_SHIFT 16
+#define PRCM_CLKOCR_CLKOUT1_SEL0_MASK BITS(16, 19)
+#define PRCM_CLKOCR_CLKOUT1_SEL_SHIFT 20
+#define PRCM_CLKOCR_CLKOUT1_SEL_MASK BITS(20, 29)
+
+/* PRCM_CLKODIV CLKOUTx Dividers */
+#define PRCM_CLKODIV 0x188
+#define PRCM_CLKODIV_CLKOUT0_DIV_SHIFT 0
+#define PRCM_CLKODIV_CLKOUT0_DIV_MASK BITS(0, 2)
+#define PRCM_CLKODIV_CLKOUT1_DIV_SHIFT 16
+#define PRCM_CLKODIV_CLKOUT1_DIV_MASK BITS(16, 18)
+
+#define PRCM_MMIP_LS_CLAMP_SET 0x420
+#define PRCM_MMIP_LS_CLAMP_CLR 0x424
+#define PRCM_DDR_SUBSYS_APE_MINBW 0x438
+
+/* Miscellaneous unit registers */
+#define PRCM_DSI_SW_RESET 0x324
+#define PRCM_RESOUTN_SET_OFFSET 0x214
+#define PRCM_RESOUTN_CLR_OFFSET 0x218
+
+/* APE - Modem Registers */
+#define PRCM_HOSTACCESS_REQ 0x334
+/* APE - Modem register bit maipulation */
+#define PRCM_HOSTACCESS_REQ_BIT BIT(0)
+#define PRCM_APE_ACK 0x49c
+#define PRCM_APE_ACK_BIT 0x01
+
+/* Watchdog - mtimer registers */
+#define PRCM_TIMER0_RTOS_COMP1_OFFSET 0x4C
+#define PRCM_TIMER0_RTOS_COUNTER_OFFSET 0x40
+#define PRCM_TIMER0_IRQ_EN_SET_OFFSET 0x70
+#define PRCM_TIMER0_IRQ_EN_CLR_OFFSET 0x6C
+#define PRCM_TIMER0_IRQ_RTOS1_SET 0x08
+#define PRCM_TIMER0_IRQ_RTOS1_CLR 0x08
+
+#endif
diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c
index bb115b2f04e..b106632d03c 100644
--- a/drivers/mfd/db5500-prcmu.c
+++ b/drivers/mfd/db5500-prcmu.c
@@ -19,12 +19,21 @@
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/regulator/db5500-prcmu.h>
+#include <linux/regulator/machine.h>
#include <linux/interrupt.h>
#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db5500-regs.h>
-#include "dbx500-prcmu-regs.h"
+#include <mach/prcmu-debug.h>
+
+#include "db5500-prcmu-regs.h"
+
+#define PRCMU_FW_VERSION_OFFSET 0xA4
+#define PRCM_SW_RST_REASON (tcdm_base + 0xFF8) /* 2 bytes */
#define _PRCM_MB_HEADER (tcdm_base + 0xFE8)
#define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0)
@@ -64,6 +73,52 @@
#define PRCM_ACK_MB6 (tcdm_base + 0xF0C)
#define PRCM_ACK_MB7 (tcdm_base + 0xF08)
+/* Share info */
+#define PRCM_SHARE_INFO (tcdm_base + 0xEC8)
+
+#define PRCM_SHARE_INFO_HOTDOG (PRCM_SHARE_INFO + 62)
+
+/* Mailbox 0 REQs */
+#define PRCM_REQ_MB0_AP_POWER_STATE (PRCM_REQ_MB0 + 0x0)
+#define PRCM_REQ_MB0_ULP_CLOCK_STATE (PRCM_REQ_MB0 + 0x1)
+#define PRCM_REQ_MB0_AP_PLL_STATE (PRCM_REQ_MB0 + 0x2)
+#define PRCM_REQ_MB0_DDR_STATE (PRCM_REQ_MB0 + 0x3)
+#define PRCM_REQ_MB0_ESRAM0_STATE (PRCM_REQ_MB0 + 0x4)
+#define PRCM_REQ_MB0_WAKEUP_DBB (PRCM_REQ_MB0 + 0x8)
+#define PRCM_REQ_MB0_WAKEUP_ABB (PRCM_REQ_MB0 + 0xC)
+
+/* Mailbox 0 ACKs */
+#define PRCM_ACK_MB0_AP_PWRSTTR_STATUS (PRCM_ACK_MB0 + 0x0)
+#define PRCM_ACK_MB0_READ_POINTER (PRCM_ACK_MB0 + 0x1)
+#define PRCM_ACK_MB0_WAKEUP_0_DBB (PRCM_ACK_MB0 + 0x4)
+#define PRCM_ACK_MB0_WAKEUP_0_ABB (PRCM_ACK_MB0 + 0x8)
+#define PRCM_ACK_MB0_WAKEUP_1_DBB (PRCM_ACK_MB0 + 0x28)
+#define PRCM_ACK_MB0_WAKEUP_1_ABB (PRCM_ACK_MB0 + 0x2C)
+#define PRCM_ACK_MB0_EVENT_ABB_NUMBERS 20
+
+/* Request mailbox 1 fields. */
+#define PRCM_REQ_MB1_ARM_OPP (PRCM_REQ_MB1 + 0x0)
+#define PRCM_REQ_MB1_APE_OPP (PRCM_REQ_MB1 + 0x1)
+
+/* Mailbox 1 ACKs */
+#define PRCM_ACK_MB1_CURRENT_ARM_OPP (PRCM_ACK_MB1 + 0x0)
+#define PRCM_ACK_MB1_CURRENT_APE_OPP (PRCM_ACK_MB1 + 0x1)
+#define PRCM_ACK_MB1_ARM_VOLT_STATUS (PRCM_ACK_MB1 + 0x2)
+#define PRCM_ACK_MB1_APE_VOLT_STATUS (PRCM_ACK_MB1 + 0x3)
+
+/* Mailbox 2 REQs */
+#define PRCM_REQ_MB2_EPOD_CLIENT (PRCM_REQ_MB2 + 0x0)
+#define PRCM_REQ_MB2_EPOD_STATE (PRCM_REQ_MB2 + 0x1)
+#define PRCM_REQ_MB2_CLK_CLIENT (PRCM_REQ_MB2 + 0x2)
+#define PRCM_REQ_MB2_CLK_STATE (PRCM_REQ_MB2 + 0x3)
+#define PRCM_REQ_MB2_PLL_CLIENT (PRCM_REQ_MB2 + 0x4)
+#define PRCM_REQ_MB2_PLL_STATE (PRCM_REQ_MB2 + 0x5)
+
+/* Mailbox 2 ACKs */
+#define PRCM_ACK_MB2_EPOD_STATUS (PRCM_ACK_MB2 + 0x2)
+#define PRCM_ACK_MB2_CLK_STATUS (PRCM_ACK_MB2 + 0x6)
+#define PRCM_ACK_MB2_PLL_STATUS (PRCM_ACK_MB2 + 0xA)
+
enum mb_return_code {
RC_SUCCESS,
RC_FAIL,
@@ -71,12 +126,58 @@ enum mb_return_code {
/* Mailbox 0 headers. */
enum mb0_header {
- /* request */
- RMB0H_PWR_STATE_TRANS = 1,
- RMB0H_WAKE_UP_CFG,
- RMB0H_RD_WAKE_UP_ACK,
/* acknowledge */
- AMB0H_WAKE_UP = 1,
+ MB0H_WAKE_UP = 0,
+ /* request */
+ MB0H_PWR_STATE_TRANS,
+ MB0H_WAKE_UP_CFG,
+ MB0H_RD_WAKE_UP_ACK,
+};
+
+/* Mailbox 1 headers.*/
+enum mb1_header {
+ MB1H_ARM_OPP = 1,
+ MB1H_APE_OPP,
+ MB1H_ARM_APE_OPP,
+};
+
+/* Mailbox 2 headers. */
+enum mb2_header {
+ MB2H_EPOD_REQUEST = 1,
+ MB2H_CLK_REQUEST,
+ MB2H_PLL_REQUEST,
+};
+
+/* Mailbox 3 headers. */
+enum mb3_header {
+ MB3H_REFCLK_REQUEST = 1,
+};
+
+enum sysclk_state {
+ SYSCLK_OFF,
+ SYSCLK_ON,
+};
+
+/* Mailbox 4 headers */
+enum mb4_header {
+ MB4H_CFG_HOTDOG = 7,
+ MB4H_CFG_HOTMON = 8,
+ MB4H_CFG_HOTPERIOD = 10,
+ MB4H_CGF_MODEM_RESET = 13,
+ MB4H_CGF_A9WDOG_EN_PREBARK = 14,
+ MB4H_CGF_A9WDOG_EN_NOPREBARK = 15,
+ MB4H_CGF_A9WDOG_DIS = 16,
+};
+
+/* Mailbox 4 ACK headers */
+enum mb4_ack_header {
+ MB4H_ACK_CFG_HOTDOG = 5,
+ MB4H_ACK_CFG_HOTMON = 6,
+ MB4H_ACK_CFG_HOTPERIOD = 8,
+ MB4H_ACK_CFG_MODEM_RESET = 11,
+ MB4H_ACK_CGF_A9WDOG_EN_PREBARK = 12,
+ MB4H_ACK_CGF_A9WDOG_EN_NOPREBARK = 13,
+ MB4H_ACK_CGF_A9WDOG_DIS = 14,
};
/* Mailbox 5 headers. */
@@ -85,6 +186,69 @@ enum mb5_header {
MB5H_I2C_READ,
};
+enum db5500_arm_opp {
+ DB5500_ARM_100_OPP = 1,
+ DB5500_ARM_50_OPP,
+ DB5500_ARM_EXT_OPP,
+};
+
+enum db5500_ape_opp {
+ DB5500_APE_100_OPP = 1,
+ DB5500_APE_50_OPP
+};
+
+enum epod_state {
+ EPOD_OFF,
+ EPOD_ON,
+};
+enum epod_onoffret_state {
+ EPOD_OOR_OFF,
+ EPOD_OOR_RET,
+ EPOD_OOR_ON,
+};
+enum db5500_prcmu_pll {
+ DB5500_PLL_SOC0,
+ DB5500_PLL_SOC1,
+ DB5500_PLL_DDR,
+ DB5500_NUM_PLL_ID,
+};
+
+enum db5500_prcmu_clk {
+ DB5500_MSP1CLK,
+ DB5500_CDCLK,
+ DB5500_IRDACLK,
+ DB5500_TVCLK,
+ DB5500_NUM_CLK_CLIENTS,
+};
+
+enum on_off_ret {
+ OFF_ST,
+ RET_ST,
+ ON_ST,
+};
+
+enum db5500_ap_pwr_state {
+ DB5500_AP_SLEEP = 2,
+ DB5500_AP_DEEP_SLEEP,
+ DB5500_AP_IDLE,
+};
+
+/* Request mailbox 3 fields */
+#define PRCM_REQ_MB3_REFCLK_MGT (PRCM_REQ_MB3 + 0x0)
+
+/* Ack. mailbox 3 fields */
+#define PRCM_ACK_MB3_REFCLK_REQ (PRCM_ACK_MB3 + 0x0)
+
+
+/* Request mailbox 4 fields */
+#define PRCM_REQ_MB4_HOTDOG_THRESHOLD (PRCM_REQ_MB4 + 32)
+#define PRCM_REQ_MB4_HOT_PERIOD (PRCM_REQ_MB4 + 34)
+#define PRCM_REQ_MB4_HOTMON_LOW (PRCM_REQ_MB4 + 36)
+#define PRCM_REQ_MB4_HOTMON_HIGH (PRCM_REQ_MB4 + 38)
+
+/* Ack. mailbox 4 field */
+#define PRCM_ACK_MB4_REQUESTS (PRCM_ACK_MB4 + 0x0)
+
/* Request mailbox 5 fields. */
#define PRCM_REQ_MB5_I2C_SLAVE (PRCM_REQ_MB5 + 0)
#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 1)
@@ -105,11 +269,12 @@ enum mb5_header {
#define PRCMU_RESET_DSIPLL 0x00004000
#define PRCMU_UNCLAMP_DSIPLL 0x00400800
-/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0x8, = 50 Mhz*/
-#define PRCMU_DSI_CLOCK_SETTING 0x00000128
+/* HDMI CLK MGT PLLSW=001 (PLLSOC0), PLLDIV=0xC, = 33.33 Mhz*/
+#define PRCMU_DSI_CLOCK_SETTING 0x0000012C
/* TVCLK_MGT PLLSW=001 (PLLSOC0) PLLDIV=0x13, = 19.05 MHZ */
#define PRCMU_DSI_LP_CLOCK_SETTING 0x00000135
-#define PRCMU_PLLDSI_FREQ_SETTING 0x00020121
+/* PRCM_PLLDSI_FREQ R=4, N=1, D= 0x65 */
+#define PRCMU_PLLDSI_FREQ_SETTING 0x00040165
#define PRCMU_DSI_PLLOUT_SEL_SETTING 0x00000002
#define PRCMU_ENABLE_ESCAPE_CLOCK_DIV 0x03000201
#define PRCMU_DISABLE_ESCAPE_CLOCK_DIV 0x00000101
@@ -125,13 +290,176 @@ enum mb5_header {
#define PRCMU_PLLDSI_LOCKP_LOCKED 0x3
/*
+ * Wakeups/IRQs
+ */
+
+#define WAKEUP_BIT_RTC BIT(0)
+#define WAKEUP_BIT_RTT0 BIT(1)
+#define WAKEUP_BIT_RTT1 BIT(2)
+#define WAKEUP_BIT_CD_IRQ BIT(3)
+#define WAKEUP_BIT_SRP_TIM BIT(4)
+#define WAKEUP_BIT_APE_REQ BIT(5)
+#define WAKEUP_BIT_USB BIT(6)
+#define WAKEUP_BIT_ABB BIT(7)
+#define WAKEUP_BIT_LOW_POWER_AUDIO BIT(8)
+#define WAKEUP_BIT_TEMP_SENSOR_LOW BIT(9)
+#define WAKEUP_BIT_ARM BIT(10)
+#define WAKEUP_BIT_AC_WAKE_ACK BIT(11)
+#define WAKEUP_BIT_TEMP_SENSOR_HIGH BIT(12)
+#define WAKEUP_BIT_MODEM_SW_RESET_REQ BIT(20)
+#define WAKEUP_BIT_GPIO0 BIT(23)
+#define WAKEUP_BIT_GPIO1 BIT(24)
+#define WAKEUP_BIT_GPIO2 BIT(25)
+#define WAKEUP_BIT_GPIO3 BIT(26)
+#define WAKEUP_BIT_GPIO4 BIT(27)
+#define WAKEUP_BIT_GPIO5 BIT(28)
+#define WAKEUP_BIT_GPIO6 BIT(29)
+#define WAKEUP_BIT_GPIO7 BIT(30)
+#define WAKEUP_BIT_AC_REL_ACK BIT(30)
+
+/*
+ * This vector maps irq numbers to the bits in the bit field used in
+ * communication with the PRCMU firmware.
+ *
+ * The reason for having this is to keep the irq numbers contiguous even though
+ * the bits in the bit field are not. (The bits also have a tendency to move
+ * around, to further complicate matters.)
+ */
+#define IRQ_INDEX(_name) ((IRQ_DB5500_PRCMU_##_name) - IRQ_DB5500_PRCMU_BASE)
+#define IRQ_ENTRY(_name)[IRQ_INDEX(_name)] = (WAKEUP_BIT_##_name)
+static u32 prcmu_irq_bit[NUM_DB5500_PRCMU_WAKEUPS] = {
+ IRQ_ENTRY(RTC),
+ IRQ_ENTRY(RTT0),
+ IRQ_ENTRY(RTT1),
+ IRQ_ENTRY(CD_IRQ),
+ IRQ_ENTRY(SRP_TIM),
+ IRQ_ENTRY(APE_REQ),
+ IRQ_ENTRY(USB),
+ IRQ_ENTRY(ABB),
+ IRQ_ENTRY(LOW_POWER_AUDIO),
+ IRQ_ENTRY(TEMP_SENSOR_LOW),
+ IRQ_ENTRY(TEMP_SENSOR_HIGH),
+ IRQ_ENTRY(ARM),
+ IRQ_ENTRY(AC_WAKE_ACK),
+ IRQ_ENTRY(MODEM_SW_RESET_REQ),
+ IRQ_ENTRY(GPIO0),
+ IRQ_ENTRY(GPIO1),
+ IRQ_ENTRY(GPIO2),
+ IRQ_ENTRY(GPIO3),
+ IRQ_ENTRY(GPIO4),
+ IRQ_ENTRY(GPIO5),
+ IRQ_ENTRY(GPIO6),
+ IRQ_ENTRY(GPIO7),
+ IRQ_ENTRY(AC_REL_ACK),
+};
+
+#define VALID_WAKEUPS (BIT(NUM_PRCMU_WAKEUP_INDICES) - 1)
+#define WAKEUP_ENTRY(_name)[PRCMU_WAKEUP_INDEX_##_name] = (WAKEUP_BIT_##_name)
+static u32 prcmu_wakeup_bit[NUM_PRCMU_WAKEUP_INDICES] = {
+ WAKEUP_ENTRY(RTC),
+ WAKEUP_ENTRY(RTT0),
+ WAKEUP_ENTRY(RTT1),
+ WAKEUP_ENTRY(CD_IRQ),
+ WAKEUP_ENTRY(USB),
+ WAKEUP_ENTRY(ABB),
+ WAKEUP_ENTRY(ARM)
+};
+
+/*
* mb0_transfer - state needed for mailbox 0 communication.
- * @lock: The transaction lock.
+ * @lock The transaction lock.
+ * @dbb_irqs_lock lock used for (un)masking DBB wakeup interrupts
+ * @mask_work: Work structure used for (un)masking wakeup interrupts.
+ * @ac_wake_lock: mutex to lock modem_req and modem_rel
+ * @req: Request data that need to persist between requests.
*/
static struct {
spinlock_t lock;
+ spinlock_t dbb_irqs_lock;
+ struct work_struct mask_work;
+ struct mutex ac_wake_lock;
+ struct {
+ u32 dbb_irqs;
+ u32 dbb_wakeups;
+ u32 abb_events;
+ } req;
} mb0_transfer;
+
+/*
+ * mb1_transfer - state needed for mailbox 1 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req_arm_opp Requested arm opp
+ * @req_ape_opp Requested ape opp
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ u8 req_arm_opp;
+ u8 req_ape_opp;
+ struct {
+ u8 header;
+ u8 arm_opp;
+ u8 ape_opp;
+ u8 arm_voltage_st;
+ u8 ape_voltage_st;
+ } ack;
+} mb1_transfer;
+
+/*
+ * mb2_transfer - state needed for mailbox 2 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @req: Request data that need to persist between requests.
+ * @ack: Reply ("acknowledge") data.
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 epod_st[DB5500_NUM_EPOD_ID];
+ u8 pll_st[DB5500_NUM_PLL_ID];
+ } req;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb2_transfer;
+
+/*
+ * mb3_transfer - state needed for mailbox 3 communication.
+ * @sysclk_lock: A lock used to handle concurrent sysclk requests.
+ * @sysclk_work: Work structure used for sysclk requests.
+ * @req_st: Requested clock state.
+ * @ack: Acknowledgement data
+ */
+static struct {
+ struct mutex sysclk_lock;
+ struct completion sysclk_work;
+ enum sysclk_state req_st;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb3_transfer;
+
+/*
+ * mb4_transfer - state needed for mailbox 4 communication.
+ * @lock: The transaction lock.
+ * @work: The transaction completion structure.
+ * @ack: Acknowledgement data
+ */
+static struct {
+ struct mutex lock;
+ struct completion work;
+ struct {
+ u8 header;
+ u8 status;
+ } ack;
+} mb4_transfer;
+
/*
* mb5_transfer - state needed for mailbox 5 communication.
* @lock: The transaction lock.
@@ -148,9 +476,825 @@ static struct {
} ack;
} mb5_transfer;
-/* PRCMU TCDM base IO address. */
+/* Spinlocks */
+static DEFINE_SPINLOCK(clkout_lock);
+
+/* PRCMU TCDM base IO address */
static __iomem void *tcdm_base;
+/* PRCMU MTIMER base IO address */
+static __iomem void *mtimer_base;
+
+struct clk_mgt {
+ unsigned int offset;
+ u32 pllsw;
+ u32 div;
+ bool scalable;
+ bool force50;
+};
+
+/* PRCMU Firmware Details */
+static struct {
+ u16 board;
+ u8 fw_version;
+ u8 api_version;
+} prcmu_version;
+
+static struct {
+ u32 timeout;
+ bool enabled;
+} a9wdog_timer;
+
+static DEFINE_SPINLOCK(clk_mgt_lock);
+
+#define CLK_MGT_ENTRY(_name, _scalable)[PRCMU_##_name] = { \
+ .offset = DB5500_PRCM_##_name##_MGT, \
+ .scalable = _scalable, \
+}
+
+static struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = {
+ CLK_MGT_ENTRY(SGACLK, true),
+ CLK_MGT_ENTRY(UARTCLK, false),
+ CLK_MGT_ENTRY(MSP02CLK, false),
+ CLK_MGT_ENTRY(I2CCLK, false),
+ [PRCMU_SDMMCCLK] {
+ .offset = DB5500_PRCM_SDMMCCLK_MGT,
+ .force50 = true,
+ .scalable = false,
+
+ },
+ [PRCMU_SPARE1CLK] {
+ .offset = DB5500_PRCM_SPARE1CLK_MGT,
+ .force50 = true,
+ .scalable = false,
+
+ },
+ CLK_MGT_ENTRY(PER1CLK, false),
+ CLK_MGT_ENTRY(PER2CLK, true),
+ CLK_MGT_ENTRY(PER3CLK, true),
+ CLK_MGT_ENTRY(PER5CLK, false), /* used for SPI */
+ CLK_MGT_ENTRY(PER6CLK, true),
+ CLK_MGT_ENTRY(PWMCLK, false),
+ CLK_MGT_ENTRY(IRDACLK, false),
+ CLK_MGT_ENTRY(IRRCCLK, false),
+ CLK_MGT_ENTRY(HDMICLK, false),
+ CLK_MGT_ENTRY(APEATCLK, false),
+ CLK_MGT_ENTRY(APETRACECLK, true),
+ CLK_MGT_ENTRY(MCDECLK, true),
+ CLK_MGT_ENTRY(DSIALTCLK, false),
+ CLK_MGT_ENTRY(DMACLK, true),
+ CLK_MGT_ENTRY(B2R2CLK, true),
+ CLK_MGT_ENTRY(TVCLK, false),
+ CLK_MGT_ENTRY(RNGCLK, false),
+ CLK_MGT_ENTRY(SIACLK, false),
+ CLK_MGT_ENTRY(SVACLK, false),
+ CLK_MGT_ENTRY(ACLK, true),
+};
+
+static atomic_t modem_req_state = ATOMIC_INIT(0);
+
+bool db5500_prcmu_is_modem_requested(void)
+{
+ return (atomic_read(&modem_req_state) != 0);
+}
+
+/**
+ * prcmu_modem_req - APE requests Modem to wake up
+ *
+ * Whenever APE wants to send message to the modem, it will have to call this
+ * function to make sure that modem is awake.
+ */
+void prcmu_modem_req(void)
+{
+ u32 val;
+
+ mutex_lock(&mb0_transfer.ac_wake_lock);
+
+ val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ if (val & PRCM_HOSTACCESS_REQ_BIT)
+ goto unlock_and_return;
+
+ writel((val | PRCM_HOSTACCESS_REQ_BIT),
+ (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+ atomic_set(&modem_req_state, 1);
+
+unlock_and_return:
+ mutex_unlock(&mb0_transfer.ac_wake_lock);
+
+}
+
+/**
+ * prcmu_modem_rel - APE has no more messages to send and hence releases modem.
+ *
+ * APE to Modem communication is initiated by modem_req and once the
+ * communication is completed, APE sends modem_rel to complete the protocol.
+ */
+void prcmu_modem_rel(void)
+{
+ u32 val;
+
+ mutex_lock(&mb0_transfer.ac_wake_lock);
+
+ val = readl(_PRCMU_BASE + PRCM_HOSTACCESS_REQ);
+ if (!(val & PRCM_HOSTACCESS_REQ_BIT))
+ goto unlock_and_return;
+
+ writel((val & ~PRCM_HOSTACCESS_REQ_BIT),
+ (_PRCMU_BASE + PRCM_HOSTACCESS_REQ));
+
+ atomic_set(&modem_req_state, 0);
+
+unlock_and_return:
+ mutex_unlock(&mb0_transfer.ac_wake_lock);
+}
+
+/**
+ * prcm_ape_ack - send an acknowledgement to modem
+ *
+ * On ape receiving ape_req, APE will have to acknowledge for the interrupt
+ * received. This function will send the acknowledgement by writing to the
+ * prcmu register and an interrupt is trigerred to modem.
+ */
+void prcmu_ape_ack(void)
+{
+ writel(PRCM_APE_ACK_BIT, (_PRCMU_BASE + PRCM_APE_ACK));
+}
+
+/**
+ * db5500_prcmu_modem_reset - Assert a Reset on modem
+ *
+ * This function will assert a reset request to the modem. Prior to that
+ * PRCM_HOSTACCESS_REQ must be '0'.
+ */
+void db5500_prcmu_modem_reset(void)
+{
+ mutex_lock(&mb4_transfer.lock);
+
+ /* PRCM_HOSTACCESS_REQ = 0, before asserting a reset */
+ prcmu_modem_rel();
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writeb(MB4H_CGF_MODEM_RESET, PRCM_REQ_MB4_HEADER);
+ writel(MBOX_BIT(4), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ wait_for_completion(&mb4_transfer.work);
+ if (mb4_transfer.ack.status != RC_SUCCESS ||
+ mb4_transfer.ack.header != MB4H_CGF_MODEM_RESET)
+ printk(KERN_ERR,
+ "ACK not received for modem reset interrupt\n");
+ mutex_unlock(&mb4_transfer.lock);
+}
+
+/**
+ * prcmu_config_clkout - Configure one of the programmable clock outputs.
+ * @clkout: The CLKOUT number (0 or 1).
+ * @source: Clock source.
+ * @div: The divider to be applied.
+ *
+ * Configures one of the programmable clock outputs (CLKOUTs).
+ */
+int prcmu_config_clkout(u8 clkout, u8 source, u8 div)
+{
+ static bool configured[2] = {false, false};
+ int r = 0;
+ unsigned long flags;
+ u32 sel_val;
+ u32 div_val;
+ u32 sel_bits;
+ u32 div_bits;
+ u32 sel_mask;
+ u32 div_mask;
+ u8 sel0 = CLKOUT_SEL0_SEL_CLK;
+ u16 sel = 0;
+
+ BUG_ON(clkout > DB5500_CLKOUT1);
+ BUG_ON(source > DB5500_CLKOUT_IRDACLK);
+ BUG_ON(div > 7);
+
+ switch (source) {
+ case DB5500_CLKOUT_REF_CLK_SEL0:
+ sel0 = CLKOUT_SEL0_REF_CLK;
+ break;
+ case DB5500_CLKOUT_RTC_CLK0_SEL0:
+ sel0 = CLKOUT_SEL0_RTC_CLK0;
+ break;
+ case DB5500_CLKOUT_ULP_CLK_SEL0:
+ sel0 = CLKOUT_SEL0_ULP_CLK;
+ break;
+ case DB5500_CLKOUT_STATIC0:
+ sel = CLKOUT_SEL_STATIC0;
+ break;
+ case DB5500_CLKOUT_REFCLK:
+ sel = CLKOUT_SEL_REFCLK;
+ break;
+ case DB5500_CLKOUT_ULPCLK:
+ sel = CLKOUT_SEL_ULPCLK;
+ break;
+ case DB5500_CLKOUT_ARMCLK:
+ sel = CLKOUT_SEL_ARMCLK;
+ break;
+ case DB5500_CLKOUT_SYSACC0CLK:
+ sel = CLKOUT_SEL_SYSACC0CLK;
+ break;
+ case DB5500_CLKOUT_SOC0PLLCLK:
+ sel = CLKOUT_SEL_SOC0PLLCLK;
+ break;
+ case DB5500_CLKOUT_SOC1PLLCLK:
+ sel = CLKOUT_SEL_SOC1PLLCLK;
+ break;
+ case DB5500_CLKOUT_DDRPLLCLK:
+ sel = CLKOUT_SEL_DDRPLLCLK;
+ break;
+ case DB5500_CLKOUT_TVCLK:
+ sel = CLKOUT_SEL_TVCLK;
+ break;
+ case DB5500_CLKOUT_IRDACLK:
+ sel = CLKOUT_SEL_IRDACLK;
+ break;
+ }
+
+ switch (clkout) {
+ case DB5500_CLKOUT0:
+ sel_mask = PRCM_CLKOCR_CLKOUT0_SEL0_MASK |
+ PRCM_CLKOCR_CLKOUT0_SEL_MASK;
+ sel_bits = ((sel0 << PRCM_CLKOCR_CLKOUT0_SEL0_SHIFT) |
+ (sel << PRCM_CLKOCR_CLKOUT0_SEL_SHIFT));
+ div_mask = PRCM_CLKODIV_CLKOUT0_DIV_MASK;
+ div_bits = div << PRCM_CLKODIV_CLKOUT0_DIV_SHIFT;
+ break;
+ case DB5500_CLKOUT1:
+ sel_mask = PRCM_CLKOCR_CLKOUT1_SEL0_MASK |
+ PRCM_CLKOCR_CLKOUT1_SEL_MASK;
+ sel_bits = ((sel0 << PRCM_CLKOCR_CLKOUT1_SEL0_SHIFT) |
+ (sel << PRCM_CLKOCR_CLKOUT1_SEL_SHIFT));
+ div_mask = PRCM_CLKODIV_CLKOUT1_DIV_MASK;
+ div_bits = div << PRCM_CLKODIV_CLKOUT1_DIV_SHIFT;
+ break;
+ }
+
+ spin_lock_irqsave(&clkout_lock, flags);
+
+ if (configured[clkout]) {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+
+ sel_val = readl(_PRCMU_BASE + PRCM_CLKOCR);
+ writel((sel_bits | (sel_val & ~sel_mask)),
+ (_PRCMU_BASE + PRCM_CLKOCR));
+
+ div_val = readl(_PRCMU_BASE + PRCM_CLKODIV);
+ writel((div_bits | (div_val & ~div_mask)),
+ (_PRCMU_BASE + PRCM_CLKODIV));
+
+ configured[clkout] = true;
+
+unlock_and_return:
+ spin_unlock_irqrestore(&clkout_lock, flags);
+
+ return r;
+}
+
+static int request_sysclk(bool enable)
+{
+ int r;
+
+ r = 0;
+ mutex_lock(&mb3_transfer.sysclk_lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(3))
+ cpu_relax();
+
+ if (enable)
+ mb3_transfer.req_st = SYSCLK_ON;
+ else
+ mb3_transfer.req_st = SYSCLK_OFF;
+
+ writeb(mb3_transfer.req_st, (PRCM_REQ_MB3_REFCLK_MGT));
+
+ writeb(MB3H_REFCLK_REQUEST, (PRCM_REQ_MB3_HEADER));
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ /*
+ * The firmware only sends an ACK if we want to enable the
+ * SysClk, and it succeeds.
+ */
+ if (!wait_for_completion_timeout(&mb3_transfer.sysclk_work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+ __func__);
+ r = -EIO;
+ WARN(1, "Failed to set sysclk");
+ goto unlock_and_return;
+ }
+
+ if ((mb3_transfer.ack.header != MB3H_REFCLK_REQUEST) ||
+ (mb3_transfer.ack.status != mb3_transfer.req_st)) {
+ r = -EIO;
+ }
+
+unlock_and_return:
+ mutex_unlock(&mb3_transfer.sysclk_lock);
+
+ return r;
+}
+
+static int request_timclk(bool enable)
+{
+ u32 val = (PRCM_TCR_DOZE_MODE | PRCM_TCR_TENSEL_MASK);
+
+ if (!enable)
+ val |= PRCM_TCR_STOP_TIMERS;
+ writel(val, _PRCMU_BASE + PRCM_TCR);
+
+ return 0;
+}
+
+static int request_clk(u8 clock, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(clock >= DB5500_NUM_CLK_CLIENTS);
+
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* fill in mailbox */
+ writeb(clock, PRCM_REQ_MB2_CLK_CLIENT);
+ writeb(enable, PRCM_REQ_MB2_CLK_STATE);
+
+ writeb(MB2H_CLK_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: request_clk() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed in request_clk");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_CLK_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
+static int request_reg_clock(u8 clock, bool enable)
+{
+ u32 val;
+ unsigned long flags;
+
+ WARN_ON(!clk_mgt[clock].offset);
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ val = readl(_PRCMU_BASE + clk_mgt[clock].offset);
+ if (enable) {
+ val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);
+ } else {
+ clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);
+ val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);
+ }
+ writel(val, (_PRCMU_BASE + clk_mgt[clock].offset));
+
+ /* Release the HW semaphore. */
+ writel(0, _PRCMU_BASE + PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ return 0;
+}
+
+/*
+ * request_pll() - Request for a pll to be enabled or disabled.
+ * @pll: The pll for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+static int request_pll(u8 pll, bool enable)
+{
+ int r = 0;
+
+ BUG_ON(pll >= DB5500_NUM_PLL_ID);
+ mutex_lock(&mb2_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ mb2_transfer.req.pll_st[pll] = enable;
+
+ /* fill in mailbox */
+ writeb(pll, PRCM_REQ_MB2_PLL_CLIENT);
+ writeb(mb2_transfer.req.pll_st[pll], PRCM_REQ_MB2_PLL_STATE);
+
+ writeb(MB2H_PLL_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: set_pll() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed to set pll");
+ goto unlock_and_return;
+ }
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_PLL_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+
+ return r;
+}
+
+/**
+ * db5500_prcmu_request_clock() - Request for a clock to be enabled or disabled.
+ * @clock: The clock for which the request is made.
+ * @enable: Whether the clock should be enabled (true) or disabled (false).
+ *
+ * This function should only be used by the clock implementation.
+ * Do not use it from any other place!
+ */
+int db5500_prcmu_request_clock(u8 clock, bool enable)
+{
+ /* MSP1 & CD clocks are handled by FW */
+ if (clock == PRCMU_MSP1CLK)
+ return request_clk(DB5500_MSP1CLK, enable);
+ else if (clock == PRCMU_CDCLK)
+ return request_clk(DB5500_CDCLK, enable);
+ else if (clock == PRCMU_IRDACLK)
+ return request_clk(DB5500_IRDACLK, enable);
+ else if (clock < PRCMU_NUM_REG_CLOCKS)
+ return request_reg_clock(clock, enable);
+ else if (clock == PRCMU_TIMCLK)
+ return request_timclk(enable);
+ else if (clock == PRCMU_PLLSOC0)
+ return request_pll(DB5500_PLL_SOC0, enable);
+ else if (clock == PRCMU_PLLSOC1)
+ return request_pll(DB5500_PLL_SOC1, enable);
+ else if (clock == PRCMU_PLLDDR)
+ return request_pll(DB5500_PLL_DDR, enable);
+ else if (clock == PRCMU_SYSCLK)
+ return request_sysclk(enable);
+ else
+ return -EINVAL;
+}
+
+/* This function should only be called while mb0_transfer.lock is held. */
+static void config_wakeups(void)
+{
+ static u32 last_dbb_events;
+ static u32 last_abb_events;
+ u32 dbb_events;
+ u32 abb_events;
+
+ dbb_events = mb0_transfer.req.dbb_irqs | mb0_transfer.req.dbb_wakeups;
+
+ abb_events = mb0_transfer.req.abb_events;
+
+ if ((dbb_events == last_dbb_events) && (abb_events == last_abb_events))
+ return;
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ writel(dbb_events, PRCM_REQ_MB0_WAKEUP_DBB);
+ writel(abb_events, PRCM_REQ_MB0_WAKEUP_ABB);
+ writeb(MB0H_WAKE_UP_CFG, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ last_dbb_events = dbb_events;
+ last_abb_events = abb_events;
+}
+
+int db5500_prcmu_config_esram0_deep_sleep(u8 state)
+{
+ unsigned long flags;
+
+ if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) ||
+ (state < ESRAM0_DEEP_SLEEP_STATE_OFF))
+ return -EINVAL;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ if (state == ESRAM0_DEEP_SLEEP_STATE_RET)
+ writeb(RET_ST, PRCM_REQ_MB0_ESRAM0_STATE);
+ else
+ writeb(OFF_ST, PRCM_REQ_MB0_ESRAM0_STATE);
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return 0;
+}
+
+int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)
+{
+ int r = 0;
+ unsigned long flags;
+
+ /* Deep Idle is not supported in DB5500 */
+ BUG_ON((state < PRCMU_AP_SLEEP) || (state >= PRCMU_AP_DEEP_IDLE));
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ cpu_relax();
+
+ switch (state) {
+ case PRCMU_AP_IDLE:
+ writeb(DB5500_AP_IDLE, PRCM_REQ_MB0_AP_POWER_STATE);
+ /* TODO: Can be high latency */
+ writeb(DDR_PWR_STATE_UNCHANGED, PRCM_REQ_MB0_DDR_STATE);
+ break;
+ case PRCMU_AP_SLEEP:
+ writeb(DB5500_AP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE);
+ break;
+ case PRCMU_AP_DEEP_SLEEP:
+ writeb(DB5500_AP_DEEP_SLEEP, PRCM_REQ_MB0_AP_POWER_STATE);
+ break;
+ default:
+ r = -EINVAL;
+ goto unlock_return;
+ }
+ writeb((keep_ap_pll ? 1 : 0), PRCM_REQ_MB0_AP_PLL_STATE);
+ writeb((keep_ulp_clk ? 1 : 0), PRCM_REQ_MB0_ULP_CLOCK_STATE);
+
+ writeb(MB0H_PWR_STATE_TRANS, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+unlock_return:
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+
+ return r;
+}
+
+u8 db5500_prcmu_get_power_state_result(void)
+{
+ u8 status = readb_relaxed(PRCM_ACK_MB0_AP_PWRSTTR_STATUS);
+
+ /*
+ * Callers expect all the status values to match 8500. Adjust for
+ * PendingReq_Er (0x2b).
+ */
+ if (status == 0x2b)
+ status = PRCMU_PRCMU2ARMPENDINGIT_ER;
+
+ return status;
+}
+
+void db5500_prcmu_enable_wakeups(u32 wakeups)
+{
+ unsigned long flags;
+ u32 bits;
+ int i;
+
+ BUG_ON(wakeups != (wakeups & VALID_WAKEUPS));
+
+ for (i = 0, bits = 0; i < NUM_PRCMU_WAKEUP_INDICES; i++) {
+ if (wakeups & BIT(i)) {
+ if (prcmu_wakeup_bit[i] == 0)
+ WARN(1, "WAKEUP NOT SUPPORTED");
+ else
+ bits |= prcmu_wakeup_bit[i];
+ }
+ }
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.dbb_wakeups = bits;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void db5500_prcmu_config_abb_event_readout(u32 abb_events)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ mb0_transfer.req.abb_events = abb_events;
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+void db5500_prcmu_get_abb_event_buffer(void __iomem **buf)
+{
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ *buf = (PRCM_ACK_MB0_WAKEUP_1_ABB);
+ else
+ *buf = (PRCM_ACK_MB0_WAKEUP_0_ABB);
+}
+
+/* This function should be called with lock */
+static int mailbox4_request(u8 mb4_request, u8 ack_request)
+{
+ int ret = 0;
+
+ writeb(mb4_request, PRCM_REQ_MB4_HEADER);
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb4_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: MB4 request %d failed", mb4_request);
+ ret = -EIO;
+ WARN(1, "prcmu: failed mb4 request");
+ goto failed;
+ }
+
+ if (mb4_transfer.ack.header != ack_request ||
+ mb4_transfer.ack.status != RC_SUCCESS)
+ ret = -EIO;
+failed:
+ return ret;
+}
+
+int db5500_prcmu_get_hotdog(void)
+{
+ return readw(PRCM_SHARE_INFO_HOTDOG);
+}
+
+int db5500_prcmu_config_hotdog(u8 threshold)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(threshold, PRCM_REQ_MB4_HOTDOG_THRESHOLD);
+ r = mailbox4_request(MB4H_CFG_HOTDOG, MB4H_ACK_CFG_HOTDOG);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+int db5500_prcmu_config_hotmon(u8 low, u8 high)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(low, PRCM_REQ_MB4_HOTMON_LOW);
+ writew(high, PRCM_REQ_MB4_HOTMON_HIGH);
+
+ r = mailbox4_request(MB4H_CFG_HOTMON, MB4H_ACK_CFG_HOTMON);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+static int config_hot_period(u16 val)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ writew(val, PRCM_REQ_MB4_HOT_PERIOD);
+ r = mailbox4_request(MB4H_CFG_HOTPERIOD, MB4H_ACK_CFG_HOTPERIOD);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+/*
+ * period in milli seconds
+ */
+int db5500_prcmu_start_temp_sense(u16 period)
+{
+ if (period == 0xFFFF)
+ return -EINVAL;
+
+ return config_hot_period(period);
+}
+
+int db5500_prcmu_stop_temp_sense(void)
+{
+ return config_hot_period(0xFFFF);
+}
+
+static int prcmu_a9wdog(u8 req, u8 ack)
+{
+ int r = 0;
+
+ mutex_lock(&mb4_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+ cpu_relax();
+
+ r = mailbox4_request(req, ack);
+
+ mutex_unlock(&mb4_transfer.lock);
+
+ return r;
+}
+
+static void prcmu_a9wdog_set_interrupt(bool enable)
+{
+ if (enable) {
+ writel(PRCM_TIMER0_IRQ_RTOS1_SET,
+ (mtimer_base + PRCM_TIMER0_IRQ_EN_SET_OFFSET));
+ } else {
+ writel(PRCM_TIMER0_IRQ_RTOS1_CLR,
+ (mtimer_base + PRCM_TIMER0_IRQ_EN_CLR_OFFSET));
+ }
+}
+
+static void prcmu_a9wdog_set_timeout(u32 timeout)
+{
+ u32 comp_timeout;
+
+ comp_timeout = readl(mtimer_base + PRCM_TIMER0_RTOS_COUNTER_OFFSET) +
+ timeout;
+ writel(comp_timeout, mtimer_base + PRCM_TIMER0_RTOS_COMP1_OFFSET);
+}
+
+int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+ /*
+ * Sleep auto off feature is not supported. Resume and
+ * suspend will be handled by watchdog driver.
+ */
+ return 0;
+}
+
+int db5500_prcmu_enable_a9wdog(u8 id)
+{
+ int r = 0;
+
+ if (a9wdog_timer.enabled)
+ return -EPERM;
+
+ prcmu_a9wdog_set_interrupt(true);
+
+ r = prcmu_a9wdog(MB4H_CGF_A9WDOG_EN_PREBARK,
+ MB4H_ACK_CGF_A9WDOG_EN_PREBARK);
+ if (!r)
+ a9wdog_timer.enabled = true;
+ else
+ prcmu_a9wdog_set_interrupt(false);
+
+ return r;
+}
+
+int db5500_prcmu_disable_a9wdog(u8 id)
+{
+ if (!a9wdog_timer.enabled)
+ return -EPERM;
+
+ prcmu_a9wdog_set_interrupt(false);
+
+ a9wdog_timer.enabled = false;
+
+ return prcmu_a9wdog(MB4H_CGF_A9WDOG_DIS,
+ MB4H_ACK_CGF_A9WDOG_DIS);
+}
+
+int db5500_prcmu_kick_a9wdog(u8 id)
+{
+ int r = 0;
+
+ if (a9wdog_timer.enabled)
+ prcmu_a9wdog_set_timeout(a9wdog_timer.timeout);
+ else
+ r = -EPERM;
+
+ return r;
+}
+
+int db5500_prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+ if (a9wdog_timer.enabled)
+ return -EPERM;
+
+ prcmu_a9wdog_set_timeout(timeout);
+ a9wdog_timer.timeout = timeout;
+
+ return 0;
+}
+
/**
* db5500_prcmu_abb_read() - Read register value(s) from the ABB.
* @slave: The I2C slave address.
@@ -170,14 +1314,14 @@ int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
writeb(reg, PRCM_REQ_MB5_I2C_REG);
writeb(size, PRCM_REQ_MB5_I2C_SIZE);
writeb(MB5H_I2C_READ, PRCM_REQ_MB5_HEADER);
- writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
wait_for_completion(&mb5_transfer.work);
r = 0;
@@ -211,7 +1355,7 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
mutex_lock(&mb5_transfer.lock);
- while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))
cpu_relax();
writeb(slave, PRCM_REQ_MB5_I2C_SLAVE);
writeb(reg, PRCM_REQ_MB5_I2C_REG);
@@ -219,7 +1363,7 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
memcpy_toio(PRCM_REQ_MB5_I2C_DATA, value, size);
writeb(MB5H_I2C_WRITE, PRCM_REQ_MB5_HEADER);
- writel(MBOX_BIT(5), PRCM_MBOX_CPU_SET);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
wait_for_completion(&mb5_transfer.work);
if ((mb5_transfer.ack.header == MB5H_I2C_WRITE) &&
@@ -233,42 +1377,385 @@ int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
return r;
}
+/**
+ * db5500_prcmu_set_arm_opp - set the appropriate ARM OPP
+ * @opp: The new ARM operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function sets the the operating point of the ARM.
+ */
+int db5500_prcmu_set_arm_opp(u8 opp)
+{
+ int r;
+ u8 db5500_opp;
+
+ r = 0;
+
+ switch (opp) {
+ case ARM_EXTCLK:
+ db5500_opp = DB5500_ARM_EXT_OPP;
+ break;
+ case ARM_50_OPP:
+ db5500_opp = DB5500_ARM_50_OPP;
+ break;
+ case ARM_100_OPP:
+ db5500_opp = DB5500_ARM_100_OPP;
+ break;
+ default:
+ pr_err("prcmu: %s() received wrong opp value: %d\n",
+ __func__, opp);
+ r = -EINVAL;
+ goto bailout;
+ }
+
+ mutex_lock(&mb1_transfer.lock);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_ARM_OPP, PRCM_REQ_MB1_HEADER);
+
+ writeb(db5500_opp, PRCM_REQ_MB1_ARM_OPP);
+ writel(MBOX_BIT(1), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ if (!wait_for_completion_timeout(&mb1_transfer.work,
+ msecs_to_jiffies(20000))) {
+ r = -EIO;
+ WARN(1, "prcmu: failed to set arm opp");
+ goto unlock_and_return;
+ }
+
+ if (mb1_transfer.ack.header != MB1H_ARM_OPP ||
+ (mb1_transfer.ack.arm_opp != db5500_opp) ||
+ (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS))
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+bailout:
+ if (!r)
+ prcmu_debug_arm_opp_log(opp);
+ return r;
+}
+
+static void __init prcmu_ape_clocks_init(void)
+{
+ u8 opp = db5500_prcmu_get_ape_opp();
+ unsigned long flags;
+ int i;
+
+ WARN(opp != APE_100_OPP, "%s: Initial APE OPP (%u) not 100%%?\n",
+ __func__, opp);
+
+ for (i = 0; i < PRCMU_NUM_REG_CLOCKS; i++) {
+ struct clk_mgt *clkmgt = &clk_mgt[i];
+ u32 clkval;
+ u32 div;
+
+ if (!clkmgt->scalable && !clkmgt->force50)
+ continue;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ clkval = readl(_PRCMU_BASE + clkmgt->offset);
+ div = clkval & PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ div >>= PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+
+ if (clkmgt->force50) {
+ div *= 2;
+
+ clkval &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ clkval |= div << PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+ writel(clkval, _PRCMU_BASE + clkmgt->offset);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+ continue;
+ }
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+
+ clkmgt->div = div;
+ if (!div)
+ pr_err("%s: scalable clock at offset %#x has zero divisor\n",
+ __func__, clkmgt->offset);
+ }
+}
+
+static void prcmu_ape_clocks_scale(u8 opp)
+{
+ unsigned long irqflags;
+ unsigned int i;
+ u32 clkval;
+
+ /*
+ * Note: calling printk() under the following lock can cause lock
+ * recursion via clk_enable() for the console UART!
+ */
+ spin_lock_irqsave(&clk_mgt_lock, irqflags);
+
+ /* take a lock on HW (HWSEM)*/
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < PRCMU_NUM_REG_CLOCKS; i++) {
+ u32 divval;
+
+ if (!clk_mgt[i].scalable)
+ continue;
+
+ clkval = readl(_PRCMU_BASE + clk_mgt[i].offset);
+ divval = clk_mgt[i].div;
+
+ pr_debug("PRCMU: reg %#x prev clk = 0x%x stored div = 0x%x\n",
+ clk_mgt[i].offset, clkval, divval);
+
+ if (opp == DB5500_APE_50_OPP)
+ divval *= 2;
+
+ clkval &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK;
+ clkval |= divval << PRCM_CLK_MGT_CLKPLLDIV_SHIFT;
+
+ pr_debug("PRCMU: wr 0x%x in reg 0x%x\n",
+ clkval, clk_mgt[i].offset);
+
+ writel(clkval, _PRCMU_BASE + clk_mgt[i].offset);
+ }
+
+ /* release lock */
+ writel(0, (_PRCMU_BASE + PRCM_SEM));
+
+ spin_unlock_irqrestore(&clk_mgt_lock, irqflags);
+}
+/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */
+static void request_even_slower_clocks(bool enable)
+{
+ void __iomem *clock_reg[] = {
+ (_PRCMU_BASE + DB5500_PRCM_ACLK_MGT),
+ (_PRCMU_BASE + DB5500_PRCM_DMACLK_MGT)
+ };
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&clk_mgt_lock, flags);
+
+ /* Grab the HW semaphore. */
+ while ((readl(_PRCMU_BASE + PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)
+ cpu_relax();
+
+ for (i = 0; i < ARRAY_SIZE(clock_reg); i++) {
+ u32 val;
+ u32 div;
+
+ val = readl(clock_reg[i]);
+ div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK);
+ if (enable) {
+ if ((div <= 1) || (div > 15)) {
+ pr_err("prcmu: Bad clock divider %d in %s\n",
+ div, __func__);
+ goto unlock_and_return;
+ }
+ div <<= 1;
+ } else {
+ if (div <= 2)
+ goto unlock_and_return;
+ div >>= 1;
+ }
+ val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) |
+ (div & PRCM_CLK_MGT_CLKPLLDIV_MASK));
+ writel(val, clock_reg[i]);
+ }
+
+unlock_and_return:
+ /* Release the HW semaphore. */
+ writel(0, _PRCMU_BASE + PRCM_SEM);
+
+ spin_unlock_irqrestore(&clk_mgt_lock, flags);
+}
+int db5500_prcmu_set_ape_opp(u8 opp)
+{
+ int ret = 0;
+ u8 db5500_opp;
+ if (opp == mb1_transfer.req_ape_opp)
+ return 0;
+
+ switch (opp) {
+ case APE_100_OPP:
+ db5500_opp = DB5500_APE_100_OPP;
+ break;
+ case APE_50_OPP:
+ case APE_50_PARTLY_25_OPP:
+ db5500_opp = DB5500_APE_50_OPP;
+ break;
+ default:
+ pr_err("prcmu: %s() received wrong opp value: %d\n",
+ __func__, opp);
+ ret = -EINVAL;
+ goto bailout;
+ }
+
+ mutex_lock(&mb1_transfer.lock);
+ if (mb1_transfer.req_ape_opp == APE_50_PARTLY_25_OPP)
+ request_even_slower_clocks(false);
+ if ((opp != APE_100_OPP) && (mb1_transfer.req_ape_opp != APE_100_OPP))
+ goto skip_message;
+
+ prcmu_ape_clocks_scale(db5500_opp);
+
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
+ cpu_relax();
+
+ writeb(MB1H_APE_OPP, PRCM_REQ_MB1_HEADER);
+ writeb(db5500_opp, PRCM_REQ_MB1_APE_OPP);
+ writel(MBOX_BIT(1), (_PRCMU_BASE + PRCM_MBOX_CPU_SET));
+
+ if (!wait_for_completion_timeout(&mb1_transfer.work,
+ msecs_to_jiffies(20000))) {
+ ret = -EIO;
+ WARN(1, "prcmu: failed to set ape opp to %u", opp);
+ goto unlock_and_return;
+ }
+
+ if (mb1_transfer.ack.header != MB1H_APE_OPP ||
+ (mb1_transfer.ack.ape_opp != db5500_opp) ||
+ (mb1_transfer.ack.arm_voltage_st != RC_SUCCESS))
+ ret = -EIO;
+
+skip_message:
+ if ((!ret && (opp == APE_50_PARTLY_25_OPP)) ||
+ (ret && (mb1_transfer.req_ape_opp == APE_50_PARTLY_25_OPP)))
+ request_even_slower_clocks(true);
+ if (!ret)
+ mb1_transfer.req_ape_opp = opp;
+unlock_and_return:
+ mutex_unlock(&mb1_transfer.lock);
+bailout:
+ return ret;
+}
+
+int db5500_prcmu_get_ape_opp(void)
+{
+ u8 opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+
+ switch (opp) {
+ case DB5500_APE_100_OPP:
+ return APE_100_OPP;
+ case DB5500_APE_50_OPP:
+ return APE_50_OPP;
+ default:
+ pr_err("prcmu: %s() read unknown opp value: %d\n",
+ __func__, opp);
+ return APE_100_OPP;
+ }
+}
+
+int db5500_prcmu_get_ddr_opp(void)
+{
+ return readb(_PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+}
+
+int db5500_prcmu_set_ddr_opp(u8 opp)
+{
+ if (opp != DDR_100_OPP && opp != DDR_50_OPP)
+ return -EINVAL;
+
+ writeb(opp, _PRCMU_BASE + PRCM_DDR_SUBSYS_APE_MINBW);
+
+ return 0;
+}
+
+/**
+ * db5500_prcmu_get_arm_opp - get the current ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int db5500_prcmu_get_arm_opp(void)
+{
+ u8 opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP);
+
+ switch (opp) {
+ case DB5500_ARM_EXT_OPP:
+ return ARM_EXTCLK;
+ case DB5500_ARM_50_OPP:
+ return ARM_50_OPP;
+ case DB5500_ARM_100_OPP:
+ return ARM_100_OPP;
+ default:
+ pr_err("prcmu: %s() read unknown opp value: %d\n",
+ __func__, opp);
+ return ARM_100_OPP;
+ }
+}
+
+int prcmu_resetout(u8 resoutn, u8 state)
+{
+ int offset;
+ int pin = -1;
+
+ offset = state > 0 ? PRCM_RESOUTN_SET_OFFSET : PRCM_RESOUTN_CLR_OFFSET;
+
+ switch (resoutn) {
+ case 0:
+ pin = PRCMU_RESOUTN0_PIN;
+ break;
+ case 1:
+ pin = PRCMU_RESOUTN1_PIN;
+ break;
+ case 2:
+ pin = PRCMU_RESOUTN2_PIN;
+ default:
+ break;
+ }
+
+ if (pin > 0)
+ writel(pin, _PRCMU_BASE + offset);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
int db5500_prcmu_enable_dsipll(void)
{
int i;
+ int ret = 0;
/* Enable DSIPLL_RESETN resets */
- writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);
+ writel(PRCMU_RESET_DSIPLL, _PRCMU_BASE + PRCM_APE_RESETN_CLR);
/* Unclamp DSIPLL in/out */
- writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR);
+ writel(PRCMU_UNCLAMP_DSIPLL, _PRCMU_BASE + PRCM_MMIP_LS_CLAMP_CLR);
/* Set DSI PLL FREQ */
- writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);
+ writel(PRCMU_PLLDSI_FREQ_SETTING, _PRCMU_BASE + PRCM_PLLDSI_FREQ);
writel(PRCMU_DSI_PLLOUT_SEL_SETTING,
- PRCM_DSI_PLLOUT_SEL);
+ _PRCMU_BASE + PRCM_DSI_PLLOUT_SEL);
/* Enable Escape clocks */
- writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+ writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, _PRCMU_BASE + PRCM_DSITVCLK_DIV);
/* Start DSI PLL */
- writel(PRCMU_ENABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+ writel(PRCMU_ENABLE_PLLDSI, _PRCMU_BASE + PRCM_PLLDSI_ENABLE);
/* Reset DSI PLL */
- writel(PRCMU_DSI_RESET_SW, PRCM_DSI_SW_RESET);
+ writel(PRCMU_DSI_RESET_SW, _PRCMU_BASE + PRCM_DSI_SW_RESET);
for (i = 0; i < 10; i++) {
- if ((readl(PRCM_PLLDSI_LOCKP) &
+ if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
PRCMU_PLLDSI_LOCKP_LOCKED) == PRCMU_PLLDSI_LOCKP_LOCKED)
break;
udelay(100);
}
+
+ if ((readl(_PRCMU_BASE + PRCM_PLLDSI_LOCKP) &
+ PRCMU_PLLDSI_LOCKP_LOCKED)
+ != PRCMU_PLLDSI_LOCKP_LOCKED)
+ ret = -EIO;
/* Release DSIPLL_RESETN */
- writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_SET);
- return 0;
+ writel(PRCMU_RESET_DSIPLL, _PRCMU_BASE + PRCM_APE_RESETN_SET);
+ return ret;
}
int db5500_prcmu_disable_dsipll(void)
{
/* Disable dsi pll */
- writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE);
+ writel(PRCMU_DISABLE_PLLDSI, _PRCMU_BASE + PRCM_PLLDSI_ENABLE);
/* Disable escapeclock */
- writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV);
+ writel(PRCMU_DISABLE_ESCAPE_CLOCK_DIV, _PRCMU_BASE + PRCM_DSITVCLK_DIV);
return 0;
}
@@ -276,27 +1763,150 @@ int db5500_prcmu_set_display_clocks(void)
{
/* HDMI and TVCLK Should be handled somewhere else */
/* PLLDIV=8, PLLSW=2, CLKEN=1 */
- writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);
+ writel(PRCMU_DSI_CLOCK_SETTING, _PRCMU_BASE + DB5500_PRCM_HDMICLK_MGT);
/* PLLDIV=14, PLLSW=2, CLKEN=1 */
- writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);
+ writel(PRCMU_DSI_LP_CLOCK_SETTING, _PRCMU_BASE + DB5500_PRCM_TVCLK_MGT);
return 0;
}
+u32 db5500_prcmu_read(unsigned int reg)
+{
+ return readl_relaxed(_PRCMU_BASE + reg);
+}
+
+void db5500_prcmu_write(unsigned int reg, u32 value)
+{
+ writel_relaxed(value, _PRCMU_BASE + reg);
+}
+
+void db5500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
+{
+ u32 val;
+
+ val = readl_relaxed(_PRCMU_BASE + reg);
+ val = (val & ~mask) | (value & mask);
+ writel_relaxed(val, _PRCMU_BASE + reg);
+}
+
+/**
+ * db5500_prcmu_system_reset - System reset
+ *
+ * Saves the reset reason code and then sets the APE_SOFTRST register which
+ * fires an interrupt to fw
+ */
+void db5500_prcmu_system_reset(u16 reset_code)
+{
+ writew(reset_code, PRCM_SW_RST_REASON);
+ writel(1, _PRCMU_BASE + PRCM_APE_SOFTRST);
+}
+
+/**
+ * db5500_prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 db5500_prcmu_get_reset_code(void)
+{
+ return readw(PRCM_SW_RST_REASON);
+}
+
static void ack_dbb_wakeup(void)
{
unsigned long flags;
spin_lock_irqsave(&mb0_transfer.lock, flags);
- while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(0))
cpu_relax();
- writeb(RMB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER);
- writel(MBOX_BIT(0), PRCM_MBOX_CPU_SET);
+ writeb(MB0H_RD_WAKE_UP_ACK, PRCM_REQ_MB0_HEADER);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
spin_unlock_irqrestore(&mb0_transfer.lock, flags);
}
+int db5500_prcmu_set_epod(u16 epod, u8 epod_state)
+{
+ int r = 0;
+ bool ram_retention = false;
+
+ /* check argument */
+ BUG_ON(epod < DB5500_EPOD_ID_BASE);
+ BUG_ON(epod_state > EPOD_STATE_ON);
+ BUG_ON((epod - DB5500_EPOD_ID_BASE) >= DB5500_NUM_EPOD_ID);
+
+ if (epod == DB5500_EPOD_ID_ESRAM12)
+ ram_retention = true;
+
+ /* check argument */
+ BUG_ON(epod_state == EPOD_STATE_RAMRET && !ram_retention);
+
+ /* get lock */
+ mutex_lock(&mb2_transfer.lock);
+
+ /* wait for mailbox */
+ while (readl(_PRCMU_BASE + PRCM_MBOX_CPU_VAL) & MBOX_BIT(2))
+ cpu_relax();
+
+ /* Retention is allowed only for ESRAM12 */
+ if (epod == DB5500_EPOD_ID_ESRAM12) {
+ switch (epod_state) {
+ case EPOD_STATE_ON:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_ON;
+ break;
+ case EPOD_STATE_OFF:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_OFF;
+ break;
+ case EPOD_STATE_RAMRET:
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OOR_RET;
+ break;
+ default:
+ r = -EINVAL;
+ goto unlock_and_return;
+ break;
+ }
+ } else {
+ if (epod_state == EPOD_STATE_ON)
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_ON;
+ else if (epod_state == EPOD_STATE_OFF)
+ mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE] =
+ EPOD_OFF;
+ else {
+ r = -EINVAL;
+ goto unlock_and_return;
+ }
+ }
+ /* fill in mailbox */
+ writeb((epod - DB5500_EPOD_ID_BASE), PRCM_REQ_MB2_EPOD_CLIENT);
+ writeb(mb2_transfer.req.epod_st[epod - DB5500_EPOD_ID_BASE],
+ PRCM_REQ_MB2_EPOD_STATE);
+
+ writeb(MB2H_EPOD_REQUEST, PRCM_REQ_MB2_HEADER);
+
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_MBOX_CPU_SET);
+
+ if (!wait_for_completion_timeout(&mb2_transfer.work,
+ msecs_to_jiffies(20000))) {
+ pr_err("prcmu: set_epod() failed.\n");
+ r = -EIO;
+ WARN(1, "Failed to set epod");
+ goto unlock_and_return;
+ }
+
+ if (mb2_transfer.ack.status != RC_SUCCESS ||
+ mb2_transfer.ack.header != MB2H_EPOD_REQUEST)
+ r = -EIO;
+
+unlock_and_return:
+ mutex_unlock(&mb2_transfer.lock);
+ return r;
+}
+
static inline void print_unknown_header_warning(u8 n, u8 header)
{
pr_warning("prcmu: Unknown message header (%d) in mailbox %d.\n",
@@ -306,11 +1916,31 @@ static inline void print_unknown_header_warning(u8 n, u8 header)
static bool read_mailbox_0(void)
{
bool r;
+ u32 ev;
+ unsigned int n;
+
u8 header;
header = readb(PRCM_ACK_MB0_HEADER);
switch (header) {
- case AMB0H_WAKE_UP:
+ case MB0H_WAKE_UP:
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ ev = readl(PRCM_ACK_MB0_WAKEUP_1_DBB);
+ else
+ ev = readl(PRCM_ACK_MB0_WAKEUP_0_DBB);
+
+ prcmu_debug_register_mbox0_event(ev,
+ (mb0_transfer.req.dbb_irqs |
+ mb0_transfer.req.dbb_wakeups));
+
+ ev &= mb0_transfer.req.dbb_irqs;
+
+ for (n = 0; n < NUM_DB5500_PRCMU_WAKEUPS; n++) {
+ if (ev & prcmu_irq_bit[n]) {
+ if (n != IRQ_INDEX(ABB))
+ generic_handle_irq(IRQ_DB5500_PRCMU_BASE + n);
+ }
+ }
r = true;
break;
default:
@@ -318,31 +1948,123 @@ static bool read_mailbox_0(void)
r = false;
break;
}
- writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(0), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return r;
}
static bool read_mailbox_1(void)
{
- writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR);
+ u8 header;
+ bool do_complete = true;
+
+ header = mb1_transfer.ack.header = readb(PRCM_ACK_MB1_HEADER);
+
+ switch (header) {
+ case MB1H_ARM_OPP:
+ mb1_transfer.ack.arm_opp = readb(PRCM_ACK_MB1_CURRENT_ARM_OPP);
+ mb1_transfer.ack.arm_voltage_st =
+ readb(PRCM_ACK_MB1_ARM_VOLT_STATUS);
+ break;
+ case MB1H_APE_OPP:
+ mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_st =
+ readb(PRCM_ACK_MB1_APE_VOLT_STATUS);
+ break;
+ case MB1H_ARM_APE_OPP:
+ mb1_transfer.ack.ape_opp = readb(PRCM_ACK_MB1_CURRENT_APE_OPP);
+ mb1_transfer.ack.ape_voltage_st =
+ readb(PRCM_ACK_MB1_APE_VOLT_STATUS);
+ break;
+ default:
+ print_unknown_header_warning(1, header);
+ do_complete = false;
+ break;
+ }
+
+ writel(MBOX_BIT(1), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+
+ if (do_complete)
+ complete(&mb1_transfer.work);
+
return false;
}
static bool read_mailbox_2(void)
{
- writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR);
+ u8 header;
+
+ header = readb(PRCM_ACK_MB2_HEADER);
+ mb2_transfer.ack.header = header;
+ switch (header) {
+ case MB2H_EPOD_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_EPOD_STATUS);
+ break;
+ case MB2H_CLK_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_CLK_STATUS);
+ break;
+ case MB2H_PLL_REQUEST:
+ mb2_transfer.ack.status = readb(PRCM_ACK_MB2_PLL_STATUS);
+ break;
+ default:
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: Wrong ACK received for MB2 request \n");
+ return false;
+ break;
+ }
+ writel(MBOX_BIT(2), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb2_transfer.work);
return false;
}
static bool read_mailbox_3(void)
{
- writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR);
+ u8 header;
+
+ header = readb(PRCM_ACK_MB3_HEADER);
+ mb3_transfer.ack.header = header;
+ switch (header) {
+ case MB3H_REFCLK_REQUEST:
+ mb3_transfer.ack.status = readb(PRCM_ACK_MB3_REFCLK_REQ);
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ complete(&mb3_transfer.sysclk_work);
+ break;
+ default:
+ writel(MBOX_BIT(3), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
+ pr_err("prcmu: wrong MB3 header\n");
+ break;
+ }
+
return false;
}
static bool read_mailbox_4(void)
{
- writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR);
+ u8 header;
+ bool do_complete = true;
+
+ header = readb(PRCM_ACK_MB4_HEADER);
+ mb4_transfer.ack.header = header;
+ switch (header) {
+ case MB4H_ACK_CFG_HOTDOG:
+ case MB4H_ACK_CFG_HOTMON:
+ case MB4H_ACK_CFG_HOTPERIOD:
+ case MB4H_ACK_CFG_MODEM_RESET:
+ case MB4H_ACK_CGF_A9WDOG_EN_PREBARK:
+ case MB4H_ACK_CGF_A9WDOG_EN_NOPREBARK:
+ case MB4H_ACK_CGF_A9WDOG_DIS:
+ mb4_transfer.ack.status = readb(PRCM_ACK_MB4_REQUESTS);
+ break;
+ default:
+ print_unknown_header_warning(4, header);
+ do_complete = false;
+ break;
+ }
+
+ writel(MBOX_BIT(4), (_PRCMU_BASE + PRCM_ARM_IT1_CLEAR));
+
+ if (do_complete)
+ complete(&mb4_transfer.work);
+
return false;
}
@@ -363,19 +2085,19 @@ static bool read_mailbox_5(void)
print_unknown_header_warning(5, header);
break;
}
- writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(5), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return false;
}
static bool read_mailbox_6(void)
{
- writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(6), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return false;
}
static bool read_mailbox_7(void)
{
- writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR);
+ writel(MBOX_BIT(7), _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
return false;
}
@@ -396,7 +2118,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
u8 n;
irqreturn_t r;
- bits = (readl(PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
+ bits = (readl(_PRCMU_BASE + PRCM_ARM_IT1_VAL) & ALL_MBOX_BITS);
if (unlikely(!bits))
return IRQ_NONE;
@@ -406,6 +2128,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
bits -= MBOX_BIT(n);
if (read_mailbox[n]())
r = IRQ_WAKE_THREAD;
+ prcmu_debug_register_interrupt(n);
}
}
return r;
@@ -413,39 +2136,271 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
static irqreturn_t prcmu_irq_thread_fn(int irq, void *data)
{
+ u32 ev;
+
+ /*
+ * ABB needs to be handled before the wakeup because
+ * the ping/pong buffers for ABB events could change
+ * after we acknowledge the wakeup.
+ */
+ if (readb(PRCM_ACK_MB0_READ_POINTER) & 1)
+ ev = readl(PRCM_ACK_MB0_WAKEUP_1_DBB);
+ else
+ ev = readl(PRCM_ACK_MB0_WAKEUP_0_DBB);
+
+ ev &= mb0_transfer.req.dbb_irqs;
+ if (ev & WAKEUP_BIT_ABB)
+ handle_nested_irq(IRQ_DB5500_PRCMU_ABB);
+
ack_dbb_wakeup();
+
return IRQ_HANDLED;
}
+static void prcmu_mask_work(struct work_struct *work)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.lock, flags);
+
+ config_wakeups();
+
+ spin_unlock_irqrestore(&mb0_transfer.lock, flags);
+}
+
+static void prcmu_irq_mask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs &= ~prcmu_irq_bit[d->irq - IRQ_DB5500_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void prcmu_irq_unmask(struct irq_data *d)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mb0_transfer.dbb_irqs_lock, flags);
+
+ mb0_transfer.req.dbb_irqs |= prcmu_irq_bit[d->irq - IRQ_DB5500_PRCMU_BASE];
+
+ spin_unlock_irqrestore(&mb0_transfer.dbb_irqs_lock, flags);
+ schedule_work(&mb0_transfer.mask_work);
+}
+
+static void noop(struct irq_data *d)
+{
+}
+
+static struct irq_chip prcmu_irq_chip = {
+ .name = "prcmu",
+ .irq_disable = prcmu_irq_mask,
+ .irq_ack = noop,
+ .irq_mask = prcmu_irq_mask,
+ .irq_unmask = prcmu_irq_unmask,
+};
+
void __init db5500_prcmu_early_init(void)
{
+ unsigned int i;
+ void *tcpm_base = ioremap_nocache(U5500_PRCMU_TCPM_BASE, SZ_4K);
+
+ if (tcpm_base != NULL) {
+ int version_high, version_low;
+
+ version_high = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+ version_low = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET + 4);
+ prcmu_version.board = (version_high >> 24) & 0xFF;
+ prcmu_version.fw_version = version_high & 0xFF;
+ prcmu_version.api_version = version_low & 0xFF;
+
+ pr_info("PRCMU Firmware Version: 0x%x\n",
+ prcmu_version.fw_version);
+ pr_info("PRCMU API Version: 0x%x\n",
+ prcmu_version.api_version);
+
+ iounmap(tcpm_base);
+ }
+
tcdm_base = __io_address(U5500_PRCMU_TCDM_BASE);
+ mtimer_base = __io_address(U5500_MTIMER_BASE);
spin_lock_init(&mb0_transfer.lock);
+ spin_lock_init(&mb0_transfer.dbb_irqs_lock);
+ mutex_init(&mb0_transfer.ac_wake_lock);
+ mutex_init(&mb1_transfer.lock);
+ init_completion(&mb1_transfer.work);
+ mutex_init(&mb2_transfer.lock);
+ init_completion(&mb2_transfer.work);
+ mutex_init(&mb3_transfer.sysclk_lock);
+ init_completion(&mb3_transfer.sysclk_work);
+ mutex_init(&mb4_transfer.lock);
+ init_completion(&mb4_transfer.work);
mutex_init(&mb5_transfer.lock);
init_completion(&mb5_transfer.work);
+
+ INIT_WORK(&mb0_transfer.mask_work, prcmu_mask_work);
+
+ /* Initalize irqs. */
+ for (i = 0; i < NUM_DB5500_PRCMU_WAKEUPS; i++) {
+ unsigned int irq;
+
+ irq = IRQ_DB5500_PRCMU_BASE + i;
+ irq_set_chip_and_handler(irq, &prcmu_irq_chip,
+ handle_simple_irq);
+ if (irq == IRQ_DB5500_PRCMU_ABB)
+ irq_set_nested_thread(irq, true);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+ prcmu_ape_clocks_init();
+}
+
+/*
+ * Power domain switches (ePODs) modeled as regulators for the DB5500 SoC
+ */
+static struct regulator_consumer_supply db5500_vape_consumers[] = {
+ REGULATOR_SUPPLY("v-ape", NULL),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.0"),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.1"),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.2"),
+ REGULATOR_SUPPLY("v-i2c", "nmk-i2c.3"),
+ REGULATOR_SUPPLY("vcore", "sdi0"),
+ REGULATOR_SUPPLY("vcore", "sdi1"),
+ REGULATOR_SUPPLY("vcore", "sdi2"),
+ REGULATOR_SUPPLY("vcore", "sdi3"),
+ REGULATOR_SUPPLY("vcore", "sdi4"),
+ REGULATOR_SUPPLY("v-uart", "uart0"),
+ REGULATOR_SUPPLY("v-uart", "uart1"),
+ REGULATOR_SUPPLY("v-uart", "uart2"),
+ REGULATOR_SUPPLY("v-uart", "uart3"),
+ REGULATOR_SUPPLY("v-ape", "db5500-keypad"),
+};
+
+static struct regulator_consumer_supply db5500_sga_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.0"),
+ REGULATOR_SUPPLY("v-mali", NULL),
+};
+
+static struct regulator_consumer_supply db5500_hva_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.1"),
+ REGULATOR_SUPPLY("v-hva", NULL),
+};
+
+static struct regulator_consumer_supply db5500_sia_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.2"),
+ REGULATOR_SUPPLY("v-sia", "mmio_camera"),
+};
+
+static struct regulator_consumer_supply db5500_disp_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.3"),
+ REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
+ REGULATOR_SUPPLY("vsupply", "mcde"),
+};
+
+static struct regulator_consumer_supply db5500_esram12_consumers[] = {
+ REGULATOR_SUPPLY("debug", "reg-virt-consumer.4"),
+ REGULATOR_SUPPLY("v-esram12", "mcde"),
+ REGULATOR_SUPPLY("esram12", "hva"),
+};
+
+#define DB5500_REGULATOR_SWITCH(lower, upper) \
+[DB5500_REGULATOR_SWITCH_##upper] = { \
+ .constraints = { \
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, \
+ }, \
+ .consumer_supplies = db5500_##lower##_consumers, \
+ .num_consumer_supplies = ARRAY_SIZE(db5500_##lower##_consumers),\
}
+#define DB5500_REGULATOR_SWITCH_VAPE(lower, upper) \
+[DB5500_REGULATOR_SWITCH_##upper] = { \
+ .supply_regulator = "db5500-vape", \
+ .constraints = { \
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS, \
+ }, \
+ .consumer_supplies = db5500_##lower##_consumers, \
+ .num_consumer_supplies = ARRAY_SIZE(db5500_##lower##_consumers),\
+} \
+
+static struct regulator_init_data db5500_regulators[DB5500_NUM_REGULATORS] = {
+ [DB5500_REGULATOR_VAPE] = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .consumer_supplies = db5500_vape_consumers,
+ .num_consumer_supplies = ARRAY_SIZE(db5500_vape_consumers),
+ },
+ DB5500_REGULATOR_SWITCH_VAPE(sga, SGA),
+ DB5500_REGULATOR_SWITCH_VAPE(hva, HVA),
+ DB5500_REGULATOR_SWITCH_VAPE(sia, SIA),
+ DB5500_REGULATOR_SWITCH_VAPE(disp, DISP),
+ /*
+ * ESRAM12 is put in retention by the firmware when VAPE is
+ * turned off so there's no need to hold VAPE.
+ */
+ DB5500_REGULATOR_SWITCH(esram12, ESRAM12),
+};
+
+static struct mfd_cell db5500_prcmu_devs[] = {
+ {
+ .name = "db5500-prcmu-regulators",
+ .platform_data = &db5500_regulators,
+ .pdata_size = sizeof(db5500_regulators),
+ },
+ {
+ .name = "cpufreq-u5500",
+ },
+};
+
/**
* prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
*
*/
-int __init db5500_prcmu_init(void)
+static int __init db5500_prcmu_probe(struct platform_device *pdev)
{
- int r = 0;
+ int err = 0;
if (ux500_is_svp() || !cpu_is_u5500())
return -ENODEV;
/* Clean up the mailbox interrupts after pre-kernel code. */
- writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);
+ writel(ALL_MBOX_BITS, _PRCMU_BASE + PRCM_ARM_IT1_CLEAR);
- r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
- prcmu_irq_thread_fn, 0, "prcmu", NULL);
- if (r < 0) {
+ err = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler,
+ prcmu_irq_thread_fn, IRQF_NO_SUSPEND, "prcmu", NULL);
+ if (err < 0) {
pr_err("prcmu: Failed to allocate IRQ_DB5500_PRCMU1.\n");
- return -EBUSY;
+ err = -EBUSY;
+ goto no_irq_return;
}
- return 0;
+
+ err = mfd_add_devices(&pdev->dev, 0, db5500_prcmu_devs,
+ ARRAY_SIZE(db5500_prcmu_devs), NULL,
+ 0);
+
+ if (err)
+ pr_err("prcmu: Failed to add subdevices\n");
+ else
+ pr_info("DB5500 PRCMU initialized\n");
+
+no_irq_return:
+ return err;
+
+}
+
+static struct platform_driver db5500_prcmu_driver = {
+ .driver = {
+ .name = "db5500-prcmu",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init db5500_prcmu_init(void)
+{
+ return platform_driver_probe(&db5500_prcmu_driver, db5500_prcmu_probe);
}
arch_initcall(db5500_prcmu_init);
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 5be32489714..7c26c41a7ef 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -30,11 +30,13 @@
#include <linux/mfd/dbx500-prcmu.h>
#include <linux/regulator/db8500-prcmu.h>
#include <linux/regulator/machine.h>
+#include <linux/mfd/abx500.h>
#include <asm/hardware/gic.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/db8500-regs.h>
#include <mach/id.h>
+#include <mach/prcmu-debug.h>
#include "dbx500-prcmu-regs.h"
/* Offset for the firmware version within the TCPM */
@@ -70,6 +72,8 @@
#define PRCM_SW_RST_REASON 0xFF8 /* 2 bytes */
+#define PRCM_TCDM_VOICE_CALL_FLAG 0xDD4 /* 4 bytes */
+
#define _PRCM_MBOX_HEADER 0xFE8 /* 16 bytes */
#define PRCM_MBOX_HEADER_REQ_MB0 (_PRCM_MBOX_HEADER + 0x0)
#define PRCM_MBOX_HEADER_REQ_MB1 (_PRCM_MBOX_HEADER + 0x1)
@@ -214,10 +218,8 @@
#define PRCM_REQ_MB5_I2C_HW_BITS (PRCM_REQ_MB5 + 0x1)
#define PRCM_REQ_MB5_I2C_REG (PRCM_REQ_MB5 + 0x2)
#define PRCM_REQ_MB5_I2C_VAL (PRCM_REQ_MB5 + 0x3)
-#define PRCMU_I2C_WRITE(slave) \
- (((slave) << 1) | (cpu_is_u8500v2() ? BIT(6) : 0))
-#define PRCMU_I2C_READ(slave) \
- (((slave) << 1) | BIT(0) | (cpu_is_u8500v2() ? BIT(6) : 0))
+#define PRCMU_I2C_WRITE(slave) (((slave) << 1) | BIT(6))
+#define PRCMU_I2C_READ(slave) (((slave) << 1) | BIT(0) | BIT(6))
#define PRCMU_I2C_STOP_EN BIT(3)
/* Mailbox 5 ACKs */
@@ -424,6 +426,13 @@ static DEFINE_SPINLOCK(clkout_lock);
/* Global var to runtime determine TCDM base for v2 or v1 */
static __iomem void *tcdm_base;
+/*
+ * Copies of the startup values of the reset status register and the SW reset
+ * code.
+ */
+static u32 reset_status_copy;
+static u16 reset_code_copy;
+
struct clk_mgt {
void __iomem *reg;
u32 pllsw;
@@ -637,6 +646,26 @@ void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
spin_unlock_irqrestore(&prcmu_lock, flags);
}
+/*
+ * Dump AB8500 registers, PRCMU registers and PRCMU data memory
+ * on critical errors.
+ */
+static void db8500_prcmu_debug_dump(const char *func,
+ bool dump_prcmu, bool dump_abb)
+{
+ printk(KERN_DEBUG"%s: timeout\n", func);
+
+ /* Dump AB8500 registers */
+ if (dump_abb)
+ abx500_dump_all_banks();
+
+ /* Dump prcmu registers and data memory */
+ if (dump_prcmu) {
+ prcmu_debug_dump_regs();
+ prcmu_debug_dump_data_mem();
+ }
+}
+
struct prcmu_fw_version *prcmu_get_fw_version(void)
{
return fw_info.valid ? &fw_info.version : NULL;
@@ -648,6 +677,11 @@ bool prcmu_has_arm_maxopp(void)
PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;
}
+void db8500_prcmu_vc(bool enable)
+{
+ writel((enable ? 0xF : 0), (tcdm_base + PRCM_TCDM_VOICE_CALL_FLAG));
+}
+
/**
* prcmu_get_boot_status - PRCMU boot status checking
* Returns: the current PRCMU boot status
@@ -1049,7 +1083,7 @@ int db8500_prcmu_set_ddr_opp(u8 opp)
if (opp < DDR_100_OPP || opp > DDR_25_OPP)
return -EINVAL;
/* Changing the DDR OPP can hang the hardware pre-v21 */
- if (cpu_is_u8500v20_or_later() && !cpu_is_u8500v20())
+ if (!cpu_is_u8500v20())
writeb(opp, PRCM_DDR_SUBSYS_APE_MINBW);
return 0;
@@ -1111,12 +1145,14 @@ unlock_and_return:
int db8500_prcmu_set_ape_opp(u8 opp)
{
int r = 0;
+ u8 prcmu_opp_req;
if (opp == mb1_transfer.ape_opp)
return 0;
mutex_lock(&mb1_transfer.lock);
+ /* Exit APE_50_PARTLY_25_OPP */
if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)
request_even_slower_clocks(false);
@@ -1126,20 +1162,22 @@ int db8500_prcmu_set_ape_opp(u8 opp)
while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))
cpu_relax();
+ prcmu_opp_req = (opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp;
+
writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));
writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP));
- writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp),
- (tcdm_base + PRCM_REQ_MB1_APE_OPP));
+ writeb(prcmu_opp_req, (tcdm_base + PRCM_REQ_MB1_APE_OPP));
writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);
wait_for_completion(&mb1_transfer.work);
if ((mb1_transfer.ack.header != MB1H_ARM_APE_OPP) ||
- (mb1_transfer.ack.ape_opp != opp))
+ (mb1_transfer.ack.ape_opp != prcmu_opp_req))
r = -EIO;
skip_message:
if ((!r && (opp == APE_50_PARTLY_25_OPP)) ||
+ /* Set APE_50_PARTLY_25_OPP back in case new opp failed */
(r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP)))
request_even_slower_clocks(true);
if (!r)
@@ -1322,6 +1360,7 @@ int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, true);
goto unlock_and_return;
}
@@ -1416,6 +1455,7 @@ static int request_sysclk(bool enable)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, true);
}
mutex_unlock(&mb3_transfer.sysclk_lock);
@@ -2190,6 +2230,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, false);
} else {
r = ((mb5_transfer.ack.status == I2C_RD_OK) ? 0 : -EIO);
}
@@ -2240,6 +2281,7 @@ int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size)
pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
__func__);
r = -EIO;
+ db8500_prcmu_debug_dump(__func__, true, false);
} else {
r = ((mb5_transfer.ack.status == I2C_WR_OK) ? 0 : -EIO);
}
@@ -2287,6 +2329,7 @@ retry:
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) {
+ db8500_prcmu_debug_dump(__func__, true, true);
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
goto unlock_and_return;
@@ -2309,6 +2352,7 @@ retry:
if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000)))
goto retry;
+ db8500_prcmu_debug_dump(__func__, true, true);
pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
__func__);
}
@@ -2335,6 +2379,7 @@ void prcmu_ac_sleep_req()
if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
msecs_to_jiffies(5000))) {
+ db8500_prcmu_debug_dump(__func__, true, true);
pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
__func__);
}
@@ -2370,7 +2415,17 @@ void db8500_prcmu_system_reset(u16 reset_code)
*/
u16 db8500_prcmu_get_reset_code(void)
{
- return readw(tcdm_base + PRCM_SW_RST_REASON);
+ return reset_code_copy;
+}
+
+/**
+ * db8500_prcmu_get_reset_status - Retrieve reset status
+ *
+ * Retrieves the value of the reset status register as read at startup.
+ */
+u32 db8500_prcmu_get_reset_status(void)
+{
+ return reset_status_copy;
}
/**
@@ -2437,6 +2492,13 @@ static bool read_mailbox_0(void)
if (ev & WAKEUP_BIT_SYSCLK_OK)
complete(&mb3_transfer.sysclk_work);
+ prcmu_debug_register_mbox0_event(ev,
+ (mb0_transfer.req.dbb_irqs |
+ mb0_transfer.req.dbb_wakeups |
+ WAKEUP_BIT_AC_WAKE_ACK |
+ WAKEUP_BIT_AC_SLEEP_ACK |
+ WAKEUP_BIT_SYSCLK_OK));
+
ev &= mb0_transfer.req.dbb_irqs;
for (n = 0; n < NUM_PRCMU_WAKEUPS; n++) {
@@ -2561,6 +2623,7 @@ static irqreturn_t prcmu_irq_handler(int irq, void *data)
bits -= MBOX_BIT(n);
if (read_mailbox[n]())
r = IRQ_WAKE_THREAD;
+ prcmu_debug_register_interrupt(n);
}
}
return r;
@@ -2646,29 +2709,38 @@ static char *fw_project_name(u8 project)
void __init db8500_prcmu_early_init(void)
{
unsigned int i;
- if (cpu_is_u8500v2()) {
- void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
-
- if (tcpm_base != NULL) {
- u32 version;
- version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
- fw_info.version.project = version & 0xFF;
- fw_info.version.api_version = (version >> 8) & 0xFF;
- fw_info.version.func_version = (version >> 16) & 0xFF;
- fw_info.version.errata = (version >> 24) & 0xFF;
- fw_info.valid = true;
- pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
- fw_project_name(fw_info.version.project),
- (version >> 8) & 0xFF, (version >> 16) & 0xFF,
- (version >> 24) & 0xFF);
- iounmap(tcpm_base);
- }
+ void *tcpm_base = ioremap_nocache(U8500_PRCMU_TCPM_BASE, SZ_4K);
+ void __iomem *sec_base;
+
+ if (tcpm_base != NULL) {
+ u32 version;
+ version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET);
+ fw_info.version.project = version & 0xFF;
+ fw_info.version.api_version = (version >> 8) & 0xFF;
+ fw_info.version.func_version = (version >> 16) & 0xFF;
+ fw_info.version.errata = (version >> 24) & 0xFF;
+ fw_info.valid = true;
+ pr_info("PRCMU firmware: %s, version %d.%d.%d\n",
+ fw_project_name(fw_info.version.project),
+ (version >> 8) & 0xFF, (version >> 16) & 0xFF,
+ (version >> 24) & 0xFF);
+ iounmap(tcpm_base);
+ }
- tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
- } else {
- pr_err("prcmu: Unsupported chip version\n");
- BUG();
+ tcdm_base = __io_address(U8500_PRCMU_TCDM_BASE);
+
+ /*
+ * Copy the value of the reset status register and if needed also
+ * the software reset code.
+ */
+ sec_base = ioremap_nocache(U8500_PRCMU_SEC_BASE, SZ_4K);
+ if (sec_base != NULL) {
+ reset_status_copy = readl(sec_base +
+ DB8500_SEC_PRCM_RESET_STATUS);
+ iounmap(sec_base);
}
+ if (reset_status_copy & DB8500_SEC_PRCM_RESET_STATUS_APE_SOFTWARE_RESET)
+ reset_code_copy = readw(tcdm_base + PRCM_SW_RST_REASON);
spin_lock_init(&mb0_transfer.lock);
spin_lock_init(&mb0_transfer.dbb_irqs_lock);
@@ -2734,6 +2806,7 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = {
REGULATOR_SUPPLY("vcore", "uart2"),
REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"),
REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"),
+ REGULATOR_SUPPLY("vddvario", "smsc911x.0"),
};
static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
@@ -2743,7 +2816,7 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
};
static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
- REGULATOR_SUPPLY("vsupply", "b2r2_bus"),
+ REGULATOR_SUPPLY("vsupply", "b2r2_core"),
REGULATOR_SUPPLY("vsupply", "mcde"),
};
@@ -2962,9 +3035,6 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
{
int err = 0;
- if (ux500_is_svp())
- return -ENODEV;
-
init_prcm_registers();
/* Clean up the mailbox interrupts after pre-kernel code. */
@@ -2978,8 +3048,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
goto no_irq_return;
}
- if (cpu_is_u8500v20_or_later())
- prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
+ prcmu_config_esram0_deep_sleep(ESRAM0_DEEP_SLEEP_STATE_RET);
err = mfd_add_devices(&pdev->dev, 0, db8500_prcmu_devs,
ARRAY_SIZE(db8500_prcmu_devs), NULL,
diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h
index 3a0bf91d778..0835a57dac1 100644
--- a/drivers/mfd/dbx500-prcmu-regs.h
+++ b/drivers/mfd/dbx500-prcmu-regs.h
@@ -34,6 +34,9 @@
#define PRCM_PER5CLK_MGT PRCM_CLK_MGT(0x038)
#define PRCM_PER6CLK_MGT PRCM_CLK_MGT(0x03C)
#define PRCM_PER7CLK_MGT PRCM_CLK_MGT(0x040)
+#define PRCM_PWMCLK_MGT PRCM_CLK_MGT(0x044) /* for DB5500 */
+#define PRCM_IRDACLK_MGT PRCM_CLK_MGT(0x048) /* for DB5500 */
+#define PRCM_IRRCCLK_MGT PRCM_CLK_MGT(0x04C) /* for DB5500 */
#define PRCM_LCDCLK_MGT PRCM_CLK_MGT(0x044)
#define PRCM_BMLCLK_MGT PRCM_CLK_MGT(0x04C)
#define PRCM_HSITXCLK_MGT PRCM_CLK_MGT(0x050)
@@ -124,7 +127,6 @@
#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168)
#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484)
#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488)
-#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018)
/* System reset register */
#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
@@ -247,4 +249,17 @@
/* System reset register */
#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228)
+/* Secure read-only registers */
+#define DB8500_SEC_PRCM_RESET_STATUS 0x03C
+#define DB8500_SEC_PRCM_RESET_STATUS_A9_CPU0_WATCHDOG_RESET BIT(0)
+#define DB8500_SEC_PRCM_RESET_STATUS_A9_CPU1_WATCHDOG_RESET BIT(1)
+#define DB8500_SEC_PRCM_RESET_STATUS_APE_SOFTWARE_RESET BIT(2)
+#define DB8500_SEC_PRCM_RESET_STATUS_APE_RESET BIT(3)
+#define DB8500_SEC_PRCM_RESET_STATUS_SECURE_WATCHDOG BIT(4)
+#define DB8500_SEC_PRCM_RESET_STATUS_POWER_ON_RESET BIT(5)
+#define DB8500_SEC_PRCM_RESET_STATUS_A9_RESTART BIT(6)
+#define DB8500_SEC_PRCM_RESET_STATUS_APE_RESTART BIT(7)
+#define DB8500_SEC_PRCM_RESET_STATUS_MODEM_SOFTWARE_RESET BIT(8)
+#define DB8500_SEC_PRCM_RESET_STATUS_BOOT_ENGI BIT(16)
+
#endif /* __DB8500_PRCMU_REGS_H */
diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c
index 2dd8d49cb30..6b8f9417c00 100644
--- a/drivers/mfd/stmpe.c
+++ b/drivers/mfd/stmpe.c
@@ -772,7 +772,7 @@ static irqreturn_t stmpe_irq(int irq, void *data)
ret = stmpe_block_read(stmpe, israddr, num, isr);
if (ret < 0)
return IRQ_NONE;
-
+back:
for (i = 0; i < num; i++) {
int bank = num - i - 1;
u8 status = isr[i];
@@ -794,6 +794,22 @@ static irqreturn_t stmpe_irq(int irq, void *data)
stmpe_reg_write(stmpe, israddr + i, clear);
}
+ /*
+ It may happen that on the first status read interrupt
+ sources may not showup, so read one more time.
+ */
+ ret = stmpe_block_read(stmpe, israddr, num, isr);
+ if (ret >= 0) {
+ for (i = 0; i < num; i++) {
+ int bank = num - i - 1;
+ u8 status = isr[i];
+
+ status &= stmpe->ier[bank];
+ if (status)
+ goto back;
+ }
+ }
+
return IRQ_HANDLED;
}
diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c
new file mode 100644
index 00000000000..91211f29623
--- /dev/null
+++ b/drivers/mfd/tc35892.c
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/tc35892.h>
+
+#define TC35892_CLKMODE_MODCTL_SLEEP 0x0
+#define TC35892_CLKMODE_MODCTL_OPERATION (1 << 0)
+
+/**
+ * tc35892_reg_read() - read a single TC35892 register
+ * @tc35892: Device to read from
+ * @reg: Register to read
+ */
+int tc35892_reg_read(struct tc35892 *tc35892, u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(tc35892->i2c, reg);
+ if (ret < 0)
+ dev_err(tc35892->dev, "failed to read reg %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_reg_read);
+
+/**
+ * tc35892_reg_read() - write a single TC35892 register
+ * @tc35892: Device to write to
+ * @reg: Register to read
+ * @data: Value to write
+ */
+int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data);
+ if (ret < 0)
+ dev_err(tc35892->dev, "failed to write reg %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_reg_write);
+
+/**
+ * tc35892_block_read() - read multiple TC35892 registers
+ * @tc35892: Device to read from
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Buffer to write to
+ */
+int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values)
+{
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values);
+ if (ret < 0)
+ dev_err(tc35892->dev, "failed to read regs %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_block_read);
+
+/**
+ * tc35892_block_write() - write multiple TC35892 registers
+ * @tc35892: Device to write to
+ * @reg: First register
+ * @length: Number of registers
+ * @values: Values to write
+ */
+int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
+ const u8 *values)
+{
+ int ret;
+
+ ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length,
+ values);
+ if (ret < 0)
+ dev_err(tc35892->dev, "failed to write regs %#x: %d\n",
+ reg, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_block_write);
+
+/**
+ * tc35892_set_bits() - set the value of a bitfield in a TC35892 register
+ * @tc35892: Device to write to
+ * @reg: Register to write
+ * @mask: Mask of bits to set
+ * @values: Value to set
+ */
+int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val)
+{
+ int ret;
+
+ mutex_lock(&tc35892->lock);
+
+ ret = tc35892_reg_read(tc35892, reg);
+ if (ret < 0)
+ goto out;
+
+ ret &= ~mask;
+ ret |= val;
+
+ ret = tc35892_reg_write(tc35892, reg, ret);
+
+out:
+ mutex_unlock(&tc35892->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tc35892_set_bits);
+
+static struct resource gpio_resources[] = {
+ {
+ .start = TC35892_INT_GPIIRQ,
+ .end = TC35892_INT_GPIIRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mfd_cell tc35892_devs[] = {
+ {
+ .name = "tc35892-gpio",
+ .num_resources = ARRAY_SIZE(gpio_resources),
+ .resources = &gpio_resources[0],
+ },
+};
+
+static irqreturn_t tc35892_irq(int irq, void *data)
+{
+ struct tc35892 *tc35892 = data;
+ int status;
+
+again:
+ status = tc35892_reg_read(tc35892, TC35892_IRQST);
+ if (status < 0)
+ return IRQ_NONE;
+
+ while (status) {
+ int bit = __ffs(status);
+
+ handle_nested_irq(tc35892->irq_base + bit);
+ status &= ~(1 << bit);
+ }
+
+ /*
+ * A dummy read or write (to any register) appears to be necessary to
+ * have the last interrupt clear (for example, GPIO IC write) take
+ * effect. In such a case, recheck for any interrupt which is still
+ * pending.
+ */
+ status = tc35892_reg_read(tc35892, TC35892_IRQST);
+ if (status)
+ goto again;
+
+ return IRQ_HANDLED;
+}
+
+static void tc35892_irq_dummy(unsigned int irq)
+{
+ /* No mask/unmask at this level */
+}
+
+static struct irq_chip tc35892_irq_chip = {
+ .name = "tc35892",
+ .irq_mask = tc35892_irq_dummy,
+ .irq_unmask = tc35892_irq_dummy,
+};
+
+static int tc35892_irq_init(struct tc35892 *tc35892)
+{
+ int base = tc35892->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
+ irq_set_chip_data(irq, tc35892);
+ irq_set_chip_and_handler(irq, &tc35892_irq_chip,
+ handle_edge_irq);
+ irq_set_nested_thread(irq, 1);
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, IRQF_VALID);
+#else
+ set_irq_noprobe(irq);
+#endif
+ }
+
+ return 0;
+}
+
+static void tc35892_irq_remove(struct tc35892 *tc35892)
+{
+ int base = tc35892->irq_base;
+ int irq;
+
+ for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) {
+#ifdef CONFIG_ARM
+ set_irq_flags(irq, 0);
+#endif
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+ }
+}
+
+static int tc35892_chip_init(struct tc35892 *tc35892)
+{
+ int manf, ver, ret;
+
+ manf = tc35892_reg_read(tc35892, TC35892_MANFCODE);
+ if (manf < 0)
+ return manf;
+
+ ver = tc35892_reg_read(tc35892, TC35892_VERSION);
+ if (ver < 0)
+ return ver;
+
+ if (manf != TC35892_MANFCODE_MAGIC) {
+ dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf);
+ return -EINVAL;
+ }
+
+ dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver);
+
+ /*
+ * Put everything except the IRQ module into reset;
+ * also spare the GPIO module for any pin initialization
+ * done during pre-kernel boot
+ */
+ ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL,
+ TC35892_RSTCTRL_TIMRST
+ | TC35892_RSTCTRL_ROTRST
+ | TC35892_RSTCTRL_KBDRST);
+ if (ret < 0)
+ return ret;
+
+ /* Clear the reset interrupt. */
+ return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1);
+}
+
+static int __devinit tc35892_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tc35892_platform_data *pdata = i2c->dev.platform_data;
+ struct tc35892 *tc35892;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA
+ | I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EIO;
+
+ tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL);
+ if (!tc35892)
+ return -ENOMEM;
+
+ mutex_init(&tc35892->lock);
+
+ tc35892->dev = &i2c->dev;
+ tc35892->i2c = i2c;
+ tc35892->pdata = pdata;
+ tc35892->irq_base = pdata->irq_base;
+ tc35892->num_gpio = id->driver_data;
+
+ i2c_set_clientdata(i2c, tc35892);
+
+ ret = tc35892_chip_init(tc35892);
+ if (ret)
+ goto out_free;
+
+ ret = tc35892_irq_init(tc35892);
+ if (ret)
+ goto out_free;
+
+ ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "tc35892", tc35892);
+ if (ret) {
+ dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret);
+ goto out_removeirq;
+ }
+
+ ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs,
+ ARRAY_SIZE(tc35892_devs), NULL,
+ tc35892->irq_base);
+ if (ret) {
+ dev_err(tc35892->dev, "failed to add children\n");
+ goto out_freeirq;
+ }
+
+ return 0;
+
+out_freeirq:
+ free_irq(tc35892->i2c->irq, tc35892);
+out_removeirq:
+ tc35892_irq_remove(tc35892);
+out_free:
+ kfree(tc35892);
+ return ret;
+}
+
+static int __devexit tc35892_remove(struct i2c_client *client)
+{
+ struct tc35892 *tc35892 = i2c_get_clientdata(client);
+
+ mfd_remove_devices(tc35892->dev);
+
+ free_irq(tc35892->i2c->irq, tc35892);
+ tc35892_irq_remove(tc35892);
+
+ kfree(tc35892);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static u32 sleep_regs[] = {
+ TC35892_IOPC0_L,
+ TC35892_IOPC0_H,
+ TC35892_IOPC1_L,
+ TC35892_IOPC1_H,
+ TC35892_IOPC2_L,
+ TC35892_IOPC2_H,
+ TC35892_DRIVE0_L,
+ TC35892_DRIVE0_H,
+ TC35892_DRIVE1_L,
+ TC35892_DRIVE1_H,
+ TC35892_DRIVE2_L,
+ TC35892_DRIVE2_H,
+ TC35892_DRIVE3,
+ TC35892_GPIODATA0,
+ TC35892_GPIOMASK0,
+ TC35892_GPIODATA1,
+ TC35892_GPIOMASK1,
+ TC35892_GPIODATA2,
+ TC35892_GPIOMASK2,
+ TC35892_GPIODIR0,
+ TC35892_GPIODIR1,
+ TC35892_GPIODIR2,
+ TC35892_GPIOIE0,
+ TC35892_GPIOIE1,
+ TC35892_GPIOIE2,
+ TC35892_RSTCTRL,
+ TC35892_CLKCFG,
+};
+
+static u8 sleep_regs_val[] = {
+ 0x00, /* TC35892_IOPC0_L */
+ 0x00, /* TC35892_IOPC0_H */
+ 0x00, /* TC35892_IOPC1_L */
+ 0x00, /* TC35892_IOPC1_H */
+ 0x00, /* TC35892_IOPC2_L */
+ 0x00, /* TC35892_IOPC2_H */
+ 0xff, /* TC35892_DRIVE0_L */
+ 0xff, /* TC35892_DRIVE0_H */
+ 0xff, /* TC35892_DRIVE1_L */
+ 0xff, /* TC35892_DRIVE1_H */
+ 0xff, /* TC35892_DRIVE2_L */
+ 0xff, /* TC35892_DRIVE2_H */
+ 0x0f, /* TC35892_DRIVE3 */
+ 0x80, /* TC35892_GPIODATA0 */
+ 0x80, /* TC35892_GPIOMASK0 */
+ 0x80, /* TC35892_GPIODATA1 */
+ 0x80, /* TC35892_GPIOMASK1 */
+ 0x06, /* TC35892_GPIODATA2 */
+ 0x06, /* TC35892_GPIOMASK2 */
+ 0xf0, /* TC35892_GPIODIR0 */
+ 0xe0, /* TC35892_GPIODIR1 */
+ 0xee, /* TC35892_GPIODIR2 */
+ 0x0f, /* TC35892_GPIOIE0 */
+ 0x1f, /* TC35892_GPIOIE1 */
+ 0x11, /* TC35892_GPIOIE2 */
+ 0x0f, /* TC35892_RSTCTRL */
+ 0xb0 /* TC35892_CLKCFG */
+
+};
+
+static u8 sleep_regs_backup[ARRAY_SIZE(sleep_regs)];
+
+static int tc35892_suspend(struct device *dev)
+{
+ struct tc35892 *tc35892 = dev_get_drvdata(dev);
+ struct i2c_client *client = tc35892->i2c;
+ int ret = 0;
+ int i, j;
+ int val;
+
+ /* Put the system to sleep mode */
+ if (!device_may_wakeup(&client->dev)) {
+ for (i = 0; i < ARRAY_SIZE(sleep_regs); i++) {
+ val = tc35892_reg_read(tc35892,
+ sleep_regs[i]);
+ if (val < 0)
+ goto out;
+
+ sleep_regs_backup[i] = (u8) (val & 0xff);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sleep_regs); i++) {
+ ret = tc35892_reg_write(tc35892,
+ sleep_regs[i],
+ sleep_regs_val[i]);
+ if (ret < 0)
+ goto fail;
+
+ }
+
+ ret = tc35892_reg_write(tc35892,
+ TC35892_CLKMODE,
+ TC35892_CLKMODE_MODCTL_SLEEP);
+ }
+out:
+ return ret;
+fail:
+ for (j = 0; j <= i; j++) {
+ ret = tc35892_reg_write(tc35892,
+ sleep_regs[i],
+ sleep_regs_backup[i]);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+static int tc35892_resume(struct device *dev)
+{
+ struct tc35892 *tc35892 = dev_get_drvdata(dev);
+ struct i2c_client *client = tc35892->i2c;
+ int ret = 0;
+ int i;
+
+ /* Enable the system into operation */
+ if (!device_may_wakeup(&client->dev))
+ {
+ ret = tc35892_reg_write(tc35892,
+ TC35892_CLKMODE,
+ TC35892_CLKMODE_MODCTL_OPERATION);
+ if (ret < 0)
+ goto out;
+
+ for (i = ARRAY_SIZE(sleep_regs) - 1; i >= 0; i--) {
+ ret = tc35892_reg_write(tc35892,
+ sleep_regs[i],
+ sleep_regs_backup[i]);
+ /* Not much to do here if we fail */
+ if (ret < 0)
+ break;
+ }
+ }
+out:
+ return ret;
+}
+
+static const struct dev_pm_ops tc35892_dev_pm_ops = {
+ .suspend = tc35892_suspend,
+ .resume = tc35892_resume,
+};
+#endif
+
+static const struct i2c_device_id tc35892_id[] = {
+ { "tc35892", 24 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tc35892_id);
+
+static struct i2c_driver tc35892_driver = {
+ .driver.name = "tc35892",
+ .driver.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .driver.pm = &tc35892_dev_pm_ops,
+#endif
+ .probe = tc35892_probe,
+ .remove = __devexit_p(tc35892_remove),
+ .id_table = tc35892_id,
+};
+
+static int __init tc35892_init(void)
+{
+ return i2c_add_driver(&tc35892_driver);
+}
+subsys_initcall(tc35892_init);
+
+static void __exit tc35892_exit(void)
+{
+ i2c_del_driver(&tc35892_driver);
+}
+module_exit(tc35892_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TC35892 MFD core driver");
+MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent");
diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c
index de979742c6f..0e79fe2d214 100644
--- a/drivers/mfd/tc3589x.c
+++ b/drivers/mfd/tc3589x.c
@@ -358,16 +358,114 @@ static int __devexit tc3589x_remove(struct i2c_client *client)
}
#ifdef CONFIG_PM
+
+static u32 sleep_regs[] = {
+ TC3589x_IOPC0_L,
+ TC3589x_IOPC0_H,
+ TC3589x_IOPC1_L,
+ TC3589x_IOPC1_H,
+ TC3589x_IOPC2_L,
+ TC3589x_IOPC2_H,
+ TC3589x_DRIVE0_L,
+ TC3589x_DRIVE0_H,
+ TC3589x_DRIVE1_L,
+ TC3589x_DRIVE1_H,
+ TC3589x_DRIVE2_L,
+ TC3589x_DRIVE2_H,
+ TC3589x_DRIVE3,
+ TC3589x_GPIODATA0,
+ TC3589x_GPIOMASK0,
+ TC3589x_GPIODATA1,
+ TC3589x_GPIOMASK1,
+ TC3589x_GPIODATA2,
+ TC3589x_GPIOMASK2,
+ TC3589x_GPIODIR0,
+ TC3589x_GPIODIR1,
+ TC3589x_GPIODIR2,
+ TC3589x_GPIOIE0,
+ TC3589x_GPIOIE1,
+ TC3589x_GPIOIE2,
+ TC3589x_RSTCTRL,
+ TC3589x_CLKCFG,
+};
+
+static u8 sleep_regs_val[] = {
+ 0x00, /* TC3589x_IOPC0_L */
+ 0x00, /* TC3589x_IOPC0_H */
+ 0x00, /* TC3589x_IOPC1_L */
+ 0x00, /* TC3589x_IOPC1_H */
+ 0x00, /* TC3589x_IOPC2_L */
+ 0x00, /* TC3589x_IOPC2_H */
+ 0xff, /* TC3589x_DRIVE0_L */
+ 0xff, /* TC3589x_DRIVE0_H */
+ 0xff, /* TC3589x_DRIVE1_L */
+ 0xff, /* TC3589x_DRIVE1_H */
+ 0xff, /* TC3589x_DRIVE2_L */
+ 0xff, /* TC3589x_DRIVE2_H */
+ 0x0f, /* TC3589x_DRIVE3 */
+ 0x80, /* TC3589x_GPIODATA0 */
+ 0x80, /* TC3589x_GPIOMASK0 */
+ 0x80, /* TC3589x_GPIODATA1 */
+ 0x80, /* TC3589x_GPIOMASK1 */
+ 0x06, /* TC3589x_GPIODATA2 */
+ 0x06, /* TC3589x_GPIOMASK2 */
+ 0xf0, /* TC3589x_GPIODIR0 */
+ 0xe0, /* TC3589x_GPIODIR1 */
+ 0xee, /* TC3589x_GPIODIR2 */
+ 0x0f, /* TC3589x_GPIOIE0 */
+ 0x1f, /* TC3589x_GPIOIE1 */
+ 0x11, /* TC3589x_GPIOIE2 */
+ 0x0f, /* TC3589x_RSTCTRL */
+ 0xb0 /* TC3589x_CLKCFG */
+
+};
+
+static u8 sleep_regs_backup[ARRAY_SIZE(sleep_regs)];
+
static int tc3589x_suspend(struct device *dev)
{
struct tc3589x *tc3589x = dev_get_drvdata(dev);
struct i2c_client *client = tc3589x->i2c;
int ret = 0;
+ int i, j;
+ int val;
+
+ /* Put the system to sleep mode */
+ if (!device_may_wakeup(&client->dev)) {
+ for (i = 0; i < ARRAY_SIZE(sleep_regs); i++) {
+ val = tc3589x_reg_read(tc3589x,
+ sleep_regs[i]);
+ if (val < 0)
+ goto out;
+
+ sleep_regs_backup[i] = (u8) (val & 0xff);
+ }
- /* put the system to sleep mode */
- if (!device_may_wakeup(&client->dev))
- ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
- TC3589x_CLKMODE_MODCTL_SLEEP);
+ for (i = 0; i < ARRAY_SIZE(sleep_regs); i++) {
+ ret = tc3589x_reg_write(tc3589x,
+ sleep_regs[i],
+ sleep_regs_val[i]);
+ if (ret < 0)
+ goto fail;
+
+ }
+
+ ret = tc3589x_reg_write(tc3589x,
+ TC3589x_CLKMODE,
+ TC3589x_CLKMODE_MODCTL_SLEEP);
+ } else {
+ enable_irq_wake(client->irq);
+ }
+out:
+ return ret;
+fail:
+ for (j = 0; j <= i; j++) {
+ ret = tc3589x_reg_write(tc3589x,
+ sleep_regs[i],
+ sleep_regs_backup[i]);
+ if (ret < 0)
+ break;
+ }
return ret;
}
@@ -377,12 +475,29 @@ static int tc3589x_resume(struct device *dev)
struct tc3589x *tc3589x = dev_get_drvdata(dev);
struct i2c_client *client = tc3589x->i2c;
int ret = 0;
+ int i;
- /* enable the system into operation */
+ /* Enable the system into operation */
if (!device_may_wakeup(&client->dev))
- ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE,
- TC3589x_CLKMODE_MODCTL_OPERATION);
-
+ {
+ ret = tc3589x_reg_write(tc3589x,
+ TC3589x_CLKMODE,
+ TC3589x_CLKMODE_MODCTL_OPERATION);
+ if (ret < 0)
+ goto out;
+
+ for (i = ARRAY_SIZE(sleep_regs) - 1; i >= 0; i--) {
+ ret = tc3589x_reg_write(tc3589x,
+ sleep_regs[i],
+ sleep_regs_backup[i]);
+ /* Not much to do here if we fail */
+ if (ret < 0)
+ break;
+ }
+ } else {
+ disable_irq_wake(client->irq);
+ }
+out:
return ret;
}
diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c
index a293b978e27..d7b9e0c60ea 100644
--- a/drivers/mfd/tps6105x.c
+++ b/drivers/mfd/tps6105x.c
@@ -195,6 +195,7 @@ static int __devinit tps6105x_probe(struct i2c_client *client,
return 0;
fail:
+ i2c_set_clientdata(client, NULL);
kfree(tps6105x);
return ret;
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index c7795096d43..7b7fc5c7489 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -498,6 +498,7 @@ config MAX8997_MUIC
Maxim MAX8997 PMIC.
The MAX8997 MUIC is a USB port accessory detector and switch.
+source "drivers/misc/Kconfig.stm"
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Kconfig.stm b/drivers/misc/Kconfig.stm
new file mode 100644
index 00000000000..d509c85c79f
--- /dev/null
+++ b/drivers/misc/Kconfig.stm
@@ -0,0 +1,120 @@
+menuconfig STM_TRACE
+ bool "STM MIPI Trace driver"
+ depends on ARCH_U8500
+ help
+ Simple System Trace Module driver. It allows to use and configure the
+ STM, either from kernel space, or from user space.
+
+if STM_TRACE
+
+config STM_NUMBER_OF_CHANNEL
+ int
+ default 512 if ARCH_U8500
+ default 256
+ help
+ Number Max of channels always a multiple of 256
+
+config STM_DEFAULT_MASTERS_MODES
+ hex "channel mode"
+ default 0xffffffff
+ help
+ Default config for enabling hardware mode tracing
+
+config STM_PRINTK
+ bool "printk support"
+ depends on STM_TRACE
+ help
+ Duplicate printk output on STM printk channel & activate stm_printk
+
+config STM_PRINTK_CHANNEL
+ int "printk channel"
+ range 0 255
+ depends on STM_PRINTK
+ default 255
+ help
+ STM printk channel number
+
+config STM_FTRACE
+ bool "functions tracing"
+ depends on FTRACE
+ default y
+ help
+ Output function tracing on STM dedicated channel
+
+config STM_FTRACE_CHANNEL
+ int "ftrace channel"
+ range 0 255
+ depends on STM_FTRACE
+ default 254
+ help
+ STM ftrace channel number
+
+config STM_CTX_SWITCH
+ bool "Context switch tracing"
+ depends on CONTEXT_SWITCH_TRACER
+ default y
+ help
+ Output scheduler context switch on STM dedicated channel
+
+config STM_CTX_SWITCH_CHANNEL
+ int "Context switch channel"
+ range 0 255
+ depends on STM_CTX_SWITCH
+ default 253
+ help
+ STM Context switch channel number
+
+config STM_WAKEUP
+ bool "Scheduler wakeup tracing"
+ depends on CONTEXT_SWITCH_TRACER
+ default y
+ help
+ Output scheduler wakeup on STM dedicated channel
+
+config STM_WAKEUP_CHANNEL
+ int "Wakeup channel"
+ range 0 255
+ depends on STM_WAKEUP
+ default 252
+ help
+ STM scheduler wakeup channel number
+
+config STM_STACK_TRACE
+ bool "Stack tracing"
+ depends on STACKTRACE
+ default y
+ help
+ Output stack tracing on STM dedicated channel
+
+config STM_STACK_TRACE_CHANNEL
+ int "Stack trace channel"
+ range 0 255
+ depends on STM_STACK_TRACE
+ default 251
+ help
+ STM stack trace channel number
+
+config STM_TRACE_PRINTK
+ bool "trace printk & binary printk support"
+ depends on TRACING
+ default y
+ help
+ Duplicate trace printk output on STM printk channel
+
+config STM_TRACE_PRINTK_CHANNEL
+ int "trace_printk channel"
+ range 0 255
+ depends on TRACING
+ default 250
+ help
+ STM trace_printk channel number
+
+config STM_TRACE_BPRINTK_CHANNEL
+ int "trace_bprintk channel"
+ range 0 255
+ depends on TRACING
+ default 249
+ help
+ STM trace binary printk channel number
+
+endif
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3e1d80106f0..90f219db507 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -46,6 +46,7 @@ obj-y += ti-st/
obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
obj-y += lis3lv02d/
obj-y += carma/
+obj-$(CONFIG_STM_TRACE) += stm.o
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o
diff --git a/drivers/misc/ab8500-pwm.c b/drivers/misc/ab8500-pwm.c
index d7a9aa14e5d..9d864e4db5a 100644
--- a/drivers/misc/ab8500-pwm.c
+++ b/drivers/misc/ab8500-pwm.c
@@ -8,10 +8,11 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/pwm.h>
+#include <linux/clk.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/module.h>
-
+#include <linux/mfd/ab8500/pwmleds.h>
/*
* PWM Out generators
* Bank: 0x10
@@ -19,6 +20,11 @@
#define AB8500_PWM_OUT_CTRL1_REG 0x60
#define AB8500_PWM_OUT_CTRL2_REG 0x61
#define AB8500_PWM_OUT_CTRL7_REG 0x66
+#define AB8505_PWM_OUT_BLINK_CTRL1_REG 0x68
+#define AB8505_PWM_OUT_BLINK_CTRL4_REG 0x6B
+#define AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT 4
+#define AB8505_PWM_OUT_BLINK_DUTYMASK (0x0F << AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT)
+
/* backlight driver constants */
#define ENABLE_PWM 1
@@ -27,12 +33,73 @@
struct pwm_device {
struct device *dev;
struct list_head node;
+ struct clk *clk;
const char *label;
unsigned int pwm_id;
+ unsigned int num_pwm;
+ unsigned int blink_en;
+ struct ab8500 *parent;
+ bool clk_enabled;
};
static LIST_HEAD(pwm_list);
+int pwm_config_blink(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ int ret;
+ unsigned int value;
+ u8 reg;
+ if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) {
+ dev_err(pwm->dev, "setting blinking for this "
+ "device not supported\n");
+ return -EINVAL;
+ }
+ /*
+ * get the period value that is to be written to
+ * AB8500_PWM_OUT_BLINK_CTRL1 REGS[0:2]
+ */
+ value = period_ns & 0x07;
+ /*
+ * get blink duty value to be written to
+ * AB8500_PWM_OUT_BLINK_CTRL REGS[7:4]
+ */
+ value |= ((duty_ns << AB8505_PWM_OUT_BLINK_CTRL_DUTYBIT) &
+ AB8505_PWM_OUT_BLINK_DUTYMASK);
+
+ reg = AB8505_PWM_OUT_BLINK_CTRL1_REG + (pwm->pwm_id - 1);
+
+ ret = abx500_set_register_interruptible(pwm->dev, AB8500_MISC,
+ reg, (u8)value);
+ if (ret < 0)
+ dev_err(pwm->dev, "%s: Failed to config PWM blink,Error %d\n",
+ pwm->label, ret);
+ return ret;
+}
+EXPORT_SYMBOL(pwm_config_blink);
+
+int pwm_blink_ctrl(struct pwm_device *pwm , int enable)
+{
+ int ret;
+
+ if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) {
+ dev_err(pwm->dev, "setting blinking for this "
+ "device not supported\n");
+ return -EINVAL;
+ }
+ /*
+ * Enable/disable blinking feature for corresponding PWMOUT
+ * channel depending on value of enable.
+ */
+ ret = abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8505_PWM_OUT_BLINK_CTRL4_REG,
+ 1 << (pwm->pwm_id-1), enable << (pwm->pwm_id-1));
+ if (ret < 0)
+ dev_err(pwm->dev, "%s: Failed to control PWM blink,Error %d\n",
+ pwm->label, ret);
+ return ret;
+}
+EXPORT_SYMBOL(pwm_blink_ctrl);
+
int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
{
int ret = 0;
@@ -67,11 +134,19 @@ int pwm_enable(struct pwm_device *pwm)
{
int ret;
+ if (!pwm->clk_enabled) {
+ ret = clk_enable(pwm->clk);
+ if (ret < 0) {
+ dev_err(pwm->dev, "failed to enable clock\n");
+ return ret;
+ }
+ pwm->clk_enabled = true;
+ }
ret = abx500_mask_and_set_register_interruptible(pwm->dev,
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
- 1 << (pwm->pwm_id-1), ENABLE_PWM);
+ 1 << (pwm->pwm_id-1), 1 << (pwm->pwm_id-1));
if (ret < 0)
- dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
+ dev_err(pwm->dev, "%s: Failed to enable PWM, Error %d\n",
pwm->label, ret);
return ret;
}
@@ -84,9 +159,27 @@ void pwm_disable(struct pwm_device *pwm)
ret = abx500_mask_and_set_register_interruptible(pwm->dev,
AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
1 << (pwm->pwm_id-1), DISABLE_PWM);
+ /*
+ * Workaround to set PWM in disable.
+ * If enable bit is not toggled the PWM might output 50/50 duty cycle
+ * even though it should be disabled
+ */
+ ret &= abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << (pwm->pwm_id-1),
+ ENABLE_PWM << (pwm->pwm_id-1));
+ ret &= abx500_mask_and_set_register_interruptible(pwm->dev,
+ AB8500_MISC, AB8500_PWM_OUT_CTRL7_REG,
+ 1 << (pwm->pwm_id-1), DISABLE_PWM);
+
if (ret < 0)
dev_err(pwm->dev, "%s: Failed to disable PWM, Error %d\n",
pwm->label, ret);
+ if (pwm->clk_enabled) {
+ clk_disable(pwm->clk);
+ pwm->clk_enabled = false;
+ }
+
return;
}
EXPORT_SYMBOL(pwm_disable);
@@ -94,7 +187,6 @@ EXPORT_SYMBOL(pwm_disable);
struct pwm_device *pwm_request(int pwm_id, const char *label)
{
struct pwm_device *pwm;
-
list_for_each_entry(pwm, &pwm_list, node) {
if (pwm->pwm_id == pwm_id) {
pwm->label = label;
@@ -113,30 +205,131 @@ void pwm_free(struct pwm_device *pwm)
}
EXPORT_SYMBOL(pwm_free);
+static ssize_t store_blink_status(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct pwm_device *pwm;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 0, &val))
+ return -EINVAL;
+ list_for_each_entry(pwm, &pwm_list, node) {
+ if (pwm->pwm_id == val)
+ break;
+ else {
+ /* check if PWM ID is valid*/
+ if (val > pwm->num_pwm) {
+ dev_err(pwm->dev, "Invalid PWM ID\n");
+ return -EINVAL;
+ }
+ }
+ }
+ if ((!is_ab8505(pwm->parent)) || (!pwm->blink_en)) {
+ dev_err(pwm->dev, "setting blinking for this "
+ "device not supported\n");
+ return -EINVAL;
+ }
+ /*Disable blink functionlity */
+ pwm_blink_ctrl(pwm, 0);
+ return count;
+}
+
+static DEVICE_ATTR(disable_blink, S_IWUGO, NULL, store_blink_status);
+
+static struct attribute *pwmled_attributes[] = {
+ &dev_attr_disable_blink.attr,
+ NULL
+};
+
+static const struct attribute_group pwmled_attr_group = {
+ .attrs = pwmled_attributes,
+};
+
static int __devinit ab8500_pwm_probe(struct platform_device *pdev)
{
+ struct ab8500 *parent = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *plat = dev_get_platdata(parent->dev);
+ struct ab8500_pwmled_platform_data *pdata;
struct pwm_device *pwm;
+ int ret = 0 , i;
+
+ /* get pwmled specific platform data */
+ if (!plat->pwmled) {
+ dev_err(&pdev->dev, "no pwm platform data supplied\n");
+ return -EINVAL;
+ }
+ pdata = plat->pwmled;
/*
* Nothing to be done in probe, this is required to get the
* device which is required for ab8500 read and write
*/
- pwm = kzalloc(sizeof(struct pwm_device), GFP_KERNEL);
+ pwm = kzalloc(((sizeof(struct pwm_device)) * pdata->num_pwm),
+ GFP_KERNEL);
if (pwm == NULL) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
- pwm->dev = &pdev->dev;
- pwm->pwm_id = pdev->id;
- list_add_tail(&pwm->node, &pwm_list);
+ for (i = 0; i < pdata->num_pwm; i++) {
+ pwm[i].dev = &pdev->dev;
+ pwm[i].parent = parent;
+ pwm[i].blink_en = pdata->leds[i].blink_en;
+ pwm[i].pwm_id = pdata->leds[i].pwm_id;
+ pwm[i].num_pwm = pdata->num_pwm;
+ list_add_tail(&pwm[i].node, &pwm_list);
+ }
+ for (i = 0; i < pdata->num_pwm; i++) {
+ /*Implement sysfs only if blink is enabled*/
+ if ((is_ab8505(pwm[i].parent)) && (pwm[i].blink_en)) {
+ /* sysfs implementation to disable the blink */
+ ret = sysfs_create_group(&pdev->dev.kobj,
+ &pwmled_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to create"
+ " sysfs entries\n");
+ goto fail;
+ }
+ break;
+ }
+ }
+ pwm->clk = clk_get(pwm->dev, NULL);
+ if (IS_ERR(pwm->clk)) {
+ dev_err(pwm->dev, "clock request failed\n");
+ ret = PTR_ERR(pwm->clk);
+ goto err_clk;
+ }
platform_set_drvdata(pdev, pwm);
+ pwm->clk_enabled = false;
dev_dbg(pwm->dev, "pwm probe successful\n");
- return 0;
+ return ret;
+
+err_clk:
+ for (i = 0; i < pdata->num_pwm; i++) {
+ if ((is_ab8505(pwm[i].parent)) && (pwm[i].blink_en)) {
+ sysfs_remove_group(&pdev->dev.kobj,
+ &pwmled_attr_group);
+ break;
+ }
+ }
+fail:
+ list_del(&pwm->node);
+ kfree(pwm);
+ return ret;
}
static int __devexit ab8500_pwm_remove(struct platform_device *pdev)
{
struct pwm_device *pwm = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < pwm->num_pwm; i++) {
+ if ((is_ab8505(pwm[i].parent)) && (pwm[i].blink_en)) {
+ sysfs_remove_group(&pdev->dev.kobj,
+ &pwmled_attr_group);
+ break;
+ }
+ }
list_del(&pwm->node);
+ clk_put(pwm->clk);
dev_dbg(&pdev->dev, "pwm driver removed\n");
kfree(pwm);
return 0;
diff --git a/drivers/misc/bh1780gli.c b/drivers/misc/bh1780gli.c
index 54f6f39f990..1035cb37695 100644
--- a/drivers/misc/bh1780gli.c
+++ b/drivers/misc/bh1780gli.c
@@ -18,11 +18,17 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/i2c.h>
+#include <linux/err.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
#define BH1780_REG_CONTROL 0x80
#define BH1780_REG_PARTID 0x8A
@@ -40,11 +46,20 @@
struct bh1780_data {
struct i2c_client *client;
+ struct regulator *regulator;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
int power_state;
/* lock for sysfs operations */
struct mutex lock;
};
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bh1780_early_suspend(struct early_suspend *ddata);
+static void bh1780_late_resume(struct early_suspend *ddata);
+#endif
+
static int bh1780_write(struct bh1780_data *ddata, u8 reg, u8 val, char *msg)
{
int ret = i2c_smbus_write_byte_data(ddata->client, reg, val);
@@ -72,6 +87,9 @@ static ssize_t bh1780_show_lux(struct device *dev,
struct bh1780_data *ddata = platform_get_drvdata(pdev);
int lsb, msb;
+ if (ddata->power_state == BH1780_POFF)
+ return -EINVAL;
+
lsb = bh1780_read(ddata, BH1780_REG_DLOW, "DLOW");
if (lsb < 0)
return lsb;
@@ -89,13 +107,9 @@ static ssize_t bh1780_show_power_state(struct device *dev,
{
struct platform_device *pdev = to_platform_device(dev);
struct bh1780_data *ddata = platform_get_drvdata(pdev);
- int state;
-
- state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL");
- if (state < 0)
- return state;
- return sprintf(buf, "%d\n", state & BH1780_POWMASK);
+ /* we already maintain a sw state */
+ return sprintf(buf, "%d\n", ddata->power_state);
}
static ssize_t bh1780_store_power_state(struct device *dev,
@@ -104,7 +118,7 @@ static ssize_t bh1780_store_power_state(struct device *dev,
{
struct platform_device *pdev = to_platform_device(dev);
struct bh1780_data *ddata = platform_get_drvdata(pdev);
- unsigned long val;
+ long val;
int error;
error = strict_strtoul(buf, 0, &val);
@@ -114,15 +128,25 @@ static ssize_t bh1780_store_power_state(struct device *dev,
if (val < BH1780_POFF || val > BH1780_PON)
return -EINVAL;
+ if (ddata->power_state == val)
+ return count;
+
mutex_lock(&ddata->lock);
+ if (ddata->power_state == BH1780_POFF)
+ regulator_enable(ddata->regulator);
+
error = bh1780_write(ddata, BH1780_REG_CONTROL, val, "CONTROL");
if (error < 0) {
mutex_unlock(&ddata->lock);
+ regulator_disable(ddata->regulator);
return error;
}
- msleep(BH1780_PON_DELAY);
+ if (val == BH1780_POFF)
+ regulator_disable(ddata->regulator);
+
+ mdelay(BH1780_PON_DELAY);
ddata->power_state = val;
mutex_unlock(&ddata->lock);
@@ -131,7 +155,7 @@ static ssize_t bh1780_store_power_state(struct device *dev,
static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL);
-static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
+static DEVICE_ATTR(power_state, S_IWUGO | S_IRUGO,
bh1780_show_power_state, bh1780_store_power_state);
static struct attribute *bh1780_attributes[] = {
@@ -153,21 +177,42 @@ static int __devinit bh1780_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) {
ret = -EIO;
- goto err_op_failed;
+ return ret;
}
ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL);
if (ddata == NULL) {
+ dev_err(&client->dev, "failed to alloc ddata\n");
ret = -ENOMEM;
- goto err_op_failed;
+ return ret;
}
ddata->client = client;
i2c_set_clientdata(client, ddata);
+ ddata->regulator = regulator_get(&client->dev, "vcc");
+ if (IS_ERR(ddata->regulator)) {
+ dev_err(&client->dev, "failed to get regulator\n");
+ ret = PTR_ERR(ddata->regulator);
+ goto free_ddata;
+ }
+
+ regulator_enable(ddata->regulator);
+
ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID");
- if (ret < 0)
- goto err_op_failed;
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read part ID\n");
+ goto disable_regulator;
+ }
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ddata->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ddata->early_suspend.suspend = bh1780_early_suspend;
+ ddata->early_suspend.resume = bh1780_late_resume;
+ register_early_suspend(&ddata->early_suspend);
+#endif
+
+ regulator_disable(ddata->regulator);
+ ddata->power_state = BH1780_POFF;
dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n",
(ret & BH1780_REVMASK));
@@ -175,12 +220,17 @@ static int __devinit bh1780_probe(struct i2c_client *client,
mutex_init(&ddata->lock);
ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group);
- if (ret)
- goto err_op_failed;
+ if (ret) {
+ dev_err(&client->dev, "failed to create sysfs group\n");
+ goto put_regulator;
+ }
return 0;
-
-err_op_failed:
+disable_regulator:
+ regulator_disable(ddata->regulator);
+put_regulator:
+ regulator_put(ddata->regulator);
+free_ddata:
kfree(ddata);
return ret;
}
@@ -196,50 +246,106 @@ static int __devexit bh1780_remove(struct i2c_client *client)
return 0;
}
-#ifdef CONFIG_PM
-static int bh1780_suspend(struct device *dev)
+#if defined(CONFIG_HAS_EARLYSUSPEND) || defined(CONFIG_PM)
+static int bh1780_do_suspend(struct bh1780_data *ddata)
{
- struct bh1780_data *ddata;
- int state, ret;
- struct i2c_client *client = to_i2c_client(dev);
+ int ret = 0;
- ddata = i2c_get_clientdata(client);
- state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL");
- if (state < 0)
- return state;
+ mutex_lock(&ddata->lock);
- ddata->power_state = state & BH1780_POWMASK;
+ if (ddata->power_state == BH1780_POFF)
+ goto unlock;
- ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF,
- "CONTROL");
+ ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF, "CONTROL");
if (ret < 0)
- return ret;
+ goto unlock;
- return 0;
+ if (ddata->regulator)
+ regulator_disable(ddata->regulator);
+unlock:
+ mutex_unlock(&ddata->lock);
+ return ret;
}
-static int bh1780_resume(struct device *dev)
+static int bh1780_do_resume(struct bh1780_data *ddata)
{
- struct bh1780_data *ddata;
- int state, ret;
- struct i2c_client *client = to_i2c_client(dev);
+ int ret = 0;
- ddata = i2c_get_clientdata(client);
- state = ddata->power_state;
- ret = bh1780_write(ddata, BH1780_REG_CONTROL, state,
- "CONTROL");
+ mutex_lock(&ddata->lock);
+
+ if (ddata->power_state == BH1780_POFF)
+ goto unlock;
+ if (ddata->regulator)
+ regulator_enable(ddata->regulator);
+
+ ret = bh1780_write(ddata, BH1780_REG_CONTROL,
+ ddata->power_state, "CONTROL");
+
+unlock:
+ mutex_unlock(&ddata->lock);
+ return ret;
+}
+#endif
+
+#ifndef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_PM
+static int bh1780_suspend(struct device *dev)
+{
+ struct bh1780_data *ddata = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = bh1780_do_suspend(ddata);
if (ret < 0)
- return ret;
+ dev_err(&ddata->client->dev,
+ "Error while suspending the device\n");
- return 0;
+ return ret;
}
+
+static int bh1780_resume(struct device *dev)
+{
+ struct bh1780_data *ddata = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = bh1780_do_resume(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device\n");
+
+ return ret;
+}
+
static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume);
#define BH1780_PMOPS (&bh1780_pm)
+#endif /* CONFIG_PM */
#else
#define BH1780_PMOPS NULL
-#endif /* CONFIG_PM */
+static void bh1780_early_suspend(struct early_suspend *data)
+{
+ struct bh1780_data *ddata =
+ container_of(data, struct bh1780_data, early_suspend);
+ int ret;
+
+ ret = bh1780_do_suspend(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while suspending the device\n");
+}
+
+static void bh1780_late_resume(struct early_suspend *data)
+{
+ struct bh1780_data *ddata =
+ container_of(data, struct bh1780_data, early_suspend);
+ int ret;
+
+ ret = bh1780_do_resume(ddata);
+ if (ret < 0)
+ dev_err(&ddata->client->dev,
+ "Error while resuming the device\n");
+}
+#endif /*!CONFIG_HAS_EARLYSUSPEND */
static const struct i2c_device_id bh1780_id[] = {
{ "bh1780", 0 },
@@ -252,7 +358,9 @@ static struct i2c_driver bh1780_driver = {
.id_table = bh1780_id,
.driver = {
.name = "bh1780",
+#if (!defined(CONFIG_HAS_EARLYSUSPEND) && defined(CONFIG_PM))
.pm = BH1780_PMOPS,
+#endif
},
};
diff --git a/drivers/misc/stm.c b/drivers/misc/stm.c
new file mode 100644
index 00000000000..33bb26c27ca
--- /dev/null
+++ b/drivers/misc/stm.c
@@ -0,0 +1,850 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * Philippe Langlais <philippe.Langlais@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <trace/stm.h>
+
+/* STM Registers */
+#define STM_CR (stm.virtbase)
+#define STM_MMC (stm.virtbase + 0x008)
+#define STM_TER (stm.virtbase + 0x010)
+#define STMPERIPHID0 (stm.virtbase + 0xFC0)
+#define STMPERIPHID1 (stm.virtbase + 0xFC8)
+#define STMPERIPHID2 (stm.virtbase + 0xFD0)
+#define STMPERIPHID3 (stm.virtbase + 0xFD8)
+#define STMPCELLID0 (stm.virtbase + 0xFE0)
+#define STMPCELLID1 (stm.virtbase + 0xFE8)
+#define STMPCELLID2 (stm.virtbase + 0xFF0)
+#define STMPCELLID3 (stm.virtbase + 0xFF8)
+
+#define STM_CLOCK_SHIFT 6
+#define STM_CLOCK_MASK 0x1C0
+
+/* Hardware mode for all sources */
+#define STM_MMC_DEFAULT CONFIG_STM_DEFAULT_MASTERS_MODES
+
+/* Max number of channels (multiple of 256) */
+#define STM_NUMBER_OF_CHANNEL CONFIG_STM_NUMBER_OF_CHANNEL
+
+/* # dynamically allocated channel with stm_trace_buffer */
+#define NB_KERNEL_DYNAMIC_CHANNEL 128
+
+static struct stm_device {
+ const struct stm_platform_data *pdata;
+ void __iomem *virtbase;
+ /* Used to register the allocated channels */
+ DECLARE_BITMAP(ch_bitmap, STM_NUMBER_OF_CHANNEL);
+} stm;
+
+volatile struct stm_channel __iomem *stm_channels;
+
+static struct cdev cdev;
+static struct class *stm_class;
+static int stm_major;
+
+static DEFINE_SPINLOCK(lock);
+
+/* Middle value for clock divisor */
+static enum clock_div stm_clockdiv = STM_CLOCK_DIV8;
+
+/* Default value for STM output connection */
+static enum stm_connection_type stm_connection = STM_DEFAULT_CONNECTION;
+
+#define STM_BUFSIZE 256
+struct channel_data {
+ DECLARE_BITMAP(bitmap, STM_NUMBER_OF_CHANNEL);
+ int numero;
+ spinlock_t lock;
+ u8 data_buffer[STM_BUFSIZE];
+};
+
+static u64 stm_printk_buf[1024/sizeof(u64)];
+static arch_spinlock_t stm_buf_lock =
+ (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED;
+
+static char *mipi60 = "none";
+module_param(mipi60, charp, S_IRUGO);
+MODULE_PARM_DESC(mipi60, "STM Trace to output on probe2 of mipi60 "
+ "('none' or 'ape' or 'modem')");
+
+static char *mipi34 = "none";
+module_param(mipi34, charp, S_IRUGO);
+MODULE_PARM_DESC(mipi34, "STM Trace to output on mipi34 "
+ "('none' or 'ape' or 'modem')");
+
+static char *microsd = "none";
+module_param(microsd, charp, S_IRUGO);
+MODULE_PARM_DESC(microsd, "STM Trace to output on SD card connector "
+ "('none' or 'ape' or 'modem')");
+
+static unsigned int stm_ter;
+module_param(stm_ter, uint, 0);
+MODULE_PARM_DESC(stm_ter, "Value for STM_TER (trace control register). "
+ "Should be set by user as environment variable stm.stm_ter");
+
+#define IS_APE_ON_MIPI34 (mipi34 && !strcmp(mipi34, "ape"))
+#define IS_APE_ON_MIPI60 (mipi60 && !strcmp(mipi60, "ape"))
+#define IS_APE_ON_MICROSD (microsd && !strcmp(microsd, "ape"))
+#define IS_MODEM_ON_MICROSD (microsd && !strcmp(microsd, "modem"))
+
+static int stm_connection_set(void *data, u64 val);
+
+int stm_alloc_channel(int offset)
+{
+ int channel;
+
+ /* Look for a free channel from offset */
+ do {
+ channel = find_next_zero_bit(stm.ch_bitmap,
+ STM_NUMBER_OF_CHANNEL, offset);
+ } while ((channel < STM_NUMBER_OF_CHANNEL)
+ && test_and_set_bit(channel, stm.ch_bitmap));
+ return channel;
+}
+EXPORT_SYMBOL(stm_alloc_channel);
+
+void stm_free_channel(int channel)
+{
+ clear_bit(channel, stm.ch_bitmap);
+}
+EXPORT_SYMBOL(stm_free_channel);
+
+static int stm_get_channel(struct channel_data *ch_data, int __user *arg)
+{
+ int channel, err;
+
+ channel = stm_alloc_channel(0);
+ if (channel < STM_NUMBER_OF_CHANNEL) {
+ /* One free found ! */
+ err = put_user(channel, arg);
+ if (err)
+ stm_free_channel(channel);
+ else
+ /* Register it in the context of the file */
+ set_bit(channel, ch_data->bitmap);
+ } else
+ err = -ENOMEM;
+ return err;
+}
+
+static int stm_release_channel(struct channel_data *ch_data, int channel)
+{
+ if ((channel < 0) || (channel >= STM_NUMBER_OF_CHANNEL))
+ return -EINVAL;
+ stm_free_channel(channel);
+ clear_bit(channel, ch_data->bitmap);
+ return 0;
+}
+
+/*
+ * Trace a buffer on a given channel
+ * with auto time stamping on last byte(s) only
+ */
+int stm_trace_buffer_onchannel(int channel,
+ const void *data, size_t length)
+{
+ int i, mod64;
+ volatile struct stm_channel __iomem *pch;
+
+ if (channel >= STM_NUMBER_OF_CHANNEL || !stm_channels)
+ return 0;
+
+ pch = &stm_channels[channel];
+
+ /* Align data pointer to u64 & time stamp last byte(s) */
+ mod64 = (int)data & 7;
+ i = length - 8 + mod64;
+ switch (mod64) {
+ case 0:
+ if (i)
+ pch->no_stamp64 = *(u64 *)data;
+ else {
+ pch->stamp64 = *(u64 *)data;
+ return length;
+ }
+ data += 8;
+ break;
+ case 1:
+ pch->no_stamp8 = *(u8 *)data;
+ pch->no_stamp16 = *(u16 *)(data+1);
+ if (i)
+ pch->no_stamp32 = *(u32 *)(data+3);
+ else {
+ pch->stamp32 = *(u32 *)(data+3);
+ return length;
+ }
+ data += 7;
+ break;
+ case 2:
+ pch->no_stamp16 = *(u16 *)data;
+ if (i)
+ pch->no_stamp32 = *(u32 *)(data+2);
+ else {
+ pch->stamp32 = *(u32 *)(data+2);
+ return length;
+ }
+ data += 6;
+ break;
+ case 3:
+ pch->no_stamp8 = *(u8 *)data;
+ if (i)
+ pch->no_stamp32 = *(u32 *)(data+1);
+ else {
+ pch->stamp32 = *(u32 *)(data+1);
+ return length;
+ }
+ data += 5;
+ break;
+ case 4:
+ if (i)
+ pch->no_stamp32 = *(u32 *)data;
+ else {
+ pch->stamp32 = *(u32 *)data;
+ return length;
+ }
+ data += 4;
+ break;
+ case 5:
+ pch->no_stamp8 = *(u8 *)data;
+ if (i)
+ pch->no_stamp16 = *(u16 *)(data+1);
+ else {
+ pch->stamp16 = *(u16 *)(data+1);
+ return length;
+ }
+ data += 3;
+ break;
+ case 6:
+ if (i)
+ pch->no_stamp16 = *(u16 *)data;
+ else {
+ pch->stamp16 = *(u16 *)data;
+ return length;
+ }
+ data += 2;
+ break;
+ case 7:
+ if (i)
+ pch->no_stamp8 = *(u8 *)data;
+ else {
+ pch->stamp8 = *(u8 *)data;
+ return length;
+ }
+ data++;
+ break;
+ }
+ for (;;) {
+ if (i > 8) {
+ pch->no_stamp64 = *(u64 *)data;
+ data += 8;
+ i -= 8;
+ } else if (i == 8) {
+ pch->stamp64 = *(u64 *)data;
+ break;
+ } else if (i > 4) {
+ pch->no_stamp32 = *(u32 *)data;
+ data += 4;
+ i -= 4;
+ } else if (i == 4) {
+ pch->stamp32 = *(u32 *)data;
+ break;
+ } else if (i > 2) {
+ pch->no_stamp16 = *(u16 *)data;
+ data += 2;
+ i -= 2;
+ } else if (i == 2) {
+ pch->stamp16 = *(u16 *)data;
+ break;
+ } else {
+ pch->stamp8 = *(u8 *)data;
+ break;
+ }
+ }
+ return length;
+}
+EXPORT_SYMBOL(stm_trace_buffer_onchannel);
+
+static int stm_open(struct inode *inode, struct file *file)
+{
+ struct channel_data *channel_data;
+ int retval = 0;
+
+ channel_data = kzalloc(sizeof(struct channel_data), GFP_KERNEL);
+ if (channel_data == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&channel_data->lock);
+ channel_data->numero = -1; /* Channel not yet allocated */
+ file->private_data = channel_data;
+
+ /*
+ * Check if microsd is selected as trace interface
+ * and enable corresponding pins muxing.
+ */
+ if (IS_MODEM_ON_MICROSD)
+ retval = stm_connection_set(NULL, STM_STE_MODEM_ON_MICROSD);
+ else if (IS_APE_ON_MICROSD)
+ retval = stm_connection_set(NULL, STM_STE_APE_ON_MICROSD);
+
+ if (retval)
+ pr_alert("stm_open: failed to connect STM output\n");
+
+ return retval;
+}
+
+static int stm_release(struct inode *inode, struct file *file)
+{
+ struct channel_data *channel;
+
+ channel = (struct channel_data *)file->private_data;
+
+ /* Free allocated channel if necessary */
+ if (channel->numero != -1)
+ stm_free_channel(channel->numero);
+
+ bitmap_andnot(stm.ch_bitmap, stm.ch_bitmap,
+ channel->bitmap, STM_NUMBER_OF_CHANNEL);
+
+ kfree(channel);
+ return 0;
+}
+
+static ssize_t stm_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *off)
+{
+ struct channel_data *channel = file->private_data;
+
+ /* Alloc channel at first write */
+ if (channel->numero == -1) {
+ channel->numero = stm_alloc_channel(0);
+ if (channel->numero > STM_NUMBER_OF_CHANNEL)
+ return -ENOMEM;
+ }
+
+ if (size > STM_BUFSIZE)
+ size = STM_BUFSIZE;
+
+ spin_lock(&channel->lock);
+
+ if (copy_from_user
+ (channel->data_buffer, (void __user *) buf, size)) {
+ spin_unlock(&channel->lock);
+ return -EFAULT;
+ }
+ size = stm_trace_buffer_onchannel(channel->numero,
+ channel->data_buffer, size);
+
+ spin_unlock(&channel->lock);
+
+ return size;
+}
+
+static int stm_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ /*
+ * Don't allow a mapping that covers more than the STM channels
+ */
+ if ((vma->vm_end - vma->vm_start) >
+ STM_NUMBER_OF_CHANNEL*sizeof(struct stm_channel))
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start,
+ stm.pdata->channels_phys_base>>PAGE_SHIFT,
+ STM_NUMBER_OF_CHANNEL*sizeof(struct stm_channel),
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ return 0;
+}
+
+/* Enable the trace for given sources (bitfield) */
+static void stm_enable_src(unsigned int v)
+{
+ unsigned int cr_val;
+ spin_lock(&lock);
+ cr_val = readl(STM_CR);
+ cr_val &= ~STM_CLOCK_MASK;
+ writel(cr_val|(stm_clockdiv<<STM_CLOCK_SHIFT), STM_CR);
+ /*
+ * If the kernel argument stm_ter has been set by the boot loader
+ * all calls to stm_enable_src will be ignored
+ */
+ v = stm_ter ? stm_ter : v;
+ writel(v, STM_TER);
+ spin_unlock(&lock);
+}
+
+/* Disable all sources */
+static void stm_disable_src(void)
+{
+ writel(0x0, STM_CR); /* stop clock */
+ writel(0x0, STM_TER); /* Disable cores */
+}
+
+/* Set clock speed */
+static int stm_set_ckdiv(enum clock_div v)
+{
+ unsigned int val;
+
+ spin_lock(&lock);
+ val = readl(STM_CR);
+ val &= ~STM_CLOCK_MASK;
+ writel(val | ((v << STM_CLOCK_SHIFT) & STM_CLOCK_MASK), STM_CR);
+ spin_unlock(&lock);
+ stm_clockdiv = v;
+
+ return 0;
+}
+
+/* Return the control register */
+static inline unsigned int stm_get_cr(void)
+{
+ return readl(STM_CR);
+}
+
+/*
+ * Set Trace MODE lossless/lossy (Software/Hardware)
+ * each bit represent the corresponding mode of this source
+ */
+static inline void stm_set_modes(unsigned int modes)
+{
+ writel(modes, STM_MMC);
+}
+
+/* Get Trace MODE lossless/lossy (Software/Hardware)
+ * each bit represent the corresponding mode of this source */
+static inline unsigned int stm_get_modes(void)
+{
+ return readl(STM_MMC);
+}
+
+/* Count # of free channels */
+static int stm_nb_free_channels(void)
+{
+ int nb_channels, offset;
+
+ nb_channels = 0;
+ offset = 0;
+ for (;;) {
+ offset = find_next_zero_bit(stm.ch_bitmap,
+ STM_NUMBER_OF_CHANNEL, offset);
+ if (offset == STM_NUMBER_OF_CHANNEL)
+ break;
+ offset++;
+ nb_channels++;
+ }
+ return nb_channels;
+}
+
+static long stm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct channel_data *channel = file->private_data;
+
+ switch (cmd) {
+
+ case STM_CONNECTION:
+ if (stm.pdata->stm_connection)
+ stm.pdata->stm_connection(arg);
+ stm_connection = arg;
+ break;
+
+ case STM_DISABLE:
+ stm_disable_src();
+ break;
+
+ case STM_GET_NB_MAX_CHANNELS:
+ err = put_user(STM_NUMBER_OF_CHANNEL, (unsigned int *)arg);
+ break;
+
+ case STM_GET_NB_FREE_CHANNELS:
+ err = put_user(stm_nb_free_channels(), (unsigned int *)arg);
+ break;
+
+ case STM_GET_CHANNEL_NO:
+ err = put_user(channel->numero, (unsigned int *)arg);
+ break;
+
+ case STM_SET_CLOCK_DIV:
+ err = stm_set_ckdiv((enum clock_div) arg);
+ break;
+
+ case STM_SET_MODE:
+ stm_set_modes(arg);
+ break;
+
+ case STM_GET_MODE:
+ err = put_user(stm_get_modes(), (unsigned int *)arg);
+ break;
+
+ case STM_GET_CTRL_REG:
+ err = put_user(stm_get_cr(), (unsigned int *)arg);
+ break;
+
+ case STM_ENABLE_SRC:
+ stm_enable_src(arg);
+ break;
+
+ case STM_GET_FREE_CHANNEL:
+ err = stm_get_channel(channel, (int *)arg);
+ break;
+
+ case STM_RELEASE_CHANNEL:
+ err = stm_release_channel(channel, arg);
+ break;
+
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+/*
+ * Trace a buffer on a dynamically allocated channel
+ * with auto time stamping on the first byte(s) only
+ * Dynamic channel number >=
+ * STM_NUMBER_OF_CHANNEL - NB_KERNEL_DYNAMIC_CHANNEL
+ */
+int stm_trace_buffer(const void *data, size_t length)
+{
+ int channel;
+
+ channel = stm_alloc_channel(STM_NUMBER_OF_CHANNEL
+ - NB_KERNEL_DYNAMIC_CHANNEL);
+ if (channel < STM_NUMBER_OF_CHANNEL) {
+ length = stm_trace_buffer_onchannel(channel, data, length);
+ stm_free_channel(channel);
+ return length;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(stm_trace_buffer);
+
+static const struct file_operations stm_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = stm_ioctl,
+ .open = stm_open,
+ .llseek = no_llseek,
+ .write = stm_write,
+ .release = stm_release,
+ .mmap = stm_mmap,
+};
+
+/*
+ * Init and deinit driver
+ */
+
+static int __devinit stm_probe(struct platform_device *pdev)
+{
+ int retval = 0;
+
+ if (!pdev || !pdev->dev.platform_data) {
+ pr_alert("No device/platform_data found on STM driver\n");
+ return -ENODEV;
+ }
+
+ stm.pdata = pdev->dev.platform_data;
+
+ cdev_init(&cdev, &stm_fops);
+ cdev.owner = THIS_MODULE;
+
+ stm_channels =
+ ioremap_nocache(stm.pdata->channels_phys_base,
+ STM_NUMBER_OF_CHANNEL*sizeof(*stm_channels));
+ if (stm_channels == NULL) {
+ dev_err(&pdev->dev, "could not remap STM Msg register\n");
+ return -ENODEV;
+ }
+
+ stm.virtbase = ioremap_nocache(stm.pdata->regs_phys_base, SZ_4K);
+ if (stm.virtbase == NULL) {
+ retval = -EIO;
+ dev_err(&pdev->dev, "could not remap STM Register\n");
+ goto err_channels;
+ }
+
+ retval = cdev_add(&cdev, MKDEV(stm_major, 0), 1);
+ if (retval) {
+ dev_err(&pdev->dev, "chardev registration failed\n");
+ goto err_channels;
+ }
+
+ if (IS_ERR(device_create(stm_class, &pdev->dev,
+ MKDEV(stm_major, 0), NULL, STM_DEV_NAME)))
+ dev_err(&pdev->dev, "can't create device\n");
+
+ /* Check chip IDs if necessary */
+ if (stm.pdata->id_mask) {
+ u32 periph_id, cell_id;
+
+ periph_id = (readb(STMPERIPHID3)<<24) +
+ (readb(STMPERIPHID2)<<16) +
+ (readb(STMPERIPHID1)<<8) +
+ readb(STMPERIPHID0);
+ cell_id = (readb(STMPCELLID3)<<24) +
+ (readb(STMPCELLID2)<<16) +
+ (readb(STMPCELLID1)<<8) +
+ readb(STMPCELLID0);
+ /* Only warns if it isn't a ST-Ericsson supported one */
+ if ((periph_id & stm.pdata->id_mask) != 0x00080dec ||
+ cell_id != 0xb105f00d) {
+ dev_warn(&pdev->dev, "STM-Trace IC not compatible\n");
+ dev_warn(&pdev->dev, "periph_id=%x\n", periph_id);
+ dev_warn(&pdev->dev, "pcell_id=%x\n", cell_id);
+ }
+ }
+
+ /* Reserve channels if necessary */
+ if (stm.pdata->channels_reserved_sz) {
+ int i;
+
+ for (i = 0; i < stm.pdata->channels_reserved_sz; i++) {
+ set_bit(stm.pdata->channels_reserved[i],
+ stm.ch_bitmap);
+ }
+ }
+ /* Reserve kernel trace channels on demand */
+#ifdef CONFIG_STM_PRINTK
+ set_bit(CONFIG_STM_PRINTK_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_FTRACE
+ set_bit(CONFIG_STM_FTRACE_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_CTX_SWITCH
+ set_bit(CONFIG_STM_CTX_SWITCH_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_WAKEUP
+ set_bit(CONFIG_STM_WAKEUP_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_STACK_TRACE
+ set_bit(CONFIG_STM_STACK_TRACE_CHANNEL, stm.ch_bitmap);
+#endif
+#ifdef CONFIG_STM_TRACE_PRINTK
+ set_bit(CONFIG_STM_TRACE_PRINTK_CHANNEL, stm.ch_bitmap);
+ set_bit(CONFIG_STM_TRACE_BPRINTK_CHANNEL, stm.ch_bitmap);
+#endif
+
+ /* Check kernel's environment parameters first */
+ if (IS_APE_ON_MIPI34)
+ stm_connection = STM_STE_APE_ON_MIPI34_NONE_ON_MIPI60;
+ else if (IS_APE_ON_MIPI60)
+ stm_connection = STM_STE_MODEM_ON_MIPI34_APE_ON_MIPI60;
+
+ /* Apply parameters to driver */
+ if (stm.pdata->stm_connection) {
+ retval = stm.pdata->stm_connection(stm_connection);
+ if (retval) {
+ dev_err(&pdev->dev, "failed to connect STM output\n");
+ goto err_channels;
+ }
+ }
+
+ /* Enable STM Masters given in pdata */
+ if (stm.pdata->masters_enabled)
+ stm_enable_src(stm.pdata->masters_enabled);
+ stm_set_modes(STM_MMC_DEFAULT); /* Set all sources in HW mode */
+
+ dev_info(&pdev->dev, "STM-Trace driver probed successfully\n");
+ stm_printk("STM-Trace driver initialized\n");
+ return 0;
+
+err_channels:
+ iounmap(stm_channels);
+ return retval;
+}
+
+static int __devexit stm_remove(struct platform_device *pdev)
+{
+ device_destroy(stm_class, MKDEV(stm_major, 0));
+ cdev_del(&cdev);
+
+ if (stm.pdata->stm_connection)
+ (void) stm.pdata->stm_connection(STM_DISCONNECT);
+
+ stm_disable_src();
+ iounmap(stm.virtbase);
+ iounmap(stm_channels);
+
+ return 0;
+}
+
+int stm_printk(const char *fmt, ...)
+{
+ int ret;
+ size_t size;
+ va_list args;
+
+ va_start(args, fmt);
+ arch_spin_lock(&stm_buf_lock);
+ size = vscnprintf((char *)stm_printk_buf,
+ sizeof(stm_printk_buf), fmt, args);
+ ret = stm_trace_buffer(stm_printk_buf, size);
+ arch_spin_unlock(&stm_buf_lock);
+ va_end(args);
+ return ret;
+}
+EXPORT_SYMBOL(stm_printk);
+
+/*
+ * Debugfs interface
+ */
+
+static int stm_connection_show(void *data, u64 *val)
+{
+ *val = stm_connection;
+ return 0;
+}
+
+static int stm_connection_set(void *data, u64 val)
+{
+ int retval = 0;
+
+ if (stm.pdata->stm_connection) {
+ stm_connection = val;
+ retval = stm.pdata->stm_connection(val);
+ }
+ return retval;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_connection_fops, stm_connection_show,
+ stm_connection_set, "%llu\n");
+
+static int stm_clockdiv_show(void *data, u64 *val)
+{
+ *val = stm_clockdiv;
+ return 0;
+}
+
+static int stm_clockdiv_set(void *data, u64 val)
+{
+ stm_set_ckdiv(val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_clockdiv_fops, stm_clockdiv_show,
+ stm_clockdiv_set, "%llu\n");
+
+static int stm_masters_enable_show(void *data, u64 *val)
+{
+ *val = readl(STM_TER);
+ return 0;
+}
+
+static int stm_masters_enable_set(void *data, u64 val)
+{
+ stm_enable_src(val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_masters_enable_fops, stm_masters_enable_show,
+ stm_masters_enable_set, "%08llx\n");
+
+static int stm_masters_modes_show(void *data, u64 *val)
+{
+ *val = stm_get_modes();
+ return 0;
+}
+
+static int stm_masters_modes_set(void *data, u64 val)
+{
+ stm_set_modes(val);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_masters_modes_fops, stm_masters_modes_show,
+ stm_masters_modes_set, "%08llx\n");
+
+/* Count # of free channels */
+static int stm_free_channels_show(void *data, u64 *val)
+{
+ *val = stm_nb_free_channels();
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(stm_free_channels_fops, stm_free_channels_show,
+ NULL, "%lld\n");
+
+static __init int stm_init_debugfs(void)
+{
+ struct dentry *d_stm;
+
+ d_stm = debugfs_create_dir(STM_DEV_NAME, NULL);
+ if (!d_stm)
+ return -ENOMEM;
+
+ (void) debugfs_create_file("connection", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_connection_fops);
+ (void) debugfs_create_file("clockdiv", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_clockdiv_fops);
+ (void) debugfs_create_file("masters_enable", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_masters_enable_fops);
+ (void) debugfs_create_file("masters_modes", S_IRUGO | S_IWUGO, d_stm,
+ NULL, &stm_masters_modes_fops);
+ (void) debugfs_create_file("free_channels", S_IRUGO, d_stm,
+ NULL, &stm_free_channels_fops);
+ return 0;
+}
+fs_initcall(stm_init_debugfs);
+
+static struct platform_driver stm_driver = {
+ .probe = stm_probe,
+ .remove = __devexit_p(stm_remove),
+ .driver = {
+ .name = STM_DEV_NAME,
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init stm_init(void)
+{
+ int retval;
+ dev_t dev;
+
+ stm_class = class_create(THIS_MODULE, STM_DEV_NAME);
+ if (IS_ERR(stm_class)) {
+ pr_err("stm: can't register stm class\n");
+ return PTR_ERR(stm_class);
+ }
+
+ retval = alloc_chrdev_region(&dev, 0, 1, STM_DEV_NAME);
+ if (retval) {
+ pr_err("stm: can't register character device\n");
+ class_destroy(stm_class);
+ return retval;
+ }
+ stm_major = MAJOR(dev);
+ return platform_driver_register(&stm_driver);
+}
+
+static void __exit stm_exit(void)
+{
+ platform_driver_unregister(&stm_driver);
+ unregister_chrdev_region(MKDEV(stm_major, 0), 1);
+ class_destroy(stm_class);
+}
+
+arch_initcall(stm_init); /* STM init ASAP need to wait GPIO init */
+module_exit(stm_exit);
+
+MODULE_AUTHOR("Paul Ghaleb - ST Microelectronics");
+MODULE_AUTHOR("Pierre Peiffer - ST-Ericsson");
+MODULE_AUTHOR("Philippe Langlais - ST-Ericsson");
+MODULE_DESCRIPTION("System Trace Module driver");
+MODULE_ALIAS("stm");
+MODULE_ALIAS("stm-trace");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index dabec556ebb..4c1b59aff9d 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -107,6 +107,7 @@ struct mmc_blk_data {
unsigned int part_curr;
struct device_attribute force_ro;
struct device_attribute power_ro_lock;
+ struct device_attribute power_ro_lock_legacy;
int area_type;
};
@@ -167,6 +168,87 @@ static void mmc_blk_put(struct mmc_blk_data *md)
mutex_unlock(&open_lock);
}
+#define EXT_CSD_BOOT_WP_PWR_WP_TEXT "pwr_ro"
+#define EXT_CSD_BOOT_WP_PERM_WP_TEXT "perm_ro"
+#define EXT_CSD_BOOT_WP_WP_DISABLED_TEXT "rw"
+static ssize_t boot_partition_ro_lock_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+ struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
+ struct mmc_card *card = md->queue.card;
+ const char *out_text;
+
+ if (card->ext_csd.boot_ro_lock
+ & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
+ out_text = EXT_CSD_BOOT_WP_PERM_WP_TEXT;
+ else if (card->ext_csd.boot_ro_lock
+ & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
+ out_text = EXT_CSD_BOOT_WP_PWR_WP_TEXT;
+ else
+ out_text = EXT_CSD_BOOT_WP_WP_DISABLED_TEXT;
+
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", out_text);
+
+ return ret;
+}
+
+static ssize_t boot_partition_ro_lock_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ret;
+ struct mmc_blk_data *md, *part_md;
+ struct mmc_card *card;
+ u8 set = 0;
+
+ md = mmc_blk_get(dev_to_disk(dev));
+ card = md->queue.card;
+
+ if (!strncmp(buf, EXT_CSD_BOOT_WP_PWR_WP_TEXT,
+ strlen(EXT_CSD_BOOT_WP_PWR_WP_TEXT)))
+ set = EXT_CSD_BOOT_WP_B_PWR_WP_EN;
+ else if (!strncmp(buf, EXT_CSD_BOOT_WP_PERM_WP_TEXT,
+ strlen(EXT_CSD_BOOT_WP_PERM_WP_TEXT)))
+ set = EXT_CSD_BOOT_WP_B_PERM_WP_EN;
+
+ if (set) {
+ mmc_claim_host(card->host);
+
+ ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BOOT_WP,
+ set,
+ card->ext_csd.part_time);
+ if (ret)
+ pr_err("Boot Partition Lock failed: %d", ret);
+ else
+ card->ext_csd.boot_ro_lock = set;
+
+ mmc_release_host(card->host);
+
+ if (!ret) {
+ pr_info("%s: Locking boot partition "
+ "%s",
+ md->disk->disk_name,
+ buf);
+ set_disk_ro(md->disk, 1);
+
+ list_for_each_entry(part_md, &md->part, part)
+ if (part_md->area_type ==
+ MMC_BLK_DATA_AREA_BOOT) {
+ pr_info("%s: Locking boot partition "
+ "%s",
+ part_md->disk->disk_name,
+ buf);
+ set_disk_ro(part_md->disk, 1);
+ }
+ }
+ }
+ ret = count;
+
+ mmc_blk_put(md);
+ return ret;
+}
+
static ssize_t power_ro_lock_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1411,6 +1493,14 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
+ /*
+ * We must make sure we have not claimed the host before
+ * doing a flush to prevent deadlock, thus we check if
+ * the host needs a resume first.
+ */
+ if (mmc_host_needs_resume(card->host))
+ mmc_resume_host_sync(card->host);
+
if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */
mmc_claim_host(card->host);
@@ -1654,9 +1744,12 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
- card->ext_csd.boot_ro_lockable)
+ card->ext_csd.boot_ro_lockable) {
device_remove_file(disk_to_dev(md->disk),
&md->power_ro_lock);
+ device_remove_file(disk_to_dev(md->disk),
+ &md->power_ro_lock_legacy);
+ }
/* Stop new requests from getting into the queue */
del_gendisk(md->disk);
@@ -1716,9 +1809,24 @@ static int mmc_add_disk(struct mmc_blk_data *md)
&md->power_ro_lock);
if (ret)
goto power_ro_lock_fail;
+
+ /* Legacy mode */
+ mode = S_IRUGO | S_IWUSR;
+
+ md->power_ro_lock_legacy.show = boot_partition_ro_lock_show;
+ md->power_ro_lock_legacy.store = boot_partition_ro_lock_store;
+ sysfs_attr_init(&md->power_ro_lock_legacy.attr);
+ md->power_ro_lock_legacy.attr.mode = mode;
+ md->power_ro_lock_legacy.attr.name = "ro_lock";
+ ret = device_create_file(disk_to_dev(md->disk),
+ &md->power_ro_lock_legacy);
+ if (ret)
+ goto power_ro_lock_fail_legacy;
}
return ret;
+power_ro_lock_fail_legacy:
+ device_remove_file(disk_to_dev(md->disk), &md->power_ro_lock);
power_ro_lock_fail:
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
force_ro_fail:
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 759714ed6be..6622f2e6e05 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -1253,6 +1253,130 @@ static int mmc_test_align_multi_read(struct mmc_test_card *test)
return 0;
}
+
+/* helper function for various address alignment and sg length alignment */
+static int mmc_test_align_multi(struct mmc_test_card *test, bool do_write,
+ struct scatterlist *sg,
+ u32 *sizes, int sg_len, int offset)
+{
+ int ret, i;
+ unsigned int size;
+ u32 buf_off;
+ u32 sg_size;
+
+ if (test->card->host->max_blk_count == 1)
+ return RESULT_UNSUP_HOST;
+
+ size = PAGE_SIZE * 2;
+ size = min(size, test->card->host->max_req_size);
+ size = min(size, test->card->host->max_seg_size);
+ size = min(size, test->card->host->max_blk_count * 512);
+ size -= offset;
+ size -= size % 512;
+
+ if (size < 1024)
+ return RESULT_UNSUP_HOST;
+
+ for (i = 0, sg_size = 0;
+ i < sg_len && sg_size + sizes[i] < size; i++)
+ sg_size += sizes[i];
+
+ if (sg_size < size)
+ sizes[i-1] += size - sg_size;
+ sg_len = i;
+
+ sg_init_table(sg, sg_len);
+ for (i = 0, buf_off = offset; i < sg_len; i++) {
+ sg_set_buf(&sg[i], test->buffer + buf_off, sizes[i]);
+ buf_off += sizes[i];
+ }
+
+ ret = mmc_test_transfer(test, sg, sg_len, 0, size/512, 512, do_write);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mmc_test_align_length_32(struct mmc_test_card *test, bool do_write)
+{
+ u32 sizes[] = {512, 32*1, 32*2, 32*3, 32*4, 32*5, 32*6, 32*7,
+ 32*8, 32*9, 32*10, 32*11, 32*12, 32*13, 2048};
+ struct scatterlist sg[ARRAY_SIZE(sizes)];
+
+ return mmc_test_align_multi(test, do_write, sg, sizes,
+ ARRAY_SIZE(sg), 0);
+}
+
+static int mmc_test_align_length_4(struct mmc_test_card *test, bool do_write)
+{
+ u32 sizes[] = {512, 4*1, 4*2, 4*3, 4*4, 4*5, 4*6, 4*7,
+ 4*8, 4*9, 520, 1040, 2080};
+ struct scatterlist sg[ARRAY_SIZE(sizes)];
+
+ return mmc_test_align_multi(test, do_write, sg, sizes,
+ ARRAY_SIZE(sg), 0);
+}
+
+static int mmc_test_align_length_4_write(struct mmc_test_card *test)
+{
+ bool do_write = true;
+ return mmc_test_align_length_4(test, do_write);
+}
+
+static int mmc_test_align_length_4_read(struct mmc_test_card *test)
+{
+ bool do_write = false;
+ return mmc_test_align_length_4(test, do_write);
+}
+
+static int mmc_test_align_length_32_write(struct mmc_test_card *test)
+{
+ bool do_write = true;
+ return mmc_test_align_length_32(test, do_write);
+}
+
+static int mmc_test_align_length_32_read(struct mmc_test_card *test)
+{
+ bool do_write = false;
+ return mmc_test_align_length_32(test, do_write);
+}
+
+/* helper function for testing address alignment */
+static int mmc_test_align_address(struct mmc_test_card *test, bool do_write,
+ u32 offset)
+{
+ u32 sizes[] = {512, 512, 1024, 1024, 2048};
+ struct scatterlist sg[ARRAY_SIZE(sizes)];
+
+ return mmc_test_align_multi(test, do_write, sg,
+ sizes, ARRAY_SIZE(sg), offset);
+}
+
+static int mmc_test_align_address_4_write(struct mmc_test_card *test)
+{
+ bool do_write = true;
+ return mmc_test_align_address(test, do_write, 4);
+}
+
+static int mmc_test_align_address_4_read(struct mmc_test_card *test)
+{
+ bool do_write = false;
+ return mmc_test_align_address(test, do_write, 4);
+}
+
+static int mmc_test_align_address_32_write(struct mmc_test_card *test)
+{
+ bool do_write = true;
+ return mmc_test_align_address(test, do_write, 32);
+}
+
+static int mmc_test_align_address_32_read(struct mmc_test_card *test)
+{
+ bool do_write = false;
+ return mmc_test_align_address(test, do_write, 32);
+}
+
static int mmc_test_xfersize_write(struct mmc_test_card *test)
{
int ret;
@@ -2451,6 +2575,62 @@ static const struct mmc_test_case mmc_test_cases[] = {
},
{
+ .name = "4 bytes aligned sg-element length write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_align_length_4_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "4 bytes aligned sg-element length read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_align_length_4_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "32 bytes aligned sg-element length write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_align_length_32_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "32 bytes aligned sg-element length read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_align_length_32_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "4 bytes aligned sg-element address write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_align_address_4_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "4 bytes aligned sg-element address read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_align_address_4_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "32 bytes aligned sg-element address write",
+ .prepare = mmc_test_prepare_write,
+ .run = mmc_test_align_address_32_write,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
+ .name = "32 bytes aligned sg-element address read",
+ .prepare = mmc_test_prepare_read,
+ .run = mmc_test_align_address_32_read,
+ .cleanup = mmc_test_cleanup,
+ },
+
+ {
.name = "Correct xfer_size at write (start failure)",
.run = mmc_test_xfersize_write,
},
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ba821fe70bc..9cce415f1ff 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2010,7 +2010,7 @@ void mmc_rescan(struct work_struct *work)
container_of(work, struct mmc_host, detect.work);
int i;
- if (host->rescan_disable)
+ if (host->rescan_disable || mmc_host_needs_resume(host))
return;
mmc_bus_get(host);
@@ -2273,8 +2273,13 @@ int mmc_suspend_host(struct mmc_host *host)
int err = 0;
cancel_delayed_work(&host->detect);
+ cancel_delayed_work_sync(&host->resume);
mmc_flush_scheduled_work();
+ /* Skip suspend, if deferred resume were scheduled but not completed. */
+ if (mmc_host_needs_resume(host))
+ return 0;
+
err = mmc_cache_ctrl(host, 0);
if (err)
goto out;
@@ -2300,6 +2305,10 @@ int mmc_suspend_host(struct mmc_host *host)
mmc_release_host(host);
host->pm_flags = 0;
err = 0;
+ } else if (mmc_card_mmc(host->card) ||
+ mmc_card_sd(host->card)) {
+ host->pm_state |= MMC_HOST_DEFERRED_RESUME |
+ MMC_HOST_NEEDS_RESUME;
}
}
mmc_bus_put(host);
@@ -2321,6 +2330,12 @@ int mmc_resume_host(struct mmc_host *host)
{
int err = 0;
+ if (mmc_host_deferred_resume(host)) {
+ mmc_schedule_delayed_work(&host->resume,
+ msecs_to_jiffies(3000));
+ return 0;
+ }
+
mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) {
if (!mmc_card_keep_power(host)) {
@@ -2355,6 +2370,24 @@ int mmc_resume_host(struct mmc_host *host)
}
EXPORT_SYMBOL(mmc_resume_host);
+void mmc_resume_work(struct work_struct *work)
+{
+ struct mmc_host *host =
+ container_of(work, struct mmc_host, resume.work);
+
+ host->pm_state &= ~MMC_HOST_DEFERRED_RESUME;
+ mmc_resume_host(host);
+ host->pm_state &= ~MMC_HOST_NEEDS_RESUME;
+
+ mmc_detect_change(host, 0);
+}
+
+void mmc_resume_host_sync(struct mmc_host *host)
+{
+ flush_delayed_work_sync(&host->resume);
+}
+EXPORT_SYMBOL(mmc_resume_host_sync);
+
/* Do the card removal on suspend if card is assumed removeable
* Do that in pm notifier while userspace isn't yet frozen, so we will be able
to sync the card.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 3bdafbca354..5796d2d85f4 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -59,6 +59,7 @@ static inline void mmc_delay(unsigned int ms)
void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host);
+void mmc_resume_work(struct work_struct *work);
int _mmc_detect_card_removed(struct mmc_host *host);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 91c84c7a182..d87d1152ea2 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -330,6 +330,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
spin_lock_init(&host->lock);
init_waitqueue_head(&host->wq);
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
+ INIT_DELAYED_WORK(&host->resume, mmc_resume_work);
#ifdef CONFIG_PM
host->pm_notify.notifier_call = mmc_pm_notify;
#endif
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 032b84791a1..7df4119aaa2 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -19,6 +19,7 @@
#include <linux/err.h>
#include <linux/highmem.h>
#include <linux/log2.h>
+#include <linux/mmc/pm.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/amba/bus.h>
@@ -46,6 +47,7 @@ static unsigned int fmax = 515633;
* struct variant_data - MMCI variant-specific quirks
* @clkreg: default value for MCICLOCK register
* @clkreg_enable: enable value for MMCICLOCK register
+ * @dma_sdio_req_ctrl: enable value for DMAREQCTL register for SDIO write
* @datalength_bits: number of bits in the MMCIDATALENGTH register
* @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
* is asserted (likewise for RX)
@@ -56,10 +58,13 @@ static unsigned int fmax = 515633;
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
* @pwrreg_powerup: power up value for MMCIPOWER register
* @signal_direction: input/out direction of bus signals can be indicated
+ * @non_power_of_2_blksize: true if block sizes can be other than power of two
+ * @pwrreg_ctrl_power: bits in MMCIPOWER register controls ext. power supply
*/
struct variant_data {
unsigned int clkreg;
unsigned int clkreg_enable;
+ unsigned int dma_sdio_req_ctrl;
unsigned int datalength_bits;
unsigned int fifosize;
unsigned int fifohalfsize;
@@ -68,6 +73,8 @@ struct variant_data {
bool blksz_datactrl16;
u32 pwrreg_powerup;
bool signal_direction;
+ bool non_power_of_2_blksize;
+ bool pwrreg_ctrl_power;
};
static struct variant_data variant_arm = {
@@ -75,6 +82,7 @@ static struct variant_data variant_arm = {
.fifohalfsize = 8 * 4,
.datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP,
+ .pwrreg_ctrl_power = true,
};
static struct variant_data variant_arm_extended_fifo = {
@@ -82,6 +90,7 @@ static struct variant_data variant_arm_extended_fifo = {
.fifohalfsize = 64 * 4,
.datalength_bits = 16,
.pwrreg_powerup = MCI_PWR_UP,
+ .pwrreg_ctrl_power = true,
};
static struct variant_data variant_u300 = {
@@ -99,6 +108,7 @@ static struct variant_data variant_ux500 = {
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = MCI_ST_UX500_HWFCEN,
+ .dma_sdio_req_ctrl = MCI_ST_DPSM_DMAREQCTL,
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
@@ -111,15 +121,42 @@ static struct variant_data variant_ux500v2 = {
.fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE,
.clkreg_enable = MCI_ST_UX500_HWFCEN,
+ .dma_sdio_req_ctrl = MCI_ST_DPSM_DMAREQCTL,
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
.blksz_datactrl16 = true,
.pwrreg_powerup = MCI_PWR_ON,
.signal_direction = true,
+ .non_power_of_2_blksize = true,
};
/*
+ * Validate mmc prerequisites
+ */
+static int mmci_validate_data(struct mmci_host *host,
+ struct mmc_data *data)
+{
+ if (!data)
+ return 0;
+
+ if (!host->variant->non_power_of_2_blksize &&
+ !is_power_of_2(data->blksz)) {
+ dev_err(mmc_dev(host->mmc),
+ "unsupported block size (%d bytes)\n", data->blksz);
+ return -EINVAL;
+ }
+
+ if (data->sg->offset & 3) {
+ dev_err(mmc_dev(host->mmc),
+ "unsupported alginment (0x%x)\n", data->sg->offset);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
* This must be called with host->lock held
*/
static void mmci_write_clkreg(struct mmci_host *host, u32 clk)
@@ -228,6 +265,7 @@ static void mmci_stop_data(struct mmci_host *host)
writel(0, host->base + MMCIDATACTRL);
mmci_set_mask1(host, 0);
host->data = NULL;
+ host->datactrl_reg = 0;
}
static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
@@ -338,10 +376,33 @@ static inline void mmci_dma_release(struct mmci_host *host)
host->dma_rx_channel = host->dma_tx_channel = NULL;
}
+static void mmci_dma_data_error(struct mmci_host *host, struct mmc_data *data)
+{
+ dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+ dmaengine_terminate_all(host->dma_current);
+ host->dma_current = NULL;
+ host->dma_desc_current = NULL;
+ data->host_cookie = 0;
+}
+
static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
{
- struct dma_chan *chan = host->dma_current;
+ struct dma_chan *chan;
enum dma_data_direction dir;
+
+ if (data->flags & MMC_DATA_READ) {
+ dir = DMA_FROM_DEVICE;
+ chan = host->dma_rx_channel;
+ } else {
+ dir = DMA_TO_DEVICE;
+ chan = host->dma_tx_channel;
+ }
+
+ dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+}
+
+static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
+{
u32 status;
int i;
@@ -360,19 +421,12 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
* contiguous buffers. On TX, we'll get a FIFO underrun error.
*/
if (status & MCI_RXDATAAVLBLMASK) {
- dmaengine_terminate_all(chan);
- if (!data->error)
- data->error = -EIO;
- }
-
- if (data->flags & MMC_DATA_WRITE) {
- dir = DMA_TO_DEVICE;
- } else {
- dir = DMA_FROM_DEVICE;
+ data->error = -EIO;
+ mmci_dma_data_error(host, data);
}
if (!data->host_cookie)
- dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
+ mmci_dma_unmap(host, data);
/*
* Use of DMA with scatter-gather is impossible.
@@ -382,16 +436,15 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n");
mmci_dma_release(host);
}
-}
-static void mmci_dma_data_error(struct mmci_host *host)
-{
- dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
- dmaengine_terminate_all(host->dma_current);
+ host->dma_current = NULL;
+ host->dma_desc_current = NULL;
}
-static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
- struct mmci_host_next *next)
+/* prepares DMA channel and DMA descriptor, returns non-zero on failure */
+static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
+ struct dma_chan **dma_chan,
+ struct dma_async_tx_descriptor **dma_desc)
{
struct variant_data *variant = host->variant;
struct dma_slave_config conf = {
@@ -409,16 +462,6 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
enum dma_data_direction buffer_dirn;
int nr_sg;
- /* Check if next job is already prepared */
- if (data->host_cookie && !next &&
- host->dma_current && host->dma_desc_current)
- return 0;
-
- if (!next) {
- host->dma_current = NULL;
- host->dma_desc_current = NULL;
- }
-
if (data->flags & MMC_DATA_READ) {
conf.direction = DMA_DEV_TO_MEM;
buffer_dirn = DMA_FROM_DEVICE;
@@ -433,8 +476,12 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
if (!chan)
return -EINVAL;
- /* If less than or equal to the fifo size, don't bother with DMA */
- if (data->blksz * data->blocks <= variant->fifosize)
+ /*
+ * If less than or equal to the fifo size, don't bother with DMA
+ * SDIO transfers may not be 4 bytes aligned, fall back to PIO
+ */
+ if (data->blksz * data->blocks <= variant->fifosize ||
+ (data->blksz * data->blocks) & 3)
return -EINVAL;
device = chan->device;
@@ -448,29 +495,42 @@ static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
if (!desc)
goto unmap_exit;
- if (next) {
- next->dma_chan = chan;
- next->dma_desc = desc;
- } else {
- host->dma_current = chan;
- host->dma_desc_current = desc;
- }
+ *dma_chan = chan;
+ *dma_desc = desc;
return 0;
unmap_exit:
- if (!next)
- dmaengine_terminate_all(chan);
dma_unmap_sg(device->dev, data->sg, data->sg_len, buffer_dirn);
return -ENOMEM;
}
-static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+static int inline mmci_dma_prep_data(struct mmci_host *host,
+ struct mmc_data *data)
+{
+ /* Check if next job is already prepared. */
+ if (host->dma_current && host->dma_desc_current)
+ return 0;
+
+ /* No job were prepared thus do it now. */
+ return __mmci_dma_prep_data(host, data, &host->dma_current,
+ &host->dma_desc_current);
+}
+
+static inline int mmci_dma_prep_next(struct mmci_host *host,
+ struct mmc_data *data)
+{
+ struct mmci_host_next *nd = &host->next_data;
+ return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc);
+}
+
+static int mmci_dma_start_data(struct mmci_host *host)
{
int ret;
struct mmc_data *data = host->data;
+ struct variant_data *variant = host->variant;
- ret = mmci_dma_prep_data(host, host->data, NULL);
+ ret = mmci_dma_prep_data(host, host->data);
if (ret)
return ret;
@@ -481,10 +541,15 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
dmaengine_submit(host->dma_desc_current);
dma_async_issue_pending(host->dma_current);
- datactrl |= MCI_DPSM_DMAENABLE;
+ host->datactrl_reg |= MCI_DPSM_DMAENABLE;
+
+ /* Some hardware versions need special flags for SDIO DMA write */
+ if (variant->sdio && host->mmc->card && mmc_card_sdio(host->mmc->card)
+ && (data->flags & MMC_DATA_WRITE))
+ host->datactrl_reg |= variant->dma_sdio_req_ctrl;
/* Trigger the DMA transfer */
- writel(datactrl, host->base + MMCIDATACTRL);
+ writel(host->datactrl_reg, host->base + MMCIDATACTRL);
/*
* Let the MMCI say when the data is ended and it's time
@@ -501,18 +566,14 @@ static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
struct mmci_host_next *next = &host->next_data;
if (data->host_cookie && data->host_cookie != next->cookie) {
- pr_warning("[%s] invalid cookie: data->host_cookie %d"
+ pr_err("[%s] invalid cookie: data->host_cookie %d"
" host->next_data.cookie %d\n",
__func__, data->host_cookie, host->next_data.cookie);
- data->host_cookie = 0;
+ BUG();
}
- if (!data->host_cookie)
- return;
-
host->dma_desc_current = next->dma_desc;
host->dma_current = next->dma_chan;
-
next->dma_desc = NULL;
next->dma_chan = NULL;
}
@@ -527,19 +588,18 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq,
if (!data)
return;
- if (data->host_cookie) {
- data->host_cookie = 0;
+ BUG_ON(data->host_cookie);
+
+ if (mmci_validate_data(host, data))
return;
- }
- /* if config for dma */
- if (((data->flags & MMC_DATA_WRITE) && host->dma_tx_channel) ||
- ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)) {
- if (mmci_dma_prep_data(host, data, nd))
- data->host_cookie = 0;
- else
- data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
- }
+ /*
+ * Don't prepare DMA if there is no previous request,
+ * is_first_req is set. Instead, prepare DMA while
+ * start command is being issued.
+ */
+ if (!is_first_req && !mmci_dma_prep_next(host, data))
+ data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
}
static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -547,29 +607,23 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
{
struct mmci_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- struct dma_chan *chan;
- enum dma_data_direction dir;
- if (!data)
+ if (!data || !data->host_cookie)
return;
- if (data->flags & MMC_DATA_READ) {
- dir = DMA_FROM_DEVICE;
- chan = host->dma_rx_channel;
- } else {
- dir = DMA_TO_DEVICE;
- chan = host->dma_tx_channel;
- }
+ mmci_dma_unmap(host, data);
+ if (err) {
+ struct mmci_host_next *next = &host->next_data;
+ struct dma_chan *chan;
+ if (data->flags & MMC_DATA_READ)
+ chan = host->dma_rx_channel;
+ else
+ chan = host->dma_tx_channel;
+ dmaengine_terminate_all(chan);
- /* if config for dma */
- if (chan) {
- if (err)
- dmaengine_terminate_all(chan);
- if (data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len, dir);
- mrq->data->host_cookie = 0;
+ next->dma_desc = NULL;
+ next->dma_chan = NULL;
}
}
@@ -590,11 +644,20 @@ static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
{
}
-static inline void mmci_dma_data_error(struct mmci_host *host)
+static inline void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
+{
+}
+
+static inline void mmci_dma_data_error(struct mmci_host *host, struct mmc_data *data)
+{
+}
+
+static inline int mmci_dma_start_data(struct mmci_host *host)
{
+ return -ENOSYS;
}
-static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
+static inline int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data)
{
return -ENOSYS;
}
@@ -604,10 +667,10 @@ static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datac
#endif
-static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+static void mmci_setup_datactrl(struct mmci_host *host, struct mmc_data *data)
{
struct variant_data *variant = host->variant;
- unsigned int datactrl, timeout, irqmask;
+ unsigned int datactrl, timeout;
unsigned long long clks;
void __iomem *base;
int blksz_bits;
@@ -629,7 +692,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(host->size, base + MMCIDATALENGTH);
blksz_bits = ffs(data->blksz) - 1;
- BUG_ON(1 << blksz_bits != data->blksz);
if (variant->blksz_datactrl16)
datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
@@ -641,14 +703,43 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
/* The ST Micro variants has a special bit to enable SDIO */
if (variant->sdio && host->mmc->card)
- if (mmc_card_sdio(host->mmc->card))
+ if (mmc_card_sdio(host->mmc->card)) {
+ /*
+ * The ST Micro variants has a special bit
+ * to enable SDIO.
+ */
+ u32 clk;
+
datactrl |= MCI_ST_DPSM_SDIOEN;
+ /*
+ * The ST Micro variant for SDIO write transfer sizes
+ * less then 8 bytes needs to have clock H/W flow
+ * control disabled.
+ */
+ if ((host->size < 8) &&
+ (data->flags & MMC_DATA_WRITE))
+ clk = host->clk_reg & ~variant->clkreg_enable;
+ else
+ clk = host->clk_reg | variant->clkreg_enable;
+
+ mmci_write_clkreg(host, clk);
+ }
+ host->datactrl_reg = datactrl;
+ writel(datactrl, base + MMCIDATACTRL);
+}
+
+static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
+{
+ unsigned int irqmask;
+ struct variant_data *variant = host->variant;
+ void __iomem *base = host->base;
+
/*
* Attempt to use DMA operation mode, if this
* should fail, fall back to PIO mode
*/
- if (!mmci_dma_start_data(host, datactrl))
+ if (!mmci_dma_start_data(host))
return;
/* IRQ mode, map the SG list for CPU reading/writing */
@@ -672,7 +763,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
irqmask = MCI_TXFIFOHALFEMPTYMASK;
}
- writel(datactrl, base + MMCIDATACTRL);
writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
mmci_set_mask1(host, irqmask);
}
@@ -699,6 +789,14 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
if (/*interrupt*/0)
c |= MCI_CPSM_INTERRUPT;
+ /*
+ * For levelshifters we must not use more than 25MHz when
+ * sending commands.
+ */
+ host->cclk_desired = host->cclk;
+ if (host->plat->ios_handler && (host->cclk_desired > 25000000))
+ mmci_set_clkreg(host, 25000000);
+
host->cmd = cmd;
writel(cmd->arg, base + MMCIARGUMENT);
@@ -715,8 +813,10 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
u32 remain, success;
/* Terminate the DMA transfer */
- if (dma_inprogress(host))
- mmci_dma_data_error(host);
+ if (dma_inprogress(host)) {
+ mmci_dma_data_error(host, data);
+ mmci_dma_unmap(host, data);
+ }
/*
* Calculate how far we are into the transfer. Note that
@@ -755,7 +855,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
if (status & MCI_DATAEND || data->error) {
if (dma_inprogress(host))
- mmci_dma_unmap(host, data);
+ mmci_dma_finalize(host, data);
mmci_stop_data(host);
if (!data->error)
@@ -789,15 +889,24 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
cmd->resp[3] = readl(base + MMCIRESPONSE3);
}
+ /*
+ * For levelshifters we might have decreased cclk to 25MHz when
+ * sending commands, then we restore the frequency here.
+ */
+ if (host->plat->ios_handler && (host->cclk_desired > host->cclk))
+ mmci_set_clkreg(host, host->cclk_desired);
+
if (!cmd->data || cmd->error) {
- if (host->data) {
- /* Terminate the DMA transfer */
- if (dma_inprogress(host))
- mmci_dma_data_error(host);
- mmci_stop_data(host);
+ /* Terminate the DMA transfer */
+ if (dma_inprogress(host)) {
+ mmci_dma_data_error(host, host->mrq->data);
+ mmci_dma_unmap(host, host->mrq->data);
}
+ if (host->data)
+ mmci_stop_data(host);
mmci_request_end(host, cmd->mrq);
} else if (!(cmd->data->flags & MMC_DATA_READ)) {
+ mmci_setup_datactrl(host, cmd->data);
mmci_start_data(host, cmd->data);
}
}
@@ -864,22 +973,6 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
count = min(remain, maxcnt);
/*
- * The ST Micro variant for SDIO transfer sizes
- * less then 8 bytes should have clock H/W flow
- * control disabled.
- */
- if (variant->sdio &&
- mmc_card_sdio(host->mmc->card)) {
- u32 clk;
- if (count < 8)
- clk = host->clk_reg & ~variant->clkreg_enable;
- else
- clk = host->clk_reg | variant->clkreg_enable;
-
- mmci_write_clkreg(host, clk);
- }
-
- /*
* SDIO especially may want to send something that is
* not divisible by 4 (as opposed to card sectors
* etc), and the FIFO only accept full 32-bit writes.
@@ -1032,13 +1125,12 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mmci_host *host = mmc_priv(mmc);
unsigned long flags;
+ bool dmaprep_after_cmd = false;
WARN_ON(host->mrq != NULL);
- if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
- dev_err(mmc_dev(mmc), "unsupported block size (%d bytes)\n",
- mrq->data->blksz);
- mrq->cmd->error = -EINVAL;
+ mrq->cmd->error = mmci_validate_data(host, mrq->data);
+ if (mrq->cmd->error) {
mmc_request_done(mmc, mrq);
return;
}
@@ -1049,14 +1141,28 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
- if (mrq->data)
+ if (mrq->data) {
+ dmaprep_after_cmd =
+ (host->variant->clkreg_enable &&
+ (mrq->data->flags & MMC_DATA_READ)) ||
+ !(mrq->data->flags & MMC_DATA_READ);
mmci_get_next_data(host, mrq->data);
-
- if (mrq->data && mrq->data->flags & MMC_DATA_READ)
- mmci_start_data(host, mrq->data);
+ if (mrq->data->flags & MMC_DATA_READ) {
+ mmci_setup_datactrl(host, mrq->data);
+ if (!dmaprep_after_cmd)
+ mmci_start_data(host, mrq->data);
+ }
+ }
mmci_start_command(host, mrq->cmd, 0);
+ if (mrq->data && dmaprep_after_cmd) {
+ mmci_dma_prep_data(host, mrq->data);
+
+ if (mrq->data->flags & MMC_DATA_READ)
+ mmci_start_data(host, mrq->data);
+ }
+
spin_unlock_irqrestore(&host->lock, flags);
}
@@ -1178,6 +1284,21 @@ static int mmci_get_cd(struct mmc_host *mmc)
return status;
}
+static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+ int ret = 0;
+
+ if (host->plat->ios_handler) {
+ pm_runtime_get_sync(mmc_dev(mmc));
+ ret = host->plat->ios_handler(mmc_dev(mmc), ios);
+ pm_runtime_mark_last_busy(mmc_dev(mmc));
+ pm_runtime_put_autosuspend(mmc_dev(mmc));
+ }
+
+ return ret;
+}
+
static irqreturn_t mmci_cd_irq(int irq, void *dev_id)
{
struct mmci_host *host = dev_id;
@@ -1194,6 +1315,7 @@ static const struct mmc_host_ops mmci_ops = {
.set_ios = mmci_set_ios,
.get_ro = mmci_get_ro,
.get_cd = mmci_get_cd,
+ .start_signal_voltage_switch = mmci_sig_volt_switch,
};
static int __devinit mmci_probe(struct amba_device *dev,
@@ -1321,6 +1443,9 @@ static int __devinit mmci_probe(struct amba_device *dev,
mmc->caps = plat->capabilities;
mmc->caps2 = plat->capabilities2;
+ /* We support these PM capabilities. */
+ mmc->pm_caps = MMC_PM_KEEP_POWER;
+
/*
* We can do SGIO
*/
@@ -1390,7 +1515,8 @@ static int __devinit mmci_probe(struct amba_device *dev,
}
if ((host->plat->status || host->gpio_cd != -ENOSYS)
- && host->gpio_cd_irq < 0)
+ && host->gpio_cd_irq < 0
+ && !(mmc->caps & MMC_CAP_NONREMOVABLE))
mmc->caps |= MMC_CAP_NEEDS_POLL;
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
@@ -1503,6 +1629,75 @@ static int __devexit mmci_remove(struct amba_device *dev)
return 0;
}
+#if defined(CONFIG_SUSPEND) || defined(CONFIG_PM_RUNTIME)
+static int mmci_save(struct amba_device *dev)
+{
+ struct mmc_host *mmc = amba_get_drvdata(dev);
+ unsigned long flags;
+ struct mmc_ios ios;
+ int ret = 0;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ /* Let the ios_handler act on a POWER_OFF to save power. */
+ if (host->plat->ios_handler) {
+ memcpy(&ios, &mmc->ios, sizeof(struct mmc_ios));
+ ios.power_mode = MMC_POWER_OFF;
+ ret = host->plat->ios_handler(mmc_dev(mmc),
+ &ios);
+ if (ret)
+ return ret;
+ }
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /*
+ * Make sure we do not get any interrupts when we disabled the
+ * clock and the regulator and as well make sure to clear the
+ * registers for clock and power.
+ */
+ writel(0, host->base + MMCIMASK0);
+ writel(0, host->base + MMCIPOWER);
+ writel(0, host->base + MMCICLOCK);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ clk_disable(host->clk);
+ }
+
+ return ret;
+}
+
+static int mmci_restore(struct amba_device *dev)
+{
+ struct mmc_host *mmc = amba_get_drvdata(dev);
+ unsigned long flags;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+
+ clk_enable(host->clk);
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Restore registers and re-enable interrupts. */
+ writel(host->clk_reg, host->base + MMCICLOCK);
+ writel(host->pwr_reg, host->base + MMCIPOWER);
+ writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ /* Restore settings done by the ios_handler. */
+ if (host->plat->ios_handler)
+ host->plat->ios_handler(mmc_dev(mmc),
+ &mmc->ios);
+ }
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_SUSPEND
static int mmci_suspend(struct device *dev)
{
@@ -1511,12 +1706,11 @@ static int mmci_suspend(struct device *dev)
int ret = 0;
if (mmc) {
- struct mmci_host *host = mmc_priv(mmc);
-
ret = mmc_suspend_host(mmc);
if (ret == 0) {
pm_runtime_get_sync(dev);
- writel(0, host->base + MMCIMASK0);
+ mmci_save(adev);
+ amba_pclk_disable(adev);
}
}
@@ -1530,9 +1724,8 @@ static int mmci_resume(struct device *dev)
int ret = 0;
if (mmc) {
- struct mmci_host *host = mmc_priv(mmc);
-
- writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ amba_pclk_enable(adev);
+ mmci_restore(adev);
pm_runtime_put(dev);
ret = mmc_resume_host(mmc);
@@ -1542,8 +1735,43 @@ static int mmci_resume(struct device *dev)
}
#endif
+#ifdef CONFIG_PM_RUNTIME
+static int mmci_runtime_suspend(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
+ int ret = 0;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
+ if (!variant->pwrreg_ctrl_power)
+ ret = mmci_save(adev);
+ }
+
+ return ret;
+}
+
+static int mmci_runtime_resume(struct device *dev)
+{
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
+ int ret = 0;
+
+ if (mmc) {
+ struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
+ if (!variant->pwrreg_ctrl_power)
+ ret = mmci_restore(adev);
+ }
+
+ return ret;
+}
+#endif
+
static const struct dev_pm_ops mmci_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume)
+ SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL)
};
static struct amba_id mmci_ids[] = {
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index d437ccf62d6..5a17beafd05 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -60,6 +60,13 @@
#define MCI_ST_DPSM_RWMOD (1 << 10)
#define MCI_ST_DPSM_SDIOEN (1 << 11)
/* Control register extensions in the ST Micro Ux500 versions */
+/*
+ * DMA request control is required for write
+ * if transfer size is not 32 byte aligned.
+ * DMA request control is also needed if the total
+ * transfer size is 32 byte aligned but any of the
+ * sg element lengths are not aligned with 32 byte.
+ */
#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
#define MCI_ST_DPSM_BUSYMODE (1 << 14)
@@ -179,8 +186,10 @@ struct mmci_host {
unsigned int mclk;
unsigned int cclk;
+ unsigned int cclk_desired;
u32 pwr_reg;
u32 clk_reg;
+ u32 datactrl_reg;
struct mmci_platform_data *plat;
struct variant_data *variant;
diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c
index cd3defb11ff..edbf830a23f 100644
--- a/drivers/net/ethernet/smsc/smsc911x.c
+++ b/drivers/net/ethernet/smsc/smsc911x.c
@@ -33,6 +33,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/crc32.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
@@ -144,6 +145,9 @@ struct smsc911x_data {
/* regulators */
struct regulator_bulk_data supplies[SMSC911X_NUM_SUPPLIES];
+
+ /* clock */
+ struct clk *fsmc_clk;
};
/* Easy access to information */
@@ -369,7 +373,7 @@ out:
}
/*
- * enable resources, currently just regulators.
+ * enable resources, regulators & clocks.
*/
static int smsc911x_enable_resources(struct platform_device *pdev)
{
@@ -379,9 +383,17 @@ static int smsc911x_enable_resources(struct platform_device *pdev)
ret = regulator_bulk_enable(ARRAY_SIZE(pdata->supplies),
pdata->supplies);
- if (ret)
+ if (ret) {
netdev_err(ndev, "failed to enable regulators %d\n",
ret);
+ return ret;
+ }
+
+ if (pdata->fsmc_clk) {
+ ret = clk_enable(pdata->fsmc_clk);
+ if (ret < 0)
+ netdev_err(ndev, "failed to enable clock %d\n", ret);
+ }
return ret;
}
@@ -396,6 +408,8 @@ static int smsc911x_disable_resources(struct platform_device *pdev)
ret = regulator_bulk_disable(ARRAY_SIZE(pdata->supplies),
pdata->supplies);
+ if (pdata->fsmc_clk)
+ clk_disable(pdata->fsmc_clk);
return ret;
}
@@ -418,9 +432,17 @@ static int smsc911x_request_resources(struct platform_device *pdev)
ret = regulator_bulk_get(&pdev->dev,
ARRAY_SIZE(pdata->supplies),
pdata->supplies);
- if (ret)
- netdev_err(ndev, "couldn't get regulators %d\n",
- ret);
+ if (ret) {
+ netdev_err(ndev, "couldn't get regulators %d\n", ret);
+ return ret;
+ }
+
+ /* Request clock, ignore if not here */
+ pdata->fsmc_clk = clk_get(NULL, "fsmc");
+ if (IS_ERR(pdata->fsmc_clk)) {
+ netdev_warn(ndev, "couldn't get clock %d\n", ret);
+ pdata->fsmc_clk = NULL;
+ }
return ret;
}
@@ -436,6 +458,12 @@ static void smsc911x_free_resources(struct platform_device *pdev)
/* Free regulators */
regulator_bulk_free(ARRAY_SIZE(pdata->supplies),
pdata->supplies);
+
+ /* Free clock */
+ if (pdata->fsmc_clk) {
+ clk_put(pdata->fsmc_clk);
+ pdata->fsmc_clk = NULL;
+ }
}
/* waits for MAC not busy, with timeout. Only called by smsc911x_mac_read
@@ -2343,6 +2371,7 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
unsigned int intcfg = 0;
int res_size, irq_flags;
int retval;
+ int to = 100;
pr_info("Driver version %s\n", SMSC_DRV_VERSION);
@@ -2419,6 +2448,18 @@ static int __devinit smsc911x_drv_probe(struct platform_device *pdev)
if (pdata->config.shift)
pdata->ops = &shifted_smsc911x_ops;
+ /* poll the READY bit in PMT_CTRL. Any other access to the device is
+ * forbidden while this bit isn't set. Try for 100ms
+ */
+ while (!(smsc911x_reg_read(pdata, PMT_CTRL) & PMT_CTRL_READY_) && --to)
+ udelay(1000);
+
+ if (to == 0) {
+ pr_err("Device not READY in 100ms aborting\n");
+ goto out_0;
+ }
+
+
retval = smsc911x_init(dev);
if (retval < 0)
goto out_disable_resources;
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 99dc29f2f2f..56dfe432b74 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -307,4 +307,23 @@ config AB8500_BATTERY_THERM_ON_BATCTRL
help
Say Y to enable battery temperature measurements using
thermistor connected on BATCTRL ADC.
+
+config AB8500_9100_LI_ION_BATTERY
+ bool "Enable support of the 9100 Li-ion battery charging"
+ depends on AB8500_BM
+ help
+ Say Y to enable support of the 9100 Li-ion battery charging.
+
+config AB5500_BM
+ bool "AB5500 Battery Management Driver"
+ depends on AB5500_CORE && AB5500_GPADC && MACH_U5500
+ help
+ Say Y to include support for AB5500 battery management.
+
+config AB5500_BATTERY_THERM_ON_BATCTRL
+ bool "Thermistor connected on BATCTRL ADC"
+ depends on AB5500_BM
+ help
+ Say Y to enable battery temperature measurements using
+ thermistor connected on BATCTRL ADC.
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index b6b243416c0..af27f1d08aa 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o
+obj-$(CONFIG_AB5500_BM) += ab5500_charger.o ab5500_btemp.o ab5500_fg.o abx500_chargalg.o
obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
diff --git a/drivers/power/ab5500_btemp.c b/drivers/power/ab5500_btemp.c
new file mode 100644
index 00000000000..3709299d6cb
--- /dev/null
+++ b/drivers/power/ab5500_btemp.c
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Battery temperature driver for ab5500
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500/ab5500-bm.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+
+#define BTEMP_THERMAL_LOW_LIMIT -10
+#define BTEMP_THERMAL_MED_LIMIT 0
+#define BTEMP_THERMAL_HIGH_LIMIT_62 62
+
+#define BTEMP_BATCTRL_CURR_SRC_7UA 7
+#define BTEMP_BATCTRL_CURR_SRC_15UA 15
+#define BTEMP_BATCTRL_CURR_SRC_20UA 20
+
+#define UART_MODE 0x0F
+#define BAT_CUR_SRC 0x1F
+#define RESIS_ID_MODE 0x03
+#define RESET 0x00
+#define ADOUT_10K_PULL_UP 0x07
+
+#define to_ab5500_btemp_device_info(x) container_of((x), \
+ struct ab5500_btemp, btemp_psy);
+
+/**
+ * struct ab5500_btemp_interrupts - ab5500 interrupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab5500_btemp_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab5500_btemp_events {
+ bool batt_rem;
+ bool usb_conn;
+};
+
+/**
+ * struct ab5500_btemp - ab5500 BTEMP device information
+ * @dev: Pointer to the structure device
+ * @chip_id: Chip-Id of the AB5500
+ * @curr_source: What current source we use, in uA
+ * @bat_temp: Battery temperature in degree Celcius
+ * @prev_bat_temp Last dispatched battery temperature
+ * @node: struct of type list_head
+ * @parent: Pointer to the struct ab5500
+ * @gpadc: Pointer to the struct gpadc
+ * @gpadc-auto: Pointer to the struct adc_auto_input
+ * @pdata: Pointer to the ab5500_btemp platform data
+ * @bat: Pointer to the ab5500_bm platform data
+ * @btemp_psy: Structure for BTEMP specific battery properties
+ * @events: Structure for information about events triggered
+ * @btemp_wq: Work queue for measuring the temperature periodically
+ * @btemp_periodic_work: Work for measuring the temperature periodically
+ */
+struct ab5500_btemp {
+ struct device *dev;
+ u8 chip_id;
+ int curr_source;
+ int bat_temp;
+ int prev_bat_temp;
+ struct list_head node;
+ struct ab5500 *parent;
+ struct ab5500_gpadc *gpadc;
+ struct adc_auto_input *gpadc_auto;
+ struct abx500_btemp_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct power_supply btemp_psy;
+ struct ab5500_btemp_events events;
+ struct workqueue_struct *btemp_wq;
+ struct delayed_work btemp_periodic_work;
+};
+
+/* BTEMP power supply properties */
+static enum power_supply_property ab5500_btemp_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_TEMP,
+};
+
+static LIST_HEAD(ab5500_btemp_list);
+
+static int ab5500_btemp_bat_temp_trig(int mux);
+
+struct ab5500_btemp *ab5500_btemp_get(void)
+{
+ struct ab5500_btemp *di;
+ di = list_first_entry(&ab5500_btemp_list, struct ab5500_btemp, node);
+
+ return di;
+}
+
+/**
+ * ab5500_btemp_get_batctrl_temp() - get the temperature
+ * @di: pointer to the ab5500_btemp structure
+ *
+ * Returns the batctrl temperature in millidegrees
+ */
+int ab5500_btemp_get_batctrl_temp(struct ab5500_btemp *di)
+{
+ return di->bat_temp * 1000;
+}
+
+/**
+ * ab5500_btemp_volt_to_res() - convert batctrl voltage to resistance
+ * @di: pointer to the ab5500_btemp structure
+ * @volt: measured batctrl/btemp_ball voltage
+ * @batcrtl: batctrl/btemp_ball node
+ *
+ * This function returns the battery resistance that is
+ * derived from the BATCTRL/BTEMP_BALL voltage.
+ * Returns value in Ohms.
+ */
+static int ab5500_btemp_volt_to_res(struct ab5500_btemp *di,
+ int volt, bool batctrl)
+{
+ int rbs;
+
+ if (batctrl) {
+ /*
+ * If the battery has internal NTC, we use the current
+ * source to calculate the resistance, 7uA or 20uA
+ */
+ rbs = volt * 1000 / di->curr_source;
+ } else {
+ /*
+ * BTEMP_BALL is internally
+ * connected to 1.8V through a 10k resistor
+ */
+ rbs = (10000 * volt) / (1800 - volt);
+ }
+ return rbs;
+}
+
+/**
+ * ab5500_btemp_read_batctrl_voltage() - measure batctrl voltage
+ * @di: pointer to the ab5500_btemp structure
+ *
+ * This function returns the voltage on BATCTRL. Returns value in mV.
+ */
+static int ab5500_btemp_read_batctrl_voltage(struct ab5500_btemp *di)
+{
+ int vbtemp;
+ static int prev;
+
+ vbtemp = ab5500_gpadc_convert(di->gpadc, BAT_CTRL);
+ if (vbtemp < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed, using previous value",
+ __func__);
+ return prev;
+ }
+ prev = vbtemp;
+ return vbtemp;
+}
+
+/**
+ * ab5500_btemp_curr_source_enable() - enable/disable batctrl current source
+ * @di: pointer to the ab5500_btemp structure
+ * @enable: enable or disable the current source
+ *
+ * Enable or disable the current sources for the BatCtrl AD channel
+ */
+static int ab5500_btemp_curr_source_enable(struct ab5500_btemp *di,
+ bool enable)
+{
+ int ret = 0;
+
+ /* Only do this for batteries with internal NTC */
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) {
+
+ dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source);
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_UART,
+ UART_MODE, RESIS_ID_MODE);
+ if (ret) {
+ dev_err(di->dev,
+ "%s failed setting resistance identification mode\n",
+ __func__);
+ return ret;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_URI,
+ BAT_CUR_SRC, BAT_CTRL_15U_ENA);
+ if (ret) {
+ dev_err(di->dev, "%s failed enabling current source\n",
+ __func__);
+ goto disable_curr_source;
+ }
+ } else if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) {
+ dev_dbg(di->dev, "Disable BATCTRL curr source\n");
+
+ /* Write 0 to the curr bits */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_URI,
+ BAT_CUR_SRC, RESET);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling current source\n",
+ __func__);
+ goto disable_curr_source;
+ }
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_UART,
+ UART_MODE, RESET);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling force comp\n",
+ __func__);
+ }
+ }
+ return ret;
+disable_curr_source:
+ /* Write 0 to the curr bits */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_URI,
+ BAT_CUR_SRC, RESET);
+ if (ret) {
+ dev_err(di->dev, "%s failed disabling current source\n",
+ __func__);
+ }
+ return ret;
+}
+
+/**
+ * ab5500_btemp_get_batctrl_res() - get battery resistance
+ * @di: pointer to the ab5500_btemp structure
+ *
+ * This function returns the battery pack identification resistance.
+ * Returns value in Ohms.
+ */
+static int ab5500_btemp_get_batctrl_res(struct ab5500_btemp *di)
+{
+ int ret;
+ int batctrl;
+ int res;
+
+ ret = ab5500_btemp_curr_source_enable(di, true);
+ /* TODO: This delay has to be optimised */
+ msleep(100);
+ if (ret) {
+ dev_err(di->dev, "%s curr source enable failed\n", __func__);
+ return ret;
+ }
+
+ batctrl = ab5500_btemp_read_batctrl_voltage(di);
+ res = ab5500_btemp_volt_to_res(di, batctrl, true);
+
+ ret = ab5500_btemp_curr_source_enable(di, false);
+ if (ret) {
+ dev_err(di->dev, "%s curr source disable failed\n", __func__);
+ return ret;
+ }
+
+ dev_dbg(di->dev, "%s batctrl: %d res: %d ",
+ __func__, batctrl, res);
+
+ return res;
+}
+
+/**
+ * ab5500_btemp_get_btemp_ball_res() - get battery resistance
+ * @di: pointer to the ab5500_btemp structure
+ *
+ * This function returns the battery pack identification
+ * resistance using resistor pull-up mode. Returns value in Ohms.
+ */
+static int ab5500_btemp_get_btemp_ball_res(struct ab5500_btemp *di)
+{
+ int ret, vntc;
+
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_UART,
+ UART_MODE, ADOUT_10K_PULL_UP);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to enable 10k pull up to Vadout\n");
+ return ret;
+ }
+
+ vntc = ab5500_gpadc_convert(di->gpadc, BTEMP_BALL);
+ if (vntc < 0) {
+ dev_err(di->dev, "%s gpadc conversion failed,"
+ " using previous value\n", __func__);
+ return vntc;
+ }
+
+ return ab5500_btemp_volt_to_res(di, vntc, false);
+}
+
+/**
+ * ab5500_btemp_temp_to_res() - temperature to resistance
+ * @di: pointer to the ab5500_btemp structure
+ * @tbl: pointer to the resiatance to temperature table
+ * @tbl_size: size of the resistance to temperature table
+ * @temp: temperature to calculate the resistance from
+ *
+ * This function returns the battery resistance in ohms
+ * based on temperature.
+ */
+static int ab5500_btemp_temp_to_res(struct ab5500_btemp *di,
+ const struct abx500_res_to_temp *tbl, int tbl_size, int temp)
+{
+ int i, res;
+ /*
+ * Calculate the formula for the straight line
+ * Simple interpolation if we are within
+ * the resistance table limits, extrapolate
+ * if resistance is outside the limits.
+ */
+ if (temp < tbl[0].temp)
+ i = 0;
+ else if (temp >= tbl[tbl_size - 1].temp)
+ i = tbl_size - 2;
+ else {
+ i = 0;
+ while (!(temp >= tbl[i].temp &&
+ temp < tbl[i + 1].temp))
+ i++;
+ }
+
+ res = tbl[i].resist + ((tbl[i + 1].resist - tbl[i].resist) *
+ (temp - tbl[i].temp)) / (tbl[i + 1].temp - tbl[i].temp);
+ return res;
+}
+
+/**
+ * ab5500_btemp_temp_to_volt() - temperature to adc voltage
+ * @di: pointer to the ab5500_btemp structure
+ * @temp: temperature to calculate the voltage from
+ *
+ * This function returns the adc voltage in millivolts
+ * based on temperature.
+ */
+static int ab5500_btemp_temp_to_volt(struct ab5500_btemp *di, int temp)
+{
+ int res, id;
+
+ id = di->bat->batt_id;
+ res = ab5500_btemp_temp_to_res(di,
+ di->bat->bat_type[id].r_to_t_tbl,
+ di->bat->bat_type[id].n_temp_tbl_elements,
+ temp);
+ /*
+ * BTEMP_BALL is internally connected to 1.8V
+ * through a 10k resistor
+ */
+ return((1800 * res) / (10000 + res));
+}
+
+/**
+ * ab5500_btemp_res_to_temp() - resistance to temperature
+ * @di: pointer to the ab5500_btemp structure
+ * @tbl: pointer to the resiatance to temperature table
+ * @tbl_size: size of the resistance to temperature table
+ * @res: resistance to calculate the temperature from
+ *
+ * This function returns the battery temperature in degrees Celcius
+ * based on the NTC resistance.
+ */
+static int ab5500_btemp_res_to_temp(struct ab5500_btemp *di,
+ const struct abx500_res_to_temp *tbl, int tbl_size, int res)
+{
+ int i, temp;
+ /*
+ * Calculate the formula for the straight line
+ * Simple interpolation if we are within
+ * the resistance table limits, extrapolate
+ * if resistance is outside the limits.
+ */
+ if (res > tbl[0].resist)
+ i = 0;
+ else if (res <= tbl[tbl_size - 1].resist)
+ i = tbl_size - 2;
+ else {
+ i = 0;
+ while (!(res <= tbl[i].resist &&
+ res > tbl[i + 1].resist))
+ i++;
+ }
+
+ temp = tbl[i].temp + ((tbl[i + 1].temp - tbl[i].temp) *
+ (res - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist);
+ return temp;
+}
+
+/**
+ * ab5500_btemp_measure_temp() - measure battery temperature
+ * @di: pointer to the ab5500_btemp structure
+ *
+ * Returns battery temperature (on success) else the previous temperature
+ */
+static int ab5500_btemp_measure_temp(struct ab5500_btemp *di)
+{
+ int temp, rbat;
+ u8 id;
+
+ id = di->bat->batt_id;
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+ id != BATTERY_UNKNOWN && !di->bat->auto_trig)
+ rbat = ab5500_btemp_get_batctrl_res(di);
+ else
+ rbat = ab5500_btemp_get_btemp_ball_res(di);
+
+ if (rbat < 0) {
+ dev_err(di->dev, "%s failed to get resistance\n", __func__);
+ /*
+ * Return out-of-range temperature so that
+ * charging is stopped
+ */
+ return BTEMP_THERMAL_LOW_LIMIT;
+ }
+
+ temp = ab5500_btemp_res_to_temp(di,
+ di->bat->bat_type[id].r_to_t_tbl,
+ di->bat->bat_type[id].n_temp_tbl_elements, rbat);
+ dev_dbg(di->dev, "Battery temperature is %d\n", temp);
+
+ return temp;
+}
+
+/**
+ * ab5500_btemp_id() - Identify the connected battery
+ * @di: pointer to the ab5500_btemp structure
+ *
+ * This function will try to identify the battery by reading the ID
+ * resistor. Some brands use a combined ID resistor with a NTC resistor to
+ * both be able to identify and to read the temperature of it.
+ */
+static int ab5500_btemp_id(struct ab5500_btemp *di)
+{
+ int res;
+ u8 i;
+
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA;
+ di->bat->batt_id = BATTERY_UNKNOWN;
+
+ res = ab5500_btemp_get_batctrl_res(di);
+ if (res < 0) {
+ dev_err(di->dev, "%s get batctrl res failed\n", __func__);
+ return -ENXIO;
+ }
+
+ /* BATTERY_UNKNOWN is defined on position 0, skip it! */
+ for (i = BATTERY_UNKNOWN + 1; i < di->bat->n_btypes; i++) {
+ if ((res <= di->bat->bat_type[i].resis_high) &&
+ (res >= di->bat->bat_type[i].resis_low)) {
+ dev_dbg(di->dev, "Battery detected on %s"
+ " low %d < res %d < high: %d"
+ " index: %d\n",
+ di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL ?
+ "BATCTRL" : "BATTEMP",
+ di->bat->bat_type[i].resis_low, res,
+ di->bat->bat_type[i].resis_high, i);
+
+ di->bat->batt_id = i;
+ break;
+ }
+ }
+
+ if (di->bat->batt_id == BATTERY_UNKNOWN) {
+ dev_warn(di->dev, "Battery identified as unknown"
+ ", resistance %d Ohm\n", res);
+ return -ENXIO;
+ }
+
+ /*
+ * We only have to change current source if the
+ * detected type is Type 1, else we use the 7uA source
+ */
+ if (di->bat->adc_therm == ABx500_ADC_THERM_BATCTRL &&
+ di->bat->batt_id == 1) {
+ dev_dbg(di->dev, "Set BATCTRL current source to 15uA\n");
+ di->curr_source = BTEMP_BATCTRL_CURR_SRC_15UA;
+ }
+
+ return di->bat->batt_id;
+}
+
+/**
+ * ab5500_btemp_periodic_work() - Measuring the temperature periodically
+ * @work: pointer to the work_struct structure
+ *
+ * Work function for measuring the temperature periodically
+ */
+static void ab5500_btemp_periodic_work(struct work_struct *work)
+{
+ struct ab5500_btemp *di = container_of(work,
+ struct ab5500_btemp, btemp_periodic_work.work);
+
+ di->bat_temp = ab5500_btemp_measure_temp(di);
+
+ if (di->bat_temp != di->prev_bat_temp) {
+ di->prev_bat_temp = di->bat_temp;
+ power_supply_changed(&di->btemp_psy);
+ }
+ di->bat->temp_now = di->bat_temp;
+
+ if (!di->bat->auto_trig) {
+ /* Check for temperature limits */
+ if (di->bat_temp <= BTEMP_THERMAL_LOW_LIMIT) {
+ dev_err(di->dev,
+ "battery temp less than lower threshold\n");
+ power_supply_changed(&di->btemp_psy);
+ } else if (di->bat_temp >= BTEMP_THERMAL_HIGH_LIMIT_62) {
+ dev_err(di->dev,
+ "battery temp greater them max threshold\n");
+ power_supply_changed(&di->btemp_psy);
+ }
+
+ /* Schedule a new measurement */
+ if (di->events.usb_conn)
+ queue_delayed_work(di->btemp_wq,
+ &di->btemp_periodic_work,
+ round_jiffies(di->bat->interval_charging * HZ));
+ else
+ queue_delayed_work(di->btemp_wq,
+ &di->btemp_periodic_work,
+ round_jiffies(di->bat->interval_not_charging * HZ));
+ } else {
+ /* Schedule a new measurement */
+ queue_delayed_work(di->btemp_wq,
+ &di->btemp_periodic_work,
+ round_jiffies(di->bat->interval_charging * HZ));
+ }
+}
+
+/**
+ * ab5500_btemp_batt_removal_handler() - battery removal detected
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab5500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_btemp_batt_removal_handler(int irq, void *_di)
+{
+ struct ab5500_btemp *di = _di;
+ dev_err(di->dev, "Battery removal detected!\n");
+
+ di->events.batt_rem = true;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_btemp_batt_attach_handler() - battery insertion detected
+ * @irq: interrupt number
+ * @_di: void pointer that has to address of ab5500_btemp
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_btemp_batt_attach_handler(int irq, void *_di)
+{
+ struct ab5500_btemp *di = _di;
+ dev_err(di->dev, "Battery attached!\n");
+
+ di->events.batt_rem = false;
+ power_supply_changed(&di->btemp_psy);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_btemp_periodic() - Periodic temperature measurements
+ * @di: pointer to the ab5500_btemp structure
+ * @enable: enable or disable periodic temperature measurements
+ *
+ * Starts of stops periodic temperature measurements. Periodic measurements
+ * should only be done when a charger is connected.
+ */
+static void ab5500_btemp_periodic(struct ab5500_btemp *di,
+ bool enable)
+{
+ dev_dbg(di->dev, "Enable periodic temperature measurements: %d\n",
+ enable);
+
+ if (enable)
+ queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
+ else
+ cancel_delayed_work_sync(&di->btemp_periodic_work);
+}
+
+/**
+ * ab5500_btemp_get_property() - get the btemp properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the btemp
+ * properties by reading the sysfs files.
+ * online: presence of the battery
+ * present: presence of the battery
+ * technology: battery technology
+ * temp: battery temperature
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab5500_btemp_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab5500_btemp *di;
+
+ di = to_ab5500_btemp_device_info(psy);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (di->events.batt_rem)
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = di->bat->bat_type[di->bat->batt_id].name;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ if (di->bat->batt_id == BATTERY_UNKNOWN)
+ /*
+ * In case the battery is not identified, its assumed that
+ * we are using the power supply and since no monitoring is
+ * done for the same, a nominal temp is hardocded.
+ */
+ val->intval = 250;
+ else
+ val->intval = di->bat_temp * 10;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ab5500_btemp_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab5500_btemp *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab5500_btemp_device_info(psy);
+
+ /*
+ * For all psy where the name of your driver
+ * appears in any supplied_to
+ */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_USB:
+ /* USB disconnected */
+ if (!ret.intval && di->events.usb_conn) {
+ di->events.usb_conn = false;
+ if (di->bat->auto_trig)
+ ab5500_btemp_periodic(di,
+ false);
+ }
+ /* USB connected */
+ else if (ret.intval && !di->events.usb_conn) {
+ di->events.usb_conn = true;
+ if (di->bat->auto_trig)
+ ab5500_btemp_periodic(di, true);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab5500_btemp_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is pointing to the function pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in the external power
+ * supply to the btemp.
+ */
+static void ab5500_btemp_external_power_changed(struct power_supply *psy)
+{
+ struct ab5500_btemp *di = to_ab5500_btemp_device_info(psy);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->btemp_psy, ab5500_btemp_get_ext_psy_data);
+}
+
+/* ab5500 btemp driver interrupts and their respective isr */
+static struct ab5500_btemp_interrupts ab5500_btemp_irq[] = {
+ {"BATT_REMOVAL", ab5500_btemp_batt_removal_handler},
+ {"BATT_ATTACH", ab5500_btemp_batt_attach_handler},
+};
+
+static int ab5500_btemp_bat_temp_trig(int mux)
+{
+ struct ab5500_btemp *di = ab5500_btemp_get();
+ int temp = ab5500_btemp_measure_temp(di);
+
+ if (temp < (BTEMP_THERMAL_LOW_LIMIT+1)) {
+ dev_err(di->dev,
+ "battery temp less than lower threshold (-10 deg cel)\n");
+ power_supply_changed(&di->btemp_psy);
+ } else if (temp > (BTEMP_THERMAL_HIGH_LIMIT_62-1)) {
+ dev_err(di->dev, "battery temp greater them max threshold\n");
+ power_supply_changed(&di->btemp_psy);
+ }
+
+ return 0;
+}
+
+static int ab5500_btemp_auto_temp(struct ab5500_btemp *di)
+{
+ struct adc_auto_input *auto_ip;
+ int ret = 0;
+
+ auto_ip = kzalloc(sizeof(struct adc_auto_input), GFP_KERNEL);
+ if (!auto_ip) {
+ dev_err(di->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ auto_ip->mux = BTEMP_BALL;
+ auto_ip->freq = MS500;
+ auto_ip->min = ab5500_btemp_temp_to_volt(di,
+ BTEMP_THERMAL_HIGH_LIMIT_62);
+ auto_ip->max = ab5500_btemp_temp_to_volt(di,
+ BTEMP_THERMAL_LOW_LIMIT);
+ auto_ip->auto_adc_callback = ab5500_btemp_bat_temp_trig;
+ di->gpadc_auto = auto_ip;
+ ret = ab5500_gpadc_convert_auto(di->gpadc, di->gpadc_auto);
+ if (ret)
+ dev_err(di->dev,
+ "failed to set auto trigger for battery temp\n");
+ return ret;
+}
+
+#if defined(CONFIG_PM)
+static int ab5500_btemp_resume(struct platform_device *pdev)
+{
+ struct ab5500_btemp *di = platform_get_drvdata(pdev);
+
+ if (di->events.usb_conn)
+ ab5500_btemp_periodic(di, true);
+
+ return 0;
+}
+
+static int ab5500_btemp_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab5500_btemp *di = platform_get_drvdata(pdev);
+
+ if (di->events.usb_conn)
+ ab5500_btemp_periodic(di, false);
+
+ return 0;
+}
+#else
+#define ab5500_btemp_suspend NULL
+#define ab5500_btemp_resume NULL
+#endif
+
+static int __devexit ab5500_btemp_remove(struct platform_device *pdev)
+{
+ struct ab5500_btemp *di = platform_get_drvdata(pdev);
+ int i, irq;
+
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab5500_btemp_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab5500_btemp_irq[i].name);
+ free_irq(irq, di);
+ }
+
+ /* Delete the work queue */
+ destroy_workqueue(di->btemp_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->btemp_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di->gpadc_auto);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab5500_btemp_probe(struct platform_device *pdev)
+{
+ int irq, i, ret = 0;
+ struct abx500_bm_plat_data *plat_data;
+
+ struct ab5500_btemp *di =
+ kzalloc(sizeof(struct ab5500_btemp), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab5500_gpadc_get("ab5500-adc.0");
+
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->btemp;
+ di->bat = plat_data->battery;
+
+ /* get btemp specific platform data */
+ if (!di->pdata) {
+ dev_err(di->dev, "no btemp platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* get battery specific platform data */
+ if (!di->bat) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* BTEMP supply */
+ di->btemp_psy.name = "ab5500_btemp";
+ di->btemp_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->btemp_psy.properties = ab5500_btemp_props;
+ di->btemp_psy.num_properties = ARRAY_SIZE(ab5500_btemp_props);
+ di->btemp_psy.get_property = ab5500_btemp_get_property;
+ di->btemp_psy.supplied_to = di->pdata->supplied_to;
+ di->btemp_psy.num_supplicants = di->pdata->num_supplicants;
+ di->btemp_psy.external_power_changed =
+ ab5500_btemp_external_power_changed;
+
+
+ /* Create a work queue for the btemp */
+ di->btemp_wq =
+ create_singlethread_workqueue("ab5500_btemp_wq");
+ if (di->btemp_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for measuring temperature periodically */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work,
+ ab5500_btemp_periodic_work);
+
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(di->dev);
+ if (ret < 0) {
+ dev_err(di->dev, "failed to get chip ID\n");
+ goto free_btemp_wq;
+ }
+ di->chip_id = ret;
+ dev_dbg(di->dev, "ab5500 CID is: 0x%02x\n",
+ di->chip_id);
+
+ /* Identify the battery */
+ if (ab5500_btemp_id(di) < 0)
+ dev_warn(di->dev, "failed to identify the battery\n");
+
+ /* Measure temperature once initially */
+ di->bat_temp = ab5500_btemp_measure_temp(di);
+ di->bat->temp_now = di->bat_temp;
+
+ /* Register BTEMP power supply class */
+ ret = power_supply_register(di->dev, &di->btemp_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register BTEMP psy\n");
+ goto free_btemp_wq;
+ }
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab5500_btemp_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab5500_btemp_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab5500_btemp_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab5500_btemp_irq[i].name, di);
+
+ if (ret) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab5500_btemp_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab5500_btemp_irq[i].name, irq, ret);
+ }
+
+ if (!di->bat->auto_trig) {
+ /* Schedule monitoring work only if battery type is known */
+ if (di->bat->batt_id != BATTERY_UNKNOWN)
+ queue_delayed_work(di->btemp_wq, &di->btemp_periodic_work, 0);
+ } else {
+ ret = ab5500_btemp_auto_temp(di);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to register auto trigger for battery temp\n");
+ goto free_irq;
+ }
+ }
+
+ platform_set_drvdata(pdev, di);
+ list_add_tail(&di->node, &ab5500_btemp_list);
+
+ dev_info(di->dev, "probe success\n");
+ return ret;
+
+free_irq:
+ power_supply_unregister(&di->btemp_psy);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab5500_btemp_irq[i].name);
+ free_irq(irq, di);
+ }
+free_btemp_wq:
+ destroy_workqueue(di->btemp_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab5500_btemp_driver = {
+ .probe = ab5500_btemp_probe,
+ .remove = __devexit_p(ab5500_btemp_remove),
+ .suspend = ab5500_btemp_suspend,
+ .resume = ab5500_btemp_resume,
+ .driver = {
+ .name = "ab5500-btemp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab5500_btemp_init(void)
+{
+ return platform_driver_register(&ab5500_btemp_driver);
+}
+
+static void __exit ab5500_btemp_exit(void)
+{
+ platform_driver_unregister(&ab5500_btemp_driver);
+}
+
+subsys_initcall_sync(ab5500_btemp_init);
+module_exit(ab5500_btemp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab5500-btemp");
+MODULE_DESCRIPTION("AB5500 battery temperature driver");
diff --git a/drivers/power/ab5500_charger.c b/drivers/power/ab5500_charger.c
new file mode 100644
index 00000000000..b90c51a4f31
--- /dev/null
+++ b/drivers/power/ab5500_charger.c
@@ -0,0 +1,1820 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Charger driver for AB5500
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500/ab5500-bm.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+#include <linux/mfd/abx500/ux500_chargalg.h>
+#include <linux/usb/otg.h>
+
+/* Charger constants */
+#define NO_PW_CONN 0
+#define USB_PW_CONN 2
+
+/* HW failure constants */
+#define VBUS_CH_NOK 0x0A
+#define VBUS_OVV_TH 0x06
+
+/* AB5500 Charger constants */
+#define AB5500_USB_LINK_STATUS 0x78
+#define CHARGER_REV_SUP 0x10
+#define SW_EOC 0x40
+#define USB_CHAR_DET 0x02
+#define VBUS_RISING 0x20
+#define VBUS_FALLING 0x40
+#define USB_LINK_UPDATE 0x02
+#define USB_CH_TH_PROT_LOW 0x02
+#define USB_CH_TH_PROT_HIGH 0x01
+#define USB_ID_HOST_DET_ENA_MASK 0x02
+#define USB_ID_HOST_DET_ENA 0x02
+#define USB_ID_DEVICE_DET_ENA_MASK 0x01
+#define USB_ID_DEVICE_DET_ENA 0x01
+#define CHARGER_ISET_IN_1_1A 0x0C
+#define LED_ENABLE 0x01
+#define RESET 0x00
+#define SSW_ENABLE_REBOOT 0x80
+#define SSW_REBOOT_EN 0x40
+#define SSW_CONTROL_AUTOC 0x04
+#define SSW_PSEL_480S 0x00
+
+/* UsbLineStatus register - usb types */
+enum ab5500_charger_link_status {
+ USB_STAT_NOT_CONFIGURED,
+ USB_STAT_STD_HOST_NC,
+ USB_STAT_STD_HOST_C_NS,
+ USB_STAT_STD_HOST_C_S,
+ USB_STAT_HOST_CHG_NM,
+ USB_STAT_HOST_CHG_HS,
+ USB_STAT_HOST_CHG_HS_CHIRP,
+ USB_STAT_DEDICATED_CHG,
+ USB_STAT_ACA_RID_A,
+ USB_STAT_ACA_RID_B,
+ USB_STAT_ACA_RID_C_NM,
+ USB_STAT_ACA_RID_C_HS,
+ USB_STAT_ACA_RID_C_HS_CHIRP,
+ USB_STAT_HM_IDGND,
+ USB_STAT_RESERVED,
+ USB_STAT_NOT_VALID_LINK,
+};
+
+enum ab5500_usb_state {
+ AB5500_BM_USB_STATE_RESET_HS, /* HighSpeed Reset */
+ AB5500_BM_USB_STATE_RESET_FS, /* FullSpeed/LowSpeed Reset */
+ AB5500_BM_USB_STATE_CONFIGURED,
+ AB5500_BM_USB_STATE_SUSPEND,
+ AB5500_BM_USB_STATE_RESUME,
+ AB5500_BM_USB_STATE_MAX,
+};
+
+/* VBUS input current limits supported in AB5500 in mA */
+#define USB_CH_IP_CUR_LVL_0P05 50
+#define USB_CH_IP_CUR_LVL_0P09 98
+#define USB_CH_IP_CUR_LVL_0P19 193
+#define USB_CH_IP_CUR_LVL_0P29 290
+#define USB_CH_IP_CUR_LVL_0P38 380
+#define USB_CH_IP_CUR_LVL_0P45 450
+#define USB_CH_IP_CUR_LVL_0P5 500
+#define USB_CH_IP_CUR_LVL_0P6 600
+#define USB_CH_IP_CUR_LVL_0P7 700
+#define USB_CH_IP_CUR_LVL_0P8 800
+#define USB_CH_IP_CUR_LVL_0P9 900
+#define USB_CH_IP_CUR_LVL_1P0 1000
+#define USB_CH_IP_CUR_LVL_1P1 1100
+#define USB_CH_IP_CUR_LVL_1P3 1300
+#define USB_CH_IP_CUR_LVL_1P4 1400
+#define USB_CH_IP_CUR_LVL_1P5 1500
+
+#define to_ab5500_charger_usb_device_info(x) container_of((x), \
+ struct ab5500_charger, usb_chg)
+
+/**
+ * struct ab5500_charger_interrupts - ab5500 interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab5500_charger_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+struct ab5500_charger_info {
+ int charger_connected;
+ int charger_online;
+ int charger_voltage;
+ int cv_active;
+ bool wd_expired;
+};
+
+struct ab5500_charger_event_flags {
+ bool usb_thermal_prot;
+ bool vbus_ovv;
+ bool usbchargernotok;
+ bool vbus_collapse;
+};
+
+struct ab5500_charger_usb_state {
+ bool usb_changed;
+ int usb_current;
+ enum ab5500_usb_state state;
+ spinlock_t usb_lock;
+};
+
+/**
+ * struct ab5500_charger - ab5500 Charger device information
+ * @dev: Pointer to the structure device
+ * @chip_id: Chip-Id of the ab5500
+ * @max_usb_in_curr: Max USB charger input current
+ * @vbus_detected: VBUS detected
+ * @vbus_detected_start:
+ * VBUS detected during startup
+ * @parent: Pointer to the struct ab5500
+ * @gpadc: Pointer to the struct gpadc
+ * @pdata: Pointer to the ab5500_charger platform data
+ * @bat: Pointer to the ab5500_bm platform data
+ * @flags: Structure for information about events triggered
+ * @usb_state: Structure for usb stack information
+ * @usb_chg: USB charger power supply
+ * @ac: Structure that holds the AC charger properties
+ * @usb: Structure that holds the USB charger properties
+ * @charger_wq: Work queue for the IRQs and checking HW state
+ * @check_hw_failure_work: Work for checking HW state
+ * @check_usbchgnotok_work: Work for checking USB charger not ok status
+ * @ac_work: Work for checking AC charger connection
+ * @detect_usb_type_work: Work for detecting the USB type connected
+ * @usb_link_status_work: Work for checking the new USB link status
+ * @usb_state_changed_work: Work for checking USB state
+ * @check_main_thermal_prot_work:
+ * Work for checking Main thermal status
+ * @check_usb_thermal_prot_work:
+ * Work for checking USB thermal status
+ * @ otg: pointer to struct otg_transceiver, used to
+ * notify the current during a standard host
+ * charger.
+ * @nb: structture of type notifier_block, which has
+ * a function pointer referenced by usb driver.
+ */
+struct ab5500_charger {
+ struct device *dev;
+ u8 chip_id;
+ int max_usb_in_curr;
+ bool vbus_detected;
+ bool vbus_detected_start;
+ struct ab5500 *parent;
+ struct ab5500_gpadc *gpadc;
+ struct abx500_charger_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct ab5500_charger_event_flags flags;
+ struct ab5500_charger_usb_state usb_state;
+ struct ux500_charger usb_chg;
+ struct ab5500_charger_info usb;
+ struct workqueue_struct *charger_wq;
+ struct delayed_work check_hw_failure_work;
+ struct delayed_work check_usbchgnotok_work;
+ struct work_struct detect_usb_type_work;
+ struct work_struct usb_link_status_work;
+ struct work_struct usb_state_changed_work;
+ struct work_struct check_usb_thermal_prot_work;
+ struct otg_transceiver *otg;
+ struct notifier_block nb;
+};
+
+/* USB properties */
+static enum power_supply_property ab5500_charger_usb_props[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+/**
+ * ab5500_charger_get_vbus_voltage() - get vbus voltage
+ * @di: pointer to the ab5500_charger structure
+ *
+ * This function returns the vbus voltage.
+ * Returns vbus voltage (on success)
+ */
+static int ab5500_charger_get_vbus_voltage(struct ab5500_charger *di)
+{
+ int vch;
+
+ /* Only measure voltage if the charger is connected */
+ if (di->usb.charger_connected) {
+ vch = ab5500_gpadc_convert(di->gpadc, VBUS_V);
+ if (vch < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ vch = 0;
+ }
+ return vch;
+}
+
+/**
+ * ab5500_charger_get_usb_current() - get usb charger current
+ * @di: pointer to the ab5500_charger structure
+ *
+ * This function returns the usb charger current.
+ * Returns usb current (on success) and error code on failure
+ */
+static int ab5500_charger_get_usb_current(struct ab5500_charger *di)
+{
+ int ich;
+
+ /* Only measure current if the charger is online */
+ if (di->usb.charger_online) {
+ ich = ab5500_gpadc_convert(di->gpadc, USB_CHARGER_C);
+ if (ich < 0)
+ dev_err(di->dev, "%s gpadc conv failed\n", __func__);
+ } else {
+ ich = 0;
+ }
+ return ich;
+}
+
+/**
+ * ab5500_charger_detect_chargers() - Detect the connected chargers
+ * @di: pointer to the ab5500_charger structure
+ *
+ * Returns the type of charger connected.
+ * For USB it will not mean we can actually charge from it
+ * but that there is a USB cable connected that we have to
+ * identify. This is used during startup when we don't get
+ * interrupts of the charger detection
+ *
+ * Returns an integer value, that means,
+ * NO_PW_CONN no power supply is connected
+ * USB_PW_CONN if the USB power supply is connected
+ */
+static int ab5500_charger_detect_chargers(struct ab5500_charger *di)
+{
+ int result = NO_PW_CONN;
+ int ret;
+ u8 val;
+ /* Check for USB charger */
+ /*
+ * TODO: Since there are no status register validating by
+ * reading the IT souce registers
+ */
+ ret = abx500_get_register_interruptible(di->dev, AB5500_BANK_IT,
+ AB5500_IT_SOURCE8, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab5500 read failed\n", __func__);
+ return ret;
+ }
+
+ if (val & VBUS_RISING)
+ result |= USB_PW_CONN;
+ else if (val & VBUS_FALLING)
+ result = NO_PW_CONN;
+
+ return result;
+}
+
+/**
+ * ab5500_charger_max_usb_curr() - get the max curr for the USB type
+ * @di: pointer to the ab5500_charger structure
+ * @link_status: the identified USB type
+ *
+ * Get the maximum current that is allowed to be drawn from the host
+ * based on the USB type.
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab5500_charger_max_usb_curr(struct ab5500_charger *di,
+ enum ab5500_charger_link_status link_status)
+{
+ int ret = 0;
+
+ switch (link_status) {
+ case USB_STAT_STD_HOST_NC:
+ case USB_STAT_STD_HOST_C_NS:
+ case USB_STAT_STD_HOST_C_S:
+ dev_dbg(di->dev, "USB Type - Standard host is "
+ "detected through USB driver\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ break;
+ case USB_STAT_HOST_CHG_HS_CHIRP:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ case USB_STAT_HOST_CHG_HS:
+ case USB_STAT_ACA_RID_C_HS:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P9;
+ break;
+ case USB_STAT_ACA_RID_A:
+ /*
+ * Dedicated charger level minus maximum current accessory
+ * can consume (300mA). Closest level is 1100mA
+ */
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P1;
+ break;
+ case USB_STAT_ACA_RID_B:
+ /*
+ * Dedicated charger level minus 120mA (20mA for ACA and
+ * 100mA for potential accessory). Closest level is 1300mA
+ */
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P3;
+ break;
+ case USB_STAT_DEDICATED_CHG:
+ case USB_STAT_HOST_CHG_NM:
+ case USB_STAT_ACA_RID_C_HS_CHIRP:
+ case USB_STAT_ACA_RID_C_NM:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
+ break;
+ case USB_STAT_RESERVED:
+ /*
+ * This state is used to indicate that VBUS has dropped below
+ * the detection level 4 times in a row. This is due to the
+ * charger output current is set to high making the charger
+ * voltage collapse. This have to be propagated through to
+ * chargalg. This is done using the property
+ * POWER_SUPPLY_PROP_CURRENT_AVG = 1
+ */
+ di->flags.vbus_collapse = true;
+ dev_dbg(di->dev, "USB Type - USB_STAT_RESERVED "
+ "VBUS has collapsed\n");
+ ret = -1;
+ break;
+ case USB_STAT_HM_IDGND:
+ case USB_STAT_NOT_CONFIGURED:
+ case USB_STAT_NOT_VALID_LINK:
+ dev_err(di->dev, "USB Type - Charging not allowed\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ default:
+ dev_err(di->dev, "USB Type - Unknown\n");
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ ret = -ENXIO;
+ break;
+ };
+
+ dev_dbg(di->dev, "USB Type - 0x%02x MaxCurr: %d",
+ link_status, di->max_usb_in_curr);
+
+ return ret;
+}
+
+/**
+ * ab5500_charger_read_usb_type() - read the type of usb connected
+ * @di: pointer to the ab5500_charger structure
+ *
+ * Detect the type of the plugged USB
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab5500_charger_read_usb_type(struct ab5500_charger *di)
+{
+ int ret;
+ u8 val;
+
+ ret = abx500_get_register_interruptible(di->dev, AB5500_BANK_USB,
+ AB5500_USB_LINE_STATUS, &val);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab5500 read failed\n", __func__);
+ return ret;
+ }
+
+ /* get the USB type */
+ val = (val & AB5500_USB_LINK_STATUS) >> 3;
+ ret = ab5500_charger_max_usb_curr(di,
+ (enum ab5500_charger_link_status) val);
+
+ return ret;
+}
+
+static int ab5500_charger_voltage_map[] = {
+ 3500 ,
+ 3525 ,
+ 3550 ,
+ 3575 ,
+ 3600 ,
+ 3625 ,
+ 3650 ,
+ 3675 ,
+ 3700 ,
+ 3725 ,
+ 3750 ,
+ 3775 ,
+ 3800 ,
+ 3825 ,
+ 3850 ,
+ 3875 ,
+ 3900 ,
+ 3925 ,
+ 3950 ,
+ 3975 ,
+ 4000 ,
+ 4025 ,
+ 4050 ,
+ 4060 ,
+ 4070 ,
+ 4080 ,
+ 4090 ,
+ 4100 ,
+ 4110 ,
+ 4120 ,
+ 4130 ,
+ 4140 ,
+ 4150 ,
+ 4160 ,
+ 4170 ,
+ 4180 ,
+ 4190 ,
+ 4200 ,
+ 4210 ,
+ 4220 ,
+ 4230 ,
+ 4240 ,
+ 4250 ,
+ 4260 ,
+ 4270 ,
+ 4280 ,
+ 4290 ,
+ 4300 ,
+ 4310 ,
+ 4320 ,
+ 4330 ,
+ 4340 ,
+ 4350 ,
+ 4360 ,
+ 4370 ,
+ 4380 ,
+ 4390 ,
+ 4400 ,
+ 4410 ,
+ 4420 ,
+ 4430 ,
+ 4440 ,
+ 4450 ,
+ 4460 ,
+ 4470 ,
+ 4480 ,
+ 4490 ,
+ 4500 ,
+ 4510 ,
+ 4520 ,
+ 4530 ,
+ 4540 ,
+ 4550 ,
+ 4560 ,
+ 4570 ,
+ 4580 ,
+ 4590 ,
+ 4600 ,
+};
+
+/*
+ * This array maps the raw hex value to charger current used by the ab5500
+ * Values taken from the AB5500 product specification manual
+ */
+static int ab5500_charger_current_map[] = {
+ 100 ,
+ 200 ,
+ 300 ,
+ 400 ,
+ 500 ,
+ 600 ,
+ 700 ,
+ 800 ,
+ 900 ,
+ 1000,
+ 1100,
+ 1200,
+ 1300,
+ 1400,
+ 1500,
+ 1500,
+};
+
+static int ab5500_icsr_current_map[] = {
+ 50,
+ 93,
+ 193,
+ 290,
+ 380,
+ 450,
+ 500 ,
+ 600 ,
+ 700 ,
+ 800 ,
+ 900 ,
+ 1000,
+ 1100,
+ 1300,
+ 1400,
+ 1500,
+};
+
+static int ab5500_cvrec_voltage_map[] = {
+ 3300,
+ 3325,
+ 3350,
+ 3375,
+ 3400,
+ 3425,
+ 3450,
+ 3475,
+ 3500,
+ 3525,
+ 3550,
+ 3575,
+ 3600,
+ 3625,
+ 3650,
+ 3675,
+ 3700,
+ 3725,
+ 3750,
+ 3775,
+ 3800,
+ 3825,
+ 3850,
+ 3875,
+ 3900,
+ 3925,
+ 4000,
+ 4025,
+ 4050,
+ 4075,
+ 4100,
+ 4125,
+ 4150,
+ 4175,
+ 4200,
+ 4225,
+ 4250,
+ 4275,
+ 4300,
+ 4325,
+ 4350,
+ 4375,
+ 4400,
+ 4425,
+ 4450,
+ 4475,
+ 4500,
+ 4525,
+ 4550,
+ 4575,
+ 4600,
+};
+
+static int ab5500_cvrec_voltage_to_regval(int voltage)
+{
+ int i;
+
+ /* Special case for voltage below 3.3V */
+ if (voltage < ab5500_cvrec_voltage_map[0])
+ return 0;
+
+ for (i = 1; i < ARRAY_SIZE(ab5500_cvrec_voltage_map); i++) {
+ if (voltage < ab5500_cvrec_voltage_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab5500_cvrec_voltage_map) - 1;
+ if (voltage == ab5500_cvrec_voltage_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab5500_voltage_to_regval(int voltage)
+{
+ int i;
+
+ /* Special case for voltage below 3.3V */
+ if (voltage < ab5500_charger_voltage_map[0])
+ return 0;
+
+ for (i = 1; i < ARRAY_SIZE(ab5500_charger_voltage_map); i++) {
+ if (voltage < ab5500_charger_voltage_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab5500_charger_voltage_map) - 1;
+ if (voltage == ab5500_charger_voltage_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab5500_icsr_curr_to_regval(int curr)
+{
+ int i;
+
+ if (curr < ab5500_icsr_current_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab5500_icsr_current_map); i++) {
+ if (curr < ab5500_icsr_current_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab5500_icsr_current_map) - 1;
+ if (curr == ab5500_icsr_current_map[i])
+ return i;
+ else
+ return -1;
+}
+
+static int ab5500_current_to_regval(int curr)
+{
+ int i;
+
+ if (curr < ab5500_charger_current_map[0])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab5500_charger_current_map); i++) {
+ if (curr < ab5500_charger_current_map[i])
+ return i - 1;
+ }
+
+ /* If not last element, return error */
+ i = ARRAY_SIZE(ab5500_charger_current_map) - 1;
+ if (curr == ab5500_charger_current_map[i])
+ return i;
+ else
+ return -1;
+}
+
+/**
+ * ab5500_charger_get_usb_cur() - get usb current
+ * @di: pointer to the ab5500_charger structre
+ *
+ * The usb stack provides the maximum current that can be drawn from
+ * the standard usb host. This will be in mA.
+ * This function converts current in mA to a value that can be written
+ * to the register. Returns -1 if charging is not allowed
+ */
+static int ab5500_charger_get_usb_cur(struct ab5500_charger *di)
+{
+ switch (di->usb_state.usb_current) {
+ case 50:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ break;
+ case 100:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P09;
+ break;
+ case 200:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P19;
+ break;
+ case 300:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P29;
+ break;
+ case 400:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P38;
+ break;
+ case 500:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P5;
+ break;
+ default:
+ di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
+ return -1;
+ break;
+ };
+ return 0;
+}
+
+/**
+ * ab5500_charger_set_vbus_in_curr() - set VBUS input current limit
+ * @di: pointer to the ab5500_charger structure
+ * @ich_in: charger input current limit
+ *
+ * Sets the current that can be drawn from the USB host
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab5500_charger_set_vbus_in_curr(struct ab5500_charger *di,
+ int ich_in)
+{
+ int ret;
+ int input_curr_index;
+ int min_value;
+
+ /* We should always use to lowest current limit */
+ min_value = min(di->bat->chg_params->usb_curr_max, ich_in);
+
+ input_curr_index = ab5500_icsr_curr_to_regval(min_value);
+ if (input_curr_index < 0) {
+ dev_err(di->dev, "VBUS input current limit too high\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev, AB5500_BANK_CHG,
+ AB5500_ICSR, input_curr_index);
+ if (ret)
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+
+ return ret;
+}
+
+/**
+ * ab5500_charger_usb_en() - enable usb charging
+ * @di: pointer to the ab5500_charger structure
+ * @enable: enable/disable flag
+ * @vset: charging voltage
+ * @ich_out: charger output current
+ *
+ * Enable/Disable USB charging and turns on/off the charging led respectively.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab5500_charger_usb_en(struct ux500_charger *charger,
+ int enable, int vset, int ich_out)
+{
+ int ret;
+ int volt_index;
+ int curr_index;
+
+ struct ab5500_charger *di = to_ab5500_charger_usb_device_info(charger);
+
+ if (enable) {
+ /* Check if USB is connected */
+ if (!di->usb.charger_connected) {
+ dev_err(di->dev, "USB charger not connected\n");
+ return -ENXIO;
+ }
+
+ /* Enable USB charging */
+ dev_dbg(di->dev, "Enable USB: %dmV %dmA\n", vset, ich_out);
+
+ volt_index = ab5500_voltage_to_regval(vset);
+ curr_index = ab5500_current_to_regval(ich_out) ;
+
+ /* ChVoltLevel: max voltage upto which battery can be charged */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_VSRC, (u8) volt_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ /* current that can be drawn from the usb */
+ ret = ab5500_charger_set_vbus_in_curr(di, ich_out);
+ if (ret) {
+ dev_err(di->dev, "%s setting icsr failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ /* ChOutputCurentLevel: protected output current */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_OCSRV, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ /*
+ * Battery voltage when charging should be resumed after
+ * completion of charging
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_CVREC,
+ ab5500_cvrec_voltage_to_regval(
+ di->bat->bat_type[di->bat->batt_id].recharge_vol));
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+ /*
+ * Battery temperature:
+ * Input to the TBDATA register corresponds to the battery
+ * temperature(temp being multiples of 2)
+ * In order to obatain the value to be written to this reg
+ * divide the temperature obtained from gpadc by 2
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_TBDATA,
+ di->bat->temp_now / 2);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ /* If success power on charging LED indication */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_LEDT, LED_ENABLE);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ /*
+ * Register DCIOCURRENT is one among the charging watchdog
+ * rekick sequence, hence irrespective of usb charging this
+ * register will have to be written.
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_DCIOCURRENT,
+ RESET);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ di->usb.charger_online = 1;
+ } else {
+ /* ChVoltLevel: max voltage upto which battery can be charged */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_VSRC, RESET);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+ /* USBChInputCurr: current that can be drawn from the usb */
+ ret = ab5500_charger_set_vbus_in_curr(di, RESET);
+ if (ret) {
+ dev_err(di->dev, "%s resetting icsr failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+ /* If success power off charging LED indication */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_LEDT, RESET);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+ di->usb.charger_online = 0;
+ di->usb.wd_expired = false;
+ dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
+ }
+ power_supply_changed(&di->usb_chg.psy);
+
+ return ret;
+}
+
+/**
+ * ab5500_charger_watchdog_kick() - kick charger watchdog
+ * @di: pointer to the ab5500_charger structure
+ *
+ * Kick charger watchdog
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab5500_charger_watchdog_kick(struct ux500_charger *charger)
+{
+ int ret;
+ struct ab5500_charger *di;
+ int volt_index, curr_index;
+ u8 value = 0;
+
+ /* TODO: update */
+ if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+ di = to_ab5500_charger_usb_device_info(charger);
+ else
+ return -ENXIO;
+
+ ret = abx500_get_register_interruptible(di->dev, AB5500_BANK_STARTUP,
+ AB5500_MCB, &value);
+ if (ret)
+ dev_err(di->dev, "Failed to read!\n");
+
+ value = value | (SSW_ENABLE_REBOOT | SSW_REBOOT_EN |
+ SSW_CONTROL_AUTOC | SSW_PSEL_480S);
+ ret = abx500_set_register_interruptible(di->dev, AB5500_BANK_STARTUP,
+ AB5500_MCB, value);
+ if (ret)
+ dev_err(di->dev, "Failed to kick WD!\n");
+
+ volt_index = ab5500_voltage_to_regval(
+ di->bat->bat_type[di->bat->batt_id].normal_vol_lvl);
+ curr_index = ab5500_current_to_regval(di->max_usb_in_curr);
+
+ /* ChVoltLevel: max voltage upto which battery can be charged */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_VSRC, (u8) volt_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+ return ret;
+ }
+
+ /* current that can be drawn from the usb */
+ ret = ab5500_charger_set_vbus_in_curr(di, di->max_usb_in_curr);
+ if (ret) {
+ dev_err(di->dev, "%s setting icsr failed %d\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ /* ChOutputCurentLevel: protected output current */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_OCSRV, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+ return ret;
+ }
+
+ /*
+ * Battery voltage when charging should be resumed after
+ * completion of charging
+ */
+ /* Charger_Vrechar[5:0] = '4.025 V' */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_CVREC,
+ ab5500_cvrec_voltage_to_regval(
+ di->bat->bat_type[di->bat->batt_id].recharge_vol));
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+ return ret;
+ }
+ /*
+ * Battery temperature:
+ * Input to the TBDATA register corresponds to the battery
+ * temperature(temp being multiples of 2)
+ * In order to obatain the value to be written to this reg
+ * divide the temperature obtained from gpadc by 2
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_TBDATA,
+ di->bat->temp_now / 2);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+ return ret;
+ }
+ /*
+ * Register DCIOCURRENT is one among the charging watchdog
+ * rekick sequence, hence irrespective of usb charging this
+ * register will have to be written.
+ */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_DCIOCURRENT,
+ RESET);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * ab5500_charger_update_charger_current() - update charger current
+ * @di: pointer to the ab5500_charger structure
+ *
+ * Update the charger output current for the specified charger
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab5500_charger_update_charger_current(struct ux500_charger *charger,
+ int ich_out)
+{
+ int ret = 0;
+ int curr_index;
+ struct ab5500_charger *di;
+
+ if (charger->psy.type == POWER_SUPPLY_TYPE_USB)
+ di = to_ab5500_charger_usb_device_info(charger);
+ else
+ return -ENXIO;
+
+ curr_index = ab5500_current_to_regval(ich_out);
+ if (curr_index < 0) {
+ dev_err(di->dev,
+ "Charger current too high, "
+ "charging not started\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_set_register_interruptible(di->dev, AB5500_BANK_CHG,
+ AB5500_OCSRV, (u8) curr_index);
+ if (ret) {
+ dev_err(di->dev, "%s write failed %d\n", __func__, __LINE__);
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * ab5500_charger_check_hw_failure_work() - check main charger failure
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the main charger status
+ */
+static void ab5500_charger_check_hw_failure_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab5500_charger *di = container_of(work,
+ struct ab5500_charger, check_hw_failure_work.work);
+
+ /* Check if the status bits for HW failure is still active */
+ if (di->flags.vbus_ovv) {
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_USB, AB5500_USB_PHY_STATUS,
+ &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab5500 read failed\n", __func__);
+ return;
+ }
+ if (!(reg_value & VBUS_OVV_TH)) {
+ di->flags.vbus_ovv = false;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ }
+ /* If we still have a failure, schedule a new check */
+ if (di->flags.vbus_ovv) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_hw_failure_work, round_jiffies(HZ));
+ }
+}
+
+/**
+ * ab5500_charger_detect_usb_type_work() - work to detect USB type
+ * @work: Pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+void ab5500_charger_detect_usb_type_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab5500_charger *di = container_of(work,
+ struct ab5500_charger, detect_usb_type_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if is
+ * connected by reading the status register
+ */
+ ret = ab5500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (!(ret & USB_PW_CONN)) {
+ di->vbus_detected = 0;
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ } else {
+ di->vbus_detected = 1;
+ }
+}
+
+/**
+ * ab5500_charger_usb_link_status_work() - work to detect USB type
+ * @work: pointer to the work_struct structure
+ *
+ * Detect the type of USB plugged
+ */
+static void ab5500_charger_usb_link_status_work(struct work_struct *work)
+{
+ int ret;
+
+ struct ab5500_charger *di = container_of(work,
+ struct ab5500_charger, usb_link_status_work);
+
+ /*
+ * Since we can't be sure that the events are received
+ * synchronously, we have the check if is
+ * connected by reading the status register
+ */
+ ret = ab5500_charger_detect_chargers(di);
+ if (ret < 0)
+ return;
+
+ if (!(ret & USB_PW_CONN)) {
+ di->vbus_detected = 0;
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ } else {
+ di->vbus_detected = 1;
+ ret = ab5500_charger_read_usb_type(di);
+ if (!ret) {
+ /* Update maximum input current */
+ ret = ab5500_charger_set_vbus_in_curr(di,
+ di->max_usb_in_curr);
+ if (ret)
+ return;
+
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_chg.psy);
+ } else if (ret == -ENXIO) {
+ /* No valid charger type detected */
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ }
+}
+
+static void ab5500_charger_usb_state_changed_work(struct work_struct *work)
+{
+ int ret;
+ unsigned long flags;
+ struct ab5500_charger *di = container_of(work,
+ struct ab5500_charger, usb_state_changed_work);
+
+ if (!di->vbus_detected)
+ return;
+
+ spin_lock_irqsave(&di->usb_state.usb_lock, flags);
+ di->usb_state.usb_changed = false;
+ spin_unlock_irqrestore(&di->usb_state.usb_lock, flags);
+
+ /*
+ * wait for some time until you get updates from the usb stack
+ * and negotiations are completed
+ */
+ msleep(250);
+
+ if (di->usb_state.usb_changed)
+ return;
+
+ dev_dbg(di->dev, "%s USB state: 0x%02x mA: %d\n",
+ __func__, di->usb_state.state, di->usb_state.usb_current);
+
+ switch (di->usb_state.state) {
+ case AB5500_BM_USB_STATE_RESET_HS:
+ case AB5500_BM_USB_STATE_RESET_FS:
+ case AB5500_BM_USB_STATE_SUSPEND:
+ case AB5500_BM_USB_STATE_MAX:
+ di->usb.charger_connected = 0;
+ power_supply_changed(&di->usb_chg.psy);
+ break;
+
+ case AB5500_BM_USB_STATE_RESUME:
+ /*
+ * when suspend->resume there should be delay
+ * of 1sec for enabling charging
+ */
+ msleep(1000);
+ /* Intentional fall through */
+ case AB5500_BM_USB_STATE_CONFIGURED:
+ /*
+ * USB is configured, enable charging with the charging
+ * input current obtained from USB driver
+ */
+ if (!ab5500_charger_get_usb_cur(di)) {
+ /* Update maximum input current */
+ ret = ab5500_charger_set_vbus_in_curr(di,
+ di->max_usb_in_curr);
+ if (ret)
+ return;
+
+ di->usb.charger_connected = 1;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+ break;
+
+ default:
+ break;
+ };
+}
+
+/**
+ * ab5500_charger_check_usbchargernotok_work() - check USB chg not ok status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB charger Not OK status
+ */
+static void ab5500_charger_check_usbchargernotok_work(struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+ bool prev_status;
+
+ struct ab5500_charger *di = container_of(work,
+ struct ab5500_charger, check_usbchgnotok_work.work);
+
+ /* Check if the status bit for usbchargernotok is still active */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_USB, AB5500_CHGFSM_CHARGER_DETECT, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab5500 read failed\n", __func__);
+ return;
+ }
+ prev_status = di->flags.usbchargernotok;
+
+ if (reg_value & VBUS_CH_NOK) {
+ di->flags.usbchargernotok = true;
+ /* Check again in 1sec */
+ queue_delayed_work(di->charger_wq,
+ &di->check_usbchgnotok_work, HZ);
+ } else {
+ di->flags.usbchargernotok = false;
+ di->flags.vbus_collapse = false;
+ }
+
+ if (prev_status != di->flags.usbchargernotok)
+ power_supply_changed(&di->usb_chg.psy);
+}
+
+/**
+ * ab5500_charger_check_usb_thermal_prot_work() - check usb thermal status
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the USB thermal prot status
+ */
+static void ab5500_charger_check_usb_thermal_prot_work(
+ struct work_struct *work)
+{
+ int ret;
+ u8 reg_value;
+
+ struct ab5500_charger *di = container_of(work,
+ struct ab5500_charger, check_usb_thermal_prot_work);
+
+ /* Check if the status bit for usb_thermal_prot is still active */
+ /* TODO: Interrupt source reg 15 bit 4 */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_USB, AB5500_CHGFSM_USB_BTEMP_CURR_LIM, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s ab5500 read failed\n", __func__);
+ return;
+ }
+ if (reg_value & USB_CH_TH_PROT_LOW || reg_value & USB_CH_TH_PROT_HIGH)
+ di->flags.usb_thermal_prot = true;
+ else
+ di->flags.usb_thermal_prot = false;
+
+ power_supply_changed(&di->usb_chg.psy);
+}
+
+/**
+ * ab5500_charger_vbusdetf_handler() - VBUS falling detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_vbusdetf_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS falling detected\n");
+ queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_vbusdetr_handler() - VBUS rising detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_vbusdetr_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ di->vbus_detected = true;
+ dev_dbg(di->dev, "VBUS rising detected\n");
+ queue_work(di->charger_wq, &di->detect_usb_type_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_usblinkstatus_handler() - USB link status has changed
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_usblinkstatus_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ dev_dbg(di->dev, "USB link status changed\n");
+
+ if (!di->usb.charger_online)
+ queue_work(di->charger_wq, &di->usb_link_status_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_usbchthprotr_handler() - Die temp is above usb charger
+ * thermal protection threshold
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_usbchthprotr_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ dev_dbg(di->dev,
+ "Die temp above USB charger thermal protection threshold\n");
+ queue_work(di->charger_wq, &di->check_usb_thermal_prot_work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_usbchargernotokr_handler() - USB charger not ok detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_usbchargernotokr_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ dev_dbg(di->dev, "Not allowed USB charger detected\n");
+ queue_delayed_work(di->charger_wq, &di->check_usbchgnotok_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_chwdexp_handler() - Charger watchdog expired
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_chwdexp_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ dev_dbg(di->dev, "Charger watchdog expired\n");
+
+ /*
+ * The charger that was online when the watchdog expired
+ * needs to be restarted for charging to start again
+ */
+ if (di->usb.charger_online) {
+ di->usb.wd_expired = true;
+ power_supply_changed(&di->usb_chg.psy);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_vbusovv_handler() - VBUS overvoltage detected
+ * @irq: interrupt number
+ * @_di: pointer to the ab5500_charger structure
+ *
+ * Returns IRQ status(IRQ_HANDLED)
+ */
+static irqreturn_t ab5500_charger_vbusovv_handler(int irq, void *_di)
+{
+ struct ab5500_charger *di = _di;
+
+ dev_dbg(di->dev, "VBUS overvoltage detected\n");
+ di->flags.vbus_ovv = true;
+ power_supply_changed(&di->usb_chg.psy);
+
+ /* Schedule a new HW failure check */
+ queue_delayed_work(di->charger_wq, &di->check_hw_failure_work, 0);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ab5500_charger_usb_get_property() - get the usb properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the usb
+ * properties by reading the sysfs files.
+ * USB properties are online, present and voltage.
+ * online: usb charging is in progress or not
+ * present: presence of the usb
+ * voltage: vbus voltage
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab5500_charger_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab5500_charger *di;
+
+ di = to_ab5500_charger_usb_device_info(psy_to_ux500_charger(psy));
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_HEALTH:
+ if (di->flags.usbchargernotok)
+ val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+ else if (di->usb.wd_expired)
+ val->intval = POWER_SUPPLY_HEALTH_DEAD;
+ else if (di->flags.usb_thermal_prot)
+ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (di->flags.vbus_ovv)
+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+ else
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = di->usb.charger_online;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = di->usb.charger_connected;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ di->usb.charger_voltage = ab5500_charger_get_vbus_voltage(di);
+ val->intval = di->usb.charger_voltage * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = ab5500_charger_get_usb_current(di) * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ /*
+ * This property is used to indicate when VBUS has collapsed
+ * due to too high output current from the USB charger
+ */
+ if (di->flags.vbus_collapse)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * ab5500_charger_hw_registers() - Set up charger related registers
+ * @di: pointer to the ab5500_charger structure
+ *
+ * Set up charger OVV, watchdog and maximum voltage registers as well as
+ * charging of the backup battery
+ */
+static int ab5500_charger_init_hw_registers(struct ab5500_charger *di)
+{
+ int ret = 0;
+
+ /* Enable ID Host and Device detection */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_USB, AB5500_USB_OTG_CTRL,
+ USB_ID_HOST_DET_ENA_MASK, USB_ID_HOST_DET_ENA);
+ if (ret) {
+ dev_err(di->dev, "failed to enable usb charger detection\n");
+ goto out;
+ }
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_USB, AB5500_USB_OTG_CTRL,
+ USB_ID_DEVICE_DET_ENA_MASK, USB_ID_DEVICE_DET_ENA);
+ if (ret) {
+ dev_err(di->dev, "failed to enable usb charger detection\n");
+ goto out;
+ }
+
+ /* Over current protection for reverse supply */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_CREVS, CHARGER_REV_SUP,
+ CHARGER_REV_SUP);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to enable over current protection for reverse supply\n");
+ goto out;
+ }
+
+ /* Enable SW EOC at flatcurrent detection */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_CHG, AB5500_CCTRL, SW_EOC, SW_EOC);
+ if (ret) {
+ dev_err(di->dev,
+ "failed to enable end of charge at flatcurrent detection\n");
+ goto out;
+ }
+out:
+ return ret;
+}
+
+/*
+ * ab5500 charger driver interrupts and their respective isr
+ */
+static struct ab5500_charger_interrupts ab5500_charger_irq[] = {
+ {"VBUS_FALLING", ab5500_charger_vbusdetf_handler},
+ {"VBUS_RISING", ab5500_charger_vbusdetr_handler},
+ {"USB_LINK_UPDATE", ab5500_charger_usblinkstatus_handler},
+ {"USB_CH_TH_PROTECTION", ab5500_charger_usbchthprotr_handler},
+ {"USB_CH_NOT_OK", ab5500_charger_usbchargernotokr_handler},
+ {"OVV", ab5500_charger_vbusovv_handler},
+ /* TODO: Interrupt missing, will be available in cut 2 */
+ /*{"CHG_SW_TIMER_OUT", ab5500_charger_chwdexp_handler},*/
+};
+
+static int ab5500_charger_usb_notifier_call(struct notifier_block *nb,
+ unsigned long event, void *power)
+{
+ struct ab5500_charger *di =
+ container_of(nb, struct ab5500_charger, nb);
+ enum ab5500_usb_state bm_usb_state;
+ unsigned mA = *((unsigned *)power);
+
+ if (event != USB_EVENT_VBUS) {
+ dev_dbg(di->dev, "not a standard host, returning\n");
+ return NOTIFY_DONE;
+ }
+
+ /* TODO: State is fabricate here. See if charger really needs USB
+ * state or if mA is enough
+ */
+ if ((di->usb_state.usb_current == 2) && (mA > 2))
+ bm_usb_state = AB5500_BM_USB_STATE_RESUME;
+ else if (mA == 0)
+ bm_usb_state = AB5500_BM_USB_STATE_RESET_HS;
+ else if (mA == 2)
+ bm_usb_state = AB5500_BM_USB_STATE_SUSPEND;
+ else if (mA >= 8) /* 8, 100, 500 */
+ bm_usb_state = AB5500_BM_USB_STATE_CONFIGURED;
+ else /* Should never occur */
+ bm_usb_state = AB5500_BM_USB_STATE_RESET_FS;
+
+ dev_dbg(di->dev, "%s usb_state: 0x%02x mA: %d\n",
+ __func__, bm_usb_state, mA);
+
+ spin_lock(&di->usb_state.usb_lock);
+ di->usb_state.usb_changed = true;
+ di->usb_state.state = bm_usb_state;
+ di->usb_state.usb_current = mA;
+ spin_unlock(&di->usb_state.usb_lock);
+
+ queue_work(di->charger_wq, &di->usb_state_changed_work);
+
+ return NOTIFY_OK;
+}
+
+#if defined(CONFIG_PM)
+static int ab5500_charger_resume(struct platform_device *pdev)
+{
+ struct ab5500_charger *di = platform_get_drvdata(pdev);
+
+ /* If we still have a HW failure, schedule a new check */
+ if (di->flags.usbchargernotok || di->flags.vbus_ovv) {
+ queue_delayed_work(di->charger_wq,
+ &di->check_hw_failure_work, 0);
+ }
+
+ return 0;
+}
+
+static int ab5500_charger_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab5500_charger *di = platform_get_drvdata(pdev);
+
+ /* Cancel any pending HW failure check */
+ if (delayed_work_pending(&di->check_hw_failure_work))
+ cancel_delayed_work(&di->check_hw_failure_work);
+
+ return 0;
+}
+#else
+#define ab5500_charger_suspend NULL
+#define ab5500_charger_resume NULL
+#endif
+
+static int __devexit ab5500_charger_remove(struct platform_device *pdev)
+{
+ struct ab5500_charger *di = platform_get_drvdata(pdev);
+ int i, irq;
+
+ /* Disable USB charging */
+ ab5500_charger_usb_en(&di->usb_chg, false, 0, 0);
+
+ /* Disable interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab5500_charger_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab5500_charger_irq[i].name);
+ free_irq(irq, di);
+ }
+
+ otg_unregister_notifier(di->otg, &di->nb);
+ otg_put_transceiver(di->otg);
+
+ /* Delete the work queue */
+ destroy_workqueue(di->charger_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->usb_chg.psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di);
+
+ return 0;
+}
+
+static int __devinit ab5500_charger_probe(struct platform_device *pdev)
+{
+ int irq, i, charger_status, ret = 0;
+ struct abx500_bm_plat_data *plat_data;
+
+ struct ab5500_charger *di =
+ kzalloc(sizeof(struct ab5500_charger), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab5500_gpadc_get("ab5500-adc.0");
+
+ /* initialize lock */
+ spin_lock_init(&di->usb_state.usb_lock);
+
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->charger;
+ di->bat = plat_data->battery;
+
+ /* get charger specific platform data */
+ if (!di->pdata) {
+ dev_err(di->dev, "no charger platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* get battery specific platform data */
+ if (!di->bat) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ /* USB supply */
+ /* power_supply base class */
+ di->usb_chg.psy.name = "ab5500_usb";
+ di->usb_chg.psy.type = POWER_SUPPLY_TYPE_USB;
+ di->usb_chg.psy.properties = ab5500_charger_usb_props;
+ di->usb_chg.psy.num_properties = ARRAY_SIZE(ab5500_charger_usb_props);
+ di->usb_chg.psy.get_property = ab5500_charger_usb_get_property;
+ di->usb_chg.psy.supplied_to = di->pdata->supplied_to;
+ di->usb_chg.psy.num_supplicants = di->pdata->num_supplicants;
+ /* ux500_charger sub-class */
+ di->usb_chg.ops.enable = &ab5500_charger_usb_en;
+ di->usb_chg.ops.kick_wd = &ab5500_charger_watchdog_kick;
+ di->usb_chg.ops.update_curr = &ab5500_charger_update_charger_current;
+ di->usb_chg.max_out_volt = ab5500_charger_voltage_map[
+ ARRAY_SIZE(ab5500_charger_voltage_map) - 1];
+ di->usb_chg.max_out_curr = ab5500_charger_current_map[
+ ARRAY_SIZE(ab5500_charger_current_map) - 1];
+
+
+ /* Create a work queue for the charger */
+ di->charger_wq =
+ create_singlethread_workqueue("ab5500_charger_wq");
+ if (di->charger_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for HW failure check */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_hw_failure_work,
+ ab5500_charger_check_hw_failure_work);
+ INIT_DELAYED_WORK_DEFERRABLE(&di->check_usbchgnotok_work,
+ ab5500_charger_check_usbchargernotok_work);
+
+ /* Init work for charger detection */
+ INIT_WORK(&di->usb_link_status_work,
+ ab5500_charger_usb_link_status_work);
+ INIT_WORK(&di->detect_usb_type_work,
+ ab5500_charger_detect_usb_type_work);
+
+ INIT_WORK(&di->usb_state_changed_work,
+ ab5500_charger_usb_state_changed_work);
+
+ /* Init work for checking HW status */
+ INIT_WORK(&di->check_usb_thermal_prot_work,
+ ab5500_charger_check_usb_thermal_prot_work);
+
+ /* Get Chip ID of the ABB ASIC */
+ ret = abx500_get_chip_id(di->dev);
+ if (ret < 0) {
+ dev_err(di->dev, "failed to get chip ID\n");
+ goto free_charger_wq;
+ }
+ di->chip_id = ret;
+ dev_dbg(di->dev, "AB5500 CID is: 0x%02x\n", di->chip_id);
+
+ /* Initialize OVV, and other registers */
+ ret = ab5500_charger_init_hw_registers(di);
+ if (ret) {
+ dev_err(di->dev, "failed to initialize ABB registers\n");
+ goto free_device_info;
+ }
+
+ /* Register USB charger class */
+ ret = power_supply_register(di->dev, &di->usb_chg.psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register USB charger\n");
+ goto free_device_info;
+ }
+
+ di->otg = otg_get_transceiver();
+ if (!di->otg) {
+ dev_err(di->dev, "failed to get otg transceiver\n");
+ goto free_usb;
+ }
+ di->nb.notifier_call = ab5500_charger_usb_notifier_call;
+ ret = otg_register_notifier(di->otg, &di->nb);
+ if (ret) {
+ dev_err(di->dev, "failed to register otg notifier\n");
+ goto put_otg_transceiver;
+ }
+
+ /* Identify the connected charger types during startup */
+ charger_status = ab5500_charger_detect_chargers(di);
+ if (charger_status & USB_PW_CONN) {
+ dev_dbg(di->dev, "VBUS Detect during startup\n");
+ di->vbus_detected = true;
+ di->vbus_detected_start = true;
+ queue_work(di->charger_wq,
+ &di->usb_link_status_work);
+ }
+
+ /* Register interrupts */
+ for (i = 0; i < ARRAY_SIZE(ab5500_charger_irq); i++) {
+ irq = platform_get_irq_byname(pdev, ab5500_charger_irq[i].name);
+ ret = request_threaded_irq(irq, NULL, ab5500_charger_irq[i].isr,
+ IRQF_SHARED | IRQF_NO_SUSPEND,
+ ab5500_charger_irq[i].name, di);
+
+ if (ret != 0) {
+ dev_err(di->dev, "failed to request %s IRQ %d: %d\n"
+ , ab5500_charger_irq[i].name, irq, ret);
+ goto free_irq;
+ }
+ dev_dbg(di->dev, "Requested %s IRQ %d: %d\n",
+ ab5500_charger_irq[i].name, irq, ret);
+ }
+
+ platform_set_drvdata(pdev, di);
+
+ dev_info(di->dev, "probe success\n");
+ return ret;
+
+free_irq:
+ otg_unregister_notifier(di->otg, &di->nb);
+
+ /* We also have to free all successfully registered irqs */
+ for (i = i - 1; i >= 0; i--) {
+ irq = platform_get_irq_byname(pdev, ab5500_charger_irq[i].name);
+ free_irq(irq, di);
+ }
+put_otg_transceiver:
+ otg_put_transceiver(di->otg);
+free_usb:
+ power_supply_unregister(&di->usb_chg.psy);
+free_charger_wq:
+ destroy_workqueue(di->charger_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab5500_charger_driver = {
+ .probe = ab5500_charger_probe,
+ .remove = __devexit_p(ab5500_charger_remove),
+ .suspend = ab5500_charger_suspend,
+ .resume = ab5500_charger_resume,
+ .driver = {
+ .name = "ab5500-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab5500_charger_init(void)
+{
+ return platform_driver_register(&ab5500_charger_driver);
+}
+
+static void __exit ab5500_charger_exit(void)
+{
+ platform_driver_unregister(&ab5500_charger_driver);
+}
+
+subsys_initcall_sync(ab5500_charger_init);
+module_exit(ab5500_charger_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab5500-charger");
+MODULE_DESCRIPTION("AB5500 charger management driver");
diff --git a/drivers/power/ab5500_fg.c b/drivers/power/ab5500_fg.c
new file mode 100644
index 00000000000..c74d351bd8b
--- /dev/null
+++ b/drivers/power/ab5500_fg.c
@@ -0,0 +1,1954 @@
+/*
+ * Copyright (C) ST-Ericsson AB 2011
+ *
+ * Main and Back-up battery management driver.
+ *
+ * Note: Backup battery management is required in case of Li-Ion battery and not
+ * for capacitive battery. HREF boards have capacitive battery and hence backup
+ * battery management is not used and the supported code is available in this
+ * driver.
+ *
+ * License Terms: GNU General Public License v2
+ * Authors:
+ * Johan Palsson <johan.palsson@stericsson.com>
+ * Karl Komierowski <karl.komierowski@stericsson.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+#include <linux/mfd/abx500/ab5500-bm.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+
+static LIST_HEAD(ab5500_fg_list);
+
+/* U5500 Constants */
+#define FG_ON_MASK 0x04
+#define FG_ON 0x04
+#define FG_ACC_RESET_ON_READ_MASK 0x08
+#define FG_ACC_RESET_ON_READ 0x08
+#define EN_READOUT_MASK 0x01
+#define EN_READOUT 0x01
+#define EN_ACC_RESET_ON_READ 0x08
+#define ACC_RESET_ON_READ 0x08
+#define RESET 0x00
+#define EOC_52_mA 0x04
+#define MILLI_TO_MICRO 1000
+#define FG_LSB_IN_MA 770
+#define QLSB_NANO_AMP_HOURS_X100 5353
+#define SEC_TO_SAMPLE(S) (S * 4)
+#define NBR_AVG_SAMPLES 20
+#define LOW_BAT_CHECK_INTERVAL (2 * HZ)
+#define FG_PERIODIC_START_INTERVAL (250 * HZ)/1000 /* 250 msec */
+
+#define VALID_CAPACITY_SEC (45 * 60) /* 45 minutes */
+
+#define interpolate(x, x1, y1, x2, y2) \
+ ((y1) + ((((y2) - (y1)) * ((x) - (x1))) / ((x2) - (x1))));
+
+#define to_ab5500_fg_device_info(x) container_of((x), \
+ struct ab5500_fg, fg_psy);
+
+/**
+ * struct ab5500_fg_interrupts - ab5500 fg interupts
+ * @name: name of the interrupt
+ * @isr function pointer to the isr
+ */
+struct ab5500_fg_interrupts {
+ char *name;
+ irqreturn_t (*isr)(int irq, void *data);
+};
+
+enum ab5500_fg_discharge_state {
+ AB5500_FG_DISCHARGE_INIT,
+ AB5500_FG_DISCHARGE_INITMEASURING,
+ AB5500_FG_DISCHARGE_INIT_RECOVERY,
+ AB5500_FG_DISCHARGE_RECOVERY,
+ AB5500_FG_DISCHARGE_READOUT,
+ AB5500_FG_DISCHARGE_WAKEUP,
+};
+
+static char *discharge_state[] = {
+ "DISCHARGE_INIT",
+ "DISCHARGE_INITMEASURING",
+ "DISCHARGE_INIT_RECOVERY",
+ "DISCHARGE_RECOVERY",
+ "DISCHARGE_READOUT",
+ "DISCHARGE_WAKEUP",
+};
+
+enum ab5500_fg_charge_state {
+ AB5500_FG_CHARGE_INIT,
+ AB5500_FG_CHARGE_READOUT,
+};
+
+static char *charge_state[] = {
+ "CHARGE_INIT",
+ "CHARGE_READOUT",
+};
+
+enum ab5500_fg_calibration_state {
+ AB5500_FG_CALIB_INIT,
+ AB5500_FG_CALIB_WAIT,
+ AB5500_FG_CALIB_END,
+};
+
+struct ab5500_fg_avg_cap {
+ int avg;
+ int samples[NBR_AVG_SAMPLES];
+ __kernel_time_t time_stamps[NBR_AVG_SAMPLES];
+ int pos;
+ int nbr_samples;
+ int sum;
+};
+
+struct ab5500_fg_battery_capacity {
+ int max_mah_design;
+ int max_mah;
+ int mah;
+ int permille;
+ int level;
+ int prev_mah;
+ int prev_percent;
+ int prev_level;
+};
+
+struct ab5500_fg_flags {
+ bool fg_enabled;
+ bool conv_done;
+ bool charging;
+ bool fully_charged;
+ bool low_bat_delay;
+ bool low_bat;
+ bool bat_ovv;
+ bool batt_unknown;
+ bool calibrate;
+};
+
+/**
+ * struct ab5500_fg - ab5500 FG device information
+ * @dev: Pointer to the structure device
+ * @vbat: Battery voltage in mV
+ * @vbat_nom: Nominal battery voltage in mV
+ * @inst_curr: Instantenous battery current in mA
+ * @avg_curr: Average battery current in mA
+ * @fg_samples: Number of samples used in the FG accumulation
+ * @accu_charge: Accumulated charge from the last conversion
+ * @recovery_cnt: Counter for recovery mode
+ * @high_curr_cnt: Counter for high current mode
+ * @init_cnt: Counter for init mode
+ * @v_to_cap: capacity based on battery voltage
+ * @recovery_needed: Indicate if recovery is needed
+ * @high_curr_mode: Indicate if we're in high current mode
+ * @init_capacity: Indicate if initial capacity measuring should be done
+ * @calib_state State during offset calibration
+ * @discharge_state: Current discharge state
+ * @charge_state: Current charge state
+ * @flags: Structure for information about events triggered
+ * @bat_cap: Structure for battery capacity specific parameters
+ * @avg_cap: Average capacity filter
+ * @parent: Pointer to the struct ab5500
+ * @gpadc: Pointer to the struct gpadc
+ * @gpadc_auto: Pointer tot he struct adc_auto_input
+ * @pdata: Pointer to the ab5500_fg platform data
+ * @bat: Pointer to the ab5500_bm platform data
+ * @fg_psy: Structure that holds the FG specific battery properties
+ * @fg_wq: Work queue for running the FG algorithm
+ * @fg_periodic_work: Work to run the FG algorithm periodically
+ * @fg_low_bat_work: Work to check low bat condition
+ * @fg_reinit_work: Work to reset and re-initialize fuel gauge
+ * @fg_work: Work to run the FG algorithm instantly
+ * @fg_acc_cur_work: Work to read the FG accumulator
+ * @cc_lock: Mutex for locking the CC
+ * @node: struct of type list_head
+ */
+struct ab5500_fg {
+ struct device *dev;
+ int vbat;
+ int vbat_nom;
+ int inst_curr;
+ int avg_curr;
+ int fg_samples;
+ int accu_charge;
+ int recovery_cnt;
+ int high_curr_cnt;
+ int init_cnt;
+ int v_to_cap;
+ bool recovery_needed;
+ bool high_curr_mode;
+ bool init_capacity;
+ enum ab5500_fg_calibration_state calib_state;
+ enum ab5500_fg_discharge_state discharge_state;
+ enum ab5500_fg_charge_state charge_state;
+ struct ab5500_fg_flags flags;
+ struct ab5500_fg_battery_capacity bat_cap;
+ struct ab5500_fg_avg_cap avg_cap;
+ struct ab5500 *parent;
+ struct ab5500_gpadc *gpadc;
+ struct adc_auto_input *gpadc_auto;
+ struct abx500_fg_platform_data *pdata;
+ struct abx500_bm_data *bat;
+ struct power_supply fg_psy;
+ struct workqueue_struct *fg_wq;
+ struct delayed_work fg_periodic_work;
+ struct delayed_work fg_low_bat_work;
+ struct delayed_work fg_reinit_work;
+ struct work_struct fg_work;
+ struct delayed_work fg_acc_cur_work;
+ struct mutex cc_lock;
+ struct list_head node;
+ struct timer_list avg_current_timer;
+};
+
+/* Main battery properties */
+static enum power_supply_property ab5500_fg_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+ POWER_SUPPLY_PROP_ENERGY_FULL,
+ POWER_SUPPLY_PROP_ENERGY_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+};
+
+/* Function Prototype */
+static int ab5500_fg_bat_v_trig(int mux);
+
+static int prev_samples, prev_val;
+
+struct ab5500_fg *ab5500_fg_get(void)
+{
+ struct ab5500_fg *di;
+ di = list_first_entry(&ab5500_fg_list, struct ab5500_fg, node);
+
+ return di;
+}
+
+/**
+ * ab5500_fg_is_low_curr() - Low or high current mode
+ * @di: pointer to the ab5500_fg structure
+ * @curr: the current to base or our decision on
+ *
+ * Low current mode if the current consumption is below a certain threshold
+ */
+static int ab5500_fg_is_low_curr(struct ab5500_fg *di, int curr)
+{
+ /*
+ * We want to know if we're in low current mode
+ */
+ if (curr > -di->bat->fg_params->high_curr_threshold)
+ return true;
+ else
+ return false;
+}
+
+/**
+ * ab5500_fg_add_cap_sample() - Add capacity to average filter
+ * @di: pointer to the ab5500_fg structure
+ * @sample: the capacity in mAh to add to the filter
+ *
+ * A capacity is added to the filter and a new mean capacity is calculated and
+ * returned
+ */
+static int ab5500_fg_add_cap_sample(struct ab5500_fg *di, int sample)
+{
+ struct timespec ts;
+ struct ab5500_fg_avg_cap *avg = &di->avg_cap;
+
+ getnstimeofday(&ts);
+
+ do {
+ avg->sum += sample - avg->samples[avg->pos];
+ avg->samples[avg->pos] = sample;
+ avg->time_stamps[avg->pos] = ts.tv_sec;
+ avg->pos++;
+
+ if (avg->pos == NBR_AVG_SAMPLES)
+ avg->pos = 0;
+
+ if (avg->nbr_samples < NBR_AVG_SAMPLES)
+ avg->nbr_samples++;
+
+ /*
+ * Check the time stamp for each sample. If too old,
+ * replace with latest sample
+ */
+ } while (ts.tv_sec - VALID_CAPACITY_SEC > avg->time_stamps[avg->pos]);
+
+ avg->avg = avg->sum / avg->nbr_samples;
+
+ return avg->avg;
+}
+
+/**
+ * ab5500_fg_clear_cap_samples() - Clear average filter
+ * @di: pointer to the ab5500_fg structure
+ *
+ * The capacity filter is is reset to zero.
+ */
+static void ab5500_fg_clear_cap_samples(struct ab5500_fg *di)
+{
+ int i;
+ struct ab5500_fg_avg_cap *avg = &di->avg_cap;
+
+ avg->pos = 0;
+ avg->nbr_samples = 0;
+ avg->sum = 0;
+ avg->avg = 0;
+
+ for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+ avg->samples[i] = 0;
+ avg->time_stamps[i] = 0;
+ }
+}
+
+
+/**
+ * ab5500_fg_fill_cap_sample() - Fill average filter
+ * @di: pointer to the ab5500_fg structure
+ * @sample: the capacity in mAh to fill the filter with
+ *
+ * The capacity filter is filled with a capacity in mAh
+ */
+static void ab5500_fg_fill_cap_sample(struct ab5500_fg *di, int sample)
+{
+ int i;
+ struct timespec ts;
+ struct ab5500_fg_avg_cap *avg = &di->avg_cap;
+
+ getnstimeofday(&ts);
+
+ for (i = 0; i < NBR_AVG_SAMPLES; i++) {
+ avg->samples[i] = sample;
+ avg->time_stamps[i] = ts.tv_sec;
+ }
+
+ avg->pos = 0;
+ avg->nbr_samples = NBR_AVG_SAMPLES;
+ avg->sum = sample * NBR_AVG_SAMPLES;
+ avg->avg = sample;
+}
+
+/**
+ * ab5500_fg_coulomb_counter() - enable coulomb counter
+ * @di: pointer to the ab5500_fg structure
+ * @enable: enable/disable
+ *
+ * Enable/Disable coulomb counter.
+ * On failure returns negative value.
+ */
+static int ab5500_fg_coulomb_counter(struct ab5500_fg *di, bool enable)
+{
+ int ret = 0;
+ mutex_lock(&di->cc_lock);
+ if (enable) {
+ /* Power-up the CC */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ (FG_ON | FG_ACC_RESET_ON_READ));
+ if (ret)
+ goto cc_err;
+
+ di->flags.fg_enabled = true;
+ } else {
+ /* Stop the CC */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ FG_ON_MASK, RESET);
+ if (ret)
+ goto cc_err;
+
+ di->flags.fg_enabled = false;
+
+ }
+ dev_dbg(di->dev, " CC enabled: %d Samples: %d\n",
+ enable, di->fg_samples);
+
+ mutex_unlock(&di->cc_lock);
+
+ return ret;
+cc_err:
+ dev_err(di->dev, "%s Enabling coulomb counter failed\n", __func__);
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+/**
+ * ab5500_fg_inst_curr() - battery instantaneous current
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Returns battery instantenous current(on success) else error code
+ */
+static int ab5500_fg_inst_curr(struct ab5500_fg *di)
+{
+ u8 low, high;
+ static int val;
+ int ret = 0;
+ bool fg_off = false;
+
+ if (!di->flags.fg_enabled) {
+ fg_off = true;
+ /* Power-up the CC */
+ ab5500_fg_coulomb_counter(di, true);
+ msleep(250);
+ }
+
+ mutex_lock(&di->cc_lock);
+
+ /* Enable read request */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_B,
+ EN_READOUT_MASK, EN_READOUT);
+ if (ret)
+ goto inst_curr_err;
+
+ /* Read CC Sample conversion value Low and high */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FGDIR_READ0, &low);
+ if (ret < 0)
+ goto inst_curr_err;
+
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FGDIR_READ1, &high);
+ if (ret < 0)
+ goto inst_curr_err;
+
+ /*
+ * negative value for Discharging
+ * convert 2's compliment into decimal
+ */
+ if (high & 0x10)
+ val = (low | (high << 8) | 0xFFFFE000);
+ else
+ val = (low | (high << 8));
+
+ /*
+ * Convert to unit value in mA
+ * R(FGSENSE) = 20 mOhm
+ * Scaling of LSB: This corresponds fro R(FGSENSE) to a current of
+ * I = Q/t = 192.7 uC * 4 Hz = 0.77mA
+ */
+ val = (val * 770) / 1000;
+
+ mutex_unlock(&di->cc_lock);
+
+ if (fg_off) {
+ dev_dbg(di->dev, "%s Disable FG\n", __func__);
+ /* Power-off the CC */
+ ab5500_fg_coulomb_counter(di, false);
+ }
+
+ return val;
+
+inst_curr_err:
+ dev_err(di->dev, "%s Get instanst current failed\n", __func__);
+ mutex_unlock(&di->cc_lock);
+ return ret;
+}
+
+static void ab5500_fg_acc_cur_timer_expired(unsigned long data)
+{
+ struct ab5500_fg *di = (struct ab5500_fg *) data;
+ dev_dbg(di->dev, "Avg current timer expired\n");
+
+ /* Trigger execution of the algorithm instantly */
+ queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work, 0);
+}
+
+/**
+ * ab5500_fg_acc_cur_work() - average battery current
+ * @work: pointer to the work_struct structure
+ *
+ * Updated the average battery current obtained from the
+ * coulomb counter.
+ */
+static void ab5500_fg_acc_cur_work(struct work_struct *work)
+{
+ int val, raw_val, sample;
+ int ret;
+ u8 low, med, high, cnt_low, cnt_high;
+
+ struct ab5500_fg *di = container_of(work,
+ struct ab5500_fg, fg_acc_cur_work.work);
+
+ if (!di->flags.fg_enabled) {
+ /* Power-up the CC */
+ ab5500_fg_coulomb_counter(di, true);
+ msleep(250);
+ }
+ mutex_lock(&di->cc_lock);
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_C,
+ EN_READOUT_MASK, EN_READOUT);
+ if (ret < 0)
+ goto exit;
+ /* If charging read charging registers for accumulated values */
+ if (di->flags.charging) {
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ ACC_RESET_ON_READ, EN_ACC_RESET_ON_READ);
+ if (ret < 0)
+ goto exit;
+ /* Read CC Sample conversion value Low and high */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_CH0, &low);
+ if (ret < 0)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_CH1, &med);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_CH2, &high);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_VAL_COUNT0, &cnt_low);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_VAL_COUNT1, &cnt_high);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ ACC_RESET_ON_READ, RESET);
+ if (ret < 0)
+ goto exit;
+ queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work,
+ di->bat->interval_charging * HZ);
+ } else { /* discharging */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ ACC_RESET_ON_READ, EN_ACC_RESET_ON_READ);
+ if (ret < 0)
+ goto exit;
+ /* Read CC Sample conversion value Low and high */
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_DIS_CH0, &low);
+ if (ret < 0)
+ goto exit;
+
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_DIS_CH1, &med);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_DIS_CH2, &high);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_VAL_COUNT0, &cnt_low);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_get_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC,
+ AB5500_FG_VAL_COUNT1, &cnt_high);
+ if (ret < 0)
+ goto exit;
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ ACC_RESET_ON_READ, RESET);
+ if (ret < 0)
+ goto exit;
+ queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work,
+ di->bat->interval_not_charging * HZ);
+ }
+ di->fg_samples = (cnt_low | (cnt_high << 8));
+ /*
+ * TODO: Workaround due to the hardware issue that accumulator is not
+ * reset after setting reset_on_read bit and reading the accumulator
+ * Registers.
+ */
+ if (prev_samples > di->fg_samples) {
+ /* overflow has occured */
+ sample = (0xFFFF - prev_samples) + di->fg_samples;
+ } else
+ sample = di->fg_samples - prev_samples;
+ prev_samples = di->fg_samples;
+ di->fg_samples = sample;
+ val = (low | (med << 8) | (high << 16));
+ /*
+ * TODO: Workaround due to the hardware issue that accumulator is not
+ * reset after setting reset_on_read bit and reading the accumulator
+ * Registers.
+ */
+ if (prev_val > val)
+ raw_val = (0xFFFFFF - prev_val) + val;
+ else
+ raw_val = val - prev_val;
+ prev_val = val;
+ val = raw_val;
+
+ if (di->fg_samples) {
+ di->accu_charge = (val * QLSB_NANO_AMP_HOURS_X100)/100000;
+ di->avg_curr = (val * FG_LSB_IN_MA) / (di->fg_samples * 1000);
+ } else
+ dev_err(di->dev,
+ "samples is zero, using previous calculated average current\n");
+ di->flags.conv_done = true;
+ di->calib_state = AB5500_FG_CALIB_END;
+
+ mutex_unlock(&di->cc_lock);
+
+ queue_work(di->fg_wq, &di->fg_work);
+
+ return;
+exit:
+ dev_err(di->dev,
+ "Failed to read or write gas gauge registers\n");
+ mutex_unlock(&di->cc_lock);
+ queue_work(di->fg_wq, &di->fg_work);
+}
+
+/**
+ * ab5500_fg_bat_voltage() - get battery voltage
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Returns battery voltage(on success) else error code
+ */
+static int ab5500_fg_bat_voltage(struct ab5500_fg *di)
+{
+ int vbat;
+ static int prev;
+
+ vbat = ab5500_gpadc_convert(di->gpadc, MAIN_BAT_V);
+ if (vbat < 0) {
+ dev_err(di->dev,
+ "%s gpadc conversion failed, using previous value\n",
+ __func__);
+ return prev;
+ }
+
+ prev = vbat;
+ return vbat;
+}
+
+/**
+ * ab5500_fg_volt_to_capacity() - Voltage based capacity
+ * @di: pointer to the ab5500_fg structure
+ * @voltage: The voltage to convert to a capacity
+ *
+ * Returns battery capacity in per mille based on voltage
+ */
+static int ab5500_fg_volt_to_capacity(struct ab5500_fg *di, int voltage)
+{
+ int i, tbl_size;
+ struct abx500_v_to_cap *tbl;
+ int cap = 0;
+
+ tbl = di->bat->bat_type[di->bat->batt_id].v_to_cap_tbl,
+ tbl_size = di->bat->bat_type[di->bat->batt_id].n_v_cap_tbl_elements;
+
+ for (i = 0; i < tbl_size; ++i) {
+ if (di->vbat < tbl[i].voltage && di->vbat > tbl[i+1].voltage)
+ di->v_to_cap = tbl[i].capacity;
+ }
+
+ for (i = 0; i < tbl_size; ++i) {
+ if (voltage > tbl[i].voltage)
+ break;
+ }
+
+ if ((i > 0) && (i < tbl_size)) {
+ cap = interpolate(voltage,
+ tbl[i].voltage,
+ tbl[i].capacity * 10,
+ tbl[i-1].voltage,
+ tbl[i-1].capacity * 10);
+ } else if (i == 0) {
+ cap = 1000;
+ } else {
+ cap = 0;
+ }
+
+ dev_dbg(di->dev, "%s Vbat: %d, Cap: %d per mille",
+ __func__, voltage, cap);
+
+ return cap;
+}
+
+/**
+ * ab5500_fg_uncomp_volt_to_capacity() - Uncompensated voltage based capacity
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is not compensated
+ * for the voltage drop due to the load
+ */
+static int ab5500_fg_uncomp_volt_to_capacity(struct ab5500_fg *di)
+{
+ di->vbat = ab5500_fg_bat_voltage(di);
+ return ab5500_fg_volt_to_capacity(di, di->vbat);
+}
+
+/**
+ * ab5500_fg_load_comp_volt_to_capacity() - Load compensated voltage based capacity
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Returns battery capacity based on battery voltage that is load compensated
+ * for the voltage drop
+ */
+static int ab5500_fg_load_comp_volt_to_capacity(struct ab5500_fg *di)
+{
+ int vbat_comp;
+
+ di->inst_curr = ab5500_fg_inst_curr(di);
+ di->vbat = ab5500_fg_bat_voltage(di);
+
+ /* Use Ohms law to get the load compensated voltage */
+ vbat_comp = di->vbat - (di->inst_curr *
+ di->bat->bat_type[di->bat->batt_id].battery_resistance) / 1000;
+
+ dev_dbg(di->dev, "%s Measured Vbat: %dmV,Compensated Vbat %dmV, "
+ "R: %dmOhm, Current: %dmA\n",
+ __func__,
+ di->vbat,
+ vbat_comp,
+ di->bat->bat_type[di->bat->batt_id].battery_resistance,
+ di->inst_curr);
+
+ return ab5500_fg_volt_to_capacity(di, vbat_comp);
+}
+
+/**
+ * ab5500_fg_convert_mah_to_permille() - Capacity in mAh to permille
+ * @di: pointer to the ab5500_fg structure
+ * @cap_mah: capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in permille
+ */
+static int ab5500_fg_convert_mah_to_permille(struct ab5500_fg *di, int cap_mah)
+{
+ return (cap_mah * 1000) / di->bat_cap.max_mah_design;
+}
+
+/**
+ * ab5500_fg_convert_permille_to_mah() - Capacity in permille to mAh
+ * @di: pointer to the ab5500_fg structure
+ * @cap_pm: capacity in permille
+ *
+ * Converts capacity in permille to capacity in mAh
+ */
+static int ab5500_fg_convert_permille_to_mah(struct ab5500_fg *di, int cap_pm)
+{
+ return cap_pm * di->bat_cap.max_mah_design / 1000;
+}
+
+/**
+ * ab5500_fg_convert_mah_to_uwh() - Capacity in mAh to uWh
+ * @di: pointer to the ab5500_fg structure
+ * @cap_mah: capacity in mAh
+ *
+ * Converts capacity in mAh to capacity in uWh
+ */
+static int ab5500_fg_convert_mah_to_uwh(struct ab5500_fg *di, int cap_mah)
+{
+ u64 div_res;
+ u32 div_rem;
+
+ div_res = ((u64) cap_mah) * ((u64) di->vbat_nom);
+ div_rem = do_div(div_res, 1000);
+
+ /* Make sure to round upwards if necessary */
+ if (div_rem >= 1000 / 2)
+ div_res++;
+
+ return (int) div_res;
+}
+
+/**
+ * ab5500_fg_calc_cap_charging() - Calculate remaining capacity while charging
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. The filter is filled with this capacity
+ */
+static int ab5500_fg_calc_cap_charging(struct ab5500_fg *di)
+{
+ dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+ __func__,
+ di->bat_cap.mah,
+ di->accu_charge);
+
+ /* Capacity should not be less than 0 */
+ if (di->bat_cap.mah + di->accu_charge > 0)
+ di->bat_cap.mah += di->accu_charge;
+ else
+ di->bat_cap.mah = 0;
+
+ /*
+ * We force capacity to 100% as long as the algorithm
+ * reports that it's full.
+ */
+ if (di->bat_cap.mah >= di->bat_cap.max_mah_design ||
+ di->flags.fully_charged)
+ di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+ ab5500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ di->bat_cap.permille =
+ ab5500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+ /* We need to update battery voltage and inst current when charging */
+ di->vbat = ab5500_fg_bat_voltage(di);
+ di->inst_curr = ab5500_fg_inst_curr(di);
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab5500_fg_calc_cap_discharge_voltage() - Capacity in discharge with voltage
+ * @di: pointer to the ab5500_fg structure
+ * @comp: if voltage should be load compensated before capacity calc
+ *
+ * Return the capacity in mAh based on the battery voltage. The voltage can
+ * either be load compensated or not. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab5500_fg_calc_cap_discharge_voltage(struct ab5500_fg *di, bool comp)
+{
+ int permille, mah;
+
+ if (comp)
+ permille = ab5500_fg_load_comp_volt_to_capacity(di);
+ else
+ permille = ab5500_fg_uncomp_volt_to_capacity(di);
+
+ mah = ab5500_fg_convert_permille_to_mah(di, permille);
+
+ di->bat_cap.mah = ab5500_fg_add_cap_sample(di, mah);
+ di->bat_cap.permille =
+ ab5500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab5500_fg_calc_cap_discharge_fg() - Capacity in discharge with FG
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Return the capacity in mAh based on previous calculated capcity and the FG
+ * accumulator register value. This value is added to the filter and a
+ * new mean value is calculated and returned.
+ */
+static int ab5500_fg_calc_cap_discharge_fg(struct ab5500_fg *di)
+{
+ int permille_volt, permille;
+
+ dev_dbg(di->dev, "%s cap_mah %d accu_charge %d\n",
+ __func__,
+ di->bat_cap.mah,
+ di->accu_charge);
+
+ /* Capacity should not be less than 0 */
+ if (di->bat_cap.mah + di->accu_charge > 0)
+ di->bat_cap.mah += di->accu_charge;
+ else
+ di->bat_cap.mah = 0;
+
+ if (di->bat_cap.mah >= di->bat_cap.max_mah_design)
+ di->bat_cap.mah = di->bat_cap.max_mah_design;
+
+ /*
+ * Check against voltage based capacity. It can not be lower
+ * than what the uncompensated voltage says
+ */
+ permille = ab5500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+ permille_volt = ab5500_fg_uncomp_volt_to_capacity(di);
+
+ if (permille < permille_volt) {
+ di->bat_cap.permille = permille_volt;
+ di->bat_cap.mah = ab5500_fg_convert_permille_to_mah(di,
+ di->bat_cap.permille);
+
+ dev_dbg(di->dev, "%s voltage based: perm %d perm_volt %d\n",
+ __func__,
+ permille,
+ permille_volt);
+
+ ab5500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ } else {
+ ab5500_fg_fill_cap_sample(di, di->bat_cap.mah);
+ di->bat_cap.permille =
+ ab5500_fg_convert_mah_to_permille(di, di->bat_cap.mah);
+ }
+
+ return di->bat_cap.mah;
+}
+
+/**
+ * ab5500_fg_capacity_level() - Get the battery capacity level
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Get the battery capacity level based on the capacity in percent
+ */
+static int ab5500_fg_capacity_level(struct ab5500_fg *di)
+{
+ int ret, percent;
+
+ percent = di->bat_cap.permille / 10;
+
+ if (percent <= di->bat->cap_levels->critical ||
+ di->flags.low_bat)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+ else if (percent <= di->bat->cap_levels->low)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+ else if (percent <= di->bat->cap_levels->normal)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+ else if (percent <= di->bat->cap_levels->high)
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
+ else
+ ret = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+
+ return ret;
+}
+
+/**
+ * ab5500_fg_check_capacity_limits() - Check if capacity has changed
+ * @di: pointer to the ab5500_fg structure
+ * @init: capacity is allowed to go up in init mode
+ *
+ * Check if capacity or capacity limit has changed and notify the system
+ * about it using the power_supply framework
+ */
+static void ab5500_fg_check_capacity_limits(struct ab5500_fg *di, bool init)
+{
+ bool changed = false;
+
+ di->bat_cap.level = ab5500_fg_capacity_level(di);
+
+ if (di->bat_cap.level != di->bat_cap.prev_level) {
+ /*
+ * We do not allow reported capacity level to go up
+ * unless we're charging or if we're in init
+ */
+ if (!(!di->flags.charging && di->bat_cap.level >
+ di->bat_cap.prev_level) || init) {
+ dev_dbg(di->dev, "level changed from %d to %d\n",
+ di->bat_cap.prev_level,
+ di->bat_cap.level);
+ di->bat_cap.prev_level = di->bat_cap.level;
+ changed = true;
+ } else {
+ dev_dbg(di->dev, "level not allowed to go up "
+ "since no charger is connected: %d to %d\n",
+ di->bat_cap.prev_level,
+ di->bat_cap.level);
+ }
+ }
+
+ /*
+ * If we have received the LOW_BAT IRQ, set capacity to 0 to initiate
+ * shutdown
+ */
+ if (di->flags.low_bat) {
+ dev_dbg(di->dev, "Battery low, set capacity to 0\n");
+ di->bat_cap.prev_percent = 0;
+ di->bat_cap.permille = 0;
+ di->bat_cap.prev_mah = 0;
+ di->bat_cap.mah = 0;
+ changed = true;
+ } else if (di->bat_cap.prev_percent != di->bat_cap.permille / 10) {
+ if (di->bat_cap.permille / 10 == 0) {
+ /*
+ * We will not report 0% unless we've got
+ * the LOW_BAT IRQ, no matter what the FG
+ * algorithm says.
+ */
+ di->bat_cap.prev_percent = 1;
+ di->bat_cap.permille = 1;
+ di->bat_cap.prev_mah = 1;
+ di->bat_cap.mah = 1;
+
+ changed = true;
+ } else if (!(!di->flags.charging &&
+ (di->bat_cap.permille / 10) >
+ di->bat_cap.prev_percent) || init) {
+ /*
+ * We do not allow reported capacity to go up
+ * unless we're charging or if we're in init
+ */
+ dev_dbg(di->dev,
+ "capacity changed from %d to %d (%d)\n",
+ di->bat_cap.prev_percent,
+ di->bat_cap.permille / 10,
+ di->bat_cap.permille);
+ di->bat_cap.prev_percent = di->bat_cap.permille / 10;
+ di->bat_cap.prev_mah = di->bat_cap.mah;
+
+ changed = true;
+ } else {
+ dev_dbg(di->dev, "capacity not allowed to go up since "
+ "no charger is connected: %d to %d (%d)\n",
+ di->bat_cap.prev_percent,
+ di->bat_cap.permille / 10,
+ di->bat_cap.permille);
+ }
+ }
+
+ if (changed)
+ power_supply_changed(&di->fg_psy);
+
+}
+
+static void ab5500_fg_charge_state_to(struct ab5500_fg *di,
+ enum ab5500_fg_charge_state new_state)
+{
+ dev_dbg(di->dev, "Charge state from %d [%s] to %d [%s]\n",
+ di->charge_state,
+ charge_state[di->charge_state],
+ new_state,
+ charge_state[new_state]);
+
+ di->charge_state = new_state;
+}
+
+static void ab5500_fg_discharge_state_to(struct ab5500_fg *di,
+ enum ab5500_fg_charge_state new_state)
+{
+ dev_dbg(di->dev, "Disharge state from %d [%s] to %d [%s]\n",
+ di->discharge_state,
+ discharge_state[di->discharge_state],
+ new_state,
+ discharge_state[new_state]);
+
+ di->discharge_state = new_state;
+}
+
+/**
+ * ab5500_fg_algorithm_charging() - FG algorithm for when charging
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're charging
+ */
+static void ab5500_fg_algorithm_charging(struct ab5500_fg *di)
+{
+ /*
+ * If we change to discharge mode
+ * we should start with recovery
+ */
+ if (di->discharge_state != AB5500_FG_DISCHARGE_INIT_RECOVERY)
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_INIT_RECOVERY);
+
+ switch (di->charge_state) {
+ case AB5500_FG_CHARGE_INIT:
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_charging);
+
+ ab5500_fg_coulomb_counter(di, true);
+ ab5500_fg_charge_state_to(di, AB5500_FG_CHARGE_READOUT);
+
+ break;
+
+ case AB5500_FG_CHARGE_READOUT:
+ /*
+ * Read the FG and calculate the new capacity
+ */
+ mutex_lock(&di->cc_lock);
+ if (!di->flags.conv_done) {
+ /* Wasn't the CC IRQ that got us here */
+ mutex_unlock(&di->cc_lock);
+ dev_dbg(di->dev, "%s CC conv not done\n",
+ __func__);
+
+ break;
+ }
+ di->flags.conv_done = false;
+ mutex_unlock(&di->cc_lock);
+
+ ab5500_fg_calc_cap_charging(di);
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* Check capacity limits */
+ ab5500_fg_check_capacity_limits(di, false);
+}
+
+/**
+ * ab5500_fg_algorithm_discharging() - FG algorithm for when discharging
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Battery capacity calculation state machine for when we're discharging
+ */
+static void ab5500_fg_algorithm_discharging(struct ab5500_fg *di)
+{
+ int sleep_time;
+
+ /* If we change to charge mode we should start with init */
+ if (di->charge_state != AB5500_FG_CHARGE_INIT)
+ ab5500_fg_charge_state_to(di, AB5500_FG_CHARGE_INIT);
+
+ switch (di->discharge_state) {
+ case AB5500_FG_DISCHARGE_INIT:
+ /* We use the FG IRQ to work on */
+ di->init_cnt = 0;
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+ ab5500_fg_coulomb_counter(di, true);
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_INITMEASURING);
+
+ /* Intentional fallthrough */
+ case AB5500_FG_DISCHARGE_INITMEASURING:
+ /*
+ * Discard a number of samples during startup.
+ * After that, use compensated voltage for a few
+ * samples to get an initial capacity.
+ * Then go to READOUT
+ */
+ sleep_time = di->bat->fg_params->init_timer;
+
+ /* Discard the first [x] seconds */
+ if (di->init_cnt >
+ di->bat->fg_params->init_discard_time) {
+
+ ab5500_fg_calc_cap_discharge_voltage(di, true);
+
+ ab5500_fg_check_capacity_limits(di, true);
+ }
+
+ di->init_cnt += sleep_time;
+ if (di->init_cnt >
+ di->bat->fg_params->init_total_time) {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+
+ ab5500_fg_coulomb_counter(di, true);
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_READOUT);
+ }
+
+ break;
+
+ case AB5500_FG_DISCHARGE_INIT_RECOVERY:
+ di->recovery_cnt = 0;
+ di->recovery_needed = true;
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_RECOVERY);
+
+ /* Intentional fallthrough */
+
+ case AB5500_FG_DISCHARGE_RECOVERY:
+ sleep_time = di->bat->fg_params->recovery_sleep_timer;
+
+ /*
+ * We should check the power consumption
+ * If low, go to READOUT (after x min) or
+ * RECOVERY_SLEEP if time left.
+ * If high, go to READOUT
+ */
+ di->inst_curr = ab5500_fg_inst_curr(di);
+
+ if (ab5500_fg_is_low_curr(di, di->inst_curr)) {
+ if (di->recovery_cnt >
+ di->bat->fg_params->recovery_total_time) {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab5500_fg_coulomb_counter(di, true);
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_READOUT);
+ di->recovery_needed = false;
+ } else {
+ queue_delayed_work(di->fg_wq,
+ &di->fg_periodic_work,
+ sleep_time * HZ);
+ }
+ di->recovery_cnt += sleep_time;
+ } else {
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ ab5500_fg_coulomb_counter(di, true);
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_READOUT);
+ }
+
+ break;
+
+ case AB5500_FG_DISCHARGE_READOUT:
+ di->inst_curr = ab5500_fg_inst_curr(di);
+
+ if (ab5500_fg_is_low_curr(di, di->inst_curr)) {
+ /* Detect mode change */
+ if (di->high_curr_mode) {
+ di->high_curr_mode = false;
+ di->high_curr_cnt = 0;
+ }
+
+ if (di->recovery_needed) {
+ ab5500_fg_discharge_state_to(di,
+ AB5500_FG_DISCHARGE_RECOVERY);
+
+ queue_delayed_work(di->fg_wq,
+ &di->fg_periodic_work,
+ 0);
+
+ break;
+ }
+
+ ab5500_fg_calc_cap_discharge_voltage(di, true);
+ } else {
+ mutex_lock(&di->cc_lock);
+ if (!di->flags.conv_done) {
+ /* Wasn't the CC IRQ that got us here */
+ mutex_unlock(&di->cc_lock);
+ dev_dbg(di->dev, "%s CC conv not done\n",
+ __func__);
+
+ break;
+ }
+ di->flags.conv_done = false;
+ mutex_unlock(&di->cc_lock);
+
+ /* Detect mode change */
+ if (!di->high_curr_mode) {
+ di->high_curr_mode = true;
+ di->high_curr_cnt = 0;
+ }
+
+ di->high_curr_cnt +=
+ di->bat->fg_params->accu_high_curr;
+ if (di->high_curr_cnt >
+ di->bat->fg_params->high_curr_time)
+ di->recovery_needed = true;
+
+ ab5500_fg_calc_cap_discharge_fg(di);
+ }
+
+ ab5500_fg_check_capacity_limits(di, false);
+
+ break;
+
+ case AB5500_FG_DISCHARGE_WAKEUP:
+ ab5500_fg_coulomb_counter(di, true);
+ di->inst_curr = ab5500_fg_inst_curr(di);
+
+ ab5500_fg_calc_cap_discharge_voltage(di, true);
+
+ di->fg_samples = SEC_TO_SAMPLE(
+ di->bat->fg_params->accu_high_curr);
+ /* Re-program number of samples set above */
+ ab5500_fg_coulomb_counter(di, true);
+ ab5500_fg_discharge_state_to(di, AB5500_FG_DISCHARGE_READOUT);
+
+ ab5500_fg_check_capacity_limits(di, false);
+
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * ab5500_fg_algorithm_calibrate() - Internal columb counter offset calibration
+ * @di: pointer to the ab5500_fg structure
+ *
+ */
+static void ab5500_fg_algorithm_calibrate(struct ab5500_fg *di)
+{
+ int ret;
+
+ switch (di->calib_state) {
+ case AB5500_FG_CALIB_INIT:
+ dev_dbg(di->dev, "Calibration ongoing...\n");
+ /* TODO: For Cut 1.1 no calibration */
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_CONTROL_A,
+ FG_ACC_RESET_ON_READ_MASK, FG_ACC_RESET_ON_READ);
+ if (ret)
+ goto err;
+ di->calib_state = AB5500_FG_CALIB_WAIT;
+ break;
+ case AB5500_FG_CALIB_END:
+ di->flags.calibrate = false;
+ dev_dbg(di->dev, "Calibration done...\n");
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ break;
+ case AB5500_FG_CALIB_WAIT:
+ dev_dbg(di->dev, "Calibration WFI\n");
+ default:
+ break;
+ }
+ return;
+err:
+ /* Something went wrong, don't calibrate then */
+ dev_err(di->dev, "failed to calibrate the CC\n");
+ di->flags.calibrate = false;
+ di->calib_state = AB5500_FG_CALIB_INIT;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+}
+
+/**
+ * ab5500_fg_algorithm() - Entry point for the FG algorithm
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Entry point for the battery capacity calculation state machine
+ */
+static void ab5500_fg_algorithm(struct ab5500_fg *di)
+{
+ if (di->flags.calibrate)
+ ab5500_fg_algorithm_calibrate(di);
+ else {
+ if (di->flags.charging)
+ ab5500_fg_algorithm_charging(di);
+ else
+ ab5500_fg_algorithm_discharging(di);
+ }
+
+ dev_dbg(di->dev, "[FG_DATA] %d %d %d %d %d %d %d %d %d "
+ "%d %d %d %d %d %d %d\n",
+ di->bat_cap.max_mah_design,
+ di->bat_cap.mah,
+ di->bat_cap.permille,
+ di->bat_cap.level,
+ di->bat_cap.prev_mah,
+ di->bat_cap.prev_percent,
+ di->bat_cap.prev_level,
+ di->vbat,
+ di->inst_curr,
+ di->avg_curr,
+ di->accu_charge,
+ di->flags.charging,
+ di->charge_state,
+ di->discharge_state,
+ di->high_curr_mode,
+ di->recovery_needed);
+}
+
+/**
+ * ab5500_fg_periodic_work() - Run the FG state machine periodically
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for periodic work
+ */
+static void ab5500_fg_periodic_work(struct work_struct *work)
+{
+ struct ab5500_fg *di = container_of(work, struct ab5500_fg,
+ fg_periodic_work.work);
+
+ if (di->init_capacity) {
+ /* A dummy read that will return 0 */
+ di->inst_curr = ab5500_fg_inst_curr(di);
+ /* Get an initial capacity calculation */
+ ab5500_fg_calc_cap_discharge_voltage(di, true);
+ ab5500_fg_check_capacity_limits(di, true);
+ di->init_capacity = false;
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+ } else
+ ab5500_fg_algorithm(di);
+}
+
+/**
+ * ab5500_fg_low_bat_work() - Check LOW_BAT condition
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for checking the LOW_BAT condition
+ */
+static void ab5500_fg_low_bat_work(struct work_struct *work)
+{
+ int vbat;
+
+ struct ab5500_fg *di = container_of(work, struct ab5500_fg,
+ fg_low_bat_work.work);
+
+ vbat = ab5500_fg_bat_voltage(di);
+
+ /* Check if LOW_BAT still fulfilled */
+ if (vbat < di->bat->fg_params->lowbat_threshold) {
+ di->flags.low_bat = true;
+ dev_warn(di->dev, "Battery voltage still LOW\n");
+
+ /*
+ * We need to re-schedule this check to be able to detect
+ * if the voltage increases again during charging
+ */
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ power_supply_changed(&di->fg_psy);
+ } else {
+ di->flags.low_bat = false;
+ dev_warn(di->dev, "Battery voltage OK again\n");
+ power_supply_changed(&di->fg_psy);
+ }
+
+ /* This is needed to dispatch LOW_BAT */
+ ab5500_fg_check_capacity_limits(di, false);
+
+ /* Set this flag to check if LOW_BAT IRQ still occurs */
+ di->flags.low_bat_delay = false;
+}
+
+/**
+ * ab5500_fg_instant_work() - Run the FG state machine instantly
+ * @work: pointer to the work_struct structure
+ *
+ * Work queue function for instant work
+ */
+static void ab5500_fg_instant_work(struct work_struct *work)
+{
+ struct ab5500_fg *di = container_of(work, struct ab5500_fg, fg_work);
+
+ ab5500_fg_algorithm(di);
+}
+
+/**
+ * ab5500_fg_get_property() - get the fg properties
+ * @psy: pointer to the power_supply structure
+ * @psp: pointer to the power_supply_property structure
+ * @val: pointer to the power_supply_propval union
+ *
+ * This function gets called when an application tries to get the
+ * fg properties by reading the sysfs files.
+ * voltage_now: battery voltage
+ * current_now: battery instant current
+ * current_avg: battery average current
+ * charge_full_design: capacity where battery is considered full
+ * charge_now: battery capacity in nAh
+ * capacity: capacity in percent
+ * capacity_level: capacity level
+ *
+ * Returns error code in case of failure else 0 on success
+ */
+static int ab5500_fg_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct ab5500_fg *di;
+
+ di = to_ab5500_fg_device_info(psy);
+
+ /*
+ * If battery is identified as unknown and charging of unknown
+ * batteries is disabled, we always report 100% capacity and
+ * capacity level UNKNOWN, since we can't calculate
+ * remaining capacity
+ */
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (di->flags.bat_ovv)
+ val->intval = 47500000;
+ else {
+ di->vbat = ab5500_gpadc_convert
+ (di->gpadc, MAIN_BAT_V);
+ val->intval = di->vbat * 1000;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ di->inst_curr = ab5500_fg_inst_curr(di);
+ val->intval = di->inst_curr * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = di->avg_curr * 1000;
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
+ val->intval = ab5500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah_design);
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_FULL:
+ val->intval = ab5500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah);
+ break;
+ case POWER_SUPPLY_PROP_ENERGY_NOW:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = ab5500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.max_mah);
+ else
+ val->intval = ab5500_fg_convert_mah_to_uwh(di,
+ di->bat_cap.prev_mah);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = di->bat_cap.max_mah_design;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = di->bat_cap.max_mah;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = di->bat_cap.max_mah;
+ else
+ val->intval = di->bat_cap.prev_mah;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = 100;
+ else
+ val->intval = di->bat_cap.prev_percent;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+ if (di->flags.batt_unknown && !di->bat->chg_unknown_bat)
+ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
+ else
+ val->intval = di->bat_cap.prev_level;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ab5500_fg_get_ext_psy_data(struct device *dev, void *data)
+{
+ struct power_supply *psy;
+ struct power_supply *ext;
+ struct ab5500_fg *di;
+ union power_supply_propval ret;
+ int i, j;
+ bool psy_found = false;
+
+ psy = (struct power_supply *)data;
+ ext = dev_get_drvdata(dev);
+ di = to_ab5500_fg_device_info(psy);
+
+ /*
+ * For all psy where the name of your driver
+ * appears in any supplied_to
+ */
+ for (i = 0; i < ext->num_supplicants; i++) {
+ if (!strcmp(ext->supplied_to[i], psy->name))
+ psy_found = true;
+ }
+
+ if (!psy_found)
+ return 0;
+
+ /* Go through all properties for the psy */
+ for (j = 0; j < ext->num_properties; j++) {
+ enum power_supply_property prop;
+ prop = ext->properties[j];
+
+ if (ext->get_property(ext, prop, &ret))
+ continue;
+
+ switch (prop) {
+ case POWER_SUPPLY_PROP_STATUS:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ switch (ret.intval) {
+ case POWER_SUPPLY_STATUS_UNKNOWN:
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ case POWER_SUPPLY_STATUS_NOT_CHARGING:
+ if (!di->flags.charging)
+ break;
+ di->flags.charging = false;
+ di->flags.fully_charged = false;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ if (di->flags.fully_charged)
+ break;
+ di->flags.fully_charged = true;
+ /* Save current capacity as maximum */
+ di->bat_cap.max_mah = di->bat_cap.mah;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if (di->flags.charging)
+ break;
+ di->flags.charging = true;
+ di->flags.fully_charged = false;
+ queue_work(di->fg_wq, &di->fg_work);
+ break;
+ };
+ default:
+ break;
+ };
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ if (ret.intval)
+ di->flags.batt_unknown = false;
+ else
+ di->flags.batt_unknown = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ab5500_fg_init_hw_registers() - Set up FG related registers
+ * @di: pointer to the ab5500_fg structure
+ *
+ * Set up battery OVV, low battery voltage registers
+ */
+static int ab5500_fg_init_hw_registers(struct ab5500_fg *di)
+{
+ int ret;
+ struct adc_auto_input *auto_ip;
+
+ auto_ip = kzalloc(sizeof(struct adc_auto_input), GFP_KERNEL);
+ if (!auto_ip) {
+ dev_err(di->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ auto_ip->mux = MAIN_BAT_V;
+ auto_ip->freq = MS500;
+ auto_ip->min = di->bat->fg_params->lowbat_threshold;
+ auto_ip->max = di->bat->fg_params->overbat_threshold;
+ auto_ip->auto_adc_callback = ab5500_fg_bat_v_trig;
+ di->gpadc_auto = auto_ip;
+ ret = ab5500_gpadc_convert_auto(di->gpadc, di->gpadc_auto);
+ if (ret)
+ dev_err(di->dev,
+ "failed to set auto trigger for battery votlage\n");
+ /* set End Of Charge current to 247mA */
+ ret = abx500_set_register_interruptible(di->dev,
+ AB5500_BANK_FG_BATTCOM_ACC, AB5500_FG_EOC, EOC_52_mA);
+ return ret;
+}
+
+static int ab5500_fg_bat_v_trig(int mux)
+{
+ struct ab5500_fg *di = ab5500_fg_get();
+
+ di->vbat = ab5500_gpadc_convert(di->gpadc, MAIN_BAT_V);
+
+ /* check if the battery voltage is below low threshold */
+ if (di->vbat < di->bat->fg_params->lowbat_threshold) {
+ dev_warn(di->dev, "Battery voltage is below LOW threshold\n");
+ di->flags.low_bat_delay = true;
+ /*
+ * Start a timer to check LOW_BAT again after some time
+ * This is done to avoid shutdown on single voltage dips
+ */
+ queue_delayed_work(di->fg_wq, &di->fg_low_bat_work,
+ round_jiffies(LOW_BAT_CHECK_INTERVAL));
+ power_supply_changed(&di->fg_psy);
+ }
+ /* check if battery votlage is above OVV */
+ else if (di->vbat > di->bat->fg_params->overbat_threshold) {
+ dev_warn(di->dev, "Battery OVV\n");
+ di->flags.bat_ovv = true;
+
+ power_supply_changed(&di->fg_psy);
+ } else
+ dev_err(di->dev,
+ "Invalid gpadc auto trigger for battery voltage\n");
+
+ kfree(di->gpadc_auto);
+ ab5500_fg_init_hw_registers(di);
+ return 0;
+}
+
+/**
+ * ab5500_fg_external_power_changed() - callback for power supply changes
+ * @psy: pointer to the structure power_supply
+ *
+ * This function is the entry point of the pointer external_power_changed
+ * of the structure power_supply.
+ * This function gets executed when there is a change in any external power
+ * supply that this driver needs to be notified of.
+ */
+static void ab5500_fg_external_power_changed(struct power_supply *psy)
+{
+ struct ab5500_fg *di = to_ab5500_fg_device_info(psy);
+
+ class_for_each_device(power_supply_class, NULL,
+ &di->fg_psy, ab5500_fg_get_ext_psy_data);
+}
+
+/**
+ * abab5500_fg_reinit_work() - work to reset the FG algorithm
+ * @work: pointer to the work_struct structure
+ *
+ * Used to reset the current battery capacity to be able to
+ * retrigger a new voltage base capacity calculation. For
+ * test and verification purpose.
+ */
+static void ab5500_fg_reinit_work(struct work_struct *work)
+{
+ struct ab5500_fg *di = container_of(work, struct ab5500_fg,
+ fg_reinit_work.work);
+
+ if (di->flags.calibrate == false) {
+ dev_dbg(di->dev, "Resetting FG state machine to init.\n");
+ ab5500_fg_clear_cap_samples(di);
+ ab5500_fg_calc_cap_discharge_voltage(di, true);
+ ab5500_fg_charge_state_to(di, AB5500_FG_CHARGE_INIT);
+ ab5500_fg_discharge_state_to(di, AB5500_FG_DISCHARGE_INIT);
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work, 0);
+
+ } else {
+ dev_err(di->dev,
+ "Residual offset calibration ongoing retrying..\n");
+ /* Wait one second until next try*/
+ queue_delayed_work(di->fg_wq, &di->fg_reinit_work,
+ round_jiffies(1));
+ }
+}
+
+/**
+ * ab5500_fg_reinit() - forces FG algorithm to reinitialize with current values
+ *
+ * This function can be used to force the FG algorithm to recalculate a new
+ * voltage based battery capacity.
+ */
+void ab5500_fg_reinit(void)
+{
+ struct ab5500_fg *di = ab5500_fg_get();
+ /* User won't be notified if a null pointer returned. */
+ if (di != NULL)
+ queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0);
+}
+
+#if defined(CONFIG_PM)
+static int ab5500_fg_resume(struct platform_device *pdev)
+{
+ struct ab5500_fg *di = platform_get_drvdata(pdev);
+
+ /*
+ * Change state if we're not charging. If we're charging we will wake
+ * up on the FG IRQ
+ */
+ if (!di->flags.charging) {
+ ab5500_fg_discharge_state_to(di, AB5500_FG_DISCHARGE_WAKEUP);
+ queue_work(di->fg_wq, &di->fg_work);
+ }
+
+ return 0;
+}
+
+static int ab5500_fg_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct ab5500_fg *di = platform_get_drvdata(pdev);
+
+ flush_delayed_work(&di->fg_periodic_work);
+
+ /*
+ * If the FG is enabled we will disable it before going to suspend
+ * only if we're not charging
+ */
+ if (di->flags.fg_enabled && !di->flags.charging)
+ ab5500_fg_coulomb_counter(di, false);
+
+ return 0;
+}
+#else
+#define ab5500_fg_suspend NULL
+#define ab5500_fg_resume NULL
+#endif
+
+static int __devexit ab5500_fg_remove(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct ab5500_fg *di = platform_get_drvdata(pdev);
+
+ /* Disable coulomb counter */
+ ret = ab5500_fg_coulomb_counter(di, false);
+ if (ret)
+ dev_err(di->dev, "failed to disable coulomb counter\n");
+
+ destroy_workqueue(di->fg_wq);
+
+ flush_scheduled_work();
+ power_supply_unregister(&di->fg_psy);
+ platform_set_drvdata(pdev, NULL);
+ kfree(di->gpadc_auto);
+ kfree(di);
+ return ret;
+}
+
+static int __devinit ab5500_fg_probe(struct platform_device *pdev)
+{
+ struct abx500_bm_plat_data *plat_data;
+ int ret = 0;
+
+ struct ab5500_fg *di =
+ kzalloc(sizeof(struct ab5500_fg), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ mutex_init(&di->cc_lock);
+
+ /* get parent data */
+ di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ di->gpadc = ab5500_gpadc_get("ab5500-adc.0");
+
+ plat_data = pdev->dev.platform_data;
+ di->pdata = plat_data->fg;
+ di->bat = plat_data->battery;
+
+ /* get fg specific platform data */
+ if (!di->pdata) {
+ dev_err(di->dev, "no fg platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+
+ /* get battery specific platform data */
+ if (!di->bat) {
+ dev_err(di->dev, "no battery platform data supplied\n");
+ ret = -EINVAL;
+ goto free_device_info;
+ }
+ /* powerup fg to start sampling */
+ ab5500_fg_coulomb_counter(di, true);
+
+ di->fg_psy.name = "ab5500_fg";
+ di->fg_psy.type = POWER_SUPPLY_TYPE_BATTERY;
+ di->fg_psy.properties = ab5500_fg_props;
+ di->fg_psy.num_properties = ARRAY_SIZE(ab5500_fg_props);
+ di->fg_psy.get_property = ab5500_fg_get_property;
+ di->fg_psy.supplied_to = di->pdata->supplied_to;
+ di->fg_psy.num_supplicants = di->pdata->num_supplicants;
+ di->fg_psy.external_power_changed = ab5500_fg_external_power_changed;
+
+ di->bat_cap.max_mah_design = MILLI_TO_MICRO *
+ di->bat->bat_type[di->bat->batt_id].charge_full_design;
+
+ di->bat_cap.max_mah = di->bat_cap.max_mah_design;
+
+ di->vbat_nom = di->bat->bat_type[di->bat->batt_id].nominal_voltage;
+
+ di->init_capacity = true;
+
+ ab5500_fg_charge_state_to(di, AB5500_FG_CHARGE_INIT);
+ ab5500_fg_discharge_state_to(di, AB5500_FG_DISCHARGE_INIT);
+
+ /* Create a work queue for running the FG algorithm */
+ di->fg_wq = create_singlethread_workqueue("ab5500_fg_wq");
+ if (di->fg_wq == NULL) {
+ dev_err(di->dev, "failed to create work queue\n");
+ goto free_device_info;
+ }
+
+ /* Init work for running the fg algorithm instantly */
+ INIT_WORK(&di->fg_work, ab5500_fg_instant_work);
+
+ /* Init work for getting the battery accumulated current */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_acc_cur_work,
+ ab5500_fg_acc_cur_work);
+
+ /* Init work for reinitialising the fg algorithm */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_reinit_work,
+ ab5500_fg_reinit_work);
+
+ /* Work delayed Queue to run the state machine */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_periodic_work,
+ ab5500_fg_periodic_work);
+
+ /* Work to check low battery condition */
+ INIT_DELAYED_WORK_DEFERRABLE(&di->fg_low_bat_work,
+ ab5500_fg_low_bat_work);
+
+ list_add_tail(&di->node, &ab5500_fg_list);
+
+ /* Consider battery unknown until we're informed otherwise */
+ di->flags.batt_unknown = true;
+
+ /* Register FG power supply class */
+ ret = power_supply_register(di->dev, &di->fg_psy);
+ if (ret) {
+ dev_err(di->dev, "failed to register FG psy\n");
+ goto free_fg_wq;
+ }
+
+ /* Initialize OVV, and other registers */
+ ret = ab5500_fg_init_hw_registers(di);
+ if (ret) {
+ dev_err(di->dev, "failed to initialize registers\n");
+ goto pow_unreg;
+ }
+
+ di->fg_samples = SEC_TO_SAMPLE(di->bat->fg_params->init_timer);
+
+ /* Initilialize avg current timer */
+ init_timer(&di->avg_current_timer);
+ di->avg_current_timer.function = ab5500_fg_acc_cur_timer_expired;
+ di->avg_current_timer.data = (unsigned long) di;
+ di->avg_current_timer.expires = 60 * HZ;
+ if (!timer_pending(&di->avg_current_timer))
+ add_timer(&di->avg_current_timer);
+ else
+ mod_timer(&di->avg_current_timer, 60 * HZ);
+
+ platform_set_drvdata(pdev, di);
+
+ /* Calibrate the fg first time */
+ di->flags.calibrate = true;
+ di->calib_state = AB5500_FG_CALIB_INIT;
+ /* Run the FG algorithm */
+ queue_delayed_work(di->fg_wq, &di->fg_periodic_work,
+ FG_PERIODIC_START_INTERVAL);
+ queue_delayed_work(di->fg_wq, &di->fg_acc_cur_work,
+ FG_PERIODIC_START_INTERVAL);
+
+ dev_info(di->dev, "probe success\n");
+ return ret;
+
+pow_unreg:
+ power_supply_unregister(&di->fg_psy);
+free_fg_wq:
+ destroy_workqueue(di->fg_wq);
+free_device_info:
+ kfree(di);
+
+ return ret;
+}
+
+static struct platform_driver ab5500_fg_driver = {
+ .probe = ab5500_fg_probe,
+ .remove = __devexit_p(ab5500_fg_remove),
+ .suspend = ab5500_fg_suspend,
+ .resume = ab5500_fg_resume,
+ .driver = {
+ .name = "ab5500-fg",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab5500_fg_init(void)
+{
+ return platform_driver_register(&ab5500_fg_driver);
+}
+
+static void __exit ab5500_fg_exit(void)
+{
+ platform_driver_unregister(&ab5500_fg_driver);
+}
+
+subsys_initcall_sync(ab5500_fg_init);
+module_exit(ab5500_fg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Johan Palsson, Karl Komierowski");
+MODULE_ALIAS("platform:ab5500-fg");
+MODULE_DESCRIPTION("AB5500 Fuel Gauge driver");
diff --git a/drivers/power/ab8500_btemp.c b/drivers/power/ab8500_btemp.c
index d8bb99394ac..5e5700cf24a 100644
--- a/drivers/power/ab8500_btemp.c
+++ b/drivers/power/ab8500_btemp.c
@@ -83,6 +83,7 @@ struct ab8500_btemp_ranges {
* @btemp_ranges: Battery temperature range structure
* @btemp_wq: Work queue for measuring the temperature periodically
* @btemp_periodic_work: Work for measuring the temperature periodically
+ * @initialized: True if battery id read.
*/
struct ab8500_btemp {
struct device *dev;
@@ -100,6 +101,7 @@ struct ab8500_btemp {
struct ab8500_btemp_ranges btemp_ranges;
struct workqueue_struct *btemp_wq;
struct delayed_work btemp_periodic_work;
+ bool initialized;
};
/* BTEMP power supply properties */
@@ -569,6 +571,13 @@ static void ab8500_btemp_periodic_work(struct work_struct *work)
struct ab8500_btemp *di = container_of(work,
struct ab8500_btemp, btemp_periodic_work.work);
+ if (!di->initialized) {
+ di->initialized = true;
+ /* Identify the battery */
+ if (ab8500_btemp_id(di) < 0)
+ dev_warn(di->dev, "failed to identify the battery\n");
+ }
+
di->bat_temp = ab8500_btemp_measure_temp(di);
if (di->bat_temp != di->prev_bat_temp) {
@@ -964,7 +973,7 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
{
int irq, i, ret = 0;
u8 val;
- struct abx500_bm_plat_data *plat_data;
+ struct ab8500_platform_data *plat_data;
struct ab8500_btemp *di =
kzalloc(sizeof(struct ab8500_btemp), GFP_KERNEL);
@@ -976,8 +985,10 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
di->parent = dev_get_drvdata(pdev->dev.parent);
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
+ di->initialized = false;
+
/* get btemp specific platform data */
- plat_data = pdev->dev.platform_data;
+ plat_data = dev_get_platdata(di->parent->dev);
di->pdata = plat_data->btemp;
if (!di->pdata) {
dev_err(di->dev, "no btemp platform data supplied\n");
@@ -1017,10 +1028,6 @@ static int __devinit ab8500_btemp_probe(struct platform_device *pdev)
INIT_DELAYED_WORK_DEFERRABLE(&di->btemp_periodic_work,
ab8500_btemp_periodic_work);
- /* Identify the battery */
- if (ab8500_btemp_id(di) < 0)
- dev_warn(di->dev, "failed to identify the battery\n");
-
/* Set BTEMP thermal limits. Low and Med are fixed */
di->btemp_ranges.btemp_low_limit = BTEMP_THERMAL_LOW_LIMIT;
di->btemp_ranges.btemp_med_limit = BTEMP_THERMAL_MED_LIMIT;
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index e2b4accbec8..19f62729a0a 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -29,6 +29,7 @@
#include <linux/mfd/abx500/ab8500-gpadc.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/usb/otg.h>
+#include <asm/mach-types.h>
/* Charger constants */
#define NO_PW_CONN 0
@@ -77,6 +78,9 @@
/* Lowest charger voltage is 3.39V -> 0x4E */
#define LOW_VOLT_REG 0x4E
+/* Step up/down delay in us */
+#define STEP_UDELAY 1000
+
/* UsbLineStatus register - usb types */
enum ab8500_charger_link_status {
USB_STAT_NOT_CONFIGURED,
@@ -934,6 +938,88 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
}
/**
+ * ab8500_charger_set_current() - set charger current
+ * @di: pointer to the ab8500_charger structure
+ * @ich: charger current, in mA
+ * @reg: select what charger register to set
+ *
+ * Set charger current.
+ * There is no state machine in the AB to step up/down the charger
+ * current to avoid dips and spikes on MAIN, VBUS and VBAT when
+ * charging is started. Instead we need to implement
+ * this charger current step-up/down here.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_current(struct ab8500_charger *di,
+ int ich, int reg)
+{
+ int ret, i;
+ int curr_index, prev_curr_index, shift_value;
+ u8 reg_value;
+
+ switch (reg) {
+ case AB8500_MCH_IPT_CURLVL_REG:
+ shift_value = MAIN_CH_INPUT_CURR_SHIFT;
+ curr_index = ab8500_current_to_regval(ich);
+ break;
+ case AB8500_USBCH_IPT_CRNTLVL_REG:
+ shift_value = VBUS_IN_CURR_LIM_SHIFT;
+ curr_index = ab8500_vbus_in_curr_to_regval(ich);
+ break;
+ case AB8500_CH_OPT_CRNTLVL_REG:
+ shift_value = 0;
+ curr_index = ab8500_current_to_regval(ich);
+ break;
+ default:
+ dev_err(di->dev, "%s current register not valid\n", __func__);
+ return -ENXIO;
+ }
+
+ if (curr_index < 0) {
+ dev_err(di->dev, "requested current limit out-of-range\n");
+ return -ENXIO;
+ }
+
+ ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+ reg, &reg_value);
+ if (ret < 0) {
+ dev_err(di->dev, "%s read failed\n", __func__);
+ return ret;
+ }
+ prev_curr_index = (reg_value >> shift_value);
+
+ /* only update current if it's been changed */
+ if (prev_curr_index == curr_index)
+ return 0;
+
+ dev_dbg(di->dev, "%s set charger current: %d mA for reg: 0x%02x\n",
+ __func__, ich, reg);
+
+ if (prev_curr_index > curr_index) {
+ for (i = prev_curr_index - 1; i >= curr_index; i--) {
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER, reg, (u8) i << shift_value);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+ }
+ } else {
+ for (i = prev_curr_index + 1; i <= curr_index; i++) {
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_CHARGER, reg, (u8) i << shift_value);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+ usleep_range(STEP_UDELAY, STEP_UDELAY * 2);
+ }
+ }
+ return ret;
+}
+
+/**
* ab8500_charger_set_vbus_in_curr() - set VBUS input current limit
* @di: pointer to the ab8500_charger structure
* @ich_in: charger input current limit
@@ -944,8 +1030,6 @@ static int ab8500_charger_get_usb_cur(struct ab8500_charger *di)
static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
int ich_in)
{
- int ret;
- int input_curr_index;
int min_value;
/* We should always use to lowest current limit */
@@ -964,19 +1048,38 @@ static int ab8500_charger_set_vbus_in_curr(struct ab8500_charger *di,
break;
}
- input_curr_index = ab8500_vbus_in_curr_to_regval(min_value);
- if (input_curr_index < 0) {
- dev_err(di->dev, "VBUS input current limit too high\n");
- return -ENXIO;
- }
+ return ab8500_charger_set_current(di, min_value,
+ AB8500_USBCH_IPT_CRNTLVL_REG);
+}
- ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
- AB8500_USBCH_IPT_CRNTLVL_REG,
- input_curr_index << VBUS_IN_CURR_LIM_SHIFT);
- if (ret)
- dev_err(di->dev, "%s write failed\n", __func__);
+/**
+ * ab8500_charger_set_main_in_curr() - set main charger input current
+ * @di: pointer to the ab8500_charger structure
+ * @ich_in: input charger current, in mA
+ *
+ * Set main charger input current.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_main_in_curr(struct ab8500_charger *di,
+ int ich_in)
+{
+ return ab8500_charger_set_current(di, ich_in,
+ AB8500_MCH_IPT_CURLVL_REG);
+}
- return ret;
+/**
+ * ab8500_charger_set_output_curr() - set charger output current
+ * @di: pointer to the ab8500_charger structure
+ * @ich_out: output charger current, in mA
+ *
+ * Set charger output current.
+ * Returns error code in case of failure else 0(on success)
+ */
+static int ab8500_charger_set_output_curr(struct ab8500_charger *di,
+ int ich_out)
+{
+ return ab8500_charger_set_current(di, ich_out,
+ AB8500_CH_OPT_CRNTLVL_REG);
}
/**
@@ -1088,18 +1191,19 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
return ret;
}
/* MainChInputCurr: current that can be drawn from the charger*/
- ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
- AB8500_MCH_IPT_CURLVL_REG,
- input_curr_index << MAIN_CH_INPUT_CURR_SHIFT);
+ ret = ab8500_charger_set_main_in_curr(di,
+ di->bat->chg_params->ac_curr_max);
if (ret) {
- dev_err(di->dev, "%s write failed\n", __func__);
+ dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
+ __func__);
return ret;
}
/* ChOutputCurentLevel: protected output current */
- ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
- AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ ret = ab8500_charger_set_output_curr(di, iset);
if (ret) {
- dev_err(di->dev, "%s write failed\n", __func__);
+ dev_err(di->dev, "%s "
+ "Failed to set ChOutputCurentLevel\n",
+ __func__);
return ret;
}
@@ -1156,12 +1260,11 @@ static int ab8500_charger_ac_en(struct ux500_charger *charger,
return ret;
}
- ret = abx500_set_register_interruptible(di->dev,
- AB8500_CHARGER,
- AB8500_CH_OPT_CRNTLVL_REG, CH_OP_CUR_LVL_0P1);
+ ret = ab8500_charger_set_output_curr(di, 0);
if (ret) {
- dev_err(di->dev,
- "%s write failed\n", __func__);
+ dev_err(di->dev, "%s "
+ "Failed to set ChOutputCurentLevel\n",
+ __func__);
return ret;
}
} else {
@@ -1264,10 +1367,11 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
return ret;
}
/* ChOutputCurentLevel: protected output current */
- ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
- AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ ret = ab8500_charger_set_output_curr(di, ich_out);
if (ret) {
- dev_err(di->dev, "%s write failed\n", __func__);
+ dev_err(di->dev, "%s "
+ "Failed to set ChOutputCurentLevel\n",
+ __func__);
return ret;
}
/* Check if VBAT overshoot control should be enabled */
@@ -1364,7 +1468,6 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
int ich_out)
{
int ret;
- int curr_index;
struct ab8500_charger *di;
if (charger->psy.type == POWER_SUPPLY_TYPE_MAINS)
@@ -1374,18 +1477,11 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
else
return -ENXIO;
- curr_index = ab8500_current_to_regval(ich_out);
- if (curr_index < 0) {
- dev_err(di->dev,
- "Charger current too high, "
- "charging not started\n");
- return -ENXIO;
- }
-
- ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
- AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
+ ret = ab8500_charger_set_output_curr(di, ich_out);
if (ret) {
- dev_err(di->dev, "%s write failed\n", __func__);
+ dev_err(di->dev, "%s "
+ "Failed to set ChOutputCurentLevel\n",
+ __func__);
return ret;
}
@@ -2354,11 +2450,18 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di)
}
/* Backup battery voltage and current */
- ret = abx500_set_register_interruptible(di->dev,
- AB8500_RTC,
- AB8500_RTC_BACKUP_CHG_REG,
- di->bat->bkup_bat_v |
- di->bat->bkup_bat_i);
+ if (machine_is_snowball())
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_RTC,
+ AB8500_RTC_BACKUP_CHG_REG,
+ BUP_VCH_SEL_3P1V |
+ BUP_ICH_SEL_150UA);
+ else
+ ret = abx500_set_register_interruptible(di->dev,
+ AB8500_RTC,
+ AB8500_RTC_BACKUP_CHG_REG,
+ di->bat->bkup_bat_v |
+ di->bat->bkup_bat_i);
if (ret) {
dev_err(di->dev, "failed to setup backup battery charging\n");
goto out;
@@ -2534,7 +2637,7 @@ static int __devexit ab8500_charger_remove(struct platform_device *pdev)
static int __devinit ab8500_charger_probe(struct platform_device *pdev)
{
int irq, i, charger_status, ret = 0;
- struct abx500_bm_plat_data *plat_data;
+ struct ab8500_platform_data *plat_data;
struct ab8500_charger *di =
kzalloc(sizeof(struct ab8500_charger), GFP_KERNEL);
@@ -2550,7 +2653,8 @@ static int __devinit ab8500_charger_probe(struct platform_device *pdev)
spin_lock_init(&di->usb_state.usb_lock);
/* get charger specific platform data */
- plat_data = pdev->dev.platform_data;
+ plat_data = dev_get_platdata(di->parent->dev);
+
di->pdata = plat_data->charger;
if (!di->pdata) {
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c
index c22f2f05657..798f5f7cef4 100644
--- a/drivers/power/ab8500_fg.c
+++ b/drivers/power/ab8500_fg.c
@@ -485,8 +485,9 @@ static int ab8500_fg_coulomb_counter(struct ab8500_fg *di, bool enable)
di->flags.fg_enabled = true;
} else {
/* Clear any pending read requests */
- ret = abx500_set_register_interruptible(di->dev,
- AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG, 0);
+ ret = abx500_mask_and_set_register_interruptible(di->dev,
+ AB8500_GAS_GAUGE, AB8500_GASG_CC_CTRL_REG,
+ (RESET_ACCU | READ_REQ), 0);
if (ret)
goto cc_err;
@@ -1404,8 +1405,7 @@ static void ab8500_fg_algorithm_discharging(struct ab8500_fg *di)
sleep_time = di->bat->fg_params->init_timer;
/* Discard the first [x] seconds */
- if (di->init_cnt >
- di->bat->fg_params->init_discard_time) {
+ if (di->init_cnt > di->bat->fg_params->init_discard_time) {
ab8500_fg_calc_cap_discharge_voltage(di, true);
ab8500_fg_check_capacity_limits(di, true);
@@ -2446,7 +2446,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
{
int i, irq;
int ret = 0;
- struct abx500_bm_plat_data *plat_data;
+ struct ab8500_platform_data *plat_data;
struct ab8500_fg *di =
kzalloc(sizeof(struct ab8500_fg), GFP_KERNEL);
@@ -2461,7 +2461,7 @@ static int __devinit ab8500_fg_probe(struct platform_device *pdev)
di->gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
/* get fg specific platform data */
- plat_data = pdev->dev.platform_data;
+ plat_data = dev_get_platdata(di->parent->dev);
di->pdata = plat_data->fg;
if (!di->pdata) {
dev_err(di->dev, "no fg platform data supplied\n");
diff --git a/drivers/power/abx500_chargalg.c b/drivers/power/abx500_chargalg.c
index 804b88c760d..032b27d35bf 100644
--- a/drivers/power/abx500_chargalg.c
+++ b/drivers/power/abx500_chargalg.c
@@ -220,6 +220,7 @@ enum maxim_ret {
*/
struct abx500_chargalg {
struct device *dev;
+ struct ab8500 *parent;
int charge_status;
int eoc_cnt;
int rch_cnt;
@@ -1802,7 +1803,7 @@ static int __devexit abx500_chargalg_remove(struct platform_device *pdev)
static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
{
- struct abx500_bm_plat_data *plat_data;
+ struct ab8500_platform_data *plat_data;
int ret = 0;
struct abx500_chargalg *di =
@@ -1812,8 +1813,8 @@ static int __devinit abx500_chargalg_probe(struct platform_device *pdev)
/* get device struct */
di->dev = &pdev->dev;
-
- plat_data = pdev->dev.platform_data;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
+ plat_data = dev_get_platdata(di->parent->dev);
di->pdata = plat_data->chargalg;
di->bat = plat_data->battery;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 36db5a441eb..7278f1e4141 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -247,6 +247,14 @@ config REGULATOR_AB8500
This driver supports the regulators found on the ST-Ericsson mixed
signal AB8500 PMIC
+config REGULATOR_AB8500_EXT
+ bool "ST-Ericsson AB8500 External Regulators"
+ depends on REGULATOR_AB8500
+ default y if REGULATOR_AB8500
+ help
+ This driver supports the external regulator controls found on the
+ ST-Ericsson mixed signal AB8500 PMIC
+
config REGULATOR_DBX500_PRCMU
bool
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 94b52745e95..b5b90fb232b 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
+obj-$(CONFIG_REGULATOR_AB8500_EXT) += ab8500-ext.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
diff --git a/drivers/regulator/ab5500.c b/drivers/regulator/ab5500.c
new file mode 100644
index 00000000000..99676f7ad8e
--- /dev/null
+++ b/drivers/regulator/ab5500.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * Based on ab3100.c.
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/regulator/ab5500.h>
+
+#define AB5500_LDO_VDIGMIC_ST 0x50
+
+#define AB5500_LDO_G_ST 0x78
+#define AB5500_LDO_G_PWR1 0x79
+#define AB5500_LDO_G_PWR0 0x7a
+
+#define AB5500_LDO_H_ST 0x7b
+#define AB5500_LDO_H_PWR1 0x7c
+#define AB5500_LDO_H_PWR0 0x7d
+
+#define AB5500_LDO_K_ST 0x7e
+#define AB5500_LDO_K_PWR1 0x7f
+#define AB5500_LDO_K_PWR0 0x80
+
+#define AB5500_LDO_L_ST 0x81
+#define AB5500_LDO_L_PWR1 0x82
+#define AB5500_LDO_L_PWR0 0x83
+
+/* In SIM bank */
+#define AB5500_SIM_SUP 0x14
+
+#define AB5500_MBIAS1 0x00
+#define AB5500_MBIAS2 0x01
+
+#define AB5500_LDO_MODE_MASK (0x3 << 4)
+#define AB5500_LDO_MODE_FULLPOWER (0x3 << 4)
+#define AB5500_LDO_MODE_PWRCTRL (0x2 << 4)
+#define AB5500_LDO_MODE_LOWPOWER (0x1 << 4)
+#define AB5500_LDO_MODE_OFF (0x0 << 4)
+#define AB5500_LDO_VOLT_MASK 0x07
+
+#define AB5500_MBIAS1_ENABLE (0x1 << 1)
+#define AB5500_MBIAS1_MODE_MASK (0x1 << 1)
+#define AB5500_MBIAS2_ENABLE (0x1 << 1)
+#define AB5500_MBIAS2_VOLT_MASK (0x1 << 2)
+#define AB5500_MBIAS2_MODE_MASK (0x1 << 1)
+
+struct ab5500_regulator {
+ struct regulator_desc desc;
+ const int *voltages;
+ int num_holes;
+ bool off_is_lowpower;
+ bool enabled;
+ int enable_time;
+ int load_lp_uA;
+ u8 bank;
+ u8 reg;
+ u8 mode;
+ u8 update_mask;
+ u8 update_val_idle;
+ u8 update_val_normal;
+ u8 voltage_mask;
+};
+
+struct ab5500_regulators {
+ struct device *dev;
+ struct ab5500_regulator *regulator[AB5500_NUM_REGULATORS];
+ struct regulator_dev *rdev[AB5500_NUM_REGULATORS];
+};
+
+static int ab5500_regulator_enable_time(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ return r->enable_time; /* microseconds */
+}
+
+static int ab5500_regulator_enable(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ int ret;
+
+ ret = abx500_mask_and_set(ab5500->dev, r->bank, r->reg,
+ r->update_mask, r->mode);
+ if (ret < 0)
+ return ret;
+
+ r->enabled = true;
+
+ return 0;
+}
+
+static int ab5500_regulator_disable(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int ret;
+
+ if (r->off_is_lowpower)
+ regval = AB5500_LDO_MODE_LOWPOWER;
+ else
+ regval = AB5500_LDO_MODE_OFF;
+
+ ret = abx500_mask_and_set(ab5500->dev, r->bank, r->reg,
+ r->update_mask, regval);
+ if (ret < 0)
+ return ret;
+
+ r->enabled = false;
+
+ return 0;
+}
+
+static unsigned int ab5500_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ if (r->mode == r->update_val_idle)
+ return REGULATOR_MODE_IDLE;
+
+ return REGULATOR_MODE_NORMAL;
+}
+
+static unsigned int
+ab5500_regulator_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ unsigned int mode;
+
+ if (load_uA <= r->load_lp_uA)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int ab5500_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ r->mode = r->update_val_normal;
+ break;
+ case REGULATOR_MODE_IDLE:
+ r->mode = r->update_val_idle;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (r->enabled)
+ return ab5500_regulator_enable(rdev);
+
+ return 0;
+}
+
+static int ab5500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int err;
+
+ err = abx500_get_register_interruptible(ab5500->dev,
+ r->bank, r->reg, &regval);
+ if (err) {
+ dev_err(rdev_get_dev(rdev), "unable to get register 0x%x\n",
+ r->reg);
+ return err;
+ }
+
+ switch (regval & r->update_mask) {
+ case AB5500_LDO_MODE_PWRCTRL:
+ case AB5500_LDO_MODE_OFF:
+ r->enabled = false;
+ break;
+ case AB5500_LDO_MODE_LOWPOWER:
+ if (r->off_is_lowpower) {
+ r->enabled = false;
+ break;
+ }
+ /* fall through */
+ default:
+ r->enabled = true;
+ break;
+ }
+
+ return r->enabled;
+}
+
+static int
+ab5500_regulator_list_voltage(struct regulator_dev *rdev, unsigned selector)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ unsigned n_voltages = r->desc.n_voltages;
+ int selindex;
+ int i;
+
+ for (i = 0, selindex = 0; selindex < n_voltages; i++) {
+ int voltage = r->voltages[i];
+
+ if (!voltage)
+ continue;
+
+ if (selindex == selector)
+ return voltage;
+
+ selindex++;
+ }
+
+ return -EINVAL;
+}
+
+static int ab5500_regulator_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+
+ return r->voltages[0];
+}
+
+static int ab5500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ u8 regval;
+ int ret;
+
+ ret = abx500_get_register_interruptible(ab5500->dev,
+ r->bank, r->reg, &regval);
+ if (ret) {
+ dev_warn(rdev_get_dev(rdev),
+ "failed to get regulator value in register "
+ "%02x\n", r->reg);
+ return ret;
+ }
+
+ regval &= r->voltage_mask;
+ if (regval >= r->desc.n_voltages + r->num_holes)
+ return -EINVAL;
+
+ if (!r->voltages[regval])
+ return -EINVAL;
+
+ return r->voltages[regval];
+}
+
+static int ab5500_get_best_voltage_index(struct ab5500_regulator *r,
+ int min_uV, int max_uV)
+{
+ unsigned n_voltages = r->desc.n_voltages;
+ int bestmatch = INT_MAX;
+ int bestindex = -EINVAL;
+ int selindex;
+ int i;
+
+ /*
+ * Locate the minimum voltage fitting the criteria on
+ * this regulator. The switchable voltages are not
+ * in strict falling order so we need to check them
+ * all for the best match.
+ */
+ for (i = 0, selindex = 0; selindex < n_voltages; i++) {
+ int voltage = r->voltages[i];
+
+ if (!voltage)
+ continue;
+
+ if (voltage <= max_uV &&
+ voltage >= min_uV &&
+ voltage < bestmatch) {
+ bestmatch = voltage;
+ bestindex = i;
+ }
+
+ selindex++;
+ }
+
+ return bestindex;
+}
+
+static int ab5500_regulator_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV,
+ unsigned *selector)
+{
+ struct ab5500_regulators *ab5500 = rdev_get_drvdata(rdev);
+ struct ab5500_regulator *r = ab5500->regulator[rdev_get_id(rdev)];
+ int bestindex;
+
+ bestindex = ab5500_get_best_voltage_index(r, min_uV, max_uV);
+ if (bestindex < 0) {
+ dev_warn(rdev_get_dev(rdev),
+ "requested %d<=x<=%d uV, out of range!\n",
+ min_uV, max_uV);
+ return bestindex;
+ }
+
+ *selector = bestindex;
+
+ return abx500_mask_and_set_register_interruptible(ab5500->dev,
+ r->bank, r->reg, r->voltage_mask, bestindex);
+
+}
+
+static struct regulator_ops ab5500_regulator_variable_ops = {
+ .enable = ab5500_regulator_enable,
+ .disable = ab5500_regulator_disable,
+ .is_enabled = ab5500_regulator_is_enabled,
+ .enable_time = ab5500_regulator_enable_time,
+ .get_voltage = ab5500_regulator_get_voltage,
+ .set_voltage = ab5500_regulator_set_voltage,
+ .list_voltage = ab5500_regulator_list_voltage,
+ .set_mode = ab5500_regulator_set_mode,
+ .get_mode = ab5500_regulator_get_mode,
+ .get_optimum_mode = ab5500_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops ab5500_regulator_fixed_ops = {
+ .enable = ab5500_regulator_enable,
+ .disable = ab5500_regulator_disable,
+ .is_enabled = ab5500_regulator_is_enabled,
+ .enable_time = ab5500_regulator_enable_time,
+ .get_voltage = ab5500_regulator_fixed_get_voltage,
+ .list_voltage = ab5500_regulator_list_voltage,
+ .set_mode = ab5500_regulator_set_mode,
+ .get_mode = ab5500_regulator_get_mode,
+ .get_optimum_mode = ab5500_regulator_get_optimum_mode,
+};
+
+static const int ab5500_ldo_lg_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 0, /* not used */
+ [0x02] = 1500000,
+ [0x03] = 1800000,
+ [0x04] = 0, /* not used */
+ [0x05] = 2500000,
+ [0x06] = 2730000,
+ [0x07] = 2910000,
+};
+
+static const int ab5500_ldo_kh_voltages[] = {
+ [0x00] = 1200000,
+ [0x01] = 1500000,
+ [0x02] = 1800000,
+ [0x03] = 2100000,
+ [0x04] = 2500000,
+ [0x05] = 2750000,
+ [0x06] = 2790000,
+ [0x07] = 2910000,
+};
+
+static const int ab5500_ldo_vdigmic_voltages[] = {
+ [0x00] = 2100000,
+};
+
+static const int ab5500_ldo_sim_voltages[] = {
+ [0x00] = 1875000,
+ [0x01] = 2800000,
+ [0x02] = 2900000,
+};
+
+static const int ab5500_bias2_voltages[] = {
+ [0x00] = 2000000,
+ [0x01] = 2200000,
+};
+
+static const int ab5500_bias1_voltages[] = {
+ [0x00] = 2000000,
+};
+
+static struct ab5500_regulator ab5500_regulators[] = {
+ [AB5500_LDO_L] = {
+ .desc = {
+ .name = "LDO_L",
+ .id = AB5500_LDO_L,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) -
+ 2,
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_L_ST,
+ .voltages = ab5500_ldo_lg_voltages,
+ .num_holes = 2, /* 2 register values unused */
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_G] = {
+ .desc = {
+ .name = "LDO_G",
+ .id = AB5500_LDO_G,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_lg_voltages) -
+ 2,
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_G_ST,
+ .voltages = ab5500_ldo_lg_voltages,
+ .num_holes = 2, /* 2 register values unused */
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_K] = {
+ .desc = {
+ .name = "LDO_K",
+ .id = AB5500_LDO_K,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_K_ST,
+ .voltages = ab5500_ldo_kh_voltages,
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_H] = {
+ .desc = {
+ .name = "LDO_H",
+ .id = AB5500_LDO_H,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_kh_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_H_ST,
+ .voltages = ab5500_ldo_kh_voltages,
+ .enable_time = 400,
+ .load_lp_uA = 20000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_VDIGMIC] = {
+ .desc = {
+ .name = "LDO_VDIGMIC",
+ .id = AB5500_LDO_VDIGMIC,
+ .ops = &ab5500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages =
+ ARRAY_SIZE(ab5500_ldo_vdigmic_voltages),
+ },
+ .bank = AB5500_BANK_STARTUP,
+ .reg = AB5500_LDO_VDIGMIC_ST,
+ .voltages = ab5500_ldo_vdigmic_voltages,
+ .enable_time = 450,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_LDO_SIM] = {
+ .desc = {
+ .name = "LDO_SIM",
+ .id = AB5500_LDO_SIM,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_ldo_sim_voltages),
+ },
+ .bank = AB5500_BANK_SIM_USBSIM,
+ .reg = AB5500_SIM_SUP,
+ .voltages = ab5500_ldo_sim_voltages,
+ .enable_time = 1000,
+ .mode = AB5500_LDO_MODE_FULLPOWER,
+ .update_mask = AB5500_LDO_MODE_MASK,
+ .update_val_normal = AB5500_LDO_MODE_FULLPOWER,
+ .update_val_idle = AB5500_LDO_MODE_LOWPOWER,
+ .voltage_mask = AB5500_LDO_VOLT_MASK,
+ },
+ [AB5500_BIAS2] = {
+ .desc = {
+ .name = "MBIAS2",
+ .id = AB5500_BIAS2,
+ .ops = &ab5500_regulator_variable_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_bias2_voltages),
+ },
+ .bank = AB5500_BANK_AUDIO_HEADSETUSB,
+ .reg = AB5500_MBIAS2,
+ .voltages = ab5500_bias2_voltages,
+ .enable_time = 1000,
+ .mode = AB5500_MBIAS2_ENABLE,
+ .update_mask = AB5500_MBIAS2_MODE_MASK,
+ .update_val_normal = AB5500_MBIAS2_ENABLE,
+ .update_val_idle = AB5500_MBIAS2_ENABLE,
+ .voltage_mask = AB5500_MBIAS2_VOLT_MASK,
+ },
+ [AB5500_BIAS1] = {
+ .desc = {
+ .name = "MBIAS1",
+ .id = AB5500_BIAS1,
+ .ops = &ab5500_regulator_fixed_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ab5500_bias1_voltages),
+ },
+ .bank = AB5500_BANK_AUDIO_HEADSETUSB,
+ .reg = AB5500_MBIAS1,
+ .voltages = ab5500_bias1_voltages,
+ .enable_time = 1000,
+ .mode = AB5500_MBIAS1_ENABLE,
+ .update_mask = AB5500_MBIAS1_MODE_MASK,
+ .update_val_normal = AB5500_MBIAS1_ENABLE,
+ .update_val_idle = AB5500_MBIAS1_ENABLE,
+ },
+};
+
+
+static int __devinit ab5500_regulator_probe(struct platform_device *pdev)
+{
+ struct ab5500_platform_data *ppdata = pdev->dev.parent->platform_data;
+ struct ab5500_regulator_platform_data *pdata = ppdata->regulator;
+ struct ab5500_regulator_data *regdata;
+ struct ab5500_regulators *ab5500;
+ int err = 0;
+ int i;
+
+ if (!pdata || !pdata->regulator)
+ return -EINVAL;
+
+ ab5500 = kzalloc(sizeof(*ab5500), GFP_KERNEL);
+ if (!ab5500)
+ return -ENOMEM;
+
+ ab5500->dev = &pdev->dev;
+ regdata = pdata->data;
+
+ platform_set_drvdata(pdev, ab5500);
+
+ for (i = 0; i < AB5500_NUM_REGULATORS; i++) {
+ struct ab5500_regulator *regulator = &ab5500_regulators[i];
+ struct regulator_dev *rdev;
+
+ if (regdata)
+ regulator->off_is_lowpower = regdata[i].off_is_lowpower;
+
+ ab5500->regulator[i] = regulator;
+
+ rdev = regulator_register(&regulator->desc, &pdev->dev,
+ &pdata->regulator[i], ab5500);
+ if (IS_ERR(rdev)) {
+ err = PTR_ERR(rdev);
+ dev_err(&pdev->dev, "failed to register regulator %s err %d\n",
+ regulator->desc.name, err);
+ goto err_unregister;
+ }
+
+ ab5500->rdev[i] = rdev;
+ }
+
+ return 0;
+
+err_unregister:
+ /* remove the already registered regulators */
+ while (--i >= 0)
+ regulator_unregister(ab5500->rdev[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ab5500);
+
+ return err;
+}
+
+static int __devexit ab5500_regulators_remove(struct platform_device *pdev)
+{
+ struct ab5500_regulators *ab5500 = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < AB5500_NUM_REGULATORS; i++)
+ regulator_unregister(ab5500->rdev[i]);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(ab5500);
+
+ return 0;
+}
+
+static struct platform_driver ab5500_regulator_driver = {
+ .driver = {
+ .name = "ab5500-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_regulator_probe,
+ .remove = __devexit_p(ab5500_regulators_remove),
+};
+
+static __init int ab5500_regulator_init(void)
+{
+ return platform_driver_register(&ab5500_regulator_driver);
+}
+
+static __exit void ab5500_regulator_exit(void)
+{
+ platform_driver_unregister(&ab5500_regulator_driver);
+}
+
+subsys_initcall(ab5500_regulator_init);
+module_exit(ab5500_regulator_exit);
+
+MODULE_DESCRIPTION("AB5500 Regulator Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ab5500-regulator");
diff --git a/drivers/regulator/ab8500-debug.c b/drivers/regulator/ab8500-debug.c
new file mode 100644
index 00000000000..f71cc26c135
--- /dev/null
+++ b/drivers/regulator/ab8500-debug.c
@@ -0,0 +1,2083 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson.
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/regulator/ab8500-debug.h>
+#include <linux/io.h>
+
+#include <mach/db8500-regs.h> /* U8500_BACKUPRAM1_BASE */
+#include <mach/hardware.h>
+
+#include "ab8500-debug.h"
+
+/* board profile address - to determine if suspend-force is default */
+#define BOOT_INFO_BACKUPRAM1 (U8500_BACKUPRAM1_BASE + 0xffc)
+#define BOARD_PROFILE_BACKUPRAM1 (0x3)
+
+/* board profile option */
+#define OPTION_BOARD_VERSION_V5X 50
+
+/* for error prints */
+struct device *dev;
+struct platform_device *pdev;
+
+/* setting for suspend force (disabled by default) */
+static bool setting_suspend_force;
+
+/*
+ * regulator states
+ */
+enum ab8500_regulator_state_id {
+ AB8500_REGULATOR_STATE_INIT,
+ AB8500_REGULATOR_STATE_SUSPEND,
+ AB8500_REGULATOR_STATE_SUSPEND_CORE,
+ AB8500_REGULATOR_STATE_RESUME_CORE,
+ AB8500_REGULATOR_STATE_RESUME,
+ AB8500_REGULATOR_STATE_CURRENT,
+ NUM_REGULATOR_STATE
+};
+
+static const char *regulator_state_name[NUM_REGULATOR_STATE] = {
+ [AB8500_REGULATOR_STATE_INIT] = "init",
+ [AB8500_REGULATOR_STATE_SUSPEND] = "suspend",
+ [AB8500_REGULATOR_STATE_SUSPEND_CORE] = "suspend-core",
+ [AB8500_REGULATOR_STATE_RESUME_CORE] = "resume-core",
+ [AB8500_REGULATOR_STATE_RESUME] = "resume",
+ [AB8500_REGULATOR_STATE_CURRENT] = "current",
+};
+
+/*
+ * regulator register definitions
+ */
+enum ab8500_register_id {
+ AB8500_REGU_NOUSE, /* if not defined */
+ AB8500_REGU_REQUEST_CTRL1,
+ AB8500_REGU_REQUEST_CTRL2,
+ AB8500_REGU_REQUEST_CTRL3,
+ AB8500_REGU_REQUEST_CTRL4,
+ AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ AB8500_REGU_HW_HP_REQ1_VALID1,
+ AB8500_REGU_HW_HP_REQ1_VALID2,
+ AB8500_REGU_HW_HP_REQ2_VALID1,
+ AB8500_REGU_HW_HP_REQ2_VALID2,
+ AB8500_REGU_SW_HP_REQ_VALID1,
+ AB8500_REGU_SW_HP_REQ_VALID2,
+ AB8500_REGU_SYSCLK_REQ1_VALID,
+ AB8500_REGU_SYSCLK_REQ2_VALID,
+ AB9540_REGU_VAUX4_REQ_VALID,
+ AB8500_REGU_MISC1,
+ AB8500_REGU_OTG_SUPPLY_CTRL,
+ AB8500_REGU_VUSB_CTRL,
+ AB8500_REGU_VAUDIO_SUPPLY,
+ AB8500_REGU_CTRL1_VAMIC,
+ AB8500_REGU_ARM_REGU1,
+ AB8500_REGU_ARM_REGU2,
+ AB8500_REGU_VAPE_REGU,
+ AB8500_REGU_VSMPS1_REGU,
+ AB8500_REGU_VSMPS2_REGU,
+ AB8500_REGU_VSMPS3_REGU,
+ AB8500_REGU_VPLL_VANA_REGU,
+ AB8500_REGU_VREF_DDR,
+ AB8500_REGU_EXT_SUPPLY_REGU,
+ AB8500_REGU_VAUX12_REGU,
+ AB8500_REGU_VRF1_VAUX3_REGU,
+ AB8500_REGU_VARM_SEL1,
+ AB8500_REGU_VARM_SEL2,
+ AB8500_REGU_VARM_SEL3,
+ AB8500_REGU_VAPE_SEL1,
+ AB8500_REGU_VAPE_SEL2,
+ AB8500_REGU_VAPE_SEL3,
+ AB9540_REGU_VAUX4_REQ_CTRL,
+ AB9540_REGU_VAUX4_REGU,
+ AB9540_REGU_VAUX4_SEL,
+ AB8500_REGU_VBB_SEL1,
+ AB8500_REGU_VBB_SEL2,
+ AB8500_REGU_VSMPS1_SEL1,
+ AB8500_REGU_VSMPS1_SEL2,
+ AB8500_REGU_VSMPS1_SEL3,
+ AB8500_REGU_VSMPS2_SEL1,
+ AB8500_REGU_VSMPS2_SEL2,
+ AB8500_REGU_VSMPS2_SEL3,
+ AB8500_REGU_VSMPS3_SEL1,
+ AB8500_REGU_VSMPS3_SEL2,
+ AB8500_REGU_VSMPS3_SEL3,
+ AB8500_REGU_VAUX1_SEL,
+ AB8500_REGU_VAUX2_SEL,
+ AB8500_REGU_VRF1_VAUX3_SEL,
+ AB8500_REGU_CTRL_EXT_SUP,
+ AB8500_REGU_VMOD_REGU,
+ AB8500_REGU_VMOD_SEL1,
+ AB8500_REGU_VMOD_SEL2,
+ AB8500_REGU_CTRL_DISCH,
+ AB8500_REGU_CTRL_DISCH2,
+ AB9540_REGU_CTRL_DISCH3,
+ AB8500_OTHER_SYSCLK_CTRL, /* Other */
+ AB8500_OTHER_VSIM_SYSCLK_CTRL, /* Other */
+ AB8500_OTHER_SYSULPCLK_CTRL1, /* Other */
+ NUM_AB8500_REGISTER
+};
+
+struct ab8500_register {
+ const char *name;
+ u8 bank;
+ u8 addr;
+ u8 unavailable; /* Used to flag when AB doesn't support a register */
+};
+
+static struct ab8500_register
+ ab8500_register[NUM_AB8500_REGISTER] = {
+ [AB8500_REGU_REQUEST_CTRL1] = {
+ .name = "ReguRequestCtrl1",
+ .bank = 0x03,
+ .addr = 0x03,
+ },
+ [AB8500_REGU_REQUEST_CTRL2] = {
+ .name = "ReguRequestCtrl2",
+ .bank = 0x03,
+ .addr = 0x04,
+ },
+ [AB8500_REGU_REQUEST_CTRL3] = {
+ .name = "ReguRequestCtrl3",
+ .bank = 0x03,
+ .addr = 0x05,
+ },
+ [AB8500_REGU_REQUEST_CTRL4] = {
+ .name = "ReguRequestCtrl4",
+ .bank = 0x03,
+ .addr = 0x06,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_HP_VALID1] = {
+ .name = "ReguSysClkReq1HPValid",
+ .bank = 0x03,
+ .addr = 0x07,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_HP_VALID2] = {
+ .name = "ReguSysClkReq1HPValid2",
+ .bank = 0x03,
+ .addr = 0x08,
+ },
+ [AB8500_REGU_HW_HP_REQ1_VALID1] = {
+ .name = "ReguHwHPReq1Valid1",
+ .bank = 0x03,
+ .addr = 0x09,
+ },
+ [AB8500_REGU_HW_HP_REQ1_VALID2] = {
+ .name = "ReguHwHPReq1Valid2",
+ .bank = 0x03,
+ .addr = 0x0a,
+ },
+ [AB8500_REGU_HW_HP_REQ2_VALID1] = {
+ .name = "ReguHwHPReq2Valid1",
+ .bank = 0x03,
+ .addr = 0x0b,
+ },
+ [AB8500_REGU_HW_HP_REQ2_VALID2] = {
+ .name = "ReguHwHPReq2Valid2",
+ .bank = 0x03,
+ .addr = 0x0c,
+ },
+ [AB8500_REGU_SW_HP_REQ_VALID1] = {
+ .name = "ReguSwHPReqValid1",
+ .bank = 0x03,
+ .addr = 0x0d,
+ },
+ [AB8500_REGU_SW_HP_REQ_VALID2] = {
+ .name = "ReguSwHPReqValid2",
+ .bank = 0x03,
+ .addr = 0x0e,
+ },
+ [AB8500_REGU_SYSCLK_REQ1_VALID] = {
+ .name = "ReguSysClkReqValid1",
+ .bank = 0x03,
+ .addr = 0x0f,
+ },
+ [AB8500_REGU_SYSCLK_REQ2_VALID] = {
+ .name = "ReguSysClkReqValid2",
+ .bank = 0x03,
+ .addr = 0x10,
+ },
+ [AB9540_REGU_VAUX4_REQ_VALID] = {
+ .name = "ReguVaux4ReqValid",
+ .bank = 0x03,
+ .addr = 0x11,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB8500_REGU_MISC1] = {
+ .name = "ReguMisc1",
+ .bank = 0x03,
+ .addr = 0x80,
+ },
+ [AB8500_REGU_OTG_SUPPLY_CTRL] = {
+ .name = "OTGSupplyCtrl",
+ .bank = 0x03,
+ .addr = 0x81,
+ },
+ [AB8500_REGU_VUSB_CTRL] = {
+ .name = "VusbCtrl",
+ .bank = 0x03,
+ .addr = 0x82,
+ },
+ [AB8500_REGU_VAUDIO_SUPPLY] = {
+ .name = "VaudioSupply",
+ .bank = 0x03,
+ .addr = 0x83,
+ },
+ [AB8500_REGU_CTRL1_VAMIC] = {
+ .name = "ReguCtrl1VAmic",
+ .bank = 0x03,
+ .addr = 0x84,
+ },
+ [AB8500_REGU_ARM_REGU1] = {
+ .name = "ArmRegu1",
+ .bank = 0x04,
+ .addr = 0x00,
+ },
+ [AB8500_REGU_ARM_REGU2] = {
+ .name = "ArmRegu2",
+ .bank = 0x04,
+ .addr = 0x01,
+ },
+ [AB8500_REGU_VAPE_REGU] = {
+ .name = "VapeRegu",
+ .bank = 0x04,
+ .addr = 0x02,
+ },
+ [AB8500_REGU_VSMPS1_REGU] = {
+ .name = "Vsmps1Regu",
+ .bank = 0x04,
+ .addr = 0x03,
+ },
+ [AB8500_REGU_VSMPS2_REGU] = {
+ .name = "Vsmps2Regu",
+ .bank = 0x04,
+ .addr = 0x04,
+ },
+ [AB8500_REGU_VSMPS3_REGU] = {
+ .name = "Vsmps3Regu",
+ .bank = 0x04,
+ .addr = 0x05,
+ },
+ [AB8500_REGU_VPLL_VANA_REGU] = {
+ .name = "VpllVanaRegu",
+ .bank = 0x04,
+ .addr = 0x06,
+ },
+ [AB8500_REGU_VREF_DDR] = {
+ .name = "VrefDDR",
+ .bank = 0x04,
+ .addr = 0x07,
+ },
+ [AB8500_REGU_EXT_SUPPLY_REGU] = {
+ .name = "ExtSupplyRegu",
+ .bank = 0x04,
+ .addr = 0x08,
+ },
+ [AB8500_REGU_VAUX12_REGU] = {
+ .name = "Vaux12Regu",
+ .bank = 0x04,
+ .addr = 0x09,
+ },
+ [AB8500_REGU_VRF1_VAUX3_REGU] = {
+ .name = "VRF1Vaux3Regu",
+ .bank = 0x04,
+ .addr = 0x0a,
+ },
+ [AB8500_REGU_VARM_SEL1] = {
+ .name = "VarmSel1",
+ .bank = 0x04,
+ .addr = 0x0b,
+ },
+ [AB8500_REGU_VARM_SEL2] = {
+ .name = "VarmSel2",
+ .bank = 0x04,
+ .addr = 0x0c,
+ },
+ [AB8500_REGU_VARM_SEL3] = {
+ .name = "VarmSel3",
+ .bank = 0x04,
+ .addr = 0x0d,
+ },
+ [AB8500_REGU_VAPE_SEL1] = {
+ .name = "VapeSel1",
+ .bank = 0x04,
+ .addr = 0x0e,
+ },
+ [AB8500_REGU_VAPE_SEL2] = {
+ .name = "VapeSel2",
+ .bank = 0x04,
+ .addr = 0x0f,
+ },
+ [AB8500_REGU_VAPE_SEL3] = {
+ .name = "VapeSel3",
+ .bank = 0x04,
+ .addr = 0x10,
+ },
+ [AB9540_REGU_VAUX4_REQ_CTRL] = {
+ .name = "Vaux4ReqCtrl",
+ .bank = 0x04,
+ .addr = 0x2d,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB9540_REGU_VAUX4_REGU] = {
+ .name = "Vaux4Regu",
+ .bank = 0x04,
+ .addr = 0x2e,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB9540_REGU_VAUX4_SEL] = {
+ .name = "Vaux4Sel",
+ .bank = 0x04,
+ .addr = 0x2f,
+ .unavailable = true, /* ab9540 register */
+ },
+ [AB8500_REGU_VBB_SEL1] = {
+ .name = "VBBSel1",
+ .bank = 0x04,
+ .addr = 0x11,
+ },
+ [AB8500_REGU_VBB_SEL2] = {
+ .name = "VBBSel2",
+ .bank = 0x04,
+ .addr = 0x12,
+ },
+ [AB8500_REGU_VSMPS1_SEL1] = {
+ .name = "Vsmps1Sel1",
+ .bank = 0x04,
+ .addr = 0x13,
+ },
+ [AB8500_REGU_VSMPS1_SEL2] = {
+ .name = "Vsmps1Sel2",
+ .bank = 0x04,
+ .addr = 0x14,
+ },
+ [AB8500_REGU_VSMPS1_SEL3] = {
+ .name = "Vsmps1Sel3",
+ .bank = 0x04,
+ .addr = 0x15,
+ },
+ [AB8500_REGU_VSMPS2_SEL1] = {
+ .name = "Vsmps2Sel1",
+ .bank = 0x04,
+ .addr = 0x17,
+ },
+ [AB8500_REGU_VSMPS2_SEL2] = {
+ .name = "Vsmps2Sel2",
+ .bank = 0x04,
+ .addr = 0x18,
+ },
+ [AB8500_REGU_VSMPS2_SEL3] = {
+ .name = "Vsmps2Sel3",
+ .bank = 0x04,
+ .addr = 0x19,
+ },
+ [AB8500_REGU_VSMPS3_SEL1] = {
+ .name = "Vsmps3Sel1",
+ .bank = 0x04,
+ .addr = 0x1b,
+ },
+ [AB8500_REGU_VSMPS3_SEL2] = {
+ .name = "Vsmps3Sel2",
+ .bank = 0x04,
+ .addr = 0x1c,
+ },
+ [AB8500_REGU_VSMPS3_SEL3] = {
+ .name = "Vsmps3Sel3",
+ .bank = 0x04,
+ .addr = 0x1d,
+ },
+ [AB8500_REGU_VAUX1_SEL] = {
+ .name = "Vaux1Sel",
+ .bank = 0x04,
+ .addr = 0x1f,
+ },
+ [AB8500_REGU_VAUX2_SEL] = {
+ .name = "Vaux2Sel",
+ .bank = 0x04,
+ .addr = 0x20,
+ },
+ [AB8500_REGU_VRF1_VAUX3_SEL] = {
+ .name = "VRF1Vaux3Sel",
+ .bank = 0x04,
+ .addr = 0x21,
+ },
+ [AB8500_REGU_CTRL_EXT_SUP] = {
+ .name = "ReguCtrlExtSup",
+ .bank = 0x04,
+ .addr = 0x22,
+ },
+ [AB8500_REGU_VMOD_REGU] = {
+ .name = "VmodRegu",
+ .bank = 0x04,
+ .addr = 0x40,
+ },
+ [AB8500_REGU_VMOD_SEL1] = {
+ .name = "VmodSel1",
+ .bank = 0x04,
+ .addr = 0x41,
+ },
+ [AB8500_REGU_VMOD_SEL2] = {
+ .name = "VmodSel2",
+ .bank = 0x04,
+ .addr = 0x42,
+ },
+ [AB8500_REGU_CTRL_DISCH] = {
+ .name = "ReguCtrlDisch",
+ .bank = 0x04,
+ .addr = 0x43,
+ },
+ [AB8500_REGU_CTRL_DISCH2] = {
+ .name = "ReguCtrlDisch2",
+ .bank = 0x04,
+ .addr = 0x44,
+ },
+ [AB9540_REGU_CTRL_DISCH3] = {
+ .name = "ReguCtrlDisch3",
+ .bank = 0x04,
+ .addr = 0x48,
+ .unavailable = true, /* ab9540 register */
+ },
+ /* Outside regulator banks */
+ [AB8500_OTHER_SYSCLK_CTRL] = {
+ .name = "SysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x0c,
+ },
+ [AB8500_OTHER_VSIM_SYSCLK_CTRL] = {
+ .name = "VsimSysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x33,
+ },
+ [AB8500_OTHER_SYSULPCLK_CTRL1] = {
+ .name = "SysUlpClkCtrl1",
+ .bank = 0x02,
+ .addr = 0x0b,
+ },
+};
+
+struct ab9540_register_update {
+ /* Identity of register to be updated */
+ u8 bank;
+ u8 addr;
+ /* New value for unavailable flag */
+ u8 unavailable;
+};
+
+static const struct ab9540_register_update ab9540_update[] = {
+ /* AB8500 register which is unavailable to AB9540 */
+ /* AB8500_REGU_VREF_DDR */
+ {
+ .bank = 0x04,
+ .addr = 0x07,
+ .unavailable = true,
+ },
+
+ /* Registers which were not available to AB8500 but are on the
+ * AB9540. */
+ /* AB9540_REGU_VAUX4_REQ_VALID */
+ {
+ .bank = 0x03,
+ .addr = 0x11,
+ },
+ /* AB9540_REGU_VAUX4_REQ_CTRL */
+ {
+ .bank = 0x04,
+ .addr = 0x2d,
+ },
+ /* AB9540_REGU_VAUX4_REGU */
+ {
+ .bank = 0x04,
+ .addr = 0x2e,
+ },
+ /* AB9540_REGU_VAUX4_SEL */
+ {
+ .bank = 0x04,
+ .addr = 0x2f,
+ },
+ /* AB9540_REGU_CTRL_DISCH3 */
+ {
+ .bank = 0x04,
+ .addr = 0x48,
+ },
+};
+
+static void ab9540_registers_update(void)
+{
+ int i;
+ int j;
+
+ for (i = 0; i < NUM_AB8500_REGISTER; i++)
+ for (j = 0; j < ARRAY_SIZE(ab9540_update); j++)
+ if (ab8500_register[i].bank == ab9540_update[j].bank &&
+ ab8500_register[i].addr == ab9540_update[j].addr) {
+ ab8500_register[i].unavailable =
+ ab9540_update[j].unavailable;
+ break;
+ }
+}
+
+static u8 ab8500_register_state[NUM_REGULATOR_STATE][NUM_AB8500_REGISTER];
+static bool ab8500_register_state_saved[NUM_REGULATOR_STATE];
+static bool ab8500_register_state_save = true;
+
+static int ab8500_regulator_record_state(int state)
+{
+ u8 val;
+ int i;
+ int ret;
+
+ /* check arguments */
+ if ((state > NUM_REGULATOR_STATE) || (state < 0)) {
+ dev_err(dev, "Wrong state specified\n");
+ return -EINVAL;
+ }
+
+ /* record */
+ if (!ab8500_register_state_save)
+ goto exit;
+
+ ab8500_register_state_saved[state] = true;
+
+ for (i = 1; i < NUM_AB8500_REGISTER; i++) {
+ if (ab8500_register[i].unavailable)
+ continue;
+
+ ret = abx500_get_register_interruptible(dev,
+ ab8500_register[i].bank,
+ ab8500_register[i].addr,
+ &val);
+ if (ret < 0) {
+ dev_err(dev, "abx500_get_reg fail %d, %d\n",
+ ret, __LINE__);
+ return -EINVAL;
+ }
+
+ ab8500_register_state[state][i] = val;
+ }
+exit:
+ return 0;
+}
+
+/*
+ * regulator register dump
+ */
+static int ab8500_regulator_dump_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int state, reg_id, i;
+ int err;
+
+ /* record current state */
+ ab8500_regulator_record_state(AB8500_REGULATOR_STATE_CURRENT);
+
+ /* print dump header */
+ err = seq_printf(s, "ab8500-regulator dump:\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print states */
+ for (state = NUM_REGULATOR_STATE - 1; state >= 0; state--) {
+ if (ab8500_register_state_saved[state])
+ err = seq_printf(s, "%16s saved -------",
+ regulator_state_name[state]);
+ else
+ err = seq_printf(s, "%12s not saved -------",
+ regulator_state_name[state]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ for (i = 0; i < NUM_REGULATOR_STATE; i++) {
+ if (i < state)
+ err = seq_printf(s, "-----");
+ else if (i == state)
+ err = seq_printf(s, "----+");
+ else
+ err = seq_printf(s, " |");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n",
+ __LINE__);
+ }
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ }
+
+ /* print labels */
+ err = seq_printf(s, "\n addr\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ /* dump registers */
+ for (reg_id = 1; reg_id < NUM_AB8500_REGISTER; reg_id++) {
+ if (ab8500_register[reg_id].unavailable)
+ continue;
+
+ err = seq_printf(s, "%22s 0x%02x%02x:",
+ ab8500_register[reg_id].name,
+ ab8500_register[reg_id].bank,
+ ab8500_register[reg_id].addr);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+
+ for (state = 0; state < NUM_REGULATOR_STATE; state++) {
+ err = seq_printf(s, " 0x%02x",
+ ab8500_register_state[state][reg_id]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+ }
+
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ reg_id, __LINE__);
+ }
+
+ return 0;
+}
+
+static int ab8500_regulator_dump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_regulator_dump_print, inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_dump_fops = {
+ .open = ab8500_regulator_dump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * regulator_voltage
+ */
+struct regulator_volt {
+ u8 value;
+ int volt;
+};
+
+struct regulator_volt_range {
+ struct regulator_volt start;
+ struct regulator_volt step;
+ struct regulator_volt end;
+};
+
+/*
+ * ab8500_regulator
+ * @name
+ * @update_regid
+ * @update_mask
+ * @update_val[4] {off, on, hw, lp}
+ * @hw_mode_regid
+ * @hw_mode_mask
+ * @hw_mode_val[4] {hp/lp, hp/off, hp, hp}
+ * @hw_valid_regid[4] {sysclkreq1, hw1, hw2, sw}
+ * @hw_valid_mask[4] {sysclkreq1, hw1, hw2, sw}
+ * @vsel_sel_regid
+ * @vsel_sel_mask
+ * @vsel_val[333] {sel1, sel2, sel3, sel3}
+ * @vsel_regid
+ * @vsel_mask
+ * @vsel_range
+ * @vsel_range_len
+ * @unavailable {true/false depending on whether AB supports the regulator}
+ */
+struct ab8500_regulator {
+ const char *name;
+ int update_regid;
+ u8 update_mask;
+ u8 update_val[4];
+ int hw_mode_regid;
+ u8 hw_mode_mask;
+ u8 hw_mode_val[4];
+ int hw_valid_regid[4];
+ u8 hw_valid_mask[4];
+ int vsel_sel_regid;
+ u8 vsel_sel_mask;
+ u8 vsel_sel_val[4];
+ int vsel_regid[3];
+ u8 vsel_mask[3];
+ struct regulator_volt_range const *vsel_range[3];
+ int vsel_range_len[3];
+ u8 unavailable;
+};
+
+static const char *update_val_name[] = {
+ "off",
+ "on ",
+ "hw ",
+ "lp ",
+ " - " /* undefined value */
+};
+
+static const char *hw_mode_val_name[] = {
+ "hp/lp ",
+ "hp/off",
+ "hp ",
+ "hp ",
+ "-/- ", /* undefined value */
+};
+
+/* voltage selection */
+/* AB8500 device - Varm_vsel in 12.5mV steps */
+#define AB8500_VARM_VSEL_MASK 0x3f
+static const struct regulator_volt_range ab8500_varm_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1362500} },
+ { {0x36, 1362500}, {0x01, 0}, {0x3f, 1362500} },
+};
+
+/* AB9540 device - Varm_vsel in 6.25mV steps */
+#define AB9540_VARM_VSEL_MASK 0x7f
+static const struct regulator_volt_range ab9540_varm_vsel[] = {
+ { {0x00, 600000}, {0x01, 6250}, {0x7f, 1393750} },
+};
+
+static const struct regulator_volt_range vape_vmod_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1362500} },
+ { {0x36, 1362500}, {0x01, 0}, {0x3f, 1362500} },
+};
+
+/* AB8500 device - Vbbp_vsel and Vbbn_sel in 100mV steps */
+static const struct regulator_volt_range ab8500_vbbp_vsel[] = {
+ { {0x00, 0}, {0x10, 100000}, {0x40, 400000} },
+ { {0x50, 400000}, {0x10, 0}, {0x70, 400000} },
+ { {0x80, -400000}, {0x10, 0}, {0xb0, -400000} },
+ { {0xc0, -400000}, {0x10, 100000}, {0xf0, -100000} },
+};
+
+static const struct regulator_volt_range ab8500_vbbn_vsel[] = {
+ { {0x00, 0}, {0x01, -100000}, {0x04, -400000} },
+ { {0x05, -400000}, {0x01, 0}, {0x07, -400000} },
+ { {0x08, 0}, {0x01, 100000}, {0x0c, 400000} },
+ { {0x0d, 400000}, {0x01, 0}, {0x0f, 400000} },
+};
+
+/* AB9540 device - Vbbp_vsel and Vbbn_sel in 50mV steps */
+static const struct regulator_volt_range ab9540_vbbp_vsel[] = {
+ { {0x00, 0}, {0x10, -50000}, {0x70, -350000} },
+ { {0x80, 50000}, {0x10, 50000}, {0xf0, 400000} },
+};
+
+static const struct regulator_volt_range ab9540_vbbn_vsel[] = {
+ { {0x00, 0}, {0x01, -50000}, {0x07, -350000} },
+ { {0x08, 50000}, {0x01, 50000}, {0x0f, 400000} },
+};
+
+static const struct regulator_volt_range vsmps1_vsel[] = {
+ { {0x00, 1100000}, {0x01, 0}, {0x1f, 1100000} },
+ { {0x20, 1100000}, {0x01, 12500}, {0x30, 1300000} },
+ { {0x31, 1300000}, {0x01, 0}, {0x3f, 1300000} },
+};
+
+static const struct regulator_volt_range vsmps2_vsel[] = {
+ { {0x00, 1800000}, {0x01, 0}, {0x38, 1800000} },
+ { {0x39, 1800000}, {0x01, 12500}, {0x7f, 1875000} },
+};
+
+static const struct regulator_volt_range vsmps3_vsel[] = {
+ { {0x00, 700000}, {0x01, 12500}, {0x35, 1363500} },
+ { {0x36, 1363500}, {0x01, 0}, {0x7f, 1363500} },
+};
+
+/* for Vaux1, Vaux2 and Vaux4 */
+static const struct regulator_volt_range vauxn_vsel[] = {
+ { {0x00, 1100000}, {0x01, 100000}, {0x04, 1500000} },
+ { {0x05, 1800000}, {0x01, 50000}, {0x07, 1900000} },
+ { {0x08, 2500000}, {0x01, 0}, {0x08, 2500000} },
+ { {0x09, 2650000}, {0x01, 50000}, {0x0c, 2800000} },
+ { {0x0d, 2900000}, {0x01, 100000}, {0x0e, 3000000} },
+ { {0x0f, 3300000}, {0x01, 0}, {0x0f, 3300000} },
+};
+
+static const struct regulator_volt_range vaux3_vsel[] = {
+ { {0x00, 1200000}, {0x01, 300000}, {0x03, 2100000} },
+ { {0x04, 2500000}, {0x01, 250000}, {0x05, 2750000} },
+ { {0x06, 2790000}, {0x01, 0}, {0x06, 2790000} },
+ { {0x07, 2910000}, {0x01, 0}, {0x07, 2910000} },
+};
+
+static const struct regulator_volt_range vrf1_vsel[] = {
+ { {0x00, 1800000}, {0x10, 200000}, {0x10, 2000000} },
+ { {0x20, 2150000}, {0x10, 0}, {0x20, 2150000} },
+ { {0x30, 2500000}, {0x10, 0}, {0x30, 2500000} },
+};
+
+static const struct regulator_volt_range vintcore12_vsel[] = {
+ { {0x00, 1200000}, {0x08, 25000}, {0x30, 1350000} },
+ { {0x38, 1350000}, {0x01, 0}, {0x38, 1350000} },
+};
+
+/* regulators */
+static struct ab8500_regulator ab8500_regulator[AB8500_NUM_REGULATORS] = {
+ [AB8500_VARM] = {
+ .name = "Varm",
+ .update_regid = AB8500_REGU_ARM_REGU1,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x02,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VARM_SEL1,
+ .vsel_mask[0] = AB8500_VARM_VSEL_MASK,
+ .vsel_range[0] = ab8500_varm_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(ab8500_varm_vsel),
+ .vsel_regid[1] = AB8500_REGU_VARM_SEL2,
+ .vsel_mask[1] = AB8500_VARM_VSEL_MASK,
+ .vsel_range[1] = ab8500_varm_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(ab8500_varm_vsel),
+ .vsel_regid[2] = AB8500_REGU_VARM_SEL3,
+ .vsel_mask[2] = AB8500_VARM_VSEL_MASK,
+ .vsel_range[2] = ab8500_varm_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(ab8500_varm_vsel),
+ },
+ [AB8500_VBBP] = {
+ .name = "Vbbp",
+ .update_regid = AB8500_REGU_ARM_REGU2,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x00},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x10,
+ .vsel_sel_val = {0x00, 0x10, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VBB_SEL1,
+ .vsel_mask[0] = 0xf0,
+ .vsel_range[0] = ab8500_vbbp_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(ab8500_vbbp_vsel),
+ .vsel_regid[1] = AB8500_REGU_VBB_SEL2,
+ .vsel_mask[1] = 0xf0,
+ .vsel_range[1] = ab8500_vbbp_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(ab8500_vbbp_vsel),
+ },
+ [AB8500_VBBN] = {
+ .name = "Vbbn",
+ .update_regid = AB8500_REGU_ARM_REGU2,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x00},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_ARM_REGU1,
+ .vsel_sel_mask = 0x20,
+ .vsel_sel_val = {0x00, 0x20, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VBB_SEL1,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = ab8500_vbbn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(ab8500_vbbn_vsel),
+ .vsel_regid[1] = AB8500_REGU_VBB_SEL2,
+ .vsel_mask[1] = 0x0f,
+ .vsel_range[1] = ab8500_vbbn_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(ab8500_vbbn_vsel),
+ },
+ [AB8500_VAPE] = {
+ .name = "Vape",
+ .update_regid = AB8500_REGU_VAPE_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_sel_regid = AB8500_REGU_VAPE_REGU,
+ .vsel_sel_mask = 0x24,
+ .vsel_sel_val = {0x00, 0x04, 0x20, 0x24},
+ .vsel_regid[0] = AB8500_REGU_VAPE_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VAPE_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vape_vmod_vsel),
+ .vsel_regid[2] = AB8500_REGU_VAPE_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vape_vmod_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vape_vmod_vsel),
+ },
+ [AB8500_VSMPS1] = {
+ .name = "Vsmps1",
+ .update_regid = AB8500_REGU_VSMPS1_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x01,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x01,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x04,
+ .vsel_sel_regid = AB8500_REGU_VSMPS1_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS1_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vsmps1_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps1_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS1_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vsmps1_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps1_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS1_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vsmps1_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps1_vsel),
+ },
+ [AB8500_VSMPS2] = {
+ .name = "Vsmps2",
+ .update_regid = AB8500_REGU_VSMPS2_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL1,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x02,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x02,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x08,
+ .vsel_sel_regid = AB8500_REGU_VSMPS2_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS2_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vsmps2_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps2_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS2_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vsmps2_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps2_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS2_SEL3,
+ .vsel_mask[2] = 0x3f,
+ .vsel_range[2] = vsmps2_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps2_vsel),
+ },
+ [AB8500_VSMPS3] = {
+ .name = "Vsmps3",
+ .update_regid = AB8500_REGU_VSMPS3_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x04,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x04,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x10,
+ .vsel_sel_regid = AB8500_REGU_VSMPS3_REGU,
+ .vsel_sel_mask = 0x0c,
+ .vsel_sel_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VSMPS3_SEL1,
+ .vsel_mask[0] = 0x7f,
+ .vsel_range[0] = vsmps3_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vsmps3_vsel),
+ .vsel_regid[1] = AB8500_REGU_VSMPS3_SEL2,
+ .vsel_mask[1] = 0x7f,
+ .vsel_range[1] = vsmps3_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vsmps3_vsel),
+ .vsel_regid[2] = AB8500_REGU_VSMPS3_SEL3,
+ .vsel_mask[2] = 0x7f,
+ .vsel_range[2] = vsmps3_vsel,
+ .vsel_range_len[2] = ARRAY_SIZE(vsmps3_vsel),
+ },
+ [AB8500_VPLL] = {
+ .name = "Vpll",
+ .update_regid = AB8500_REGU_VPLL_VANA_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x10,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x10,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x10,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x40,
+ },
+ [AB8500_VREFDDR] = {
+ .name = "VrefDDR",
+ .update_regid = AB8500_REGU_VREF_DDR,
+ .update_mask = 0x01,
+ .update_val = {0x00, 0x01, 0x00, 0x00},
+ },
+ [AB8500_VMOD] = {
+ .name = "Vmod",
+ .update_regid = AB8500_REGU_VMOD_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_VMOD_REGU,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x08,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x08,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x20,
+ .vsel_sel_regid = AB8500_REGU_VMOD_REGU,
+ .vsel_sel_mask = 0x04,
+ .vsel_sel_val = {0x00, 0x04, 0x00, 0x00},
+ .vsel_regid[0] = AB8500_REGU_VMOD_SEL1,
+ .vsel_mask[0] = 0x3f,
+ .vsel_range[0] = vape_vmod_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vape_vmod_vsel),
+ .vsel_regid[1] = AB8500_REGU_VMOD_SEL2,
+ .vsel_mask[1] = 0x3f,
+ .vsel_range[1] = vape_vmod_vsel,
+ .vsel_range_len[1] = ARRAY_SIZE(vape_vmod_vsel),
+ },
+ [AB8500_VEXTSUPPLY1] = {
+ .name = "Vextsupply1",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x10,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x01,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x01,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x04,
+ },
+ [AB8500_VEXTSUPPLY2] = {
+ .name = "VextSupply2",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x20,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x02,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x08,
+ },
+ [AB8500_VEXTSUPPLY3] = {
+ .name = "VextSupply3",
+ .update_regid = AB8500_REGU_EXT_SUPPLY_REGU,
+ .update_mask = 0x30,
+ .update_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x0c,
+ .hw_mode_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID2,
+ .hw_valid_mask[0] = 0x40,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID2,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID2,
+ .hw_valid_mask[2] = 0x04,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x10,
+ },
+ [AB8500_VRF1] = {
+ .name = "Vrf1",
+ .update_regid = AB8500_REGU_VRF1_VAUX3_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .vsel_regid[0] = AB8500_REGU_VRF1_VAUX3_SEL,
+ .vsel_mask[0] = 0x30,
+ .vsel_range[0] = vrf1_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vrf1_vsel),
+ },
+ [AB8500_VANA] = {
+ .name = "Vana",
+ .update_regid = AB8500_REGU_VPLL_VANA_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL2,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x08,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x08,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x20,
+ },
+ [AB8500_VAUX1] = {
+ .name = "Vaux1",
+ .update_regid = AB8500_REGU_VAUX12_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0x30,
+ .hw_mode_val = {0x00, 0x10, 0x20, 0x30},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x20,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x20,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x20,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID1,
+ .hw_valid_mask[3] = 0x80,
+ .vsel_regid[0] = AB8500_REGU_VAUX1_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vauxn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vauxn_vsel),
+ },
+ [AB8500_VAUX2] = {
+ .name = "Vaux2",
+ .update_regid = AB8500_REGU_VAUX12_REGU,
+ .update_mask = 0x0c,
+ .update_val = {0x00, 0x04, 0x08, 0x0c},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL3,
+ .hw_mode_mask = 0xc0,
+ .hw_mode_val = {0x00, 0x40, 0x80, 0xc0},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x40,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x40,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x40,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_regid[0] = AB8500_REGU_VAUX2_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vauxn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vauxn_vsel),
+ },
+ [AB8500_VAUX3] = {
+ .name = "Vaux3",
+ .update_regid = AB8500_REGU_VRF1_VAUX3_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB8500_REGU_REQUEST_CTRL4,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB8500_REGU_SYSCLK_REQ1_HP_VALID1,
+ .hw_valid_mask[0] = 0x80,
+ .hw_valid_regid[1] = AB8500_REGU_HW_HP_REQ1_VALID1,
+ .hw_valid_mask[1] = 0x80,
+ .hw_valid_regid[2] = AB8500_REGU_HW_HP_REQ2_VALID1,
+ .hw_valid_mask[2] = 0x80,
+ .hw_valid_regid[3] = AB8500_REGU_SW_HP_REQ_VALID2,
+ .hw_valid_mask[3] = 0x02,
+ .vsel_regid[0] = AB8500_REGU_VRF1_VAUX3_SEL,
+ .vsel_mask[0] = 0x07,
+ .vsel_range[0] = vaux3_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vaux3_vsel),
+ },
+ [AB9540_VAUX4] = {
+ .name = "Vaux4",
+ .update_regid = AB9540_REGU_VAUX4_REGU,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_mode_regid = AB9540_REGU_VAUX4_REQ_CTRL,
+ .hw_mode_mask = 0x03,
+ .hw_mode_val = {0x00, 0x01, 0x02, 0x03},
+ .hw_valid_regid[0] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[0] = 0x08,
+ .hw_valid_regid[1] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[1] = 0x04,
+ .hw_valid_regid[2] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[2] = 0x02,
+ .hw_valid_regid[3] = AB9540_REGU_VAUX4_REQ_VALID,
+ .hw_valid_mask[3] = 0x01,
+ .vsel_regid[0] = AB9540_REGU_VAUX4_SEL,
+ .vsel_mask[0] = 0x0f,
+ .vsel_range[0] = vauxn_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vauxn_vsel),
+ .unavailable = true, /* AB9540 regulator */
+ },
+ [AB8500_VINTCORE] = {
+ .name = "VintCore12",
+ .update_regid = AB8500_REGU_MISC1,
+ .update_mask = 0x44,
+ .update_val = {0x00, 0x04, 0x00, 0x44},
+ .vsel_regid[0] = AB8500_REGU_MISC1,
+ .vsel_mask[0] = 0x38,
+ .vsel_range[0] = vintcore12_vsel,
+ .vsel_range_len[0] = ARRAY_SIZE(vintcore12_vsel),
+ },
+ [AB8500_VTVOUT] = {
+ .name = "VTVout",
+ .update_regid = AB8500_REGU_MISC1,
+ .update_mask = 0x82,
+ .update_val = {0x00, 0x02, 0x00, 0x82},
+ },
+ [AB8500_VAUDIO] = {
+ .name = "Vaudio",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x02,
+ .update_val = {0x00, 0x02, 0x00, 0x00},
+ },
+ [AB8500_VANAMIC1] = {
+ .name = "Vanamic1",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x08,
+ .update_val = {0x00, 0x08, 0x00, 0x00},
+ },
+ [AB8500_VANAMIC2] = {
+ .name = "Vanamic2",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x10,
+ .update_val = {0x00, 0x10, 0x00, 0x00},
+ },
+ [AB8500_VDMIC] = {
+ .name = "Vdmic",
+ .update_regid = AB8500_REGU_VAUDIO_SUPPLY,
+ .update_mask = 0x04,
+ .update_val = {0x00, 0x04, 0x00, 0x00},
+ },
+ [AB8500_VUSB] = {
+ .name = "Vusb",
+ .update_regid = AB8500_REGU_VUSB_CTRL,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x00, 0x03},
+ },
+ [AB8500_VOTG] = {
+ .name = "VOTG",
+ .update_regid = AB8500_REGU_OTG_SUPPLY_CTRL,
+ .update_mask = 0x03,
+ .update_val = {0x00, 0x01, 0x00, 0x03},
+ },
+ [AB8500_VBUSBIS] = {
+ .name = "Vbusbis",
+ .update_regid = AB8500_REGU_OTG_SUPPLY_CTRL,
+ .update_mask = 0x08,
+ .update_val = {0x00, 0x08, 0x00, 0x00},
+ },
+};
+
+static void ab9540_regulators_update(void)
+{
+ /* Update unavailable regulators */
+ ab8500_regulator[AB8500_VREFDDR].unavailable = true;
+ ab8500_regulator[AB9540_VAUX4].unavailable = false;
+
+ /* Update regulator characteristics for AB9540 */
+ ab8500_regulator[AB8500_VARM].vsel_mask[0] = AB9540_VARM_VSEL_MASK;
+ ab8500_regulator[AB8500_VARM].vsel_range[0] = ab9540_varm_vsel;
+ ab8500_regulator[AB8500_VARM].vsel_range_len[0] =
+ ARRAY_SIZE(ab9540_varm_vsel);
+ ab8500_regulator[AB8500_VARM].vsel_mask[1] = AB9540_VARM_VSEL_MASK;
+ ab8500_regulator[AB8500_VARM].vsel_range[1] = ab9540_varm_vsel;
+ ab8500_regulator[AB8500_VARM].vsel_range_len[1] =
+ ARRAY_SIZE(ab9540_varm_vsel);
+ ab8500_regulator[AB8500_VARM].vsel_mask[2] = AB9540_VARM_VSEL_MASK;
+ ab8500_regulator[AB8500_VARM].vsel_range[2] = ab9540_varm_vsel;
+ ab8500_regulator[AB8500_VARM].vsel_range_len[2] =
+ ARRAY_SIZE(ab9540_varm_vsel);
+
+ ab8500_regulator[AB8500_VBBP].vsel_range[0] = ab9540_vbbp_vsel;
+ ab8500_regulator[AB8500_VBBP].vsel_range_len[0] =
+ ARRAY_SIZE(ab9540_vbbp_vsel);
+ ab8500_regulator[AB8500_VBBP].vsel_range[1] = ab9540_vbbp_vsel;
+ ab8500_regulator[AB8500_VBBP].vsel_range_len[1] =
+ ARRAY_SIZE(ab9540_vbbp_vsel);
+
+ ab8500_regulator[AB8500_VBBN].vsel_range[0] = ab9540_vbbn_vsel;
+ ab8500_regulator[AB8500_VBBN].vsel_range_len[0] =
+ ARRAY_SIZE(ab9540_vbbn_vsel);
+ ab8500_regulator[AB8500_VBBN].vsel_range[1] = ab9540_vbbn_vsel;
+ ab8500_regulator[AB8500_VBBN].vsel_range_len[1] =
+ ARRAY_SIZE(ab9540_vbbn_vsel);
+}
+
+static int status_state = AB8500_REGULATOR_STATE_CURRENT;
+
+static int _get_voltage(struct regulator_volt_range const *volt_range,
+ u8 value, int *volt)
+{
+ u8 start = volt_range->start.value;
+ u8 end = volt_range->end.value;
+ u8 step = volt_range->step.value;
+
+ /* Check if witin range */
+ if (step == 0) {
+ if (value == start) {
+ *volt = volt_range->start.volt;
+ return 1;
+ }
+ } else {
+ if ((start <= value) && (value <= end)) {
+ if ((value - start) % step != 0)
+ return -EINVAL; /* invalid setting */
+ *volt = volt_range->start.volt
+ + volt_range->step.volt
+ *((value - start) / step);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int get_voltage(struct regulator_volt_range const *volt_range,
+ int volt_range_len,
+ u8 value)
+{
+ int volt;
+ int i, ret;
+
+ for (i = 0; i < volt_range_len; i++) {
+ ret = _get_voltage(&volt_range[i], value, &volt);
+ if (ret < 0)
+ break; /* invalid setting */
+ if (ret == 1)
+ return volt; /* successful */
+ }
+
+ return -EINVAL;
+}
+
+static bool get_reg_and_mask(int regid, u8 mask, u8 *val)
+{
+ int ret;
+ u8 t;
+
+ if (!regid)
+ return false;
+
+ ret = abx500_get_register_interruptible(dev,
+ ab8500_register[regid].bank,
+ ab8500_register[regid].addr,
+ &t);
+ if (ret < 0)
+ return false;
+
+ (*val) = t & mask;
+
+ return true;
+}
+
+/* Convert regulator register value to index */
+static bool val2idx(u8 val, u8 *v, int len, int *idx)
+{
+ int i;
+
+ for (i = 0; i < len && v[i] != val; i++);
+
+ if (i == len)
+ return false;
+
+ (*idx) = i;
+ return true;
+}
+
+int ab8500_regulator_debug_read(enum ab8500_regulator_id id,
+ struct ab8500_debug_regulator_status *s)
+{
+ int i;
+ u8 val;
+ bool found;
+ int idx = 0;
+
+ if (id >= AB8500_NUM_REGULATORS)
+ return -EINVAL;
+
+ s->name = (char *)ab8500_regulator[id].name;
+
+ /* read mode */
+ (void) get_reg_and_mask(ab8500_regulator[id].update_regid,
+ ab8500_regulator[id].update_mask,
+ &val);
+
+ (void) val2idx(val, ab8500_regulator[id].update_val,
+ 4, &idx);
+
+ s->mode = (u8) idx;
+
+ /* read hw mode */
+ found = get_reg_and_mask(ab8500_regulator[id].hw_mode_regid,
+ ab8500_regulator[id].hw_mode_mask,
+ &val);
+
+ if (found)
+ found = val2idx(val, ab8500_regulator[id].hw_mode_val, 4, &idx);
+
+ if (found)
+ /* +1 since 0 = HWMODE_NONE */
+ s->hwmode = idx + 1;
+ else
+ s->hwmode = AB8500_HWMODE_NONE;
+
+ for (i = 0; i < 4 && found; i++) {
+
+ bool f = get_reg_and_mask(ab8500_regulator[id].hw_valid_regid[i],
+ ab8500_regulator[id].hw_valid_mask[i],
+ &val);
+ if (f)
+ s->hwmode_auto[i] = !!val;
+ else
+ s->hwmode_auto[i] = HWM_INVAL;
+ }
+
+ /* read voltage */
+ found = get_reg_and_mask(ab8500_regulator[id].vsel_sel_regid,
+ ab8500_regulator[id].vsel_sel_mask,
+ &val);
+ if (found)
+ found = val2idx(val, ab8500_regulator[id].vsel_sel_val,
+ 3, &idx);
+
+ if (found && idx < 3)
+ s->volt_selected = idx + 1;
+ else
+ s->volt_selected = 0;
+
+ for (s->volt_len = 0; s->volt_len < 3; s->volt_len++) {
+ int volt;
+ int i = s->volt_len;
+
+ found = get_reg_and_mask(ab8500_regulator[id].vsel_regid[i],
+ ab8500_regulator[id].vsel_mask[i],
+ &val);
+ if (!found)
+ break;
+
+ volt = get_voltage(ab8500_regulator[id].vsel_range[i],
+ ab8500_regulator[id].vsel_range_len[i],
+ val);
+ s->volt[i] = volt;
+ }
+ return 0;
+}
+
+static int ab8500_regulator_status_print(struct seq_file *s, void *p)
+{
+ struct device *dev = s->private;
+ int id, regid;
+ int i;
+ u8 val;
+ int err;
+
+ /* record current state */
+ ab8500_regulator_record_state(AB8500_REGULATOR_STATE_CURRENT);
+
+ /* check if chosen state is recorded */
+ if (!ab8500_register_state_saved[status_state]) {
+ seq_printf(s, "ab8500-regulator status is not recorded.\n");
+ goto exit;
+ }
+
+ /* print dump header */
+ err = seq_printf(s, "ab8500-regulator status:\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+
+ /* print state */
+ for (i = 0; i < NUM_REGULATOR_STATE; i++) {
+ if (i == status_state)
+ err = seq_printf(s, "-> %i. %12s\n",
+ i, regulator_state_name[i]);
+ else
+ err = seq_printf(s, " %i. %12s\n",
+ i, regulator_state_name[i]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow\n");
+ }
+
+ /* print labels */
+ err = seq_printf(s,
+ "+-----------+----+--------------+-------------------------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "| name|man |auto |voltage |\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "+-----------+----+--------------+ +-----------------------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "| |mode|mode |0|1|2|3| | 1 | 2 | 3 |\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "+-----------+----+------+-+-+-+-+-+-------+-------+-------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+ /* dump registers */
+ for (id = 0; id < AB8500_NUM_REGULATORS; id++) {
+ if (ab8500_register[id].unavailable ||
+ ab8500_regulator[id].unavailable)
+ continue;
+
+ /* print name */
+ err = seq_printf(s, "|%11s|",
+ ab8500_regulator[id].name);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print manual mode */
+ regid = ab8500_regulator[id].update_regid;
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].update_mask;
+ for (i = 0; i < 4; i++) {
+ if (val == ab8500_regulator[id].update_val[i])
+ break;
+ }
+ err = seq_printf(s, "%4s|",
+ update_val_name[i]);
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print auto mode */
+ regid = ab8500_regulator[id].hw_mode_regid;
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].hw_mode_mask;
+ for (i = 0; i < 4; i++) {
+ if (val == ab8500_regulator[id].hw_mode_val[i])
+ break;
+ }
+ err = seq_printf(s, "%6s|",
+ hw_mode_val_name[i]);
+ } else {
+ err = seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ id, __LINE__);
+
+ /* print valid bits */
+ for (i = 0; i < 4; i++) {
+ regid = ab8500_regulator[id].hw_valid_regid[i];
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].hw_valid_mask[i];
+ if (val)
+ err = seq_printf(s, "1|");
+ else
+ err = seq_printf(s, "0|");
+ } else {
+ err = seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+ }
+
+ /* print voltage selection */
+ regid = ab8500_regulator[id].vsel_sel_regid;
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].vsel_sel_mask;
+ for (i = 0; i < 3; i++) {
+ if (val == ab8500_regulator[id].vsel_sel_val[i])
+ break;
+ }
+ if (i < 3)
+ seq_printf(s, "%i|", i + 1);
+ else
+ seq_printf(s, "-|");
+ } else {
+ seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+
+ for (i = 0; i < 3; i++) {
+ int volt;
+
+ regid = ab8500_regulator[id].vsel_regid[i];
+ if (regid) {
+ val = ab8500_register_state[status_state][regid]
+ & ab8500_regulator[id].vsel_mask[i];
+ volt = get_voltage(
+ ab8500_regulator[id].vsel_range[i],
+ ab8500_regulator[id].vsel_range_len[i],
+ val);
+ seq_printf(s, "%7i|", volt);
+ } else {
+ seq_printf(s, " |");
+ }
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+ }
+
+ err = seq_printf(s, "\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i, %i\n",
+ regid, __LINE__);
+
+ }
+ err = seq_printf(s,
+ "+-----------+----+------+-+-+-+-+-+-------+-------+-------+\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+ err = seq_printf(s,
+ "Note! In HW mode, voltage selection is controlled by HW.\n");
+ if (err < 0)
+ dev_err(dev, "seq_printf overflow: %i\n", __LINE__);
+
+
+exit:
+ return 0;
+}
+
+static int ab8500_regulator_status_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* copy user data */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* convert */
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ /* set suspend force setting */
+ if (user_val > NUM_REGULATOR_STATE) {
+ dev_err(dev, "debugfs error input > number of states\n");
+ return -EINVAL;
+ }
+
+ status_state = user_val;
+
+ return buf_size;
+}
+
+
+static int ab8500_regulator_status_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, ab8500_regulator_status_print,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_status_fops = {
+ .open = ab8500_regulator_status_open,
+ .write = ab8500_regulator_status_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_PM
+
+struct ab8500_force_reg {
+ char *name;
+ u8 bank;
+ u8 addr;
+ u8 mask;
+ u8 val;
+ bool restore;
+ u8 restore_val;
+ u8 unavailable;
+};
+
+static struct ab8500_force_reg ab8500_force_reg[] = {
+ {
+ /*
+ * SysClkCtrl
+ * OTP: 0x00, HSI: 0x06, suspend: 0x00/0x07 (value/mask)
+ * [ 2] USBClkEna = disable SysClk path to USB block
+ * [ 1] TVoutClkEna = disable 27Mhz clock to TVout block
+ * [ 0] TVoutPllEna = disable TVout pll
+ * (generate 27Mhz from SysClk)
+ */
+ .name = "SysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x0c,
+ .mask = 0x07,
+ .val = 0x00,
+ },
+ {
+ /*
+ * VsimSysClkCtrl
+ * OTP: 0x01, HSI: 0x21, suspend: 0x01/0xff (value/mask)
+ * [ 7] VsimSysClkReq8Valid = no connection
+ * [ 6] VsimSysClkReq7Valid = no connection
+ * [ 5] VsimSysClkReq6Valid = no connection
+ * [ 4] VsimSysClkReq5Valid = no connection
+ * [ 3] VsimSysClkReq4Valid = no connection
+ * [ 2] VsimSysClkReq3Valid = no connection
+ * [ 1] VsimSysClkReq2Valid = no connection
+ * [ 0] VsimSysClkReq1Valid = Vsim set by SysClkReq1
+ */
+ .name = "VsimSysClkCtrl",
+ .bank = 0x02,
+ .addr = 0x33,
+ .mask = 0xff,
+ .val = 0x01,
+ },
+ {
+ /*
+ * SysUlpClkCtrl1
+ * OTP: 0x00, HSI: 0x00, suspend: 0x00/0x0f (value/mask)
+ * [ 3] 4500SysClkReq = inactive
+ * [ 2] UlpClkReq = inactive
+ * [1:0] SysUlpClkIntSel[1:0] = no internal clock switching.
+ * Internal clock is SysClk.
+ */
+ .name = "SysUlpClkCtrl1",
+ .bank = 0x02,
+ .addr = 0x0b,
+ .mask = 0x0f,
+ .val = 0x00,
+ },
+ {
+ /*
+ * TVoutCtrl
+ * OTP: N/A, HSI: N/A, suspend: 0x00/0x03 (value/mask)
+ * [ 2] PlugTvOn = plug/unplug detection disabled
+ * [1:0] TvoutDacCtrl[1:0] = "0" forced on DAC input (test)
+ */
+ .name = "TVoutCtrl",
+ .bank = 0x06,
+ .addr = 0x80,
+ .mask = 0x03,
+ .val = 0x00,
+ },
+};
+
+static void ab9540_force_reg_update(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_force_reg); i++) {
+ if (ab8500_force_reg[i].bank == 0x02 &&
+ ab8500_force_reg[i].addr == 0x0C) {
+ /*
+ * SysClkCtrl
+ * OTP: 0x00, HSI: 0x06, suspend: 0x00/0x07 (value/mask)
+ * [ 2] USBClkEna = disable SysClk path to USB block
+ */
+ ab8500_force_reg[i].mask = 0x04;
+ ab8500_force_reg[i].val = 0x00;
+ } else if (ab8500_force_reg[i].bank == 0x06 &&
+ ab8500_force_reg[i].addr == 0x80) {
+ /* TVoutCtrl not supported by AB9540 */
+ ab8500_force_reg[i].unavailable = true;
+ }
+ }
+}
+
+void ab8500_regulator_debug_force(void)
+{
+ int ret, i;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_SUSPEND);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record suspend state.\n");
+
+ /* check if registers should be forced */
+ if (!setting_suspend_force)
+ goto exit;
+
+ /*
+ * Optimize href v2_v50_pwr board for ApSleep/ApDeepSleep
+ * power consumption measurements
+ */
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_force_reg); i++) {
+ if (ab8500_force_reg[i].unavailable)
+ continue;
+
+ dev_vdbg(&pdev->dev, "Save and set %s: "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x.\n",
+ ab8500_force_reg[i].name,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].val);
+
+ /* assume that register should be restored */
+ ab8500_force_reg[i].restore = true;
+
+ /* get register value before forcing it */
+ ret = abx500_get_register_interruptible(&pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ &ab8500_force_reg[i].restore_val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read %s.\n",
+ ab8500_force_reg[i].name);
+ ab8500_force_reg[i].restore = false;
+ break;
+ }
+
+ /* force register value */
+ ret = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].val);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to write %s.\n",
+ ab8500_force_reg[i].name);
+ ab8500_force_reg[i].restore = false;
+ }
+ }
+
+exit:
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(
+ AB8500_REGULATOR_STATE_SUSPEND_CORE);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record suspend state.\n");
+
+ return;
+}
+
+void ab8500_regulator_debug_restore(void)
+{
+ int ret, i;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_RESUME_CORE);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record resume state.\n");
+ for (i = ARRAY_SIZE(ab8500_force_reg) - 1; i >= 0; i--) {
+ if (ab8500_force_reg[i].unavailable)
+ continue;
+
+ /* restore register value */
+ if (ab8500_force_reg[i].restore) {
+ ret = abx500_mask_and_set_register_interruptible(
+ &pdev->dev,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].restore_val);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to restore %s.\n",
+ ab8500_force_reg[i].name);
+ dev_vdbg(&pdev->dev, "Restore %s: "
+ "0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ ab8500_force_reg[i].name,
+ ab8500_force_reg[i].bank,
+ ab8500_force_reg[i].addr,
+ ab8500_force_reg[i].mask,
+ ab8500_force_reg[i].restore_val);
+ }
+ }
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_RESUME);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to record resume state.\n");
+
+ return;
+}
+
+#endif
+
+static int ab8500_regulator_suspend_force_show(struct seq_file *s, void *p)
+{
+ /* print suspend standby status */
+ if (setting_suspend_force)
+ return seq_printf(s, "suspend force enabled\n");
+ else
+ return seq_printf(s, "no suspend force\n");
+}
+
+static int ab8500_regulator_suspend_force_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ int buf_size;
+ unsigned long user_val;
+ int err;
+
+ /* copy user data */
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ /* convert */
+ err = strict_strtoul(buf, 0, &user_val);
+ if (err)
+ return -EINVAL;
+
+ /* set suspend force setting */
+ if (user_val > 1) {
+ dev_err(dev, "debugfs error input > 1\n");
+ return -EINVAL;
+ }
+
+ if (user_val)
+ setting_suspend_force = true;
+ else
+ setting_suspend_force = false;
+
+ return buf_size;
+}
+
+static int ab8500_regulator_suspend_force_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ab8500_regulator_suspend_force_show,
+ inode->i_private);
+}
+
+static const struct file_operations ab8500_regulator_suspend_force_fops = {
+ .open = ab8500_regulator_suspend_force_open,
+ .write = ab8500_regulator_suspend_force_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static struct dentry *ab8500_regulator_dir;
+static struct dentry *ab8500_regulator_dump_file;
+static struct dentry *ab8500_regulator_status_file;
+static struct dentry *ab8500_regulator_suspend_force_file;
+
+int __devinit ab8500_regulator_debug_init(struct platform_device *plf)
+{
+ void __iomem *boot_info_backupram;
+ int ret;
+ struct ab8500 *ab8500;
+
+ /* setup dev pointers */
+ dev = &plf->dev;
+ pdev = plf;
+
+ /* save state of registers */
+ ret = ab8500_regulator_record_state(AB8500_REGULATOR_STATE_INIT);
+ if (ret < 0)
+ dev_err(&plf->dev, "Failed to record init state.\n");
+
+ ab8500 = dev_get_drvdata(plf->dev.parent);
+ /* Update data structures for AB9540 */
+ if (is_ab9540(ab8500)) {
+ ab9540_registers_update();
+ ab9540_regulators_update();
+ ab9540_force_reg_update();
+ }
+ /* make suspend-force default if board profile is v5x-power */
+ boot_info_backupram = ioremap(BOOT_INFO_BACKUPRAM1, 0x4);
+
+ if (boot_info_backupram) {
+ u8 board_profile;
+ board_profile = readb(
+ boot_info_backupram + BOARD_PROFILE_BACKUPRAM1);
+ dev_dbg(dev, "Board profile is 0x%02x\n", board_profile);
+
+ if (board_profile >= OPTION_BOARD_VERSION_V5X)
+ setting_suspend_force = true;
+
+ iounmap(boot_info_backupram);
+ } else {
+ dev_err(dev, "Failed to read backupram.\n");
+ }
+
+ /* create directory */
+ ab8500_regulator_dir = debugfs_create_dir("ab8500-regulator", NULL);
+ if (!ab8500_regulator_dir)
+ goto exit_no_debugfs;
+
+ /* create "dump" file */
+ ab8500_regulator_dump_file = debugfs_create_file("dump",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_dump_fops);
+ if (!ab8500_regulator_dump_file)
+ goto exit_destroy_dir;
+
+ /* create "status" file */
+ ab8500_regulator_status_file = debugfs_create_file("status",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_status_fops);
+ if (!ab8500_regulator_status_file)
+ goto exit_destroy_dump_file;
+
+ /*
+ * create "suspend-force-v5x" file. As indicated by the name, this is
+ * only applicable for v2_v5x hardware versions.
+ */
+ ab8500_regulator_suspend_force_file = debugfs_create_file(
+ "suspend-force-v5x",
+ S_IRUGO, ab8500_regulator_dir, &plf->dev,
+ &ab8500_regulator_suspend_force_fops);
+ if (!ab8500_regulator_suspend_force_file)
+ goto exit_destroy_status_file;
+
+ return 0;
+
+exit_destroy_status_file:
+ debugfs_remove(ab8500_regulator_status_file);
+exit_destroy_dump_file:
+ debugfs_remove(ab8500_regulator_dump_file);
+exit_destroy_dir:
+ debugfs_remove(ab8500_regulator_dir);
+exit_no_debugfs:
+ dev_err(&plf->dev, "failed to create debugfs entries.\n");
+ return -ENOMEM;
+}
+
+int __devexit ab8500_regulator_debug_exit(struct platform_device *plf)
+{
+ debugfs_remove(ab8500_regulator_suspend_force_file);
+ debugfs_remove(ab8500_regulator_status_file);
+ debugfs_remove(ab8500_regulator_dump_file);
+ debugfs_remove(ab8500_regulator_dir);
+
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com");
+MODULE_DESCRIPTION("AB8500 Regulator Debug");
+MODULE_ALIAS("platform:ab8500-regulator-debug");
diff --git a/drivers/regulator/ab8500-debug.h b/drivers/regulator/ab8500-debug.h
new file mode 100644
index 00000000000..2b59e556a3f
--- /dev/null
+++ b/drivers/regulator/ab8500-debug.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010-2011
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson.
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __AB8500_DEBUG_H__
+#define __AB8500_DEBUG_H__
+
+/*
+ * regulator status print
+ */
+enum ab8500_regulator_id {
+ AB8500_VARM,
+ AB8500_VBBP,
+ AB8500_VBBN,
+ AB8500_VAPE,
+ AB8500_VSMPS1,
+ AB8500_VSMPS2,
+ AB8500_VSMPS3,
+ AB8500_VPLL,
+ AB8500_VREFDDR,
+ AB8500_VMOD,
+ AB8500_VEXTSUPPLY1,
+ AB8500_VEXTSUPPLY2,
+ AB8500_VEXTSUPPLY3,
+ AB8500_VRF1,
+ AB8500_VANA,
+ AB8500_VAUX1,
+ AB8500_VAUX2,
+ AB8500_VAUX3,
+ AB9540_VAUX4, /* Note: AB9540 only */
+ AB8500_VINTCORE,
+ AB8500_VTVOUT,
+ AB8500_VAUDIO,
+ AB8500_VANAMIC1,
+ AB8500_VANAMIC2,
+ AB8500_VDMIC,
+ AB8500_VUSB,
+ AB8500_VOTG,
+ AB8500_VBUSBIS,
+ AB8500_NUM_REGULATORS,
+};
+
+enum ab8500_regulator_mode {
+ AB8500_MODE_OFF = 0,
+ AB8500_MODE_ON,
+ AB8500_MODE_HW,
+ AB8500_MODE_LP
+};
+
+enum ab8500_regulator_hwmode {
+ AB8500_HWMODE_NONE = 0,
+ AB8500_HWMODE_HPLP,
+ AB8500_HWMODE_HPOFF,
+ AB8500_HWMODE_HP,
+ AB8500_HWMODE_HP2,
+};
+
+enum hwmode_auto {
+ HWM_OFF = 0,
+ HWM_ON = 1,
+ HWM_INVAL = 2,
+};
+
+struct ab8500_debug_regulator_status {
+ char *name;
+ enum ab8500_regulator_mode mode;
+ enum ab8500_regulator_hwmode hwmode;
+ enum hwmode_auto hwmode_auto[4];
+ int volt_selected;
+ int volt_len;
+ int volt[4];
+};
+
+int ab8500_regulator_debug_read(enum ab8500_regulator_id id,
+ struct ab8500_debug_regulator_status *s);
+#endif /* __AB8500_DEBUG_H__ */
diff --git a/drivers/regulator/ab8500-ext.c b/drivers/regulator/ab8500-ext.c
new file mode 100644
index 00000000000..8a5064c07fb
--- /dev/null
+++ b/drivers/regulator/ab8500-ext.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com>
+ *
+ * This file is based on drivers/regulator/ab8500.c
+ *
+ * AB8500 external regulators
+ *
+ * ab8500-ext supports the following regulators:
+ * - VextSupply3
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/regulator/ab8500.h>
+
+/**
+ * struct ab8500_ext_regulator_info - ab8500 regulator information
+ * @dev: device pointer
+ * @desc: regulator description
+ * @rdev: regulator device
+ * @cfg: regulator configuration (extension of regulator FW configuration)
+ * @is_enabled: status of regulator (on/off)
+ * @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @update_bank: bank to control on/off
+ * @update_reg: register to control on/off
+ * @update_mask: mask to enable/disable and set mode of regulator
+ * @update_val: bits holding the regulator current mode
+ * @update_val_hp: bits to set EN pin active (LPn pin deactive)
+ * normally this means high power mode
+ * @update_val_lp: bits to set EN pin active and LPn pin active
+ * normally this means low power mode
+ * @update_val_hw: bits to set regulator pins in HW control
+ * SysClkReq pins and logic will choose mode
+ */
+struct ab8500_ext_regulator_info {
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct ab8500_ext_regulator_cfg *cfg;
+ bool is_enabled;
+ int fixed_uV;
+ u8 update_bank;
+ u8 update_reg;
+ u8 update_mask;
+ u8 update_val;
+ u8 update_val_hp;
+ u8 update_val_lp;
+ u8 update_val_hw;
+};
+
+static int enable(struct ab8500_ext_regulator_info *info, u8 *regval)
+{
+ int ret;
+
+ *regval = info->update_val;
+
+ /*
+ * To satisfy both HW high power request and SW request, the regulator
+ * must be on in high power.
+ */
+ if (info->cfg && info->cfg->hwreq)
+ *regval = info->update_val_hp;
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, *regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(info->rdev),
+ "couldn't set enable bits for regulator\n");
+
+ info->is_enabled = true;
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = enable(info, &regval);
+
+ dev_dbg(rdev_get_dev(rdev), "%s-enable (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_set_suspend_enable(struct regulator_dev *rdev)
+{
+ dev_dbg(rdev_get_dev(rdev), "suspend: ");
+
+ return ab8500_ext_regulator_enable(rdev);
+}
+
+static int disable(struct ab8500_ext_regulator_info *info, u8 *regval)
+{
+ int ret;
+
+ *regval = 0x0;
+
+ /*
+ * Set the regulator in HW request mode if configured
+ */
+ if (info->cfg && info->cfg->hwreq)
+ *regval = info->update_val_hw;
+
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, *regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(info->rdev),
+ "couldn't set disable bits for regulator\n");
+
+ info->is_enabled = false;
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = disable(info, &regval);
+
+ dev_dbg(rdev_get_dev(rdev), "%s-disable (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ return ret;
+}
+
+static int ab8500_ext_regulator_set_suspend_disable(struct regulator_dev *rdev)
+{
+ dev_dbg(rdev_get_dev(rdev), "suspend: ");
+
+ return ab8500_ext_regulator_disable(rdev);
+}
+
+static int ab8500_ext_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ u8 regval;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = abx500_get_register_interruptible(info->dev,
+ info->update_bank, info->update_reg, &regval);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read 0x%x register\n", info->update_reg);
+ return ret;
+ }
+
+ dev_dbg(rdev_get_dev(rdev), "%s-is_enabled (bank, reg, mask, value):"
+ " 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+
+ if (((regval & info->update_mask) == info->update_val_lp) ||
+ ((regval & info->update_mask) == info->update_val_hp))
+ info->is_enabled = true;
+ else
+ info->is_enabled = false;
+
+ return info->is_enabled;
+}
+
+static int ab8500_ext_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int ret = 0;
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ info->update_val = info->update_val_hp;
+ break;
+ case REGULATOR_MODE_IDLE:
+ info->update_val = info->update_val_lp;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (info->is_enabled) {
+ u8 regval;
+
+ ret = enable(info, &regval);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "Could not set regulator mode.\n");
+
+ dev_dbg(rdev_get_dev(rdev),
+ "%s-set_mode (bank, reg, mask, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, regval);
+ }
+
+ return ret;
+}
+
+static unsigned int ab8500_ext_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (info->update_val == info->update_val_hp)
+ ret = REGULATOR_MODE_NORMAL;
+ else if (info->update_val == info->update_val_lp)
+ ret = REGULATOR_MODE_IDLE;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int ab8500_ext_fixed_get_voltage(struct regulator_dev *rdev)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ return info->fixed_uV;
+}
+
+static int ab8500_ext_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct ab8500_ext_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ /* return the uV for the fixed regulators */
+ if (info->fixed_uV)
+ return info->fixed_uV;
+
+ return -EINVAL;
+}
+
+static struct regulator_ops ab8500_ext_regulator_ops = {
+ .enable = ab8500_ext_regulator_enable,
+ .set_suspend_enable = ab8500_ext_regulator_set_suspend_enable,
+ .disable = ab8500_ext_regulator_disable,
+ .set_suspend_disable = ab8500_ext_regulator_set_suspend_disable,
+ .is_enabled = ab8500_ext_regulator_is_enabled,
+ .set_mode = ab8500_ext_regulator_set_mode,
+ .get_mode = ab8500_ext_regulator_get_mode,
+ .get_voltage = ab8500_ext_fixed_get_voltage,
+ .list_voltage = ab8500_ext_list_voltage,
+};
+
+static struct ab8500_ext_regulator_info
+ ab8500_ext_regulator_info[AB8500_NUM_EXT_REGULATORS] = {
+ [AB8500_EXT_SUPPLY1] = {
+ .desc = {
+ .name = "VEXTSUPPLY1",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY1,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1800000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_hp = 0x01,
+ .update_val_lp = 0x03,
+ .update_val_hw = 0x02,
+ },
+ [AB8500_EXT_SUPPLY2] = {
+ .desc = {
+ .name = "VEXTSUPPLY2",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1360000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_hp = 0x04,
+ .update_val_lp = 0x0c,
+ .update_val_hw = 0x08,
+ },
+ [AB8500_EXT_SUPPLY3] = {
+ .desc = {
+ .name = "VEXTSUPPLY3",
+ .ops = &ab8500_ext_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_EXT_SUPPLY3,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 3400000,
+ .update_bank = 0x04,
+ .update_reg = 0x08,
+ .update_mask = 0x30,
+ .update_val = 0x10,
+ .update_val_hp = 0x10,
+ .update_val_lp = 0x30,
+ .update_val_hw = 0x20,
+ },
+};
+
+__devinit int ab8500_ext_regulator_init(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_platform_data *ppdata;
+ struct ab8500_regulator_platform_data *pdata;
+ int i, err;
+
+ if (!ab8500) {
+ dev_err(&pdev->dev, "null mfd parent\n");
+ return -EINVAL;
+ }
+ ppdata = dev_get_platdata(ab8500->dev);
+ if (!ppdata) {
+ dev_err(&pdev->dev, "null parent pdata\n");
+ return -EINVAL;
+ }
+
+ pdata = ppdata->regulator;
+ if (!pdata) {
+ dev_err(&pdev->dev, "null pdata\n");
+ return -EINVAL;
+ }
+
+ /* make sure the platform data has the correct size */
+ if (pdata->num_ext_regulator != ARRAY_SIZE(ab8500_ext_regulator_info)) {
+ dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
+ return -EINVAL;
+ }
+
+ /* check for AB8500 2.x */
+ if (is_ab8500_2p0_or_earlier(ab8500)) {
+ struct ab8500_ext_regulator_info *info;
+
+ /* VextSupply3LPn is inverted on AB8500 2.x */
+ info = &ab8500_ext_regulator_info[AB8500_EXT_SUPPLY3];
+ info->update_val = 0x30;
+ info->update_val_hp = 0x30;
+ info->update_val_lp = 0x10;
+ }
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) {
+ struct ab8500_ext_regulator_info *info = NULL;
+
+ /* assign per-regulator data */
+ info = &ab8500_ext_regulator_info[i];
+ info->dev = &pdev->dev;
+ info->cfg = (struct ab8500_ext_regulator_cfg *)
+ pdata->ext_regulator[i].driver_data;
+
+ /* register regulator with framework */
+ info->rdev = regulator_register(&info->desc, &pdev->dev,
+ &pdata->ext_regulator[i], info, NULL);
+ if (IS_ERR(info->rdev)) {
+ err = PTR_ERR(info->rdev);
+ dev_err(&pdev->dev, "failed to register regulator %s\n",
+ info->desc.name);
+ /* when we fail, un-register all earlier regulators */
+ while (--i >= 0) {
+ info = &ab8500_ext_regulator_info[i];
+ regulator_unregister(info->rdev);
+ }
+ return err;
+ }
+
+ dev_dbg(rdev_get_dev(info->rdev),
+ "%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+__devexit int ab8500_ext_regulator_exit(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_ext_regulator_info); i++) {
+ struct ab8500_ext_regulator_info *info = NULL;
+ info = &ab8500_ext_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->rdev),
+ "%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->rdev);
+ }
+
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 external regulator driver");
+MODULE_ALIAS("platform:ab8500-ext-regulator");
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index c7ee4c15d6f..71328249659 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -21,43 +21,55 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h>
+#include <linux/mfd/abx500/ab8500-gpio.h> /* for sysclkreq pins */
+#include <mach/id.h>
/**
* struct ab8500_regulator_info - ab8500 regulator information
* @dev: device pointer
* @desc: regulator description
* @regulator_dev: regulator device
+ * @is_enabled: status of regulator (on/off)
* @max_uV: maximum voltage (for variable voltage supplies)
* @min_uV: minimum voltage (for variable voltage supplies)
* @fixed_uV: typical voltage (for fixed voltage supplies)
+ * @load_lp_uA: maximum load in idle (low power) mode
* @update_bank: bank to control on/off
* @update_reg: register to control on/off
- * @update_mask: mask to enable/disable regulator
- * @update_val_enable: bits to enable the regulator in normal (high power) mode
+ * @update_mask: mask to enable/disable and set mode of regulator
+ * @update_val: bits holding the regulator current mode
+ * @update_val_idle: bits to enable the regulator in idle (low power) mode
+ * @update_val_normal: bits to enable the regulator in normal (high power) mode
* @voltage_bank: bank to control regulator voltage
* @voltage_reg: register to control regulator voltage
* @voltage_mask: mask to control regulator voltage
* @voltages: supported voltage table
* @voltages_len: number of supported voltages for the regulator
* @delay: startup/set voltage delay in us
+ * @gpio_pin: ab8500 gpio pin offset number (for sysclkreq regulator only)
*/
struct ab8500_regulator_info {
struct device *dev;
struct regulator_desc desc;
struct regulator_dev *regulator;
+ bool is_enabled;
int max_uV;
int min_uV;
int fixed_uV;
+ int load_lp_uA;
u8 update_bank;
u8 update_reg;
u8 update_mask;
- u8 update_val_enable;
+ u8 update_val;
+ u8 update_val_idle;
+ u8 update_val_normal;
u8 voltage_bank;
u8 voltage_reg;
u8 voltage_mask;
int const *voltages;
int voltages_len;
unsigned int delay;
+ enum ab8500_pin gpio_pin;
};
/* voltage tables for the vauxn/vintcore supplies */
@@ -113,15 +125,17 @@ static int ab8500_regulator_enable(struct regulator_dev *rdev)
ret = abx500_mask_and_set_register_interruptible(info->dev,
info->update_bank, info->update_reg,
- info->update_mask, info->update_val_enable);
+ info->update_mask, info->update_val);
if (ret < 0)
dev_err(rdev_get_dev(rdev),
"couldn't set enable bits for regulator\n");
+ info->is_enabled = true;
+
dev_vdbg(rdev_get_dev(rdev),
"%s-enable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
info->desc.name, info->update_bank, info->update_reg,
- info->update_mask, info->update_val_enable);
+ info->update_mask, info->update_val);
return ret;
}
@@ -143,6 +157,8 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev)
dev_err(rdev_get_dev(rdev),
"couldn't set disable bits for regulator\n");
+ info->is_enabled = false;
+
dev_vdbg(rdev_get_dev(rdev),
"%s-disable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
info->desc.name, info->update_bank, info->update_reg,
@@ -151,6 +167,88 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev)
return ret;
}
+static unsigned int ab8500_regulator_get_optimum_mode(
+ struct regulator_dev *rdev, int input_uV,
+ int output_uV, int load_uA)
+{
+ unsigned int mode;
+
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (load_uA <= info->load_lp_uA)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ int ret = 0;
+
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ info->update_val = info->update_val_normal;
+ break;
+ case REGULATOR_MODE_IDLE:
+ info->update_val = info->update_val_idle;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (info->is_enabled) {
+ ret = abx500_mask_and_set_register_interruptible(info->dev,
+ info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ if (ret < 0)
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set regulator mode\n");
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-set_mode (bank, reg, mask, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->update_bank, info->update_reg,
+ info->update_mask, info->update_val);
+ }
+
+ return ret;
+}
+
+static unsigned int ab8500_regulator_get_mode(struct regulator_dev *rdev)
+{
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ if (info->update_val == info->update_val_normal)
+ ret = REGULATOR_MODE_NORMAL;
+ else if (info->update_val == info->update_val_idle)
+ ret = REGULATOR_MODE_IDLE;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
{
int ret;
@@ -177,9 +275,11 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
info->update_mask, regval);
if (regval & info->update_mask)
- return true;
+ info->is_enabled = true;
else
- return false;
+ info->is_enabled = false;
+
+ return info->is_enabled;
}
static int ab8500_list_voltage(struct regulator_dev *rdev, unsigned selector)
@@ -273,8 +373,13 @@ static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
*selector = ret;
+ /* vintcore register has a different layout */
+ if (info->desc.id == AB8500_LDO_INTCORE)
+ regval = ((u8)ret) << 3;
+ else
+ regval = (u8)ret;
+
/* set the registers for the request */
- regval = (u8)ret;
ret = abx500_mask_and_set_register_interruptible(info->dev,
info->voltage_bank, info->voltage_reg,
info->voltage_mask, regval);
@@ -314,9 +419,12 @@ static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
return info->delay;
}
-static struct regulator_ops ab8500_regulator_ops = {
+static struct regulator_ops ab8500_regulator_volt_mode_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
+ .get_optimum_mode = ab8500_regulator_get_optimum_mode,
+ .set_mode = ab8500_regulator_set_mode,
+ .get_mode = ab8500_regulator_get_mode,
.is_enabled = ab8500_regulator_is_enabled,
.get_voltage_sel = ab8500_regulator_get_voltage_sel,
.set_voltage = ab8500_regulator_set_voltage,
@@ -337,16 +445,116 @@ static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
return info->fixed_uV;
}
-static struct regulator_ops ab8500_regulator_fixed_ops = {
+static struct regulator_ops ab8500_regulator_mode_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
.is_enabled = ab8500_regulator_is_enabled,
+ .get_optimum_mode = ab8500_regulator_get_optimum_mode,
+ .set_mode = ab8500_regulator_set_mode,
+ .get_mode = ab8500_regulator_get_mode,
.get_voltage = ab8500_fixed_get_voltage,
.list_voltage = ab8500_list_voltage,
.enable_time = ab8500_regulator_enable_time,
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
};
+static struct regulator_ops ab8500_regulator_ops = {
+ .enable = ab8500_regulator_enable,
+ .disable = ab8500_regulator_disable,
+ .is_enabled = ab8500_regulator_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+static int ab8500_sysclkreq_enable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_select(info->dev, info->gpio_pin, false);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set sysclkreq pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = true;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-enable (gpio_pin, gpio_select): %i, false\n",
+ info->desc.name, info->gpio_pin);
+
+ return ret;
+}
+
+static int ab8500_sysclkreq_disable(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_select(info->dev, info->gpio_pin, true);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't set gpio pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = false;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-disable (gpio_pin, gpio_select): %i, true\n",
+ info->desc.name, info->gpio_pin);
+
+ return ret;
+}
+
+static int ab8500_sysclkreq_is_enabled(struct regulator_dev *rdev)
+{
+ int ret;
+ struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+ bool gpio_select;
+
+ if (info == NULL) {
+ dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
+ return -EINVAL;
+ }
+
+ ret = ab8500_gpio_config_get_select(info->dev, info->gpio_pin,
+ &gpio_select);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "couldn't read gpio pin selection\n");
+ return ret;
+ }
+
+ info->is_enabled = !gpio_select;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "%s-is_enabled (gpio_pin, is_enabled): %i, %i\n",
+ info->desc.name, info->gpio_pin, !gpio_select);
+
+ return info->is_enabled;
+}
+
+static struct regulator_ops ab8500_sysclkreq_ops = {
+ .enable = ab8500_sysclkreq_enable,
+ .disable = ab8500_sysclkreq_disable,
+ .is_enabled = ab8500_sysclkreq_is_enabled,
+ .get_voltage = ab8500_fixed_get_voltage,
+ .list_voltage = ab8500_list_voltage,
+};
+
+/* AB8500 regulator information */
static struct ab8500_regulator_info
ab8500_regulator_info[AB8500_NUM_REGULATORS] = {
/*
@@ -358,7 +566,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_AUX1] = {
.desc = {
.name = "LDO-AUX1",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUX1,
.owner = THIS_MODULE,
@@ -366,10 +574,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x04,
.update_reg = 0x09,
.update_mask = 0x03,
- .update_val_enable = 0x01,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
.voltage_bank = 0x04,
.voltage_reg = 0x1f,
.voltage_mask = 0x0f,
@@ -379,7 +590,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_AUX2] = {
.desc = {
.name = "LDO-AUX2",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUX2,
.owner = THIS_MODULE,
@@ -387,10 +598,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x04,
.update_reg = 0x09,
.update_mask = 0x0c,
- .update_val_enable = 0x04,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
.voltage_bank = 0x04,
.voltage_reg = 0x20,
.voltage_mask = 0x0f,
@@ -400,7 +614,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_AUX3] = {
.desc = {
.name = "LDO-AUX3",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUX3,
.owner = THIS_MODULE,
@@ -408,10 +622,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x04,
.update_reg = 0x0a,
.update_mask = 0x03,
- .update_val_enable = 0x01,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
.voltage_bank = 0x04,
.voltage_reg = 0x21,
.voltage_mask = 0x07,
@@ -421,7 +638,7 @@ static struct ab8500_regulator_info
[AB8500_LDO_INTCORE] = {
.desc = {
.name = "LDO-INTCORE",
- .ops = &ab8500_regulator_ops,
+ .ops = &ab8500_regulator_volt_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_INTCORE,
.owner = THIS_MODULE,
@@ -429,10 +646,13 @@ static struct ab8500_regulator_info
},
.min_uV = 1100000,
.max_uV = 3300000,
+ .load_lp_uA = 5000,
.update_bank = 0x03,
.update_reg = 0x80,
.update_mask = 0x44,
- .update_val_enable = 0x04,
+ .update_val = 0x44,
+ .update_val_idle = 0x44,
+ .update_val_normal = 0x04,
.voltage_bank = 0x03,
.voltage_reg = 0x80,
.voltage_mask = 0x38,
@@ -448,7 +668,275 @@ static struct ab8500_regulator_info
[AB8500_LDO_TVOUT] = {
.desc = {
.name = "LDO-TVOUT",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_TVOUT,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .delay = 500,
+ .fixed_uV = 2000000,
+ .load_lp_uA = 1000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x82,
+ .update_val = 0x02,
+ .update_val_idle = 0x82,
+ .update_val_normal = 0x02,
+ },
+ [AB8500_LDO_AUDIO] = {
+ .desc = {
+ .name = "LDO-AUDIO",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUDIO,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2000000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x02,
+ .update_val = 0x02,
+ },
+ [AB8500_LDO_ANAMIC1] = {
+ .desc = {
+ .name = "LDO-ANAMIC1",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC1,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x08,
+ .update_val = 0x08,
+ },
+ [AB8500_LDO_ANAMIC2] = {
+ .desc = {
+ .name = "LDO-ANAMIC2",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANAMIC2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 2050000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x10,
+ .update_val = 0x10,
+ },
+ [AB8500_LDO_DMIC] = {
+ .desc = {
+ .name = "LDO-DMIC",
+ .ops = &ab8500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_DMIC,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1800000,
+ .update_bank = 0x03,
+ .update_reg = 0x83,
+ .update_mask = 0x04,
+ .update_val = 0x04,
+ },
+
+ /*
+ * Regulators with fixed voltage and normal/idle modes
+ */
+ [AB8500_LDO_ANA] = {
+ .desc = {
+ .name = "LDO-ANA",
+ .ops = &ab8500_regulator_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_ANA,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1200000,
+ .load_lp_uA = 1000,
+ .update_bank = 0x04,
+ .update_reg = 0x06,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
+ },
+
+ /*
+ * SysClkReq regulators
+ */
+ [AB8500_SYSCLKREQ_2] = {
+ .desc = {
+ .name = "SYSCLKREQ-2",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO1,
+ },
+ [AB8500_SYSCLKREQ_4] = {
+ .desc = {
+ .name = "SYSCLKREQ-4",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_4,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO3,
+ },
+};
+
+/* AB9540 regulator information */
+static struct ab8500_regulator_info
+ ab9540_regulator_info[AB9540_NUM_REGULATORS] = {
+ /*
+ * Variable Voltage Regulators
+ * name, min mV, max mV,
+ * update bank, reg, mask, enable val
+ * volt bank, reg, mask, table, table length
+ */
+ [AB9540_LDO_AUX1] = {
+ .desc = {
+ .name = "LDO-AUX1",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX1,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x1f,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB9540_LDO_AUX2] = {
+ .desc = {
+ .name = "LDO-AUX2",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX2,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x09,
+ .update_mask = 0x0c,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x20,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB9540_LDO_AUX3] = {
+ .desc = {
+ .name = "LDO-AUX3",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_AUX3,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x04,
+ .update_reg = 0x0a,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x21,
+ .voltage_mask = 0x07,
+ .voltages = ldo_vaux3_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vaux3_voltages),
+ },
+ [AB9540_LDO_AUX4] = {
+ .desc = {
+ .name = "LDO-AUX4",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB9540_LDO_AUX4,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ /* values for Vaux4Regu register */
+ .update_bank = 0x04,
+ .update_reg = 0x2e,
+ .update_mask = 0x03,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
+ /* values for Vaux4SEL register */
+ .voltage_bank = 0x04,
+ .voltage_reg = 0x2f,
+ .voltage_mask = 0x0f,
+ .voltages = ldo_vauxn_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vauxn_voltages),
+ },
+ [AB9540_LDO_INTCORE] = {
+ .desc = {
+ .name = "LDO-INTCORE",
+ .ops = &ab8500_regulator_volt_mode_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_LDO_INTCORE,
+ .owner = THIS_MODULE,
+ .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+ .min_uV = 1100000,
+ .max_uV = 3300000,
+ .load_lp_uA = 5000,
+ .update_bank = 0x03,
+ .update_reg = 0x80,
+ .update_mask = 0x44,
+ .update_val = 0x44,
+ .update_val_idle = 0x44,
+ .update_val_normal = 0x04,
+ .voltage_bank = 0x03,
+ .voltage_reg = 0x80,
+ .voltage_mask = 0x38,
+ .voltages = ldo_vintcore_voltages,
+ .voltages_len = ARRAY_SIZE(ldo_vintcore_voltages),
+ },
+
+ /*
+ * Fixed Voltage Regulators
+ * name, fixed mV,
+ * update bank, reg, mask, enable val
+ */
+ [AB9540_LDO_TVOUT] = {
+ .desc = {
+ .name = "LDO-TVOUT",
+ .ops = &ab8500_regulator_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_TVOUT,
.owner = THIS_MODULE,
@@ -456,17 +944,20 @@ static struct ab8500_regulator_info
},
.delay = 10000,
.fixed_uV = 2000000,
+ .load_lp_uA = 1000,
.update_bank = 0x03,
.update_reg = 0x80,
.update_mask = 0x82,
- .update_val_enable = 0x02,
+ .update_val = 0x02,
+ .update_val_idle = 0x82,
+ .update_val_normal = 0x02,
},
- [AB8500_LDO_USB] = {
+ [AB9540_LDO_USB] = {
.desc = {
.name = "LDO-USB",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
- .id = AB8500_LDO_USB,
+ .id = AB9540_LDO_USB,
.owner = THIS_MODULE,
.n_voltages = 1,
},
@@ -474,12 +965,14 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x82,
.update_mask = 0x03,
- .update_val_enable = 0x01,
+ .update_val = 0x01,
+ .update_val_idle = 0x03,
+ .update_val_normal = 0x01,
},
- [AB8500_LDO_AUDIO] = {
+ [AB9540_LDO_AUDIO] = {
.desc = {
.name = "LDO-AUDIO",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_AUDIO,
.owner = THIS_MODULE,
@@ -489,12 +982,12 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x02,
- .update_val_enable = 0x02,
+ .update_val = 0x02,
},
- [AB8500_LDO_ANAMIC1] = {
+ [AB9540_LDO_ANAMIC1] = {
.desc = {
.name = "LDO-ANAMIC1",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_ANAMIC1,
.owner = THIS_MODULE,
@@ -504,12 +997,12 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x08,
- .update_val_enable = 0x08,
+ .update_val = 0x08,
},
- [AB8500_LDO_ANAMIC2] = {
+ [AB9540_LDO_ANAMIC2] = {
.desc = {
.name = "LDO-ANAMIC2",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_ANAMIC2,
.owner = THIS_MODULE,
@@ -519,12 +1012,12 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x10,
- .update_val_enable = 0x10,
+ .update_val = 0x10,
},
- [AB8500_LDO_DMIC] = {
+ [AB9540_LDO_DMIC] = {
.desc = {
.name = "LDO-DMIC",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_DMIC,
.owner = THIS_MODULE,
@@ -534,25 +1027,58 @@ static struct ab8500_regulator_info
.update_bank = 0x03,
.update_reg = 0x83,
.update_mask = 0x04,
- .update_val_enable = 0x04,
+ .update_val = 0x04,
},
- [AB8500_LDO_ANA] = {
+
+ /*
+ * Regulators with fixed voltage and normal/idle modes
+ */
+ [AB9540_LDO_ANA] = {
.desc = {
.name = "LDO-ANA",
- .ops = &ab8500_regulator_fixed_ops,
+ .ops = &ab8500_regulator_mode_ops,
.type = REGULATOR_VOLTAGE,
.id = AB8500_LDO_ANA,
.owner = THIS_MODULE,
.n_voltages = 1,
},
.fixed_uV = 1200000,
+ .load_lp_uA = 1000,
.update_bank = 0x04,
.update_reg = 0x06,
.update_mask = 0x0c,
- .update_val_enable = 0x04,
+ .update_val = 0x04,
+ .update_val_idle = 0x0c,
+ .update_val_normal = 0x04,
},
-
+ /*
+ * SysClkReq regulators
+ */
+ [AB9540_SYSCLKREQ_2] = {
+ .desc = {
+ .name = "SYSCLKREQ-2",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_2,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO1,
+ },
+ [AB9540_SYSCLKREQ_4] = {
+ .desc = {
+ .name = "SYSCLKREQ-4",
+ .ops = &ab8500_sysclkreq_ops,
+ .type = REGULATOR_VOLTAGE,
+ .id = AB8500_SYSCLKREQ_4,
+ .owner = THIS_MODULE,
+ .n_voltages = 1,
+ },
+ .fixed_uV = 1, /* bogus value */
+ .gpio_pin = AB8500_PIN_GPIO3,
+ },
};
struct ab8500_reg_init {
@@ -568,13 +1094,13 @@ struct ab8500_reg_init {
.mask = _mask, \
}
+/* AB8500 register init */
static struct ab8500_reg_init ab8500_reg_init[] = {
/*
* 0x30, VanaRequestCtrl
- * 0x0C, VpllRequestCtrl
* 0xc0, VextSupply1RequestCtrl
*/
- REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xfc),
+ REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xf0),
/*
* 0x03, VextSupply2RequestCtrl
* 0x0c, VextSupply3RequestCtrl
@@ -641,13 +1167,21 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f),
/*
* 0x02, SysClkReq2Valid1
- * ...
+ * 0x04, SysClkReq3Valid1
+ * 0x08, SysClkReq4Valid1
+ * 0x10, SysClkReq5Valid1
+ * 0x20, SysClkReq6Valid1
+ * 0x40, SysClkReq7Valid1
* 0x80, SysClkReq8Valid1
*/
REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe),
/*
* 0x02, SysClkReq2Valid2
- * ...
+ * 0x04, SysClkReq3Valid2
+ * 0x08, SysClkReq4Valid2
+ * 0x10, SysClkReq5Valid2
+ * 0x20, SysClkReq6Valid2
+ * 0x40, SysClkReq7Valid2
* 0x80, SysClkReq8Valid2
*/
REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe),
@@ -672,8 +1206,8 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
*/
REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03),
/*
+ * 0x03, VpllRegu (NOTE! PRCMU register bits)
* 0x0c, VanaRegu
- * 0x03, VpllRegu
*/
REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f),
/*
@@ -699,10 +1233,6 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
*/
REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x03),
/*
- * 0x3f, Vsmps1Sel1
- */
- REG_INIT(AB8500_VSMPS1SEL1, 0x04, 0x13, 0x3f),
- /*
* 0x0f, Vaux1Sel
*/
REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f),
@@ -735,79 +1265,412 @@ static struct ab8500_reg_init ab8500_reg_init[] = {
REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16),
};
+/* Possibility to add debug */
+int __attribute__((weak)) ab8500_regulator_debug_init(
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+int __attribute__((weak)) ab8500_regulator_debug_exit(
+ struct platform_device *pdev)
+{
+ return 0;
+}
+
+/* AB9540 register init */
+static struct ab8500_reg_init ab9540_reg_init[] = {
+ /*
+ * 0x03, VarmRequestCtrl
+ * 0x0c, VapeRequestCtrl
+ * 0x30, Vsmps1RequestCtrl
+ * 0xc0, Vsmps2RequestCtrl
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL1, 0x03, 0x03, 0xff),
+ /*
+ * 0x03, Vsmps3RequestCtrl
+ * 0x0c, VpllRequestCtrl
+ * 0x30, VanaRequestCtrl
+ * 0xc0, VextSupply1RequestCtrl
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL2, 0x03, 0x04, 0xff),
+ /*
+ * 0x03, VextSupply2RequestCtrl
+ * 0x0c, VextSupply3RequestCtrl
+ * 0x30, Vaux1RequestCtrl
+ * 0xc0, Vaux2RequestCtrl
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL3, 0x03, 0x05, 0xff),
+ /*
+ * 0x03, Vaux3RequestCtrl
+ * 0x04, SwHPReq
+ */
+ REG_INIT(AB9540_REGUREQUESTCTRL4, 0x03, 0x06, 0x07),
+ /*
+ * 0x01, Vsmps1SysClkReq1HPValid
+ * 0x02, Vsmps2SysClkReq1HPValid
+ * 0x04, Vsmps3SysClkReq1HPValid
+ * 0x08, VanaSysClkReq1HPValid
+ * 0x10, VpllSysClkReq1HPValid
+ * 0x20, Vaux1SysClkReq1HPValid
+ * 0x40, Vaux2SysClkReq1HPValid
+ * 0x80, Vaux3SysClkReq1HPValid
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff),
+ /*
+ * 0x01, VapeSysClkReq1HPValid
+ * 0x02, VarmSysClkReq1HPValid
+ * 0x04, VbbSysClkReq1HPValid
+ * 0x08, VmodSysClkReq1HPValid
+ * 0x10, VextSupply1SysClkReq1HPValid
+ * 0x20, VextSupply2SysClkReq1HPValid
+ * 0x40, VextSupply3SysClkReq1HPValid
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x7f),
+ /*
+ * 0x01, Vsmps1HwHPReq1Valid
+ * 0x02, Vsmps2HwHPReq1Valid
+ * 0x04, Vsmps3HwHPReq1Valid
+ * 0x08, VanaHwHPReq1Valid
+ * 0x10, VpllHwHPReq1Valid
+ * 0x20, Vaux1HwHPReq1Valid
+ * 0x40, Vaux2HwHPReq1Valid
+ * 0x80, Vaux3HwHPReq1Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff),
+ /*
+ * 0x01, VextSupply1HwHPReq1Valid
+ * 0x02, VextSupply2HwHPReq1Valid
+ * 0x04, VextSupply3HwHPReq1Valid
+ * 0x08, VmodHwHPReq1Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x0f),
+ /*
+ * 0x01, Vsmps1HwHPReq2Valid
+ * 0x02, Vsmps2HwHPReq2Valid
+ * 0x03, Vsmps3HwHPReq2Valid
+ * 0x08, VanaHwHPReq2Valid
+ * 0x10, VpllHwHPReq2Valid
+ * 0x20, Vaux1HwHPReq2Valid
+ * 0x40, Vaux2HwHPReq2Valid
+ * 0x80, Vaux3HwHPReq2Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff),
+ /*
+ * 0x01, VextSupply1HwHPReq2Valid
+ * 0x02, VextSupply2HwHPReq2Valid
+ * 0x04, VextSupply3HwHPReq2Valid
+ * 0x08, VmodHwHPReq2Valid
+ */
+ REG_INIT(AB9540_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x0f),
+ /*
+ * 0x01, VapeSwHPReqValid
+ * 0x02, VarmSwHPReqValid
+ * 0x04, Vsmps1SwHPReqValid
+ * 0x08, Vsmps2SwHPReqValid
+ * 0x10, Vsmps3SwHPReqValid
+ * 0x20, VanaSwHPReqValid
+ * 0x40, VpllSwHPReqValid
+ * 0x80, Vaux1SwHPReqValid
+ */
+ REG_INIT(AB9540_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff),
+ /*
+ * 0x01, Vaux2SwHPReqValid
+ * 0x02, Vaux3SwHPReqValid
+ * 0x04, VextSupply1SwHPReqValid
+ * 0x08, VextSupply2SwHPReqValid
+ * 0x10, VextSupply3SwHPReqValid
+ * 0x20, VmodSwHPReqValid
+ */
+ REG_INIT(AB9540_REGUSWHPREQVALID2, 0x03, 0x0e, 0x3f),
+ /*
+ * 0x02, SysClkReq2Valid1
+ * ...
+ * 0x80, SysClkReq8Valid1
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe),
+ /*
+ * 0x02, SysClkReq2Valid2
+ * ...
+ * 0x80, SysClkReq8Valid2
+ */
+ REG_INIT(AB9540_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe),
+ /*
+ * 0x01, Vaux4SwHPReqValid
+ * 0x02, Vaux4HwHPReq2Valid
+ * 0x04, Vaux4HwHPReq1Valid
+ * 0x08, Vaux4SysClkReq1HPValid
+ */
+ REG_INIT(AB9540_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f),
+ /*
+ * 0x02, VTVoutEna
+ * 0x04, Vintcore12Ena
+ * 0x38, Vintcore12Sel
+ * 0x40, Vintcore12LP
+ * 0x80, VTVoutLP
+ */
+ REG_INIT(AB9540_REGUMISC1, 0x03, 0x80, 0xfe),
+ /*
+ * 0x02, VaudioEna
+ * 0x04, VdmicEna
+ * 0x08, Vamic1Ena
+ * 0x10, Vamic2Ena
+ */
+ REG_INIT(AB9540_VAUDIOSUPPLY, 0x03, 0x83, 0x1e),
+ /*
+ * 0x01, Vamic1_dzout
+ * 0x02, Vamic2_dzout
+ */
+ REG_INIT(AB9540_REGUCTRL1VAMIC, 0x03, 0x84, 0x03),
+ /*
+ * 0x03, Vsmps1Regu
+ * 0x0c, Vsmps1SelCtrl
+ * 0x10, Vsmps1AutoMode
+ * 0x20, Vsmps1PWMMode
+ */
+ REG_INIT(AB9540_VSMPS1REGU, 0x04, 0x03, 0x3f),
+ /*
+ * 0x03, Vsmps2Regu
+ * 0x0c, Vsmps2SelCtrl
+ * 0x10, Vsmps2AutoMode
+ * 0x20, Vsmps2PWMMode
+ */
+ REG_INIT(AB9540_VSMPS2REGU, 0x04, 0x04, 0x3f),
+ /*
+ * 0x03, Vsmps3Regu
+ * 0x0c, Vsmps3SelCtrl
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB9540_VSMPS3REGU, 0x04, 0x05, 0x0f),
+ /*
+ * 0x03, VpllRegu
+ * 0x0c, VanaRegu
+ */
+ REG_INIT(AB9540_VPLLVANAREGU, 0x04, 0x06, 0x0f),
+ /*
+ * 0x03, VextSupply1Regu
+ * 0x0c, VextSupply2Regu
+ * 0x30, VextSupply3Regu
+ * 0x40, ExtSupply2Bypass
+ * 0x80, ExtSupply3Bypass
+ */
+ REG_INIT(AB9540_EXTSUPPLYREGU, 0x04, 0x08, 0xff),
+ /*
+ * 0x03, Vaux1Regu
+ * 0x0c, Vaux2Regu
+ */
+ REG_INIT(AB9540_VAUX12REGU, 0x04, 0x09, 0x0f),
+ /*
+ * 0x0c, Vrf1Regu
+ * 0x03, Vaux3Regu
+ */
+ REG_INIT(AB9540_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f),
+ /*
+ * 0x3f, Vsmps1Sel1
+ */
+ REG_INIT(AB9540_VSMPS1SEL1, 0x04, 0x13, 0x3f),
+ /*
+ * 0x3f, Vsmps1Sel2
+ */
+ REG_INIT(AB9540_VSMPS1SEL2, 0x04, 0x14, 0x3f),
+ /*
+ * 0x3f, Vsmps1Sel3
+ */
+ REG_INIT(AB9540_VSMPS1SEL3, 0x04, 0x15, 0x3f),
+ /*
+ * 0x3f, Vsmps2Sel1
+ */
+ REG_INIT(AB9540_VSMPS2SEL1, 0x04, 0x17, 0x3f),
+ /*
+ * 0x3f, Vsmps2Sel2
+ */
+ REG_INIT(AB9540_VSMPS2SEL2, 0x04, 0x18, 0x3f),
+ /*
+ * 0x3f, Vsmps2Sel3
+ */
+ REG_INIT(AB9540_VSMPS2SEL3, 0x04, 0x19, 0x3f),
+ /*
+ * 0x7f, Vsmps3Sel1
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB9540_VSMPS3SEL1, 0x04, 0x1b, 0x7f),
+ /*
+ * 0x7f, Vsmps3Sel2
+ * NOTE! PRCMU register
+ */
+ REG_INIT(AB9540_VSMPS3SEL2, 0x04, 0x1c, 0x7f),
+ /*
+ * 0x0f, Vaux1Sel
+ */
+ REG_INIT(AB9540_VAUX1SEL, 0x04, 0x1f, 0x0f),
+ /*
+ * 0x0f, Vaux2Sel
+ */
+ REG_INIT(AB9540_VAUX2SEL, 0x04, 0x20, 0x0f),
+ /*
+ * 0x07, Vaux3Sel
+ * 0x30, Vrf1Sel
+ */
+ REG_INIT(AB9540_VRF1VAUX3SEL, 0x04, 0x21, 0x37),
+ /*
+ * 0x01, VextSupply12LP
+ */
+ REG_INIT(AB9540_REGUCTRL2SPARE, 0x04, 0x22, 0x01),
+ /*
+ * 0x03, Vaux4RequestCtrl
+ */
+ REG_INIT(AB9540_VAUX4REQCTRL, 0x04, 0x2d, 0x03),
+ /*
+ * 0x03, Vaux4Regu
+ */
+ REG_INIT(AB9540_VAUX4REGU, 0x04, 0x2e, 0x03),
+ /*
+ * 0x08, Vaux4Sel
+ */
+ REG_INIT(AB9540_VAUX4SEL, 0x04, 0x2f, 0x0f),
+ /*
+ * 0x01, VpllDisch
+ * 0x02, Vrf1Disch
+ * 0x04, Vaux1Disch
+ * 0x08, Vaux2Disch
+ * 0x10, Vaux3Disch
+ * 0x20, Vintcore12Disch
+ * 0x40, VTVoutDisch
+ * 0x80, VaudioDisch
+ */
+ REG_INIT(AB9540_REGUCTRLDISCH, 0x04, 0x43, 0xff),
+ /*
+ * 0x01, VsimDisch
+ * 0x02, VanaDisch
+ * 0x04, VdmicPullDownEna
+ * 0x08, VpllPullDownEna
+ * 0x10, VdmicDisch
+ */
+ REG_INIT(AB9540_REGUCTRLDISCH2, 0x04, 0x44, 0x1f),
+ /*
+ * 0x01, Vaux4Disch
+ */
+ REG_INIT(AB9540_REGUCTRLDISCH3, 0x04, 0x48, 0x01),
+};
+
static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
{
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
- struct ab8500_platform_data *pdata;
+ struct ab8500_platform_data *ppdata;
+ struct ab8500_regulator_platform_data *pdata;
int i, err;
+ struct ab8500_regulator_info *regulator_info;
+ int regulator_info_size;
+ struct ab8500_reg_init *reg_init;
+ int reg_init_size;
+ /* cache values needed repeatedly inside for-loops */
if (!ab8500) {
dev_err(&pdev->dev, "null mfd parent\n");
return -EINVAL;
}
- pdata = dev_get_platdata(ab8500->dev);
+
+ ppdata = dev_get_platdata(ab8500->dev);
+ if (!ppdata) {
+ dev_err(&pdev->dev, "null parent pdata\n");
+ return -EINVAL;
+ }
+
+ pdata = ppdata->regulator;
if (!pdata) {
dev_err(&pdev->dev, "null pdata\n");
return -EINVAL;
}
+ if (is_ab9540(ab8500)) {
+ regulator_info = ab9540_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab9540_regulator_info);
+ reg_init = ab9540_reg_init;
+ reg_init_size = AB9540_NUM_REGULATOR_REGISTERS;
+ } else {
+ regulator_info = ab8500_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab8500_regulator_info);
+ reg_init = ab8500_reg_init;
+ reg_init_size = AB8500_NUM_REGULATOR_REGISTERS;
+ }
+
/* make sure the platform data has the correct size */
- if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) {
+ if (pdata->num_regulator != regulator_info_size) {
dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
return -EINVAL;
}
+ /* initialize debug (initial state is recorded with this call) */
+ err = ab8500_regulator_debug_init(pdev);
+ if (err)
+ return err;
+
/* initialize registers */
- for (i = 0; i < pdata->num_regulator_reg_init; i++) {
+ for (i = 0; i < pdata->num_reg_init; i++) {
int id;
- u8 value;
+ u8 mask, value;
- id = pdata->regulator_reg_init[i].id;
- value = pdata->regulator_reg_init[i].value;
+ id = pdata->reg_init[i].id;
+ mask = pdata->reg_init[i].mask;
+ value = pdata->reg_init[i].value;
/* check for configuration errors */
- if (id >= AB8500_NUM_REGULATOR_REGISTERS) {
- dev_err(&pdev->dev,
- "Configuration error: id outside range.\n");
- return -EINVAL;
- }
- if (value & ~ab8500_reg_init[id].mask) {
- dev_err(&pdev->dev,
- "Configuration error: value outside mask.\n");
- return -EINVAL;
- }
+ BUG_ON(id >= reg_init_size);
+ BUG_ON(value & ~mask);
+ BUG_ON(mask & ~reg_init[id].mask);
/* initialize register */
err = abx500_mask_and_set_register_interruptible(&pdev->dev,
- ab8500_reg_init[id].bank,
- ab8500_reg_init[id].addr,
- ab8500_reg_init[id].mask,
- value);
+ reg_init[id].bank,
+ reg_init[id].addr,
+ mask, value);
if (err < 0) {
dev_err(&pdev->dev,
"Failed to initialize 0x%02x, 0x%02x.\n",
- ab8500_reg_init[id].bank,
- ab8500_reg_init[id].addr);
+ reg_init[id].bank,
+ reg_init[id].addr);
return err;
}
dev_vdbg(&pdev->dev,
" init: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
- ab8500_reg_init[id].bank,
- ab8500_reg_init[id].addr,
- ab8500_reg_init[id].mask,
- value);
+ reg_init[id].bank,
+ reg_init[id].addr,
+ mask, value);
+ }
+
+ /*
+ * This changes the default setting for VextSupply3Regu to low power.
+ * Active high or low is depending on OTP which is changed from ab8500v3.0.
+ * Remove this when ab8500v2.0 is no longer important.
+ * This only affects power consumption and it depends on the
+ * HREF OTP configurations.
+ */
+ if (is_ab8500_2p0_or_earlier(ab8500)) {
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev,
+ AB8500_REGU_CTRL2, 0x08, 0x30, 0x30);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Failed to override 0x%02x, 0x%02x.\n",
+ AB8500_REGU_CTRL2, 0x08);
+ return err;
+ }
}
+ /* register external regulators (before Vaux1, 2 and 3) */
+ err = ab8500_ext_regulator_init(pdev);
+ if (err)
+ return err;
+
/* register all regulators */
- for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ for (i = 0; i < regulator_info_size; i++) {
struct ab8500_regulator_info *info = NULL;
/* assign per-regulator data */
- info = &ab8500_regulator_info[i];
+ info = &regulator_info[i];
info->dev = &pdev->dev;
/* fix for hardware before ab8500v2.0 */
- if (abx500_get_chip_id(info->dev) < 0x20) {
+ if (is_ab8500_1p1_or_earlier(ab8500)) {
if (info->desc.id == AB8500_LDO_AUX3) {
info->desc.n_voltages =
ARRAY_SIZE(ldo_vauxn_voltages);
@@ -827,7 +1690,7 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
info->desc.name);
/* when we fail, un-register all earlier regulators */
while (--i >= 0) {
- info = &ab8500_regulator_info[i];
+ info = &regulator_info[i];
regulator_unregister(info->regulator);
}
return err;
@@ -842,11 +1705,23 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
{
- int i;
+ int i, err;
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_regulator_info *regulator_info;
+ int regulator_info_size;
+
- for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
+ if (is_ab9540(ab8500)) {
+ regulator_info = ab9540_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab9540_regulator_info);
+ } else {
+ regulator_info = ab8500_regulator_info;
+ regulator_info_size = ARRAY_SIZE(ab8500_regulator_info);
+ }
+
+ for (i = 0; i < regulator_info_size; i++) {
struct ab8500_regulator_info *info = NULL;
- info = &ab8500_regulator_info[i];
+ info = &regulator_info[i];
dev_vdbg(rdev_get_dev(info->regulator),
"%s-remove\n", info->desc.name);
@@ -854,6 +1729,16 @@ static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
regulator_unregister(info->regulator);
}
+ /* remove external regulators (after Vaux1, 2 and 3) */
+ err = ab8500_ext_regulator_exit(pdev);
+ if (err)
+ return err;
+
+ /* remove regulator debug */
+ err = ab8500_regulator_debug_exit(pdev);
+ if (err)
+ return err;
+
return 0;
}
@@ -886,5 +1771,6 @@ module_exit(ab8500_regulator_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_AUTHOR("Bengt Jonsson <bengt.g.jonsson@stericsson.com>");
MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
MODULE_ALIAS("platform:ab8500-regulator");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 046fb1bd861..bc6c12c8a76 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -81,6 +81,7 @@ struct regulator {
struct device_attribute dev_attr;
struct regulator_dev *rdev;
struct dentry *debugfs;
+ int use;
};
static int _regulator_is_enabled(struct regulator_dev *rdev);
@@ -199,11 +200,13 @@ static int regulator_check_consumers(struct regulator_dev *rdev,
*/
if (!regulator->min_uV && !regulator->max_uV)
continue;
-
- if (*max_uV > regulator->max_uV)
- *max_uV = regulator->max_uV;
- if (*min_uV < regulator->min_uV)
- *min_uV = regulator->min_uV;
+
+ if (regulator->use) {
+ if (*max_uV > regulator->max_uV)
+ *max_uV = regulator->max_uV;
+ if (*min_uV < regulator->min_uV)
+ *min_uV = regulator->min_uV;
+ }
}
if (*min_uV > *max_uV)
@@ -602,6 +605,32 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
static DEVICE_ATTR(suspend_standby_state, 0444,
regulator_suspend_standby_state_show, NULL);
+static ssize_t regulator_use_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ struct regulator *reg;
+ size_t size = 0;
+
+ if (rdev->use_count == 0)
+ return sprintf(buf, "no users\n");
+
+ list_for_each_entry(reg, &rdev->consumer_list, list) {
+ if (!reg->use)
+ continue;
+
+ if (reg->dev != NULL)
+ size += sprintf((buf + size), "%s (%d) ",
+ dev_name(reg->dev), reg->use);
+ else
+ size += sprintf((buf + size), "unknown (%d) ",
+ reg->use);
+ }
+ size += sprintf((buf + size), "\n");
+
+ return size;
+}
+static DEVICE_ATTR(use, 0444, regulator_use_show, NULL);
/*
* These are the only attributes are present for all regulators.
@@ -1491,12 +1520,8 @@ static int _regulator_enable(struct regulator_dev *rdev)
trace_regulator_enable_delay(rdev_get_name(rdev));
- if (delay >= 1000) {
- mdelay(delay / 1000);
- udelay(delay % 1000);
- } else if (delay) {
- udelay(delay);
- }
+ if (delay)
+ usleep_range(delay, delay);
trace_regulator_enable_complete(rdev_get_name(rdev));
@@ -1540,6 +1565,8 @@ int regulator_enable(struct regulator *regulator)
if (ret != 0 && rdev->supply)
regulator_disable(rdev->supply);
+ else
+ regulator->use++;
return ret;
}
@@ -1613,6 +1640,9 @@ int regulator_disable(struct regulator *regulator)
if (ret == 0 && rdev->supply)
regulator_disable(rdev->supply);
+ if (ret == 0)
+ regulator->use--;
+
return ret;
}
EXPORT_SYMBOL_GPL(regulator_disable);
@@ -2699,6 +2729,10 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
struct regulator_ops *ops = rdev->desc->ops;
int status = 0;
+ status = device_create_file(dev, &dev_attr_use);
+ if (status < 0)
+ dev_warn(dev, "Create sysfs file \"use\" failed");
+
/* some attributes need specific methods to be displayed */
if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
(ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0)) {
diff --git a/drivers/regulator/db5500-prcmu.c b/drivers/regulator/db5500-prcmu.c
new file mode 100644
index 00000000000..189362ab8e0
--- /dev/null
+++ b/drivers/regulator/db5500-prcmu.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Power domain regulators on DB5500
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/db5500-prcmu.h>
+
+#include <linux/mfd/dbx500-prcmu.h>
+
+#include "dbx500-prcmu.h"
+static int db5500_regulator_enable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-enable\n",
+ info->desc.name);
+
+ if (!info->is_enabled) {
+ info->is_enabled = true;
+ if (!info->exclude_from_power_state)
+ power_state_active_enable();
+ }
+
+ return 0;
+}
+
+static int db5500_regulator_disable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret = 0;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-disable\n",
+ info->desc.name);
+
+ if (info->is_enabled) {
+ info->is_enabled = false;
+ if (!info->exclude_from_power_state)
+ ret = power_state_active_disable();
+ }
+
+ return ret;
+}
+
+static int db5500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-%s-is_enabled (is_enabled):"
+ " %i\n", info->desc.name, info->is_enabled);
+
+ return info->is_enabled;
+}
+
+/* db5500 regulator operations */
+static struct regulator_ops db5500_regulator_ops = {
+ .enable = db5500_regulator_enable,
+ .disable = db5500_regulator_disable,
+ .is_enabled = db5500_regulator_is_enabled,
+};
+
+/*
+ * EPOD control
+ */
+static bool epod_on[NUM_EPOD_ID];
+static bool epod_ramret[NUM_EPOD_ID];
+
+static inline int epod_id_to_index(u16 epod_id)
+{
+ return epod_id - DB5500_EPOD_ID_BASE;
+}
+
+static int enable_epod(u16 epod_id, bool ramret)
+{
+ int idx = epod_id_to_index(epod_id);
+ int ret;
+
+ if (ramret) {
+ if (!epod_on[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
+ if (ret < 0)
+ return ret;
+ }
+ epod_ramret[idx] = true;
+ } else {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_ON);
+ if (ret < 0)
+ return ret;
+ epod_on[idx] = true;
+ }
+
+ return 0;
+}
+
+static int disable_epod(u16 epod_id, bool ramret)
+{
+ int idx = epod_id_to_index(epod_id);
+ int ret;
+
+ if (ramret) {
+ if (!epod_on[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
+ if (ret < 0)
+ return ret;
+ }
+ epod_ramret[idx] = false;
+ } else {
+ if (epod_ramret[idx]) {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
+ if (ret < 0)
+ return ret;
+ }
+ epod_on[idx] = false;
+ }
+
+ return 0;
+}
+
+/*
+ * Regulator switch
+ */
+static int db5500_regulator_switch_enable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-enable\n",
+ info->desc.name);
+
+ ret = enable_epod(info->epod_id, info->is_ramret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "regulator-switch-%s-enable: prcmu call failed\n",
+ info->desc.name);
+ goto out;
+ }
+
+ info->is_enabled = true;
+out:
+ return ret;
+}
+
+static int db5500_regulator_switch_disable(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev), "regulator-switch-%s-disable\n",
+ info->desc.name);
+
+ ret = disable_epod(info->epod_id, info->is_ramret);
+ if (ret < 0) {
+ dev_err(rdev_get_dev(rdev),
+ "regulator_switch-%s-disable: prcmu call failed\n",
+ info->desc.name);
+ goto out;
+ }
+
+ info->is_enabled = 0;
+out:
+ return ret;
+}
+
+static int db5500_regulator_switch_is_enabled(struct regulator_dev *rdev)
+{
+ struct dbx500_regulator_info *info = rdev_get_drvdata(rdev);
+
+ if (info == NULL)
+ return -EINVAL;
+
+ dev_vdbg(rdev_get_dev(rdev),
+ "regulator-switch-%s-is_enabled (is_enabled): %i\n",
+ info->desc.name, info->is_enabled);
+
+ return info->is_enabled;
+}
+
+static struct regulator_ops db5500_regulator_switch_ops = {
+ .enable = db5500_regulator_switch_enable,
+ .disable = db5500_regulator_switch_disable,
+ .is_enabled = db5500_regulator_switch_is_enabled,
+};
+
+/*
+ * Regulator information
+ */
+#define DB5500_REGULATOR_SWITCH(_name, reg) \
+ [DB5500_REGULATOR_SWITCH_##reg] = { \
+ .desc = { \
+ .name = _name, \
+ .id = DB5500_REGULATOR_SWITCH_##reg, \
+ .ops = &db5500_regulator_switch_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }, \
+ .epod_id = DB5500_EPOD_ID_##reg, \
+}
+
+static struct dbx500_regulator_info
+ dbx500_regulator_info[DB5500_NUM_REGULATORS] = {
+ [DB5500_REGULATOR_VAPE] = {
+ .desc = {
+ .name = "db5500-vape",
+ .id = DB5500_REGULATOR_VAPE,
+ .ops = &db5500_regulator_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ },
+ DB5500_REGULATOR_SWITCH("db5500-sga", SGA),
+ DB5500_REGULATOR_SWITCH("db5500-hva", HVA),
+ DB5500_REGULATOR_SWITCH("db5500-sia", SIA),
+ DB5500_REGULATOR_SWITCH("db5500-disp", DISP),
+ DB5500_REGULATOR_SWITCH("db5500-esram12", ESRAM12),
+};
+
+static int __devinit db5500_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_init_data *db5500_init_data =
+ dev_get_platdata(&pdev->dev);
+ int i, err;
+
+ /* register all regulators */
+ for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) {
+ struct dbx500_regulator_info *info;
+ struct regulator_init_data *init_data = &db5500_init_data[i];
+
+ /* assign per-regulator data */
+ info = &dbx500_regulator_info[i];
+ info->dev = &pdev->dev;
+
+ /* register with the regulator framework */
+ info->rdev = regulator_register(&info->desc, &pdev->dev,
+ init_data, info);
+ if (IS_ERR(info->rdev)) {
+ err = PTR_ERR(info->rdev);
+ dev_err(&pdev->dev, "failed to register %s: err %i\n",
+ info->desc.name, err);
+
+ /* if failing, unregister all earlier regulators */
+ i--;
+ while (i >= 0) {
+ info = &dbx500_regulator_info[i];
+ regulator_unregister(info->rdev);
+ i--;
+ }
+ return err;
+ }
+
+ dev_dbg(rdev_get_dev(info->rdev),
+ "regulator-%s-probed\n", info->desc.name);
+ }
+
+ return 0;
+}
+
+static int __exit db5500_regulator_remove(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dbx500_regulator_info); i++) {
+ struct dbx500_regulator_info *info;
+ info = &dbx500_regulator_info[i];
+
+ dev_vdbg(rdev_get_dev(info->rdev),
+ "regulator-%s-remove\n", info->desc.name);
+
+ regulator_unregister(info->rdev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver db5500_regulator_driver = {
+ .driver = {
+ .name = "db5500-prcmu-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = db5500_regulator_probe,
+ .remove = __exit_p(db5500_regulator_remove),
+};
+
+static int __init db5500_regulator_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&db5500_regulator_driver);
+ if (ret < 0)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit db5500_regulator_exit(void)
+{
+ platform_driver_unregister(&db5500_regulator_driver);
+}
+
+arch_initcall(db5500_regulator_init);
+module_exit(db5500_regulator_exit);
+
+MODULE_AUTHOR("STMicroelectronics/ST-Ericsson");
+MODULE_DESCRIPTION("DB5500 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/dbx500-prcmu.c b/drivers/regulator/dbx500-prcmu.c
index f2e5ecdc586..bee4f7be93b 100644
--- a/drivers/regulator/dbx500-prcmu.c
+++ b/drivers/regulator/dbx500-prcmu.c
@@ -9,6 +9,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/regulator/driver.h>
#include <linux/debugfs.h>
@@ -62,12 +63,105 @@ out:
return ret;
}
+struct ux500_regulator {
+ char *name;
+ void (*enable)(void);
+ int (*disable)(void);
+ int count;
+};
+
+static struct ux500_regulator ux500_atomic_regulators[] = {
+ {
+ .name = "dma40.0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "ssp0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "ssp1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi0",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi2",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "spi3",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "cryp1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+ {
+ .name = "hash1",
+ .enable = power_state_active_enable,
+ .disable = power_state_active_disable,
+ },
+};
+
+struct ux500_regulator *__must_check ux500_regulator_get(struct device *dev)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ux500_atomic_regulators); i++) {
+ if (!strcmp(dev_name(dev), ux500_atomic_regulators[i].name))
+ return &ux500_atomic_regulators[i];
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_get);
+
+int ux500_regulator_atomic_enable(struct ux500_regulator *regulator)
+{
+ if (regulator) {
+ regulator->count++;
+ regulator->enable();
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_atomic_enable);
+
+int ux500_regulator_atomic_disable(struct ux500_regulator *regulator)
+{
+ if (regulator) {
+ regulator->count--;
+ return regulator->disable();
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_atomic_disable);
+
+void ux500_regulator_put(struct ux500_regulator *regulator)
+{
+ /* Here for symetric reasons and for possible future use */
+}
+EXPORT_SYMBOL_GPL(ux500_regulator_put);
+
#ifdef CONFIG_REGULATOR_DEBUG
static struct ux500_regulator_debug {
struct dentry *dir;
- struct dentry *status_file;
- struct dentry *power_state_cnt_file;
struct dbx500_regulator_info *regulator_array;
int num_regulators;
u8 *state_before_suspend;
@@ -119,6 +213,35 @@ static const struct file_operations ux500_regulator_power_state_cnt_fops = {
.owner = THIS_MODULE,
};
+static int ux500_regulator_power_state_use_print(struct seq_file *s, void *p)
+{
+ int i;
+
+ seq_printf(s, "\nPower state usage:\n\n");
+
+ for (i = 0; i < ARRAY_SIZE(ux500_atomic_regulators); i++) {
+ seq_printf(s, "%s\t : %d\n",
+ ux500_atomic_regulators[i].name,
+ ux500_atomic_regulators[i].count);
+ }
+ return 0;
+}
+
+static int ux500_regulator_power_state_use_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, ux500_regulator_power_state_use_print,
+ inode->i_private);
+}
+
+static const struct file_operations ux500_regulator_power_state_use_fops = {
+ .open = ux500_regulator_power_state_use_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
static int ux500_regulator_status_print(struct seq_file *s, void *p)
{
struct device *dev = s->private;
@@ -180,22 +303,27 @@ ux500_regulator_debug_init(struct platform_device *pdev,
{
/* create directory */
rdebug.dir = debugfs_create_dir("ux500-regulator", NULL);
- if (!rdebug.dir)
+ if (IS_ERR_OR_NULL(rdebug.dir))
goto exit_no_debugfs;
/* create "status" file */
- rdebug.status_file = debugfs_create_file("status",
- S_IRUGO, rdebug.dir, &pdev->dev,
- &ux500_regulator_status_fops);
- if (!rdebug.status_file)
- goto exit_destroy_dir;
+ if (IS_ERR_OR_NULL(debugfs_create_file("status",
+ S_IRUGO, rdebug.dir, &pdev->dev,
+ &ux500_regulator_status_fops)))
+ goto exit_fail;
+
+ /* create "power-state-count" file */
+ if (IS_ERR_OR_NULL(debugfs_create_file("power-state-count",
+ S_IRUGO, rdebug.dir, &pdev->dev,
+ &ux500_regulator_power_state_cnt_fops)))
+ goto exit_fail;
/* create "power-state-count" file */
- rdebug.power_state_cnt_file = debugfs_create_file("power-state-count",
- S_IRUGO, rdebug.dir, &pdev->dev,
- &ux500_regulator_power_state_cnt_fops);
- if (!rdebug.power_state_cnt_file)
- goto exit_destroy_status;
+ if (IS_ERR_OR_NULL(debugfs_create_file("power-state-usage",
+ S_IRUGO, rdebug.dir, &pdev->dev,
+ &ux500_regulator_power_state_use_fops)))
+ goto exit_fail;
+
rdebug.regulator_array = regulator_info;
rdebug.num_regulators = num_regulators;
@@ -204,27 +332,22 @@ ux500_regulator_debug_init(struct platform_device *pdev,
if (!rdebug.state_before_suspend) {
dev_err(&pdev->dev,
"could not allocate memory for saving state\n");
- goto exit_destroy_power_state;
+ goto exit_fail;
}
rdebug.state_after_suspend = kzalloc(num_regulators, GFP_KERNEL);
if (!rdebug.state_after_suspend) {
dev_err(&pdev->dev,
"could not allocate memory for saving state\n");
- goto exit_free;
+ goto exit_fail;
}
dbx500_regulator_testcase(regulator_info, num_regulators);
return 0;
-exit_free:
+exit_fail:
kfree(rdebug.state_before_suspend);
-exit_destroy_power_state:
- debugfs_remove(rdebug.power_state_cnt_file);
-exit_destroy_status:
- debugfs_remove(rdebug.status_file);
-exit_destroy_dir:
- debugfs_remove(rdebug.dir);
+ debugfs_remove_recursive(rdebug.dir);
exit_no_debugfs:
dev_err(&pdev->dev, "failed to create debugfs entries.\n");
return -ENOMEM;
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8c8377d50c4..3e47885660e 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -704,6 +704,13 @@ config RTC_DRV_PCF50633
If you say yes here you get support for the RTC subsystem of the
NXP PCF50633 used in embedded systems.
+config RTC_DRV_AB
+ tristate "ST-Ericsson AB5500 RTC"
+ depends on AB5500_CORE
+ help
+ Select this to enable the ST-Ericsson AB5500 Mixed Signal IC RTC
+ support. This chip contains a battery- and capacitor-backed RTC.
+
config RTC_DRV_AB3100
tristate "ST-Ericsson AB3100 RTC"
depends on AB3100_CORE
@@ -715,6 +722,7 @@ config RTC_DRV_AB3100
config RTC_DRV_AB8500
tristate "ST-Ericsson AB8500 RTC"
depends on AB8500_CORE
+ select RTC_INTF_DEV_UIE_EMUL
help
Select this to enable the ST-Ericsson AB8500 power management IC RTC
support. This chip contains a battery- and capacitor-backed RTC.
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 727ae7786e6..56766bbc519 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -16,6 +16,7 @@ rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
# Keep the list ordered.
obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
+obj-$(CONFIG_RTC_DRV_AB) += rtc-ab.o
obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
diff --git a/drivers/rtc/rtc-ab.c b/drivers/rtc/rtc-ab.c
new file mode 100644
index 00000000000..009409f39d7
--- /dev/null
+++ b/drivers/rtc/rtc-ab.c
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Rabin Vincent <rabin.vincent@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab5500.h>
+
+#define AB5500_RTC_CLOCK_RATE 32768
+#define AB5500_RTC 0x00
+#define AB5500_RTC_ALARM (1 << 1)
+#define AB5500_READREQ 0x01
+#define AB5500_READREQ_REQ 0x01
+#define AB5500_AL0 0x02
+#define AB5500_TI0 0x06
+
+/**
+ * struct ab_rtc - variant specific data
+ * @irqname: optional name for the alarm interrupt resource
+ * @epoch: epoch to adjust year to
+ * @bank: AB bank where this block is present
+ * @rtc: address of the "RTC" (control) register
+ * @rtc_alarmon: mask of the alarm enable bit in the above register
+ * @ti0: address of the TI0 register. The rest of the TI
+ * registers are assumed to contiguously follow this one.
+ * @nr_ti: number of TI* registers
+ * @al0: address of the AL0 register. The rest of the
+ * AL registers are assumed to contiguously follow this one.
+ * @nr_al: number of AL* registers
+ * @startup: optional function to initialize the RTC
+ * @alarm_to_regs: function to convert alarm time in seconds
+ * to a list of AL register values
+ * @time_to_regs: function to convert alarm time in seconds
+ * to a list of TI register values
+ * @regs_to_alarm: function to convert a list of AL register
+ * values to the alarm time in seconds
+ * @regs_to_time: function to convert a list of TI register
+ * values to the alarm time in seconds
+ * @request_read: optional function to request a read from the TI* registers
+ * @request_write: optional function to request a write to the TI* registers
+ */
+struct ab_rtc {
+ const char *irqname;
+ unsigned int epoch;
+
+ u8 bank;
+ u8 rtc;
+ u8 rtc_alarmon;
+ u8 ti0;
+ int nr_ti;
+ u8 al0;
+ int nr_al;
+
+ int (*startup)(struct device *dev);
+ void (*alarm_to_regs)(struct device *dev, unsigned long secs, u8 *regs);
+ void (*time_to_regs)(struct device *dev, unsigned long secs, u8 *regs);
+ unsigned long (*regs_to_alarm)(struct device *dev, u8 *regs);
+ unsigned long (*regs_to_time)(struct device *dev, u8 *regs);
+ int (*request_read)(struct device *dev);
+ int (*request_write)(struct device *dev);
+};
+
+static const struct ab_rtc *to_ab_rtc(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ return (struct ab_rtc *)pdev->id_entry->driver_data;
+}
+
+/* Calculate the number of seconds since year, for epoch adjustment */
+static unsigned long ab_rtc_get_elapsed_seconds(unsigned int year)
+{
+ unsigned long secs;
+ struct rtc_time tm = {
+ .tm_year = year - 1900,
+ .tm_mday = 1,
+ };
+
+ rtc_tm_to_time(&tm, &secs);
+
+ return secs;
+}
+
+static int ab5500_rtc_request_read(struct device *dev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned long timeout;
+ int err;
+
+ err = abx500_set_register_interruptible(dev, variant->bank,
+ AB5500_READREQ,
+ AB5500_READREQ_REQ);
+ if (err < 0)
+ return err;
+
+ timeout = jiffies + HZ;
+ while (time_before(jiffies, timeout)) {
+ u8 value;
+
+ err = abx500_get_register_interruptible(dev, variant->bank,
+ AB5500_READREQ, &value);
+ if (err < 0)
+ return err;
+
+ if (!(value & AB5500_READREQ_REQ))
+ return 0;
+
+ msleep(1);
+ }
+
+ return -EIO;
+}
+
+static void
+ab5500_rtc_time_to_regs(struct device *dev, unsigned long secs, u8 *regs)
+{
+ unsigned long mins = secs / 60;
+ u64 fat_time;
+
+ secs %= 60;
+
+ fat_time = secs * AB5500_RTC_CLOCK_RATE;
+ fat_time |= (u64)mins << 21;
+
+ regs[0] = (fat_time) & 0xFF;
+ regs[1] = (fat_time >> 8) & 0xFF;
+ regs[2] = (fat_time >> 16) & 0xFF;
+ regs[3] = (fat_time >> 24) & 0xFF;
+ regs[4] = (fat_time >> 32) & 0xFF;
+ regs[5] = (fat_time >> 40) & 0xFF;
+}
+
+static unsigned long
+ab5500_rtc_regs_to_time(struct device *dev, u8 *regs)
+{
+ u64 fat_time = ((u64)regs[5] << 40) | ((u64)regs[4] << 32) |
+ ((u64)regs[3] << 24) | ((u64)regs[2] << 16) |
+ ((u64)regs[1] << 8) | regs[0];
+ unsigned long secs = (fat_time & 0x1fffff) / AB5500_RTC_CLOCK_RATE;
+ unsigned long mins = fat_time >> 21;
+
+ return mins * 60 + secs;
+}
+
+static void
+ab5500_rtc_alarm_to_regs(struct device *dev, unsigned long secs, u8 *regs)
+{
+ unsigned long mins = secs / 60;
+
+#ifdef CONFIG_ANDROID
+ /*
+ * Needed because Android believes all hw have a wake-up resolution in
+ * seconds.
+ */
+ mins++;
+#endif
+
+ regs[0] = mins & 0xFF;
+ regs[1] = (mins >> 8) & 0xFF;
+ regs[2] = (mins >> 16) & 0xFF;
+}
+
+static unsigned long
+ab5500_rtc_regs_to_alarm(struct device *dev, u8 *regs)
+{
+ unsigned long mins = ((unsigned long)regs[2] << 16) |
+ ((unsigned long)regs[1] << 8) |
+ regs[0];
+ unsigned long secs = mins * 60;
+
+ return secs;
+}
+
+static const struct ab_rtc ab5500_rtc = {
+ .irqname = "RTC_Alarm",
+ .bank = AB5500_BANK_RTC,
+ .rtc = AB5500_RTC,
+ .rtc_alarmon = AB5500_RTC_ALARM,
+ .ti0 = AB5500_TI0,
+ .nr_ti = 6,
+ .al0 = AB5500_AL0,
+ .nr_al = 3,
+ .epoch = 2000,
+ .time_to_regs = ab5500_rtc_time_to_regs,
+ .regs_to_time = ab5500_rtc_regs_to_time,
+ .alarm_to_regs = ab5500_rtc_alarm_to_regs,
+ .regs_to_alarm = ab5500_rtc_regs_to_alarm,
+ .request_read = ab5500_rtc_request_read,
+};
+
+static int ab_rtc_request_read(struct device *dev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (!variant->request_read)
+ return 0;
+
+ return variant->request_read(dev);
+}
+
+static int ab_rtc_request_write(struct device *dev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (!variant->request_write)
+ return 0;
+
+ return variant->request_write(dev);
+}
+
+static bool ab_rtc_valid_time(struct device *dev, struct rtc_time *time)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (!variant->epoch)
+ return true;
+
+ return time->tm_year >= variant->epoch - 1900;
+}
+
+static int
+ab_rtc_tm_to_time(struct device *dev, struct rtc_time *tm, unsigned long *secs)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ rtc_tm_to_time(tm, secs);
+
+ if (variant->epoch)
+ *secs -= ab_rtc_get_elapsed_seconds(variant->epoch);
+
+ return 0;
+}
+
+static int
+ab_rtc_time_to_tm(struct device *dev, unsigned long secs, struct rtc_time *tm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+
+ if (variant->epoch)
+ secs += ab_rtc_get_elapsed_seconds(variant->epoch);
+
+ rtc_time_to_tm(secs, tm);
+
+ return 0;
+}
+
+static int ab_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned char buf[variant->nr_ti];
+ unsigned long secs;
+ int err;
+
+ err = ab_rtc_request_read(dev);
+ if (err)
+ return err;
+
+ err = abx500_get_register_page_interruptible(dev, variant->bank,
+ variant->ti0,
+ buf, variant->nr_ti);
+ if (err)
+ return err;
+
+ secs = variant->regs_to_time(dev, buf);
+ ab_rtc_time_to_tm(dev, secs, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int ab_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned char buf[variant->nr_ti];
+ unsigned long secs;
+ u8 reg = variant->ti0;
+ int err;
+ int i;
+
+ if (!ab_rtc_valid_time(dev, tm))
+ return -EINVAL;
+
+ ab_rtc_tm_to_time(dev, tm, &secs);
+ variant->time_to_regs(dev, secs, buf);
+
+ for (i = 0; i < variant->nr_ti; i++, reg++) {
+ err = abx500_set_register_interruptible(dev, variant->bank,
+ reg, buf[i]);
+ if (err)
+ return err;
+ }
+
+ return ab_rtc_request_write(dev);
+}
+
+static int ab_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned long secs;
+ u8 buf[variant->nr_al];
+ u8 rtcval;
+ int err;
+
+ err = abx500_get_register_interruptible(dev, variant->bank,
+ variant->rtc, &rtcval);
+ if (err)
+ return err;
+
+ alarm->enabled = !!(rtcval & variant->rtc_alarmon);
+ alarm->pending = 0;
+
+ err = abx500_get_register_page_interruptible(dev, variant->bank,
+ variant->al0, buf,
+ variant->nr_al);
+ if (err)
+ return err;
+
+ secs = variant->regs_to_alarm(dev, buf);
+ ab_rtc_time_to_tm(dev, secs, &alarm->time);
+
+ return rtc_valid_tm(&alarm->time);
+}
+
+static int ab_rtc_alarm_enable(struct device *dev, unsigned int enabled)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ u8 mask = variant->rtc_alarmon;
+ u8 value = enabled ? mask : 0;
+
+ return abx500_mask_and_set_register_interruptible(dev, variant->bank,
+ variant->rtc, mask,
+ value);
+}
+
+static int ab_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ const struct ab_rtc *variant = to_ab_rtc(dev);
+ unsigned char buf[variant->nr_al];
+ unsigned long secs;
+ u8 reg = variant->al0;
+ int err;
+ int i;
+
+ if (!ab_rtc_valid_time(dev, &alarm->time))
+ return -EINVAL;
+
+ ab_rtc_tm_to_time(dev, &alarm->time, &secs);
+ variant->alarm_to_regs(dev, secs, buf);
+
+ /*
+ * Disable alarm first. Otherwise the RTC may not detect an alarm
+ * reprogrammed for the same time without disabling the alarm in
+ * between the programmings.
+ */
+ err = ab_rtc_alarm_enable(dev, false);
+ if (err)
+ return err;
+
+ for (i = 0; i < variant->nr_al; i++, reg++) {
+ err = abx500_set_register_interruptible(dev, variant->bank,
+ reg, buf[i]);
+ if (err)
+ return err;
+ }
+
+ return alarm->enabled ? ab_rtc_alarm_enable(dev, true) : 0;
+}
+
+static const struct rtc_class_ops ab_rtc_ops = {
+ .read_time = ab_rtc_read_time,
+ .set_time = ab_rtc_set_time,
+ .read_alarm = ab_rtc_read_alarm,
+ .set_alarm = ab_rtc_set_alarm,
+ .alarm_irq_enable = ab_rtc_alarm_enable,
+};
+
+static irqreturn_t ab_rtc_irq(int irq, void *dev_id)
+{
+ unsigned long events = RTC_IRQF | RTC_AF;
+ struct rtc_device *rtc = dev_id;
+
+ rtc_update_irq(rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit ab_rtc_probe(struct platform_device *pdev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(&pdev->dev);
+ int err;
+ struct rtc_device *rtc;
+ int irq = -ENXIO;
+
+ if (variant->irqname) {
+ irq = platform_get_irq_byname(pdev, variant->irqname);
+ if (irq < 0)
+ return irq;
+ }
+
+ if (variant->startup) {
+ err = variant->startup(&pdev->dev);
+ if (err)
+ return err;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+
+ rtc = rtc_device_register("ab8500-rtc", &pdev->dev, &ab_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "Registration failed\n");
+ err = PTR_ERR(rtc);
+ return err;
+ }
+
+ if (irq >= 0) {
+ err = request_any_context_irq(irq, ab_rtc_irq,
+ IRQF_NO_SUSPEND,
+ pdev->id_entry->name,
+ rtc);
+ if (err < 0) {
+ dev_err(&pdev->dev, "could not get irq: %d\n", err);
+ goto out_unregister;
+ }
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+
+out_unregister:
+ rtc_device_unregister(rtc);
+ return err;
+}
+
+static int __devexit ab_rtc_remove(struct platform_device *pdev)
+{
+ const struct ab_rtc *variant = to_ab_rtc(&pdev->dev);
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ int irq = platform_get_irq_byname(pdev, variant->irqname);
+
+ if (irq >= 0)
+ free_irq(irq, rtc);
+ rtc_device_unregister(rtc);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_device_id ab_rtc_id_table[] = {
+ { "ab5500-rtc", (kernel_ulong_t)&ab5500_rtc, },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, ab_rtc_id_table);
+
+static struct platform_driver ab_rtc_driver = {
+ .driver.name = "ab-rtc",
+ .driver.owner = THIS_MODULE,
+ .id_table = ab_rtc_id_table,
+ .probe = ab_rtc_probe,
+ .remove = __devexit_p(ab_rtc_remove),
+};
+
+static int __init ab_rtc_init(void)
+{
+ return platform_driver_register(&ab_rtc_driver);
+}
+module_init(ab_rtc_init);
+
+static void __exit ab_rtc_exit(void)
+{
+ platform_driver_unregister(&ab_rtc_driver);
+}
+module_exit(ab_rtc_exit);
+
+MODULE_AUTHOR("Rabin Vincent <rabin.vincent@stericsson.com>");
+MODULE_DESCRIPTION("AB5500 RTC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 4bcf9ca2818..63b3a672a17 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -88,22 +88,17 @@ static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (retval < 0)
return retval;
- /* Early AB8500 chips will not clear the rtc read request bit */
- if (abx500_get_chip_id(dev) == 0) {
- usleep_range(1000, 1000);
- } else {
- /* Wait for some cycles after enabling the rtc read in ab8500 */
- while (time_before(jiffies, timeout)) {
- retval = abx500_get_register_interruptible(dev,
- AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
- if (retval < 0)
- return retval;
-
- if (!(value & RTC_READ_REQUEST))
- break;
-
- usleep_range(1000, 5000);
- }
+ /* Wait for some cycles after enabling the rtc read in ab8500 */
+ while (time_before(jiffies, timeout)) {
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
+ if (retval < 0)
+ return retval;
+
+ if (!(value & RTC_READ_REQUEST))
+ break;
+
+ usleep_range(1000, 5000);
}
/* Read the Watchtime registers */
@@ -224,8 +219,8 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
int retval, i;
unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
- unsigned long mins, secs = 0;
-
+ unsigned long mins, secs = 0, cursec=0;
+ struct rtc_time curtm;
if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
dev_dbg(dev, "year should be equal to or greater than %d\n",
AB8500_RTC_EPOCH);
@@ -235,14 +230,36 @@ static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
/* Get the number of seconds since 1970 */
rtc_tm_to_time(&alarm->time, &secs);
+ /* Check whether alarm is set less than 1min.
+ * Since our RTC doesn't support alarm resolution less than 1min,
+ * return -EINVAL, so UIE EMUL can take it up, incase of UIE_ON
+ */
+ ab8500_rtc_read_time(dev, &curtm); /* Read current time */
+ rtc_tm_to_time(&curtm, &cursec);
+ if ((secs - cursec) < 59) {
+ dev_dbg(dev, "Alarm less than 1 minute not supported\n");
+ return -EINVAL;
+ }
+
/*
* Convert it to the number of seconds since 01-01-2000 00:00:00, since
* we only have a small counter in the RTC.
*/
secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+#ifndef CONFIG_ANDROID
+ secs += 30; /* Round to nearest minute */
+#endif
+
mins = secs / 60;
+#ifdef CONFIG_ANDROID
+ /*
+ * Needed due to Android believes all hw have a wake-up resolution
+ * in seconds.
+ */
+ mins++;
+#endif
buf[2] = mins & 0xFF;
buf[1] = (mins >> 8) & 0xFF;
buf[0] = (mins >> 16) & 0xFF;
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index f027c063fb2..cc0533994f6 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -220,17 +220,9 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id)
unsigned long events = 0;
rtcmis = readl(ldata->base + RTC_MIS);
- if (rtcmis) {
- writel(rtcmis, ldata->base + RTC_ICR);
-
- if (rtcmis & RTC_BIT_AI)
- events |= (RTC_AF | RTC_IRQF);
-
- /* Timer interrupt is only available in ST variants */
- if ((rtcmis & RTC_BIT_PI) &&
- (ldata->hw_designer == AMBA_VENDOR_ST))
- events |= (RTC_PF | RTC_IRQF);
-
+ if (rtcmis & RTC_BIT_AI) {
+ writel(RTC_BIT_AI, ldata->base + RTC_ICR);
+ events |= (RTC_AF | RTC_IRQF);
rtc_update_irq(ldata->rtc, 1, events);
return IRQ_HANDLED;
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index 400ae2121a2..8bf53a76a33 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -489,6 +489,13 @@ static void giveback(struct pl022 *pl022)
pl022->cur_transfer = NULL;
pl022->cur_chip = NULL;
spi_finalize_current_message(pl022->master);
+
+ /* disable the SPI/SSP operation */
+ writew((readw(SSP_CR1(pl022->virtbase)) &
+ (~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
+
+ /* This message is completed, so let's turn off the clocks & power */
+ pm_runtime_put(&pl022->adev->dev);
}
/**
@@ -895,6 +902,12 @@ static int configure_dma(struct pl022 *pl022)
struct dma_async_tx_descriptor *rxdesc;
struct dma_async_tx_descriptor *txdesc;
+ /* DMA burstsize should be same as the FIFO trigger level */
+ rx_conf.src_maxburst = pl022->rx_lev_trig ? 1 <<
+ (pl022->rx_lev_trig + 1) : pl022->rx_lev_trig;
+ tx_conf.dst_maxburst = pl022->tx_lev_trig ? 1 <<
+ (pl022->tx_lev_trig + 1) : pl022->tx_lev_trig;
+
/* Check that the channels are available */
if (!rxchan || !txchan)
return -ENODEV;
@@ -2048,6 +2061,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
+ pm_runtime_enable(dev);
+ pm_runtime_resume(dev);
+
pl022->clk = clk_get(&adev->dev, NULL);
if (IS_ERR(pl022->clk)) {
status = PTR_ERR(pl022->clk);
@@ -2158,6 +2174,7 @@ pl022_remove(struct amba_device *adev)
clk_disable(pl022->clk);
clk_unprepare(pl022->clk);
clk_put(pl022->clk);
+ pm_runtime_disable(&adev->dev);
iounmap(pl022->virtbase);
amba_release_regions(adev);
tasklet_disable(&pl022->pump_transfers);
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index eb1dee26bda..a4495997d1b 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -8,6 +8,16 @@ config ANDROID
if ANDROID
+config ANDROID_AB5500_TIMED_VIBRA
+ bool "AB5500 Timed Output Vibrator"
+ depends on AB5500_CORE
+ depends on ANDROID_TIMED_OUTPUT
+ default y
+ help
+ Say Y here to enable linear/rotary vibrator driver using timed
+ output class device for ST-Ericsson's based on ST-Ericsson's
+ AB5500 Mix-Sig PMIC
+
config ANDROID_BINDER_IPC
bool "Android Binder IPC Driver"
default n
@@ -53,6 +63,14 @@ config ANDROID_LOW_MEMORY_KILLER
---help---
Register processes to be killed when memory is low
+config ANDROID_STE_TIMED_VIBRA
+ bool "ST-Ericsson Timed Output Vibrator"
+ depends on SND_SOC_AB8500
+ depends on ANDROID_TIMED_OUTPUT
+ default y
+ help
+ ST-Ericsson's vibrator driver using timed output class device
+
source "drivers/staging/android/switch/Kconfig"
config ANDROID_INTF_ALARM
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index 9b6c9ed91f6..0d642936a0b 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_ANDROID_AB5500_TIMED_VIBRA) += ab5500-timed-vibra.o
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o
obj-$(CONFIG_ASHMEM) += ashmem.o
obj-$(CONFIG_ANDROID_LOGGER) += logger.o
@@ -6,6 +7,7 @@ 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
+obj-$(CONFIG_ANDROID_STE_TIMED_VIBRA) += ste_timed_vibra.o
obj-$(CONFIG_ANDROID_SWITCH) += switch/
obj-$(CONFIG_ANDROID_INTF_ALARM) += alarm.o
obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o
diff --git a/drivers/staging/android/ab5500-timed-vibra.c b/drivers/staging/android/ab5500-timed-vibra.c
new file mode 100644
index 00000000000..35c627a6285
--- /dev/null
+++ b/drivers/staging/android/ab5500-timed-vibra.c
@@ -0,0 +1,490 @@
+/*
+ * ab5500-vibra.c - driver for vibrator in ST-Ericsson AB5500 chip
+ *
+ * Copyright (C) 2011 ST-Ericsson SA.
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/err.h>
+#include "timed_output.h"
+
+#include <linux/mfd/abx500.h> /* abx500_* */
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500/ab5500-gpadc.h>
+#include <linux/ab5500-vibra.h>
+
+#define AB5500_VIBRA_DEV_NAME "ab5500:vibra"
+#define AB5500_VIBRA_DRV_NAME "ab5500-vibrator"
+
+/* Vibrator Register Address Offsets */
+#define AB5500_VIB_CTRL 0x10
+#define AB5500_VIB_VOLT 0x11
+#define AB5500_VIB_FUND_FREQ 0x12 /* Linear vibra resonance freq. */
+#define AB5500_VIB_FUND_DUTY 0x13
+#define AB5500_KELVIN_ANA 0xB1
+#define AB5500_VIBRA_KELVIN 0xFE
+
+/* Vibrator Control */
+#define AB5500_VIB_DISABLE (0x80)
+#define AB5500_VIB_PWR_ON (0x40)
+#define AB5500_VIB_FUND_EN (0x20)
+#define AB5500_VIB_FREQ_SHIFT (0)
+#define AB5500_VIB_DUTY_SHIFT (3)
+#define AB5500_VIB_VOLT_SHIFT (0)
+#define AB5500_VIB_PULSE_SHIFT (4)
+#define VIBRA_KELVIN_ENABLE (0x90)
+#define VIBRA_KELVIN_VOUT (0x20)
+
+/* Vibrator Freq. (in HZ) and Duty */
+enum ab5500_vibra_freq {
+ AB5500_VIB_FREQ_1HZ = 1,
+ AB5500_VIB_FREQ_2HZ,
+ AB5500_VIB_FREQ_4HZ,
+ AB5500_VIB_FREQ_8HZ,
+};
+
+enum ab5500_vibra_duty {
+ AB5500_VIB_DUTY_100 = 0,
+ AB5500_VIB_DUTY_75 = 8,
+ AB5500_VIB_DUTY_50 = 16,
+ AB5500_VIB_DUTY_25 = 24,
+};
+
+/* Linear vibrator resonance freq. duty */
+#define AB5500_VIB_RDUTY_50 (0x7F)
+
+/* Vibration magnitudes */
+#define AB5500_VIB_FREQ_MAX (4)
+#define AB5500_VIB_DUTY_MAX (4)
+
+static u8 vib_freq[AB5500_VIB_FREQ_MAX] = {
+ AB5500_VIB_FREQ_1HZ,
+ AB5500_VIB_FREQ_2HZ,
+ AB5500_VIB_FREQ_4HZ,
+ AB5500_VIB_FREQ_8HZ,
+};
+
+static u8 vib_duty[AB5500_VIB_DUTY_MAX] = {
+ AB5500_VIB_DUTY_100,
+ AB5500_VIB_DUTY_75,
+ AB5500_VIB_DUTY_50,
+ AB5500_VIB_DUTY_25,
+};
+
+/**
+ * struct ab5500_vibra - Vibrator driver interal info.
+ * @tdev: Pointer to timed output device structure
+ * @dev: Reference to vibra device structure
+ * @vibra_workqueue: Pointer to vibrator workqueue structure
+ * @vibra_work: Vibrator work
+ * @gpadc: Gpadc instance
+ * @vibra_wait: Vibrator wait queue head
+ * @vibra_lock: Vibrator lock
+ * @timeout_ms: Indicates how long time the vibrator will be enabled
+ * @timeout_start: Start of vibrator in jiffies
+ * @pdata: Local pointer to platform data with vibrator parameters
+ * @magnitude: required vibration strength
+ * @enable: Vibrator running status
+ * @eol: Vibrator end of life(eol) status
+ **/
+struct ab5500_vibra {
+ struct timed_output_dev tdev;
+ struct device *dev;
+ struct workqueue_struct *vibra_workqueue;
+ struct work_struct vibra_work;
+ struct ab5500_gpadc *gpadc;
+ wait_queue_head_t vibra_wait;
+ spinlock_t vibra_lock;
+ unsigned int timeout_ms;
+ unsigned long timeout_start;
+ struct ab5500_vibra_platform_data *pdata;
+ u8 magnitude;
+ bool enable;
+ bool eol;
+};
+
+static inline u8 vibra_magnitude(u8 mag)
+{
+ mag /= (AB5500_VIB_FREQ_MAX * AB5500_VIB_DUTY_MAX);
+ mag = vib_freq[mag / AB5500_VIB_FREQ_MAX] << AB5500_VIB_FREQ_SHIFT;
+ mag |= vib_duty[mag % AB5500_VIB_DUTY_MAX] << AB5500_VIB_DUTY_SHIFT;
+
+ return mag;
+}
+
+static int ab5500_setup_vibra_kelvin(struct ab5500_vibra* vibra)
+{
+ int ret;
+
+ /* Establish the kelvin IP connection to be measured */
+ ret = abx500_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP,
+ AB5500_KELVIN_ANA, VIBRA_KELVIN_ENABLE);
+ if (ret < 0) {
+ dev_err(vibra->dev, "failed to set kelvin network\n");
+ return ret;
+ }
+
+ /* Select vibra parameter to be measured */
+ ret = abx500_set_register_interruptible(vibra->dev, AB5500_BANK_VIBRA,
+ AB5500_VIBRA_KELVIN, VIBRA_KELVIN_VOUT);
+ if (ret < 0)
+ dev_err(vibra->dev, "failed to select the kelvin param\n");
+
+ return ret;
+}
+
+static int ab5500_vibra_start(struct ab5500_vibra* vibra)
+{
+ u8 ctrl = 0;
+
+ ctrl = AB5500_VIB_PWR_ON |
+ vibra_magnitude(vibra->magnitude);
+
+ if (vibra->pdata->type == AB5500_VIB_LINEAR)
+ ctrl |= AB5500_VIB_FUND_EN;
+
+ return abx500_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIBRA, AB5500_VIB_CTRL, ctrl);
+}
+
+static int ab5500_vibra_stop(struct ab5500_vibra* vibra)
+{
+ return abx500_mask_and_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIBRA, AB5500_VIB_CTRL,
+ AB5500_VIB_PWR_ON, 0);
+}
+
+static int ab5500_vibra_eol_check(struct ab5500_vibra* vibra)
+{
+ int ret, vout;
+
+ ret = ab5500_setup_vibra_kelvin(vibra);
+ if (ret < 0) {
+ dev_err(vibra->dev, "failed to setup kelvin network\n");
+ return ret;
+ }
+
+ /* Start vibra to measure voltage */
+ ret = ab5500_vibra_start(vibra);
+ if (ret < 0) {
+ dev_err(vibra->dev, "failed to start vibra\n");
+ return ret;
+ }
+ /* 20ms delay required for voltage rampup */
+ wait_event_interruptible_timeout(vibra->vibra_wait,
+ 0, msecs_to_jiffies(20));
+
+ vout = ab5500_gpadc_convert(vibra->gpadc, VIBRA_KELVIN);
+ if (vout < 0) {
+ dev_err(vibra->dev, "failed to read gpadc vibra\n");
+ return vout;
+ }
+
+ /* Stop vibra after measuring voltage */
+ ret = ab5500_vibra_stop(vibra);
+ if (ret < 0) {
+ dev_err(vibra->dev, "failed to stop vibra\n");
+ return ret;
+ }
+ /* Check for vibra eol condition */
+ if (vout < vibra->pdata->eol_voltage) {
+ vibra->eol = true;
+ dev_err(vibra->dev, "Vibra eol detected. Disabling vibra!\n");
+ }
+
+ return ret;
+}
+
+/**
+ * ab5500_vibra_work() - Vibrator work, turns on/off vibrator
+ * @work: Pointer to work structure
+ *
+ * This function is called from workqueue, turns on/off vibrator
+ **/
+static void ab5500_vibra_work(struct work_struct *work)
+{
+ struct ab5500_vibra *vibra = container_of(work,
+ struct ab5500_vibra, vibra_work);
+ unsigned long flags;
+ int ret;
+
+ ret = ab5500_vibra_start(vibra);
+ if (ret < 0)
+ dev_err(vibra->dev, "reg[%d] w failed: %d\n",
+ AB5500_VIB_CTRL, ret);
+
+ wait_event_interruptible_timeout(vibra->vibra_wait,
+ 0, msecs_to_jiffies(vibra->timeout_ms));
+
+ ret = ab5500_vibra_stop(vibra);
+ if (ret < 0)
+ dev_err(vibra->dev, "reg[%d] w failed: %d\n",
+ AB5500_VIB_CTRL, ret);
+
+ spin_lock_irqsave(&vibra->vibra_lock, flags);
+
+ vibra->timeout_start = 0;
+ vibra->enable = false;
+
+ spin_unlock_irqrestore(&vibra->vibra_lock, flags);
+}
+
+/**
+ * vibra_enable() - Enables vibrator
+ * @tdev: Pointer to timed output device structure
+ * @timeout: Time indicating how long vibrator will be enabled
+ *
+ * This function enables vibrator
+ **/
+static void vibra_enable(struct timed_output_dev *tdev, int timeout)
+{
+ struct ab5500_vibra *vibra = dev_get_drvdata(tdev->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vibra->vibra_lock, flags);
+
+ if ((!vibra->enable || timeout) && !vibra->eol) {
+ vibra->enable = true;
+
+ vibra->timeout_ms = timeout;
+ vibra->timeout_start = jiffies;
+ queue_work(vibra->vibra_workqueue, &vibra->vibra_work);
+ }
+
+ spin_unlock_irqrestore(&vibra->vibra_lock, flags);
+}
+
+/**
+ * vibra_get_time() - Returns remaining time to disabling vibration
+ * @tdev: Pointer to timed output device structure
+ *
+ * This function returns time remaining to disabling vibration
+ *
+ * Returns:
+ * Returns remaining time to disabling vibration
+ **/
+static int vibra_get_time(struct timed_output_dev *tdev)
+{
+ struct ab5500_vibra *vibra = dev_get_drvdata(tdev->dev);
+ unsigned int ms;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vibra->vibra_lock, flags);
+
+ if (vibra->enable)
+ ms = jiffies_to_msecs(vibra->timeout_start +
+ msecs_to_jiffies(vibra->timeout_ms) - jiffies);
+ else
+ ms = 0;
+
+ spin_unlock_irqrestore(&vibra->vibra_lock, flags);
+
+ return ms;
+}
+
+static int ab5500_vibra_reg_init(struct ab5500_vibra *vibra)
+{
+ int ret = 0;
+ u8 ctrl = 0;
+ u8 pulse = 0;
+
+ ctrl = (AB5500_VIB_DUTY_50 << AB5500_VIB_DUTY_SHIFT) |
+ (AB5500_VIB_FREQ_8HZ << AB5500_VIB_FREQ_SHIFT);
+
+ if (vibra->pdata->type == AB5500_VIB_LINEAR) {
+ ctrl |= AB5500_VIB_FUND_EN;
+
+ if (vibra->pdata->voltage > AB5500_VIB_VOLT_MAX)
+ vibra->pdata->voltage = AB5500_VIB_VOLT_MAX;
+
+ pulse = (vibra->pdata->pulse << AB5500_VIB_PULSE_SHIFT) |
+ (vibra->pdata->voltage << AB5500_VIB_VOLT_SHIFT);
+ ret = abx500_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIBRA, AB5500_VIB_VOLT,
+ pulse);
+ if (ret < 0) {
+ dev_err(vibra->dev,
+ "reg[%#x] w %#x failed: %d\n",
+ AB5500_VIB_VOLT, vibra->pdata->voltage, ret);
+ return ret;
+ }
+
+ ret = abx500_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIBRA, AB5500_VIB_FUND_FREQ,
+ vibra->pdata->res_freq);
+ if (ret < 0) {
+ dev_err(vibra->dev, "reg[%#x] w %#x failed: %d\n",
+ AB5500_VIB_FUND_FREQ,
+ vibra->pdata->res_freq, ret);
+ return ret;
+ }
+
+ ret = abx500_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIBRA, AB5500_VIB_FUND_DUTY,
+ AB5500_VIB_RDUTY_50);
+ if (ret < 0) {
+ dev_err(vibra->dev, "reg[%#x] w %#x failed: %d\n",
+ AB5500_VIB_FUND_DUTY,
+ AB5500_VIB_RDUTY_50, ret);
+ return ret;
+ }
+ }
+
+ ret = abx500_set_register_interruptible(vibra->dev,
+ AB5500_BANK_VIBRA, AB5500_VIB_CTRL, ctrl);
+ if (ret < 0) {
+ dev_err(vibra->dev, "reg[%#x] w %#x failed: %d\n",
+ AB5500_VIB_CTRL, ctrl, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int ab5500_vibra_register_dev(struct ab5500_vibra *vibra,
+ struct platform_device *pdev)
+{
+ int ret = 0;
+
+ ret = timed_output_dev_register(&vibra->tdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register timed output device\n");
+ goto err_out;
+ }
+
+ dev_set_drvdata(vibra->tdev.dev, vibra);
+
+
+ /* Create workqueue just for timed output vibrator */
+ vibra->vibra_workqueue =
+ create_singlethread_workqueue("ste-timed-output-vibra");
+ if (!vibra->vibra_workqueue) {
+ dev_err(&pdev->dev, "failed to allocate workqueue\n");
+ ret = -ENOMEM;
+ goto exit_output_unregister;
+ }
+
+ init_waitqueue_head(&vibra->vibra_wait);
+ INIT_WORK(&vibra->vibra_work, ab5500_vibra_work);
+ spin_lock_init(&vibra->vibra_lock);
+
+ platform_set_drvdata(pdev, vibra);
+
+ return ret;
+
+exit_output_unregister:
+ timed_output_dev_unregister(&vibra->tdev);
+err_out:
+ return ret;
+}
+
+static int __devinit ab5500_vibra_probe(struct platform_device *pdev)
+{
+ struct ab5500_vibra_platform_data *pdata = pdev->dev.platform_data;
+ struct ab5500_vibra *vibra = NULL;
+ int ret = 0;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "platform data required. Quitting...\n");
+ return -ENODEV;
+ }
+
+ vibra = kzalloc(sizeof(struct ab5500_vibra), GFP_KERNEL);
+ if (vibra == NULL)
+ return -ENOMEM;
+
+ vibra->tdev.name = "vibrator";
+ vibra->tdev.enable = vibra_enable;
+ vibra->tdev.get_time = vibra_get_time;
+ vibra->timeout_start = 0;
+ vibra->enable = false;
+ vibra->magnitude = pdata->magnitude;
+ vibra->pdata = pdata;
+ vibra->dev = &pdev->dev;
+
+ if (vibra->pdata->eol_voltage) {
+ vibra->gpadc = ab5500_gpadc_get("ab5500-adc.0");
+ if (IS_ERR(vibra->gpadc))
+ goto err_alloc;
+ }
+
+ if (vibra->pdata->type == AB5500_VIB_LINEAR)
+ dev_info(&pdev->dev, "Linear Type Vibrators\n");
+ else
+ dev_info(&pdev->dev, "Rotary Type Vibrators\n");
+
+ ret = ab5500_vibra_reg_init(vibra);
+ if (ret < 0)
+ goto err_alloc;
+
+ ret = ab5500_vibra_register_dev(vibra, pdev);
+ if (ret < 0)
+ goto err_alloc;
+
+ /* Perform vibra eol diagnostics if eol_voltage is set */
+ if (vibra->pdata->eol_voltage) {
+ ret = ab5500_vibra_eol_check(vibra);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "EOL check failed\n");
+ }
+
+ dev_info(&pdev->dev, "initialization success\n");
+
+ return ret;
+
+err_alloc:
+ kfree(vibra);
+
+ return ret;
+}
+
+static int __devexit ab5500_vibra_remove(struct platform_device *pdev)
+{
+ struct ab5500_vibra *vibra = platform_get_drvdata(pdev);
+
+ timed_output_dev_unregister(&vibra->tdev);
+ destroy_workqueue(vibra->vibra_workqueue);
+ kfree(vibra);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ab5500_vibra_driver = {
+ .probe = ab5500_vibra_probe,
+ .remove = __devexit_p(ab5500_vibra_remove),
+ .driver = {
+ .name = AB5500_VIBRA_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ab5500_vibra_module_init(void)
+{
+ return platform_driver_register(&ab5500_vibra_driver);
+}
+
+static void __exit ab5500_vibra_module_exit(void)
+{
+ platform_driver_unregister(&ab5500_vibra_driver);
+}
+
+module_init(ab5500_vibra_module_init);
+module_exit(ab5500_vibra_module_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>");
+MODULE_DESCRIPTION("Timed Output Driver for AB5500 Vibrator");
+
diff --git a/drivers/staging/android/ste_timed_vibra.c b/drivers/staging/android/ste_timed_vibra.c
new file mode 100644
index 00000000000..4621b2fb441
--- /dev/null
+++ b/drivers/staging/android/ste_timed_vibra.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>
+ * for ST-Ericsson
+ * License Terms: GNU General Public License v2
+ */
+
+#include <linux/kernel.h>
+#include <linux/hrtimer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/ste_timed_vibra.h>
+#include <linux/delay.h>
+#include "timed_output.h"
+
+/**
+ * struct vibra_info - Vibrator information structure
+ * @tdev: Pointer to timed output device structure
+ * @linear_workqueue: Pointer to linear vibrator workqueue structure
+ * @linear_work: Linear Vibrator work
+ * @linear_tick: Linear Vibrator high resolution timer
+ * @vibra_workqueue: Pointer to vibrator workqueue structure
+ * @vibra_work: Vibrator work
+ * @vibra_timer: Vibrator high resolution timer
+ * @vibra_lock: Vibrator lock
+ * @vibra_state: Actual vibrator state
+ * @state_force: Indicates if oppositive state is requested
+ * @timeout: Indicates how long time the vibrator will be enabled
+ * @time_passed: Total time passed in states
+ * @pdata: Local pointer to platform data with vibrator parameters
+ *
+ * Structure vibra_info holds vibrator information
+ **/
+struct vibra_info {
+ struct timed_output_dev tdev;
+ struct workqueue_struct *linear_workqueue;
+ struct work_struct linear_work;
+ struct hrtimer linear_tick;
+ struct workqueue_struct *vibra_workqueue;
+ struct work_struct vibra_work;
+ struct hrtimer vibra_timer;
+ spinlock_t vibra_lock;
+ enum ste_timed_vibra_states vibra_state;
+ bool state_force;
+ unsigned int timeout;
+ unsigned int time_passed;
+ struct ste_timed_vibra_platform_data *pdata;
+};
+
+/*
+ * Linear vibrator hardware operates on a particular resonance
+ * frequency. The resonance frequency (f) may also vary with h/w.
+ * This define is half time period (t) in micro seconds (us).
+ * For resonance frequency f = 150 Hz
+ * t = T/2 = ((1/150) / 2) = 3333 usec.
+ */
+#define LINEAR_RESONANCE 3333
+
+/**
+ * linear_vibra_work() - Linear Vibrator work, turns on/off vibrator
+ * @work: Pointer to work structure
+ *
+ * This function is called from workqueue, turns on/off vibrator
+ **/
+static void linear_vibra_work(struct work_struct *work)
+{
+ struct vibra_info *vinfo =
+ container_of(work, struct vibra_info, linear_work);
+ unsigned char speed_pos = 0, speed_neg = 0;
+ ktime_t ktime;
+ static unsigned char toggle;
+
+ if (toggle) {
+ speed_pos = vinfo->pdata->boost_level;
+ speed_neg = 0;
+ } else {
+ speed_neg = vinfo->pdata->boost_level;
+ speed_pos = 0;
+ }
+
+ toggle = !toggle;
+ vinfo->pdata->timed_vibra_control(speed_pos, speed_neg,
+ speed_pos, speed_neg);
+
+ if ((vinfo->vibra_state != STE_VIBRA_IDLE) &&
+ (vinfo->vibra_state != STE_VIBRA_OFF)) {
+ ktime = ktime_set((LINEAR_RESONANCE / USEC_PER_SEC),
+ (LINEAR_RESONANCE % USEC_PER_SEC) * NSEC_PER_USEC);
+ hrtimer_start(&vinfo->linear_tick, ktime, HRTIMER_MODE_REL);
+ }
+}
+
+/**
+ * vibra_control_work() - Vibrator work, turns on/off vibrator
+ * @work: Pointer to work structure
+ *
+ * This function is called from workqueue, turns on/off vibrator
+ **/
+static void vibra_control_work(struct work_struct *work)
+{
+ struct vibra_info *vinfo =
+ container_of(work, struct vibra_info, vibra_work);
+ unsigned val = 0;
+ unsigned char speed_pos = 0, speed_neg = 0;
+ unsigned long flags;
+
+ /*
+ * Cancel scheduled timer if it has not started
+ * else it will wait for timer callback to complete.
+ * It should be done before taking vibra_lock to
+ * prevent race condition, as timer callback also
+ * takes same lock.
+ */
+ hrtimer_cancel(&vinfo->vibra_timer);
+
+ spin_lock_irqsave(&vinfo->vibra_lock, flags);
+
+ switch (vinfo->vibra_state) {
+ case STE_VIBRA_BOOST:
+ /* Turn on both vibrators with boost speed */
+ speed_pos = vinfo->pdata->boost_level;
+ val = vinfo->pdata->boost_time;
+ break;
+ case STE_VIBRA_ON:
+ /* Turn on both vibrators with speed */
+ speed_pos = vinfo->pdata->on_level;
+ val = vinfo->timeout - vinfo->pdata->boost_time;
+ break;
+ case STE_VIBRA_OFF:
+ /* Turn on both vibrators with reversed speed */
+ speed_neg = vinfo->pdata->off_level;
+ val = vinfo->pdata->off_time;
+ break;
+ case STE_VIBRA_IDLE:
+ vinfo->time_passed = 0;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&vinfo->vibra_lock, flags);
+
+ /* Send new settings (only for rotary vibrators) */
+ if (!vinfo->pdata->is_linear_vibra)
+ vinfo->pdata->timed_vibra_control(speed_pos, speed_neg,
+ speed_pos, speed_neg);
+
+ if (vinfo->vibra_state != STE_VIBRA_IDLE) {
+ /* Start timer if it's not in IDLE state */
+ ktime_t ktime;
+ ktime = ktime_set((val / MSEC_PER_SEC),
+ (val % MSEC_PER_SEC) * NSEC_PER_MSEC),
+ hrtimer_start(&vinfo->vibra_timer, ktime, HRTIMER_MODE_REL);
+ } else if (vinfo->pdata->is_linear_vibra) {
+ /* Cancel work and timers of linear vibrator in IDLE state */
+ hrtimer_cancel(&vinfo->linear_tick);
+ flush_workqueue(vinfo->linear_workqueue);
+ vinfo->pdata->timed_vibra_control(0, 0, 0, 0);
+ }
+}
+
+/**
+ * vibra_enable() - Enables vibrator
+ * @tdev: Pointer to timed output device structure
+ * @timeout: Time indicating how long vibrator will be enabled
+ *
+ * This function enables vibrator
+ **/
+static void vibra_enable(struct timed_output_dev *tdev, int timeout)
+{
+ struct vibra_info *vinfo = dev_get_drvdata(tdev->dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vinfo->vibra_lock, flags);
+ switch (vinfo->vibra_state) {
+ case STE_VIBRA_IDLE:
+ if (timeout)
+ vinfo->vibra_state = STE_VIBRA_BOOST;
+ else /* Already disabled */
+ break;
+
+ vinfo->state_force = false;
+ /* Trim timeout */
+ vinfo->timeout = timeout < vinfo->pdata->boost_time ?
+ vinfo->pdata->boost_time : timeout;
+
+ if (vinfo->pdata->is_linear_vibra)
+ queue_work(vinfo->linear_workqueue,
+ &vinfo->linear_work);
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+ break;
+ case STE_VIBRA_BOOST:
+ /* Force only when user requested OFF while BOOST */
+ if (!timeout)
+ vinfo->state_force = true;
+ break;
+ case STE_VIBRA_ON:
+ /* If user requested OFF */
+ if (!timeout) {
+ if (vinfo->pdata->is_linear_vibra)
+ hrtimer_cancel(&vinfo->linear_tick);
+ /* Cancel timer if it has not expired yet.
+ * Else setting the vibra_state to STE_VIBRA_OFF
+ * will make take care that vibrator will move to
+ * STE_VIBRA_IDLE in timer callback just after
+ * this function call.
+ */
+ hrtimer_try_to_cancel(&vinfo->vibra_timer);
+ vinfo->vibra_state = STE_VIBRA_OFF;
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+ }
+ break;
+ case STE_VIBRA_OFF:
+ /* Force only when user requested ON while OFF */
+ if (timeout)
+ vinfo->state_force = true;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&vinfo->vibra_lock, flags);
+}
+
+/**
+ * linear_vibra_tick() - Generate resonance frequency waveform
+ * @hrtimer: Pointer to high resolution timer structure
+ *
+ * This function helps in generating the resonance frequency
+ * waveform required for linear vibrators
+ *
+ * Returns:
+ * Returns value which indicates whether hrtimer should be restarted
+ **/
+static enum hrtimer_restart linear_vibra_tick(struct hrtimer *hrtimer)
+{
+ struct vibra_info *vinfo =
+ container_of(hrtimer, struct vibra_info, linear_tick);
+
+ if ((vinfo->vibra_state != STE_VIBRA_IDLE) &&
+ (vinfo->vibra_state != STE_VIBRA_OFF)) {
+ queue_work(vinfo->linear_workqueue, &vinfo->linear_work);
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * vibra_timer_expired() - Handles vibrator machine state
+ * @hrtimer: Pointer to high resolution timer structure
+ *
+ * This function handles vibrator machine state
+ *
+ * Returns:
+ * Returns value which indicates wether hrtimer should be restarted
+ **/
+static enum hrtimer_restart vibra_timer_expired(struct hrtimer *hrtimer)
+{
+ struct vibra_info *vinfo =
+ container_of(hrtimer, struct vibra_info, vibra_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vinfo->vibra_lock, flags);
+ switch (vinfo->vibra_state) {
+ case STE_VIBRA_BOOST:
+ /* If BOOST finished and force, go to OFF */
+ if (vinfo->state_force)
+ vinfo->vibra_state = STE_VIBRA_OFF;
+ else
+ vinfo->vibra_state = STE_VIBRA_ON;
+ vinfo->time_passed = vinfo->pdata->boost_time;
+ break;
+ case STE_VIBRA_ON:
+ vinfo->vibra_state = STE_VIBRA_OFF;
+ vinfo->time_passed = vinfo->timeout;
+ break;
+ case STE_VIBRA_OFF:
+ /* If OFF finished and force, go to ON */
+ if (vinfo->state_force)
+ vinfo->vibra_state = STE_VIBRA_ON;
+ else
+ vinfo->vibra_state = STE_VIBRA_IDLE;
+ vinfo->time_passed += vinfo->pdata->off_time;
+ break;
+ case STE_VIBRA_IDLE:
+ break;
+ default:
+ break;
+ }
+ vinfo->state_force = false;
+ spin_unlock_irqrestore(&vinfo->vibra_lock, flags);
+
+ queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work);
+
+ return HRTIMER_NORESTART;
+}
+
+/**
+ * vibra_get_time() - Returns remaining time to disabling vibration
+ * @tdev: Pointer to timed output device structure
+ *
+ * This function returns time remaining to disabling vibration
+ *
+ * Returns:
+ * Returns remaining time to disabling vibration
+ **/
+static int vibra_get_time(struct timed_output_dev *tdev)
+{
+ struct vibra_info *vinfo = dev_get_drvdata(tdev->dev);
+ u32 ms;
+
+ if (hrtimer_active(&vinfo->vibra_timer)) {
+ ktime_t remain = hrtimer_get_remaining(&vinfo->vibra_timer);
+ ms = (u32) ktime_to_ms(remain);
+ return ms + vinfo->time_passed;
+ } else
+ return 0;
+}
+
+static int __devinit ste_timed_vibra_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct vibra_info *vinfo;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -ENODEV;
+ }
+
+ vinfo = kmalloc(sizeof *vinfo, GFP_KERNEL);
+ if (!vinfo) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ vinfo->tdev.name = "vibrator";
+ vinfo->tdev.enable = vibra_enable;
+ vinfo->tdev.get_time = vibra_get_time;
+ vinfo->time_passed = 0;
+ vinfo->vibra_state = STE_VIBRA_IDLE;
+ vinfo->state_force = false;
+ vinfo->pdata = pdev->dev.platform_data;
+
+ if (vinfo->pdata->is_linear_vibra)
+ dev_info(&pdev->dev, "Linear Type Vibrators\n");
+ else
+ dev_info(&pdev->dev, "Rotary Type Vibrators\n");
+
+ ret = timed_output_dev_register(&vinfo->tdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register timed output device\n");
+ goto exit_free_vinfo;
+ }
+
+ dev_set_drvdata(vinfo->tdev.dev, vinfo);
+
+ vinfo->linear_workqueue =
+ create_singlethread_workqueue("ste-timed-linear-vibra");
+ if (!vinfo->linear_workqueue) {
+ dev_err(&pdev->dev, "failed to allocate workqueue\n");
+ ret = -ENOMEM;
+ goto exit_timed_output_unregister;
+ }
+
+ /* Create workqueue just for timed output vibrator */
+ vinfo->vibra_workqueue =
+ create_singlethread_workqueue("ste-timed-output-vibra");
+ if (!vinfo->vibra_workqueue) {
+ dev_err(&pdev->dev, "failed to allocate workqueue\n");
+ ret = -ENOMEM;
+ goto exit_destroy_workqueue;
+ }
+
+ INIT_WORK(&vinfo->linear_work, linear_vibra_work);
+ INIT_WORK(&vinfo->vibra_work, vibra_control_work);
+ spin_lock_init(&vinfo->vibra_lock);
+ hrtimer_init(&vinfo->linear_tick, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer_init(&vinfo->vibra_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vinfo->linear_tick.function = linear_vibra_tick;
+ vinfo->vibra_timer.function = vibra_timer_expired;
+
+ platform_set_drvdata(pdev, vinfo);
+ return 0;
+
+exit_destroy_workqueue:
+ destroy_workqueue(vinfo->linear_workqueue);
+exit_timed_output_unregister:
+ timed_output_dev_unregister(&vinfo->tdev);
+exit_free_vinfo:
+ kfree(vinfo);
+ return ret;
+}
+
+static int __devexit ste_timed_vibra_remove(struct platform_device *pdev)
+{
+ struct vibra_info *vinfo = platform_get_drvdata(pdev);
+
+ timed_output_dev_unregister(&vinfo->tdev);
+ destroy_workqueue(vinfo->linear_workqueue);
+ destroy_workqueue(vinfo->vibra_workqueue);
+ kfree(vinfo);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ste_timed_vibra_driver = {
+ .driver = {
+ .name = "ste_timed_output_vibra",
+ .owner = THIS_MODULE,
+ },
+ .probe = ste_timed_vibra_probe,
+ .remove = __devexit_p(ste_timed_vibra_remove)
+};
+
+static int __init ste_timed_vibra_init(void)
+{
+ return platform_driver_register(&ste_timed_vibra_driver);
+}
+module_init(ste_timed_vibra_init);
+
+static void __exit ste_timed_vibra_exit(void)
+{
+ platform_driver_unregister(&ste_timed_vibra_driver);
+}
+module_exit(ste_timed_vibra_exit);
+
+MODULE_AUTHOR("Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>");
+MODULE_DESCRIPTION("STE Timed Output Vibrator");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 070b442c1f8..b2831ed01bb 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -63,6 +63,14 @@ config SERIAL_AMBA_PL011_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
+config SERIAL_AMBA_PL011_CLOCK_CONTROL
+ bool "Support for clock control on AMBA serial port"
+ depends on SERIAL_AMBA_PL011
+ select CONSOLE_POLL
+ ---help---
+ Say Y here if you wish to use amba set_termios function to control
+ the pl011 clock. Any positive baudrate passed enables clock,
+
config SERIAL_SB1250_DUART
tristate "BCM1xxx on-chip DUART serial support"
depends on SIBYTE_SB1xxx_SOC=y
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 3d569cd68f5..d356b940382 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -47,11 +47,13 @@
#include <linux/amba/serial.h>
#include <linux/clk.h>
#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/delay.h>
#include <linux/types.h>
+#include <linux/pm_runtime.h>
#include <asm/io.h>
#include <asm/sizes.h>
@@ -67,6 +69,37 @@
#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
#define UART_DUMMY_DR_RX (1 << 16)
+/*
+ * The console UART is handled differently for power management (it doesn't
+ * take the regulator, in order to allow the system to go to sleep even if the
+ * console is open). This should be removed once cable detect is in place.
+ */
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#define uart_console(port) ((port)->cons \
+ && (port)->cons->index == (port)->line)
+#else
+#define uart_console(port) (0)
+#endif
+
+/* Available amba pl011 port clock states */
+enum pl011_clk_states {
+ PL011_CLK_OFF = 0, /* clock disabled */
+ PL011_CLK_REQUEST_OFF, /* disable after TX flushed */
+ PL011_CLK_ON, /* clock enabled */
+ PL011_PORT_OFF, /* port disabled */
+};
+
+/*
+ * Backup registers to be used during regulator startup/shutdown
+ */
+static const u32 backup_regs[] = {
+ UART011_IBRD,
+ UART011_FBRD,
+ ST_UART011_LCRH_RX,
+ ST_UART011_LCRH_TX,
+ UART011_CR,
+ UART011_IMSC,
+};
#define UART_WA_SAVE_NR 14
@@ -89,7 +122,9 @@ static const u32 uart_wa_reg[UART_WA_SAVE_NR] = {
};
static u32 uart_wa_regdata[UART_WA_SAVE_NR];
-static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0);
+static unsigned int uart_wa_tlet_line;
+static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa,
+ (unsigned long) &uart_wa_tlet_line);
/* There is by now at least one vendor with differing details, so handle it */
struct vendor_data {
@@ -158,10 +193,18 @@ struct uart_amba_port {
unsigned int im; /* interrupt mask */
unsigned int old_status;
unsigned int fifosize; /* vendor-specific */
+ unsigned int ifls; /* vendor-specific */
unsigned int lcrh_tx; /* vendor-specific */
unsigned int lcrh_rx; /* vendor-specific */
unsigned int old_cr; /* state during shutdown */
bool autorts;
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+ enum pl011_clk_states clk_state; /* actual clock state */
+ struct delayed_work clk_off_work; /* work used for clock off */
+ unsigned int clk_off_delay; /* clock off delay */
+#endif
+ struct regulator *regulator;
+ u32 backup[ARRAY_SIZE(backup_regs)];
char type[12];
bool interrupt_may_hang; /* vendor-specific */
#ifdef CONFIG_DMA_ENGINE
@@ -1070,13 +1113,17 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
*/
static void pl011_lockup_wa(unsigned long data)
{
- struct uart_amba_port *uap = amba_ports[0];
+ struct uart_amba_port *uap = amba_ports[*(unsigned int *)data];
void __iomem *base = uap->port.membase;
struct circ_buf *xmit = &uap->port.state->xmit;
struct tty_struct *tty = uap->port.state->port.tty;
int buf_empty_retries = 200;
int loop;
+ /* Exit early if there is no tty */
+ if (!tty)
+ return;
+
/* Stop HCI layer from submitting data for tx */
tty->hw_stopped = 1;
while (!uart_circ_empty(xmit)) {
@@ -1117,6 +1164,260 @@ static void pl011_lockup_wa(unsigned long data)
tty->hw_stopped = 0;
}
+static void __pl011_startup(struct uart_amba_port *uap)
+{
+ unsigned int cr;
+
+ writew(uap->ifls, uap->port.membase + UART011_IFLS);
+
+ /*
+ * Provoke TX FIFO interrupt into asserting.
+ */
+ cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
+ writew(cr, uap->port.membase + UART011_CR);
+ writew(0, uap->port.membase + UART011_FBRD);
+ writew(1, uap->port.membase + UART011_IBRD);
+ writew(0, uap->port.membase + uap->lcrh_rx);
+ if (uap->lcrh_tx != uap->lcrh_rx) {
+ int i;
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX register,
+ * to get this delay write read only register 10 times
+ */
+ for (i = 0; i < 10; ++i)
+ writew(0xff, uap->port.membase + UART011_MIS);
+ writew(0, uap->port.membase + uap->lcrh_tx);
+ }
+ writew(0, uap->port.membase + UART01x_DR);
+ while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
+ barrier();
+}
+
+/* Backup the registers during regulator startup/shutdown */
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+static int pl011_backup(struct uart_amba_port *uap, bool suspend)
+{
+ int i, cnt;
+
+ if (!suspend) {
+ __pl011_startup(uap);
+ writew(0, uap->port.membase + UART011_CR);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(backup_regs); i++) {
+ if (suspend)
+ uap->backup[i] = readw(uap->port.membase +
+ backup_regs[i]);
+ else {
+ if (backup_regs[i] == ST_UART011_LCRH_TX) {
+ /*
+ * Wait 10 PCLKs before writing LCRH_TX
+ * register, to get this delay write read
+ * only register 10 times
+ */
+ for (cnt = 0; cnt < 10; ++cnt)
+ writew(0xff, uap->port.membase +
+ UART011_MIS);
+ }
+
+ writew(uap->backup[i],
+ uap->port.membase + backup_regs[i]);
+ }
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+/* Turn clock off if TX buffer is empty, otherwise reschedule */
+static void pl011_clock_off(struct work_struct *work)
+{
+ struct uart_amba_port *uap = container_of(work, struct uart_amba_port,
+ clk_off_work.work);
+ struct uart_port *port = &uap->port;
+ struct circ_buf *xmit = &port->state->xmit;
+ unsigned long flags;
+ bool disable_regulator = false;
+ bool runtime_put = false;
+ unsigned int busy, interrupt_status;
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ interrupt_status = readw(uap->port.membase + UART011_MIS);
+ busy = readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY;
+
+ if (uap->clk_state == PL011_CLK_REQUEST_OFF) {
+ if (uart_circ_empty(xmit) && !interrupt_status && !busy) {
+ if (!uart_console(&uap->port) && uap->regulator) {
+ pl011_backup(uap, true);
+ disable_regulator = true;
+ }
+ runtime_put = true;
+ uap->clk_state = PL011_CLK_OFF;
+ clk_disable(uap->clk);
+ } else
+ schedule_delayed_work(&uap->clk_off_work,
+ uap->clk_off_delay);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ if (disable_regulator)
+ regulator_disable(uap->regulator);
+ if (runtime_put)
+ pm_runtime_put_sync(uap->port.dev);
+}
+
+/* Request to turn off uart clock once pending TX is flushed */
+static void pl011_clock_request_off(struct uart_port *port)
+{
+ unsigned long flags;
+ struct uart_amba_port *uap = (struct uart_amba_port *)(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ if (uap->clk_state == PL011_CLK_ON) {
+ uap->clk_state = PL011_CLK_REQUEST_OFF;
+ /* Turn off later */
+ schedule_delayed_work(&uap->clk_off_work,
+ uap->clk_off_delay);
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+/* Request to immediately turn on uart clock */
+static void pl011_clock_on(struct uart_port *port)
+{
+ unsigned long flags;
+ struct uart_amba_port *uap = (struct uart_amba_port *)(port);
+
+ spin_lock_irqsave(&port->lock, flags);
+
+ switch (uap->clk_state) {
+ case PL011_CLK_OFF:
+ pm_runtime_get_sync(uap->port.dev);
+ clk_enable(uap->clk);
+ if (!uart_console(&uap->port) && uap->regulator) {
+ spin_unlock_irqrestore(&port->lock, flags);
+ regulator_enable(uap->regulator);
+ spin_lock_irqsave(&port->lock, flags);
+ pl011_backup(uap, false);
+ }
+ /* fallthrough */
+ case PL011_CLK_REQUEST_OFF:
+ __cancel_delayed_work(&uap->clk_off_work);
+ uap->clk_state = PL011_CLK_ON;
+ break;
+ default:
+ break;
+ }
+
+ spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void pl011_clock_check(struct uart_amba_port *uap)
+{
+ /* Reshedule work during off request */
+ if (uap->clk_state == PL011_CLK_REQUEST_OFF)
+ /* New TX - restart work */
+ if (__cancel_delayed_work(&uap->clk_off_work))
+ schedule_delayed_work(&uap->clk_off_work,
+ uap->clk_off_delay);
+}
+
+static int pl011_power_startup(struct uart_amba_port *uap)
+{
+ int retval = 0;
+
+ if (uap->clk_state == PL011_PORT_OFF) {
+ pm_runtime_get_sync(uap->port.dev);
+ if (!uart_console(&uap->port) && uap->regulator)
+ regulator_enable(uap->regulator);
+ retval = clk_enable(uap->clk);
+ if (!retval) {
+ uap->clk_state = PL011_CLK_ON;
+ } else {
+ uap->clk_state = PL011_PORT_OFF;
+ pm_runtime_put_sync(uap->port.dev);
+ }
+ }
+
+ return retval;
+}
+
+static void pl011_power_shutdown(struct uart_amba_port *uap)
+{
+ bool disable_regulator = false;
+ bool runtime_put = false;
+
+ cancel_delayed_work_sync(&uap->clk_off_work);
+
+ spin_lock_irq(&uap->port.lock);
+ if (uap->clk_state == PL011_CLK_ON ||
+ uap->clk_state == PL011_CLK_REQUEST_OFF) {
+ clk_disable(uap->clk);
+ runtime_put = true;
+ if (!uart_console(&uap->port) && uap->regulator)
+ disable_regulator = true;
+ }
+ uap->clk_state = PL011_PORT_OFF;
+ spin_unlock_irq(&uap->port.lock);
+
+ if (disable_regulator)
+ regulator_disable(uap->regulator);
+ if (runtime_put)
+ pm_runtime_put_sync(uap->port.dev);
+}
+
+static void
+pl011_clock_control(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+ speed_t new_baud = tty_termios_baud_rate(termios);
+
+ if (new_baud == 0)
+ pl011_clock_request_off(port);
+ else
+ pl011_clock_on(port);
+}
+
+static void pl011_clock_control_init(struct uart_amba_port *uap)
+{
+ uap->clk_state = PL011_PORT_OFF;
+ INIT_DELAYED_WORK(&uap->clk_off_work, pl011_clock_off);
+ uap->clk_off_delay = HZ / 10; /* 100 ms */
+}
+
+#else
+/* Blank functions for clock control */
+static inline void pl011_clock_check(struct uart_amba_port *uap)
+{
+}
+
+static inline int pl011_power_startup(struct uart_amba_port *uap)
+{
+ pm_runtime_get_sync(uap->port.dev);
+ return clk_enable(uap->clk);
+}
+
+static inline void pl011_power_shutdown(struct uart_amba_port *uap)
+{
+ clk_disable(uap->clk);
+ pm_runtime_put_sync(uap->port.dev);
+}
+
+static inline void
+pl011_clock_control(struct uart_port *port, struct ktermios *termios,
+ struct ktermios *old)
+{
+}
+
+static inline void pl011_clock_control_init(struct uart_amba_port *uap)
+{
+}
+#endif
+
static void pl011_stop_tx(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -1208,6 +1509,9 @@ static void pl011_tx_chars(struct uart_amba_port *uap)
break;
} while (--count > 0);
+ if (count)
+ pl011_clock_check(uap);
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&uap->port);
@@ -1253,7 +1557,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
do {
writew(status & ~(UART011_TXIS|UART011_RTIS|
UART011_RXIS),
- uap->port.membase + UART011_ICR);
+ uap->port.membase + UART011_ICR);
if (status & (UART011_RTIS|UART011_RXIS)) {
if (pl011_dma_rx_running(uap))
@@ -1268,8 +1572,10 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
pl011_tx_chars(uap);
if (pass_counter-- == 0) {
- if (uap->interrupt_may_hang)
+ if (uap->interrupt_may_hang) {
+ uart_wa_tlet_line = uap->port.line;
tasklet_schedule(&pl011_lockup_tlet);
+ }
break;
}
@@ -1389,9 +1695,9 @@ static int pl011_startup(struct uart_port *port)
goto out;
/*
- * Try to enable the clock producer.
+ * Try to enable the clock producer and the regulator.
*/
- retval = clk_enable(uap->clk);
+ retval = pl011_power_startup(uap);
if (retval)
goto clk_unprep;
@@ -1408,29 +1714,7 @@ static int pl011_startup(struct uart_port *port)
if (retval)
goto clk_dis;
- writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS);
-
- /*
- * Provoke TX FIFO interrupt into asserting.
- */
- cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE;
- writew(cr, uap->port.membase + UART011_CR);
- writew(0, uap->port.membase + UART011_FBRD);
- writew(1, uap->port.membase + UART011_IBRD);
- writew(0, uap->port.membase + uap->lcrh_rx);
- if (uap->lcrh_tx != uap->lcrh_rx) {
- int i;
- /*
- * Wait 10 PCLKs before writing LCRH_TX register,
- * to get this delay write read only register 10 times
- */
- for (i = 0; i < 10; ++i)
- writew(0xff, uap->port.membase + UART011_MIS);
- writew(0, uap->port.membase + uap->lcrh_tx);
- }
- writew(0, uap->port.membase + UART01x_DR);
- while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY)
- barrier();
+ __pl011_startup(uap);
/* restore RTS and DTR */
cr = uap->old_cr & (UART011_CR_RTS | UART011_CR_DTR);
@@ -1471,7 +1755,7 @@ static int pl011_startup(struct uart_port *port)
return 0;
clk_dis:
- clk_disable(uap->clk);
+ pl011_power_shutdown(uap);
clk_unprep:
clk_unprepare(uap->clk);
out:
@@ -1529,10 +1813,18 @@ static void pl011_shutdown(struct uart_port *port)
if (uap->lcrh_rx != uap->lcrh_tx)
pl011_shutdown_channel(uap, uap->lcrh_tx);
+ if (uap->port.dev->platform_data) {
+ struct amba_pl011_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->exit)
+ plat->exit();
+ }
+
/*
- * Shut down the clock producer
+ * Shut down the clock producer and the producer
*/
- clk_disable(uap->clk);
+ pl011_power_shutdown(uap);
clk_unprepare(uap->clk);
if (uap->port.dev->platform_data) {
@@ -1545,6 +1837,32 @@ static void pl011_shutdown(struct uart_port *port)
}
+/* Power/Clock management. */
+static void pl011_serial_pm(struct uart_port *port, unsigned int state,
+unsigned int oldstate)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+
+ switch (state) {
+ case 0: /*fully on */
+ /*
+ * Enable the peripheral clock for this serial port.
+ * This is called on uart_open() or a resume event.
+ */
+ pl011_power_startup(uap);
+ break;
+ case 3: /* powered down */
+ /*
+ * Disable the peripheral clock for this serial port.
+ * This is called on uart_close() or a suspend event.
+ */
+ pl011_power_shutdown(uap);
+ break;
+ default:
+ printk(KERN_ERR "pl011_serial: unknown pm %d\n", state);
+ }
+}
+
static void
pl011_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
@@ -1558,7 +1876,12 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
clkdiv = 8;
else
clkdiv = 16;
-
+ /*
+ * Must be before uart_get_baud_rate() call, because
+ * this function changes baudrate to default in case of 0
+ * B0 hangup !!!
+ */
+ pl011_clock_control(port, termios, old);
/*
* Ask the core to calculate the divisor for us.
*/
@@ -1746,14 +2069,13 @@ static struct uart_ops amba_pl011_pops = {
.request_port = pl010_request_port,
.config_port = pl010_config_port,
.verify_port = pl010_verify_port,
+ .pm = pl011_serial_pm,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = pl010_get_poll_char,
.poll_put_char = pl010_put_poll_char,
#endif
};
-static struct uart_amba_port *amba_ports[UART_NR];
-
#ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
static void pl011_console_putchar(struct uart_port *port, int ch)
@@ -1897,6 +2219,13 @@ static struct console amba_console = {
.data = &amba_reg,
};
+static int __init pl011_console_init(void)
+{
+ register_console(&amba_console);
+ return 0;
+}
+console_initcall(pl011_console_init);
+
#define AMBA_CONSOLE (&amba_console)
#else
#define AMBA_CONSOLE NULL
@@ -1911,7 +2240,6 @@ static struct uart_driver amba_reg = {
.nr = UART_NR,
.cons = AMBA_CONSOLE,
};
-
static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
{
struct uart_amba_port *uap;
@@ -1940,6 +2268,12 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
goto free;
}
+ uap->regulator = regulator_get(&dev->dev, "v-uart");
+ if (IS_ERR(uap->regulator)) {
+ dev_warn(&dev->dev, "could not get uart regulator\n");
+ uap->regulator = NULL;
+ }
+
uap->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(uap->clk)) {
ret = PTR_ERR(uap->clk);
@@ -1947,6 +2281,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
}
uap->vendor = vendor;
+ uap->ifls = vendor->ifls;
uap->lcrh_rx = vendor->lcrh_rx;
uap->lcrh_tx = vendor->lcrh_tx;
uap->old_cr = 0;
@@ -1972,18 +2307,30 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
amba_ports[i] = uap;
amba_set_drvdata(dev, uap);
+
+ pm_runtime_irq_safe(&dev->dev);
+
+ pl011_clock_control_init(uap);
+
ret = uart_add_one_port(&amba_reg, &uap->port);
+
+ if (!ret)
+ pm_runtime_put(&dev->dev);
+
if (ret) {
amba_set_drvdata(dev, NULL);
amba_ports[i] = NULL;
pl011_dma_remove(uap);
clk_put(uap->clk);
unmap:
+ if (uap->regulator)
+ regulator_put(uap->regulator);
iounmap(base);
free:
kfree(uap);
}
out:
+
return ret;
}
@@ -1994,6 +2341,8 @@ static int pl011_remove(struct amba_device *dev)
amba_set_drvdata(dev, NULL);
+ pm_runtime_get_sync(uap->port.dev);
+
uart_remove_one_port(&amba_reg, &uap->port);
for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
@@ -2002,6 +2351,8 @@ static int pl011_remove(struct amba_device *dev)
pl011_dma_remove(uap);
iounmap(uap->port.membase);
+ if (uap->regulator)
+ regulator_put(uap->regulator);
clk_put(uap->clk);
kfree(uap);
return 0;
@@ -2014,7 +2365,12 @@ static int pl011_suspend(struct amba_device *dev, pm_message_t state)
if (!uap)
return -EINVAL;
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+ cancel_delayed_work_sync(&uap->clk_off_work);
+ if (uap->clk_state == PL011_CLK_OFF)
+ return 0;
+#endif
return uart_suspend_port(&amba_reg, &uap->port);
}
@@ -2024,6 +2380,10 @@ static int pl011_resume(struct amba_device *dev)
if (!uap)
return -EINVAL;
+#ifdef CONFIG_SERIAL_AMBA_PL011_CLOCK_CONTROL
+ if (uap->clk_state == PL011_CLK_OFF)
+ return 0;
+#endif
return uart_resume_port(&amba_reg, &uap->port);
}
@@ -2082,7 +2442,7 @@ static void __exit pl011_exit(void)
* While this can be a module, if builtin it's most likely the console
* So let's leave module_exit but move module_init to an earlier place
*/
-arch_initcall(pl011_init);
+subsys_initcall(pl011_init);
module_exit(pl011_exit);
MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 9a56635dc19..b3eeb3e1d60 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1211,6 +1211,19 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
* and flush any outstanding URBs.
*/
} else {
+#ifdef CONFIG_USB_OTG
+ /* According to OTG supplement Rev 2.0 section 6.3
+ * Unless an A-device enables b_hnp_enable before entering
+ * suspend it shall also continue polling while the bus is
+ * suspended.
+ *
+ * We don't have to perform HNP polling, as we are going to
+ * enable b_hnp_enable before suspending.
+ */
+ if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ cancel_delayed_work(&udev->bus->hnp_polling);
+#endif
udev->can_submit = 0;
for (i = 0; i < 16; ++i) {
usb_hcd_flush_endpoint(udev, udev->ep_out[i]);
@@ -1274,6 +1287,44 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
return status;
}
+#ifdef CONFIG_USB_OTG
+void usb_hnp_polling_work(struct work_struct *work)
+{
+ int ret;
+ struct usb_bus *bus =
+ container_of(work, struct usb_bus, hnp_polling.work);
+ struct usb_device *udev = bus->root_hub->children[bus->otg_port - 1];
+ u8 *status = kmalloc(sizeof(*status), GFP_KERNEL);
+
+ if (!status)
+ return;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE,
+ 0, OTG_STATUS_SELECTOR, status, sizeof(*status),
+ USB_CTRL_GET_TIMEOUT);
+ if (ret < 0) {
+ /* Peripheral may not be supporting HNP polling */
+ dev_vdbg(&udev->dev, "HNP polling failed. status %d\n", ret);
+ ret = usb_suspend_both(udev, PMSG_USER_SUSPEND);
+ goto out;
+ }
+
+ /* Spec says host must suspend the bus with in 2 sec. */
+ if (*status & (1 << HOST_REQUEST_FLAG)) {
+ unbind_no_pm_drivers_interfaces(udev);
+ ret = usb_suspend_both(udev, PMSG_USER_SUSPEND);
+ if (ret)
+ dev_info(&udev->dev, "suspend failed\n");
+ } else {
+ schedule_delayed_work(&bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ }
+out:
+ kfree(status);
+}
+#endif
+
static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
{
int w;
@@ -1620,7 +1671,7 @@ EXPORT_SYMBOL_GPL(usb_autopm_get_interface_no_resume);
/* Internal routine to check whether we may autosuspend a device. */
static int autosuspend_check(struct usb_device *udev)
{
- int w, i;
+ int w, i, audio_class = 0;
struct usb_interface *intf;
/* Fail if autosuspend is disabled, or any interfaces are in use, or
@@ -1654,13 +1705,28 @@ static int autosuspend_check(struct usb_device *udev)
intf->needs_remote_wakeup)
return -EOPNOTSUPP;
}
+
+ if (intf->cur_altsetting->desc.bInterfaceClass
+ == USB_CLASS_AUDIO) {
+ dev_dbg(&udev->dev,
+ "audio interface class present\n");
+ audio_class = 1;
+ }
}
+ if (audio_class) {
+ dev_dbg(&udev->dev,
+ "disabling remote wakeup for audio class\n");
+ udev->do_remote_wakeup = 0;
+ } else {
+ if (w && !device_can_wakeup(&udev->dev)) {
+ dev_dbg(&udev->dev,
+ "remote wakeup needed for autosuspend\n");
+ return -EOPNOTSUPP;
+ }
+ udev->do_remote_wakeup = w;
+ }
+
}
- if (w && !device_can_wakeup(&udev->dev)) {
- dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
- return -EOPNOTSUPP;
- }
- udev->do_remote_wakeup = w;
return 0;
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 140d3e11f21..f4f78a30b1d 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -897,6 +897,9 @@ static void usb_bus_init (struct usb_bus *bus)
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD (&bus->bus_list);
+#ifdef CONFIG_USB_OTG
+ INIT_DELAYED_WORK(&bus->hnp_polling, usb_hnp_polling_work);
+#endif
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ec6c97dadbe..f78df1d3163 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -30,6 +30,12 @@
#include "usb.h"
+#ifdef CONFIG_ARCH_U8500
+#define MAX_TOPO_LEVEL_U8500 2
+#define MAX_USB_DEVICE_U8500 8
+int usb_device_count;
+#endif
+
/* if we are in debug mode, always announce new devices */
#ifdef DEBUG
#ifndef CONFIG_USB_ANNOUNCE_NEW_DEVICES
@@ -633,7 +639,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%s failed (err = %d)\n", __func__, ret);
else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
- *change = le16_to_cpu(hub->status->hub.wHubChange);
+ *change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@@ -1326,11 +1332,20 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
/* Hubs have proper suspend/resume support. */
usb_enable_autosuspend(hdev);
+#ifdef CONFIG_ARCH_U8500
+ if (hdev->level > MAX_TOPO_LEVEL_U8500) {
+ dev_err(&intf->dev,
+ "Unsupported bus topology: > %d "
+ " hub nesting\n", MAX_TOPO_LEVEL_U8500);
+ return -E2BIG;
+ }
+#else
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
return -E2BIG;
}
+#endif
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
@@ -1612,12 +1627,14 @@ static void choose_devnum(struct usb_device *udev)
* bus->devnum_next. */
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,
bus->devnum_next);
- if (devnum >= 128)
+ /* Due to Hardware bugs we need to reserve a device address
+ * for flushing of endpoints. */
+ if (devnum >= 127)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
- bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+ bus->devnum_next = devnum >= 126 ? 1 : devnum + 1;
}
- if (devnum < 128) {
+ if (devnum < 127) {
set_bit(devnum, bus->devmap.devicemap);
udev->devnum = devnum;
}
@@ -1676,6 +1693,12 @@ void usb_disconnect(struct usb_device **pdev)
dev_info(&udev->dev, "USB disconnect, device number %d\n",
udev->devnum);
+#ifdef CONFIG_USB_OTG_20
+ if (udev->bus->hnp_support && udev->portnum == udev->bus->otg_port) {
+ cancel_delayed_work_sync(&udev->bus->hnp_polling);
+ udev->bus->hnp_support = 0;
+ }
+#endif
usb_lock_device(udev);
/* Free up all the children before we remove this device */
@@ -1752,11 +1775,13 @@ static inline void announce_device(struct usb_device *udev) { }
*
* Finish enumeration for On-The-Go devices
*/
+
+#ifdef CONFIG_USB_OTG_20
+
static int usb_enumerate_device_otg(struct usb_device *udev)
{
int err = 0;
-#ifdef CONFIG_USB_OTG
/*
* OTG-aware devices on OTG-capable root hubs may be able to use SRP,
* to wake us after we've powered off VBUS; and HNP, switching roles
@@ -1780,6 +1805,84 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
(port1 == bus->otg_port)
? "" : "non-");
+ if (port1 != bus->otg_port)
+ goto out;
+ bus->hnp_support = 1;
+
+ err = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_A_HNP_SUPPORT,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
+ /* OTG MESSAGE: report errors here,
+ * customize to match your product.
+ */
+ dev_info(&udev->dev,
+ "can't set HNP mode: %d\n",
+ err);
+ bus->hnp_support = 0;
+ }
+ }
+ }
+ }
+
+out:
+ if (!is_targeted(udev)) {
+
+ /* Maybe it can talk to us, though we can't talk to it.
+ * (Includes HNP test device.)
+ */
+ if (udev->bus->hnp_support) {
+ err = usb_port_suspend(udev, PMSG_SUSPEND);
+ if (err < 0)
+ dev_dbg(&udev->dev, "HNP fail, %d\n", err);
+ }
+ } else if (udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ /* HNP polling is introduced in OTG supplement Rev 2.0
+ * and older devices does not support. Work is not
+ * re-armed if device returns STALL. B-Host also performs
+ * HNP polling.
+ */
+ schedule_delayed_work(&udev->bus->hnp_polling,
+ msecs_to_jiffies(THOST_REQ_POLL));
+ }
+fail:
+
+ return err;
+}
+
+#else
+
+static int usb_enumerate_device_otg(struct usb_device *udev)
+{
+ int err = 0;
+
+#ifdef CONFIG_USB_OTG
+ /*
+ * OTG-aware devices on OTG-capable root hubs may be able to use SRP,
+ * to wake us after we've powered off VBUS; and HNP, switching roles
+ * "host" to "peripheral". The OTG descriptor helps figure this out.
+ */
+ if (!udev->bus->is_b_host
+ && udev->config
+ && udev->parent == udev->bus->root_hub) {
+ struct usb_otg_descriptor *desc = NULL;
+ struct usb_bus *bus = udev->bus;
+
+ /* descriptor may appear anywhere in config */
+ if (__usb_get_extra_descriptor(udev->rawdescriptors[0],
+ le16_to_cpu(udev->config[0].desc.wTotalLength),
+ USB_DT_OTG, (void **) &desc) == 0) {
+ if (desc->bmAttributes & USB_OTG_HNP) {
+ unsigned port1 = udev->portnum;
+
+ dev_info(&udev->dev,
+ "Dual-Role OTG device on %sHNP port\n",
+ (port1 == bus->otg_port)
+ ? "" : "non-");
+
/* enable HNP before suspend, it's simpler */
if (port1 == bus->otg_port)
bus->b_hnp_enable = 1;
@@ -1821,6 +1924,7 @@ fail:
return err;
}
+#endif
/**
* usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
@@ -2480,6 +2584,21 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0);
+#ifdef CONFIG_USB_OTG_20
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port) {
+ status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_B_HNP_ENABLE,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't enable HNP on port %d, "
+ "status %d\n", port1, status);
+ else
+ udev->bus->b_hnp_enable = 1;
+ }
+#endif
+
/* see 7.1.7.6 */
if (hub_is_superspeed(hub->hdev))
status = set_port_feature(hub->hdev,
@@ -2634,6 +2753,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
int status;
u16 portchange, portstatus;
+#ifdef CONFIG_USB_OTG_20
+ if (!udev->bus->is_b_host && udev->bus->hnp_support &&
+ udev->portnum == udev->bus->otg_port)
+ udev->bus->b_hnp_enable = 0;
+#endif
+
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (status == 0 && !port_is_suspended(hub, portstatus))
@@ -2837,7 +2962,7 @@ EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
- *
+ *
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@@ -3035,7 +3160,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
-
+
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
@@ -3195,7 +3320,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
-
+
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
@@ -3425,6 +3550,22 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop;
}
+#ifdef CONFIG_ARCH_U8500
+ if (hdev->parent == NULL)
+ usb_device_count = 1;
+
+ if (usb_device_count > MAX_USB_DEVICE_U8500) {
+
+ dev_err(&udev->dev,
+ "device connected is more than %d\n",
+ MAX_USB_DEVICE_U8500);
+
+ status = -ENOTCONN; /* Don't retry */
+ goto loop;
+ }
+#endif
+
+
/* reset (non-USB 3.0 devices) and get descriptor */
status = hub_port_init(hub, udev, port1, i);
if (status < 0)
@@ -3464,7 +3605,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop_disable;
}
}
-
+
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@@ -3505,6 +3646,115 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (status)
dev_dbg(hub_dev, "%dmA power budget left\n", status);
+#ifdef CONFIG_USB_OTG_20
+ struct usb_otg_descriptor *desc = NULL;
+ int ret;
+ /* descriptor may appear anywhere in config */
+ __usb_get_extra_descriptor(udev->rawdescriptors[0],
+ le16_to_cpu(udev->config[0].desc.wTotalLength),
+ USB_DT_OTG, (void **) &desc);
+
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_B_HNP_ENABLE,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ dev_dbg(hub_dev, "set feature error\n");
+
+ u16 idVendor = le16_to_cpu(udev->descriptor.idVendor);
+ if (idVendor == USB_OTG_TEST_MODE_VID) {
+ u16 wValue, typeReq, wIndex;
+ u32 set_feature = 0;
+ int err = 0;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ u16 idProduct = le16_to_cpu(
+ udev->descriptor.idProduct);
+ /* Convert the Test Mode Request
+ * to control request
+ */
+ wValue = USB_PORT_FEAT_TEST;
+ typeReq = SetPortFeature;
+ wIndex = 1;
+
+ switch (idProduct) {
+ case USB_OTG_TEST_SE0_NAK_PID:
+ wIndex |= USB_OTG_TEST_SE0_NAK << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_J_PID:
+ wIndex |= USB_OTG_TEST_J << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_K_PID:
+ wIndex |= USB_OTG_TEST_K << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_PACKET_PID:
+ wIndex |= USB_OTG_TEST_PACKET << 8;
+ set_feature = 1;
+ break;
+ case USB_OTG_TEST_HS_HOST_PORT_SUSPEND_RESUME_PID:
+ /* Sleep for 15 sec. Suspend
+ * for 15 Sec, Then Resume
+ */
+ ssleep(15);
+
+ err = usb_port_suspend(udev,
+ PMSG_SUSPEND);
+ if (err < 0) {
+ dev_err(&udev->dev, "OTG TEST_MODE:"
+ "Suspend Fail, %d\n", err);
+ goto loop_disable;
+ }
+ ssleep(15);
+ err = usb_port_resume(udev, PMSG_RESUME);
+ if (err < 0) {
+ dev_err(&udev->dev,
+ "can't resume for"
+ "OTG TEST_MODE: %d\n", err);
+ goto loop_disable;
+ }
+ break;
+ case USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_PID:
+ /* Sleep for 15 Sec. Issue the GetDeviceDescriptor */
+ ssleep(15);
+ err = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (err < 0) {
+ dev_err(&udev->dev, "can't re-read"
+ "device descriptor for "
+ "OTG TEST MODE: %d\n", err);
+ goto loop_disable;
+ }
+ break;
+ case USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_DATA_PID:
+ /* Issue GetDeviceDescriptor, Sleep for 15 Sec. */
+ err = usb_get_device_descriptor(udev,
+ sizeof(udev->descriptor));
+ if (err < 0) {
+ dev_err(&udev->dev, "can't re-read"
+ "device descriptor for "
+ "OTG TEST MODE: %d\n", err);
+ goto loop_disable;
+ }
+ ssleep(15);
+ break;
+ default:
+ /* is_targeted() will take care for wrong PID */
+ dev_dbg(&udev->dev, "OTG TEST_MODE:Wrong"
+ "PID %d\n", idProduct);
+ break;
+ }
+
+ if (set_feature) {
+ err = hcd->driver->hub_control(hcd,
+ typeReq, wValue, wIndex,
+ NULL, 0);
+ }
+ }
+
+#endif
return;
loop_disable:
@@ -3522,7 +3772,7 @@ loop:
!(hcd->driver->port_handed_over)(hcd, port1))
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
-
+
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -3689,7 +3939,7 @@ static void hub_events(void)
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
- * Works at least with mouse driver.
+ * Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
@@ -4029,7 +4279,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret < 0)
goto re_enumerate;
-
+
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor)) {
dev_info(&udev->dev, "device firmware changed\n");
@@ -4062,6 +4312,16 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
goto re_enumerate;
}
mutex_unlock(hcd->bandwidth_mutex);
+
+#ifdef CONFIG_USB_OTG_20
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ USB_REQ_SET_FEATURE, 0,
+ USB_DEVICE_A_HNP_SUPPORT,
+ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (ret < 0)
+ dev_err(&udev->dev, "set feature error\n");
+#endif
usb_set_device_state(udev, USB_STATE_CONFIGURED);
/* Put interfaces back into the same altsettings as before.
@@ -4102,7 +4362,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
done:
return 0;
-
+
re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index 7728c91dfa2..a5fdc3ac0d7 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -46,11 +46,18 @@ EXPORT_SYMBOL_GPL(usb_unregister_notify);
void usb_notify_add_device(struct usb_device *udev)
{
+#ifdef CONFIG_ARCH_U8500
+ usb_device_count++;
+#endif
+
blocking_notifier_call_chain(&usb_notifier_list, USB_DEVICE_ADD, udev);
}
void usb_notify_remove_device(struct usb_device *udev)
{
+#ifdef CONFIG_ARCH_U8500
+ usb_device_count--;
+#endif
/* Protect against simultaneous usbfs open */
mutex_lock(&usbfs_mutex);
blocking_notifier_call_chain(&usb_notifier_list,
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index e8cdce571bb..86a09972836 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -43,12 +43,50 @@ static struct usb_device_id whitelist_table [] = {
{ USB_DEVICE(0x0525, 0xa4a0), },
#endif
+#ifdef CONFIG_USB_OTG_20
+{ USB_DEVICE_INFO(8, 6, 80) },/* Mass Storage Devices */
+{ USB_DEVICE_INFO(1, 1, 0) },/* Audio Devices */
+{ USB_DEVICE_INFO(3, 0, 0) },/* keyboard Devices */
+{ USB_DEVICE_INFO(3, 1, 2) },/* Mouse Devices */
+
+/* Test Devices */
+{ USB_DEVICE(0x1A0A, 0x0101), },/* Test_SE0_NAK */
+{ USB_DEVICE(0x1A0A, 0x0102), },/* Test_J */
+{ USB_DEVICE(0x1A0A, 0x0103), },/* Test_K */
+{ USB_DEVICE(0x1A0A, 0x0104), },/* Test_Packet */
+{ USB_DEVICE(0x1A0A, 0x0106), },/* HS_HOST_PORT_SUSPEND_RESUME */
+{ USB_DEVICE(0x1A0A, 0x0107), },/* SINGLE_STEP_GET_DEV_DESC */
+{ USB_DEVICE(0x1A0A, 0x0108), },/* SINGLE_STEP_ GET_DEV_DESC_DATA*/
+{ USB_DEVICE(0x1A0A, 0x0201), },/* OTG 2 TEST DEVICE*/
+#endif
{ } /* Terminating entry */
};
+/* The TEST_MODE Definition for OTG as per 6.4 of OTG Rev 2.0 */
+
+#ifdef CONFIG_USB_OTG_20
+#define USB_OTG_TEST_MODE_VID 0x1A0A
+#define USB_OTG_TEST_SE0_NAK_PID 0x0101
+#define USB_OTG_TEST_J_PID 0x0102
+#define USB_OTG_TEST_K_PID 0x0103
+#define USB_OTG_TEST_PACKET_PID 0x0104
+#define USB_OTG_TEST_HS_HOST_PORT_SUSPEND_RESUME_PID 0x0106
+#define USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_PID 0x0107
+#define USB_OTG_TEST_SINGLE_STEP_GET_DEV_DESC_DATA_PID 0x0108
+
+#define USB_OTG_TEST_SE0_NAK 0x01
+#define USB_OTG_TEST_J 0x02
+#define USB_OTG_TEST_K 0x03
+#define USB_OTG_TEST_PACKET 0x04
+#endif
+
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
+#ifdef CONFIG_USB_OTG_20
+ u8 number_configs = 0;
+ u8 number_interface = 0;
+#endif
/* possible in developer configs only! */
if (!dev->bus->otg_port)
@@ -98,6 +136,36 @@ static int is_targeted(struct usb_device *dev)
/* add other match criteria here ... */
+#ifdef CONFIG_USB_OTG_20
+
+ /* Checking class,subclass and protocal at interface level */
+ for (number_configs = dev->descriptor.bNumConfigurations;
+ number_configs > 0; number_configs--)
+ for (number_interface = dev->config->desc.bNumInterfaces;
+ number_interface > 0;
+ number_interface--)
+ for (id = whitelist_table; id->match_flags; id++) {
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_CLASS) &&
+ (id->bDeviceClass !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceClass))
+ continue;
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_SUBCLASS)
+ && (id->bDeviceSubClass !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceSubClass))
+ continue;
+ if ((id->match_flags &
+ USB_DEVICE_ID_MATCH_DEV_PROTOCOL)
+ && (id->bDeviceProtocol !=
+ dev->config->intf_cache[number_interface-1]
+ ->altsetting[0].desc.bInterfaceProtocol))
+ continue;
+ return 1;
+ }
+#endif
/* OTG MESSAGE: report errors here, customize to match your product */
dev_err(&dev->dev, "device v%04x p%04x is not supported\n",
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 71648dcbe43..f7962567c1b 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,5 +1,9 @@
#include <linux/pm.h>
+#ifdef CONFIG_ARCH_U8500
+extern int usb_device_count;
+#endif
+
/* Functions local to drivers/usb/core/ */
extern int usb_create_sysfs_dev_files(struct usb_device *dev);
@@ -74,7 +78,9 @@ static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
}
#endif
-
+#ifdef CONFIG_USB_OTG
+extern void usb_hnp_polling_work(struct work_struct *work);
+#endif
#ifdef CONFIG_USB_SUSPEND
extern void usb_autosuspend_device(struct usb_device *udev);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 2633f759511..7e99677b81b 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -977,4 +977,15 @@ config USB_G_WEBCAM
endchoice
+config USB_OTG_20
+ bool "OTG 2.0 USB SUPPORT"
+ select USB_OTG
+ select PM_RUNTIME
+ select USB_OTG_WHITELIST
+ select USB_SUSPEND
+ default n
+ help
+ Enabling the whitelist (Target Peripheral List-TPL) and runtime power
+ management at system level and usb level for OTG 2.0.
+
endif # USB_GADGET
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 51f3d42f5a6..14cbfa80afb 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -315,6 +315,12 @@ struct usb_ep *usb_ep_autoconfig_ss(
#endif
}
+ if (gadget->ops->configure_ep) {
+ ep = gadget->ops->configure_ep(gadget, type, desc);
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ return ep;
+ }
+
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches(gadget, ep, desc, ep_comp))
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index cb8c162cae5..8473424e31f 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2786,6 +2786,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
curlun->cdrom = !!lcfg->cdrom;
curlun->ro = lcfg->cdrom || lcfg->ro;
+ curlun->nofua = 1;
curlun->initially_ro = curlun->ro;
curlun->removable = lcfg->removable;
curlun->dev.release = fsg_lun_release;
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 52343654f5d..b8bfda4fc58 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -174,8 +174,8 @@ rndis_iad_descriptor = {
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
- .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
- .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_VENDOR,
/* .iFunction = DYNAMIC */
};
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index f70cab3beee..f9e42041b5f 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -34,6 +34,8 @@ if USB_MUSB_HDRC
choice
prompt "Platform Glue Layer"
+ bool
+ default USB_MUSB_UX500 if ARCH_U8500 || ARCH_U5500
config USB_MUSB_DAVINCI
tristate "DaVinci"
@@ -60,7 +62,7 @@ config USB_MUSB_BLACKFIN
config USB_MUSB_UX500
tristate "U8500 and U5500"
- depends on (ARCH_U8500 && AB8500_USB)
+ depends on (ARCH_U8500) || (ARCH_U5500)
endchoice
@@ -114,4 +116,13 @@ config MUSB_PIO_ONLY
endchoice
+config USB_MUSB_DEBUG
+ depends on USB_MUSB_HDRC
+ bool "Enable debugging messages"
+ default n
+ help
+ This enables musb debugging. To set the logging level use the debug
+ module parameter. Starting at level 3, per-transfer (urb, usb_request,
+ packet, or dma transfer) tracing may kick in.
+
endif # USB_MUSB_HDRC
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 66aaccf0449..2923752b858 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -520,9 +520,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
/* see manual for the order of the tests */
if (int_usb & MUSB_INTR_SESSREQ) {
void __iomem *mbase = musb->mregs;
-
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS
- && (devctl & MUSB_DEVCTL_BDEVICE)) {
+ || (devctl & MUSB_DEVCTL_BDEVICE)) {
dev_dbg(musb->controller, "SessReq while on B state\n");
return IRQ_HANDLED;
}
@@ -715,6 +714,9 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
b_host:
musb->xceiv->state = OTG_STATE_B_HOST;
hcd->self.is_b_host = 1;
+#ifdef CONFIG_USB_OTG_20
+ musb->g.otg_hnp_reqd = 0;
+#endif
musb->ignore_disconnect = 0;
del_timer(&musb->otg_timer);
break;
@@ -1036,9 +1038,6 @@ static void musb_shutdown(struct platform_device *pdev)
|| defined(CONFIG_USB_MUSB_AM35X) \
|| defined(CONFIG_USB_MUSB_AM35X_MODULE)
static ushort __devinitdata fifo_mode = 4;
-#elif defined(CONFIG_USB_MUSB_UX500) \
- || defined(CONFIG_USB_MUSB_UX500_MODULE)
-static ushort __devinitdata fifo_mode = 5;
#else
static ushort __devinitdata fifo_mode = 2;
#endif
@@ -1123,8 +1122,8 @@ static struct musb_fifo_cfg __devinitdata mode_4_cfg[] = {
/* mode 5 - fits in 8KB */
static struct musb_fifo_cfg __devinitdata mode_5_cfg[] = {
-{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
-{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, .mode = BUF_DOUBLE, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, .mode = BUF_DOUBLE },
{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
{ .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
{ .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
@@ -1752,7 +1751,9 @@ musb_srp_store(struct device *dev, struct device_attribute *attr,
{
struct musb *musb = dev_to_musb(dev);
unsigned short srp;
-
+#ifdef CONFIG_USB_OTG_20
+ musb->xceiv->start_srp(musb->xceiv);
+#endif
if (sscanf(buf, "%hu", &srp) != 1
|| (srp != 1)) {
dev_err(dev, "SRP: Value must be 1\n");
@@ -1766,10 +1767,45 @@ musb_srp_store(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR(srp, 0644, NULL, musb_srp_store);
+static ssize_t
+ux500_set_extvbus(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
+ unsigned short extvbus;
+
+ if (sscanf(buf, "%hu", &extvbus) != 1
+ || ((extvbus != 1) && (extvbus != 0))) {
+ dev_err(dev, "Invalid value EXTVBUS must be 1 or 0\n");
+ return -EINVAL;
+ }
+
+ plat->extvbus = extvbus;
+
+ return n;
+}
+
+static ssize_t
+ux500_get_extvbus(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct musb_hdrc_platform_data *plat = dev->platform_data;
+ int extvbus;
+
+ /* FIXME get_vbus_status() is normally #defined as false...
+ * and is effectively TUSB-specific.
+ */
+ extvbus = plat->extvbus;
+
+ return sprintf(buf, "EXTVBUS is %s\n",
+ extvbus ? "on" : "off");
+}
+static DEVICE_ATTR(extvbus, 0644, ux500_get_extvbus, ux500_set_extvbus);
+
static struct attribute *musb_attributes[] = {
&dev_attr_mode.attr,
&dev_attr_vbus.attr,
&dev_attr_srp.attr,
+ &dev_attr_extvbus.attr,
NULL
};
@@ -1886,7 +1922,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
status = -ENODEV;
goto fail0;
}
-
/* allocate */
musb = allocate_instance(dev, plat->config, ctrl);
if (!musb) {
@@ -2330,7 +2365,7 @@ static int musb_suspend(struct device *dev)
return 0;
}
-static int musb_resume_noirq(struct device *dev)
+static int musb_resume(struct device *dev)
{
/* for static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down or the USB
@@ -2371,13 +2406,17 @@ static int musb_runtime_resume(struct device *dev)
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
- .resume_noirq = musb_resume_noirq,
+ .resume = musb_resume,
.runtime_suspend = musb_runtime_suspend,
.runtime_resume = musb_runtime_resume,
};
-
+#ifdef CONFIG_UX500_SOC_DB8500
#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops)
#else
+#define MUSB_DEV_PM_OPS NULL
+#endif
+
+#else
#define MUSB_DEV_PM_OPS NULL
#endif
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index f4a40f001c8..2d52530d3e4 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -153,7 +153,10 @@ enum musb_g_ep0_state {
#define OTG_TIME_A_WAIT_BCON 1100 /* min 1 second */
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
-
+#ifdef CONFIG_USB_OTG_20
+#define USB_SUSP_DET_DURATION 5 /* suspend time 5ms */
+#define TTST_SRP 3000 /* max 5 sec */
+#endif
/*************************** REGISTER ACCESS ********************************/
@@ -229,6 +232,8 @@ struct musb_platform_ops {
int (*adjust_channel_params)(struct dma_channel *channel,
u16 packet_sz, u8 *mode,
dma_addr_t *dma_addr, u32 *len);
+ struct usb_ep* (*configure_endpoints)(struct musb *musb, u8 type,
+ struct usb_endpoint_descriptor *desc);
};
/*
@@ -430,7 +435,6 @@ struct musb {
unsigned set_address:1;
unsigned test_mode:1;
unsigned softconnect:1;
-
u8 address;
u8 test_mode_nr;
u16 ackpend; /* ep0 */
@@ -603,4 +607,13 @@ static inline int musb_platform_exit(struct musb *musb)
return musb->ops->exit(musb);
}
+static inline struct usb_ep *musb_platform_configure_ep(struct musb *musb,
+ u8 type, struct usb_endpoint_descriptor *desc)
+{
+ struct usb_ep *ep = NULL;
+
+ if (musb->ops->configure_endpoints)
+ ep = musb->ops->configure_endpoints(musb, type, desc);
+ return ep;
+}
#endif /* __MUSB_CORE_H__ */
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 40a37c91cc1..19e3c91c22e 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -68,6 +68,7 @@ static const struct musb_register_map musb_regmap[] = {
{ "RxFIFOadd", 0x66, 16 },
{ "VControl", 0x68, 32 },
{ "HWVers", 0x6C, 16 },
+ { "EXTVBUS", 0x70, 8 },
{ "EPInfo", 0x78, 8 },
{ "RAMInfo", 0x79, 8 },
{ "LinkInfo", 0x7A, 8 },
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index f42c29b11f7..b2abef69dc3 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -401,7 +401,21 @@ static void txstate(struct musb *musb, struct musb_request *req)
csr |= (MUSB_TXCSR_DMAENAB
| MUSB_TXCSR_DMAMODE
| MUSB_TXCSR_MODE);
- if (!musb_ep->hb_mult)
+ /*
+ * Enable Autoset according to table
+ * below
+ * ************************************
+ * bulk_split hb_mult Autoset_Enable
+ * ************************************
+ * 0 0 Yes(Normal)
+ * 0 >0 No(High BW ISO)
+ * 1 0 Yes(HS bulk)
+ * 1 >0 Yes(FS bulk)
+ */
+ if (!musb_ep->hb_mult ||
+ (musb_ep->hb_mult &&
+ can_bulk_split(musb,
+ musb_ep->type)))
csr |= MUSB_TXCSR_AUTOSET;
}
csr &= ~MUSB_TXCSR_P_UNDERRUN;
@@ -1097,6 +1111,12 @@ static int musb_gadget_enable(struct usb_ep *ep,
/* REVISIT if can_bulk_split(), use by updating "tmp";
* likewise high bandwidth periodic tx
*/
+ /* Set the TXMAXP register correctly for Bulk IN
+ * endpoints in device mode
+ */
+ if (can_bulk_split(musb, musb_ep->type))
+ musb_ep->hb_mult = (hw_ep->max_packet_sz_tx /
+ musb_ep->packet_sz) - 1;
/* Set TXMAXP with the FIFO size of the endpoint
* to disable double buffering mode.
*/
@@ -1642,7 +1662,9 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
}
spin_unlock_irqrestore(&musb->lock, flags);
+#ifndef CONFIG_USB_OTG_20
otg_start_srp(musb->xceiv->otg);
+#endif
spin_lock_irqsave(&musb->lock, flags);
/* Block idling for at least 1s */
@@ -1753,6 +1775,14 @@ static int musb_gadget_start(struct usb_gadget *g,
static int musb_gadget_stop(struct usb_gadget *g,
struct usb_gadget_driver *driver);
+static struct usb_ep *musb_gadget_configure_ep(struct usb_gadget *gadget,
+ u8 type, struct usb_endpoint_descriptor *desc)
+{
+ struct musb *musb = gadget_to_musb(gadget);
+
+ return musb_platform_configure_ep(musb, type, desc);
+}
+
static const struct usb_gadget_ops musb_gadget_operations = {
.get_frame = musb_gadget_get_frame,
.wakeup = musb_gadget_wakeup,
@@ -1762,6 +1792,7 @@ static const struct usb_gadget_ops musb_gadget_operations = {
.pullup = musb_gadget_pullup,
.udc_start = musb_gadget_start,
.udc_stop = musb_gadget_stop,
+ .configure_ep = musb_gadget_configure_ep,
};
/* ----------------------------------------------------------------------- */
diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c
index e40d7647caf..631aab86240 100644
--- a/drivers/usb/musb/musb_gadget_ep0.c
+++ b/drivers/usb/musb/musb_gadget_ep0.c
@@ -45,6 +45,11 @@
/* ep0 is always musb->endpoints[0].ep_in */
#define next_ep0_request(musb) next_in_request(&(musb)->endpoints[0])
+/* OTG 2.0 Specification 6.2.3 GetStatus commands */
+#ifdef CONFIG_USB_OTG_20
+#define OTG_STATUS_SELECT 0xF
+#endif
+
/*
* locking note: we use only the controller lock, for simpler correctness.
* It's always held with IRQs blocked.
@@ -80,21 +85,33 @@ static int service_tx_status_request(
int handled = 1;
u8 result[2], epnum = 0;
const u8 recip = ctrlrequest->bRequestType & USB_RECIP_MASK;
-
+#ifdef CONFIG_USB_OTG_20
+ unsigned int otg_recip = ctrlrequest->wIndex >> 12;
+#endif
result[1] = 0;
switch (recip) {
case USB_RECIP_DEVICE:
- result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
- result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
- if (musb->g.is_otg) {
- result[0] |= musb->g.b_hnp_enable
- << USB_DEVICE_B_HNP_ENABLE;
- result[0] |= musb->g.a_alt_hnp_support
- << USB_DEVICE_A_ALT_HNP_SUPPORT;
- result[0] |= musb->g.a_hnp_support
- << USB_DEVICE_A_HNP_SUPPORT;
+#ifdef CONFIG_USB_OTG_20
+ if (!(otg_recip == OTG_STATUS_SELECT)) {
+#endif
+ result[0] = musb->is_self_powered <<
+ USB_DEVICE_SELF_POWERED;
+ result[0] |= musb->may_wakeup <<
+ USB_DEVICE_REMOTE_WAKEUP;
+ if (musb->g.is_otg) {
+ result[0] |= musb->g.b_hnp_enable
+ << USB_DEVICE_B_HNP_ENABLE;
+ result[0] |= musb->g.a_alt_hnp_support
+ << USB_DEVICE_A_ALT_HNP_SUPPORT;
+ result[0] |= musb->g.a_hnp_support
+ << USB_DEVICE_A_HNP_SUPPORT;
+ }
+#ifdef CONFIG_USB_OTG_20
+ } else {
+ result[0] = 1 & musb->g.otg_hnp_reqd;
}
+#endif
break;
case USB_RECIP_INTERFACE:
@@ -356,7 +373,22 @@ __acquires(musb->lock)
musb->test_mode_nr =
MUSB_TEST_PACKET;
break;
-
+#ifdef CONFIG_USB_OTG_20
+ case 6:
+ if (!musb->g.is_otg)
+ goto stall;
+ musb->g.otg_srp_reqd = 1;
+
+ mod_timer(&musb->otg_timer,
+ jiffies
+ + msecs_to_jiffies(TTST_SRP));
+ break;
+ case 7:
+ if (!musb->g.is_otg)
+ goto stall;
+ musb->g.otg_hnp_reqd = 1;
+ break;
+#endif
case 0xc0:
/* TEST_FORCE_HS */
pr_debug("TEST_FORCE_HS\n");
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index ef8d744800a..0a108587e2e 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -46,7 +46,6 @@
#include "musb_core.h"
#include "musb_host.h"
-
/* MUSB HOST status 22-mar-2006
*
* - There's still lots of partial code duplication for fault paths, so
@@ -108,24 +107,41 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
{
struct musb *musb = ep->musb;
void __iomem *epio = ep->regs;
+ void __iomem *regs = ep->musb->mregs;
u16 csr;
- u16 lastcsr = 0;
- int retries = 1000;
+ u8 addr;
+ int retries = 3000; /* 3ms */
+ /*
+ * NOTE: We are using a hack here because the FIFO-FLUSH
+ * bit is broken in hardware! The hack consists of changing
+ * the TXFUNCADDR to an unused device address and waiting
+ * for any pending USB packets to hit the 3-strikes and your
+ * gone rule.
+ */
+ addr = musb_readb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR));
csr = musb_readw(epio, MUSB_TXCSR);
while (csr & MUSB_TXCSR_FIFONOTEMPTY) {
- if (csr != lastcsr)
- dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
- lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
- musb_writew(epio, MUSB_TXCSR, csr);
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum,
+ MUSB_TXFUNCADDR), 127);
csr = musb_readw(epio, MUSB_TXCSR);
- if (WARN(retries-- < 1,
- "Could not flush host TX%d fifo: csr: %04x\n",
- ep->epnum, csr))
- return;
- mdelay(1);
+ retries--;
+ if (retries == 0) {
+ /* can happen if the USB clocks are OFF */
+ dev_dbg(musb->controller, "Could not flush host TX%d "
+ "fifo: csr=0x%04x\n", ep->epnum, csr);
+ break;
+ }
+ udelay(1);
}
+ /* clear any errors */
+ csr &= ~(MUSB_TXCSR_H_ERROR
+ | MUSB_TXCSR_H_RXSTALL
+ | MUSB_TXCSR_H_NAKTIMEOUT);
+ musb_writew(epio, MUSB_TXCSR, csr);
+
+ /* restore endpoint address */
+ musb_writeb(regs, MUSB_BUSCTL_OFFSET(ep->epnum, MUSB_TXFUNCADDR), addr);
}
static void musb_h_ep0_flush_fifo(struct musb_hw_ep *ep)
@@ -615,16 +631,26 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
u16 csr;
u8 mode;
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
if (length > channel->max_len)
length = channel->max_len;
csr = musb_readw(epio, MUSB_TXCSR);
- if (length > pkt_size) {
+ if (length >= pkt_size) {
mode = 1;
csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB;
/* autoset shouldn't be set in high bandwidth */
- if (qh->hb_mult == 1)
+ /*
+ * Enable Autoset according to table
+ * below
+ * bulk_split hb_mult Autoset_Enable
+ * 0 1 Yes(Normal)
+ * 0 >1 No(High BW ISO)
+ * 1 1 Yes(HS bulk)
+ * 1 >1 Yes(FS bulk)
+ */
+ if (qh->hb_mult == 1 || (qh->hb_mult > 1 &&
+ can_bulk_split(hw_ep->musb, qh->type)))
csr |= MUSB_TXCSR_AUTOSET;
} else {
mode = 0;
@@ -771,6 +797,13 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
/* protocol/endpoint/interval/NAKlimit */
if (epnum) {
musb_writeb(epio, MUSB_TXTYPE, qh->type_reg);
+ /*
+ * Set the TXMAXP register correctly for Bulk OUT
+ * endpoints in host mode
+ */
+ if (can_bulk_split(musb, qh->type))
+ qh->hb_mult = hw_ep->max_packet_sz_tx
+ / packet_sz;
if (musb->double_buffer_not_ok)
musb_writew(epio, MUSB_TXMAXP,
hw_ep->max_packet_sz_tx);
@@ -802,6 +835,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
if (load_count) {
/* PIO to load FIFO */
+ /* Unmap the buffer so that CPU can use it */
+ usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
qh->segsize = load_count;
musb_write_fifo(hw_ep, load_count, buf);
}
@@ -894,6 +929,8 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
if (fifo_count < len)
urb->status = -EOVERFLOW;
+ /* Unmap the buffer so that CPU can use it */
+ usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
musb_read_fifo(hw_ep, fifo_count, fifo_dest);
urb->actual_length += fifo_count;
@@ -933,6 +970,8 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb)
fifo_count,
(fifo_count == 1) ? "" : "s",
fifo_dest);
+ /* Unmap the buffer so that CPU can use it */
+ usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
musb_write_fifo(hw_ep, fifo_count, fifo_dest);
urb->actual_length += fifo_count;
@@ -1134,6 +1173,22 @@ void musb_host_tx(struct musb *musb, u8 epnum)
dev_dbg(musb->controller, "TX 3strikes on ep=%d\n", epnum);
status = -ETIMEDOUT;
+ } else if (tx_csr & MUSB_TXCSR_TXPKTRDY) {
+ /* BUSY - can happen during USB transfer cancel */
+
+ /* MUSB_TXCSR_TXPKTRDY indicates that the data written
+ * to the FIFO by DMA has not still gone on the USB bus.
+ * DMA completion callback doesn't indicate that data has
+ * gone on the USB bus. So, if we reach this case, need to
+ * wait for the MUSB_TXCSR_TXPKTRDY to be cleared and then
+ * proceed.
+ */
+ dev_dbg(musb->controller, "TXPKTRDY set. Data transfer ongoing. Wait...\n");
+
+ do {
+ tx_csr = musb_readw(epio, MUSB_TXCSR);
+ } while ((tx_csr & MUSB_TXCSR_TXPKTRDY) != 0);
+ dev_dbg(musb->controller, "TXPKTRDY Cleared. Continue...\n");
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
dev_dbg(musb->controller, "TX end=%d device not responding\n", epnum);
@@ -1427,7 +1482,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
size_t xfer_len;
void __iomem *mbase = musb->mregs;
int pipe;
- u16 rx_csr, val;
+ u16 rx_csr, val, restore_csr;
bool iso_err = false;
bool done = false;
u32 status;
@@ -1537,7 +1592,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
-#ifndef CONFIG_USB_INVENTRA_DMA
+#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA)
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
/* REVISIT this happened for a while on some short reads...
* the cleanup still needs investigation... looks bad...
@@ -1569,7 +1624,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
| MUSB_RXCSR_RXPKTRDY);
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
if (usb_pipeisoc(pipe)) {
struct usb_iso_packet_descriptor *d;
@@ -1625,7 +1680,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
/* we are expecting IN packets */
-#ifdef CONFIG_USB_INVENTRA_DMA
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
if (dma) {
struct dma_controller *c;
u16 rx_count;
@@ -1709,6 +1764,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
val = musb_readw(epio, MUSB_RXCSR);
+
+ /* retain the original value,
+ * which will be used to reset CSR
+ */
+ restore_csr = val;
val &= ~MUSB_RXCSR_H_REQPKT;
if (dma->desired_mode == 0)
@@ -1736,7 +1796,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
c->channel_release(dma);
hw_ep->rx_channel = NULL;
dma = NULL;
- /* REVISIT reset CSR */
+ musb_writew(epio, MUSB_RXCSR, restore_csr);
}
}
#endif /* Mentor DMA */
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 03f2655af29..1c96cf60907 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -246,7 +246,9 @@
*/
#define MUSB_DEVCTL 0x60 /* 8 bit */
-
+#ifdef CONFIG_USB_OTG_20
+#define MUSB_MISC 0x61 /* 8 bit */
+#endif
/* These are always controlled through the INDEX register */
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
#define MUSB_RXFIFOSZ 0x63 /* 8-bit (see masks) */
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 22ec3e37998..702d5efe9ef 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -379,7 +379,7 @@ int musb_hub_control(
musb_port_suspend(musb, true);
break;
case USB_PORT_FEAT_TEST:
- if (unlikely(is_host_active(musb)))
+ if (unlikely(!is_host_active(musb)))
goto error;
wIndex >>= 8;
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index aa09dd417b9..c1a205cd5e3 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -25,9 +25,15 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <mach/id.h>
+#include <mach/usb.h>
#include "musb_core.h"
+#define DEFAULT_DEVCTL 0x81
+static void ux500_musb_set_vbus(struct musb *musb, int is_on);
+
struct ux500_glue {
struct device *dev;
struct platform_device *musb;
@@ -35,17 +41,439 @@ struct ux500_glue {
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
+static struct timer_list notify_timer;
+static struct musb_context_registers context;
+static bool context_stored;
+struct musb *_musb;
+
+static void ux500_store_context(struct musb *musb)
+{
+#ifdef CONFIG_PM
+ int i;
+ void __iomem *musb_base;
+ void __iomem *epio;
+
+ if (cpu_is_u5500()) {
+ if (musb != NULL)
+ _musb = musb;
+ else
+ return;
+ }
+
+ musb_base = musb->mregs;
+
+ if (is_host_enabled(musb)) {
+ context.frame = musb_readw(musb_base, MUSB_FRAME);
+ context.testmode = musb_readb(musb_base, MUSB_TESTMODE);
+ context.busctl = musb_read_ulpi_buscontrol(musb->mregs);
+ }
+ context.power = musb_readb(musb_base, MUSB_POWER);
+ context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE);
+ context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE);
+ context.intrusbe = musb_readb(musb_base, MUSB_INTRUSBE);
+ context.index = musb_readb(musb_base, MUSB_INDEX);
+ context.devctl = DEFAULT_DEVCTL;
+
+ for (i = 0; i < musb->config->num_eps; ++i) {
+ struct musb_hw_ep *hw_ep;
+
+ musb_writeb(musb_base, MUSB_INDEX, i);
+ hw_ep = &musb->endpoints[i];
+ if (!hw_ep)
+ continue;
+
+ epio = hw_ep->regs;
+ if (!epio)
+ continue;
+
+ context.index_regs[i].txmaxp =
+ musb_readw(epio, MUSB_TXMAXP);
+ context.index_regs[i].txcsr =
+ musb_readw(epio, MUSB_TXCSR);
+ context.index_regs[i].rxmaxp =
+ musb_readw(epio, MUSB_RXMAXP);
+ context.index_regs[i].rxcsr =
+ musb_readw(epio, MUSB_RXCSR);
+
+ if (musb->dyn_fifo) {
+ context.index_regs[i].txfifoadd =
+ musb_read_txfifoadd(musb_base);
+ context.index_regs[i].rxfifoadd =
+ musb_read_rxfifoadd(musb_base);
+ context.index_regs[i].txfifosz =
+ musb_read_txfifosz(musb_base);
+ context.index_regs[i].rxfifosz =
+ musb_read_rxfifosz(musb_base);
+ }
+ if (is_host_enabled(musb)) {
+ context.index_regs[i].txtype =
+ musb_readb(epio, MUSB_TXTYPE);
+ context.index_regs[i].txinterval =
+ musb_readb(epio, MUSB_TXINTERVAL);
+ context.index_regs[i].rxtype =
+ musb_readb(epio, MUSB_RXTYPE);
+ context.index_regs[i].rxinterval =
+ musb_readb(epio, MUSB_RXINTERVAL);
+
+ context.index_regs[i].txfunaddr =
+ musb_read_txfunaddr(musb_base, i);
+ context.index_regs[i].txhubaddr =
+ musb_read_txhubaddr(musb_base, i);
+ context.index_regs[i].txhubport =
+ musb_read_txhubport(musb_base, i);
+
+ context.index_regs[i].rxfunaddr =
+ musb_read_rxfunaddr(musb_base, i);
+ context.index_regs[i].rxhubaddr =
+ musb_read_rxhubaddr(musb_base, i);
+ context.index_regs[i].rxhubport =
+ musb_read_rxhubport(musb_base, i);
+ }
+ }
+ context_stored = true;
+#endif
+}
+
+void ux500_restore_context(struct musb *musb)
+{
+#ifdef CONFIG_PM
+ int i;
+ void __iomem *musb_base;
+ void __iomem *ep_target_regs;
+ void __iomem *epio;
+
+ if (!context_stored)
+ return;
+
+ if (cpu_is_u5500()) {
+ if (_musb != NULL)
+ musb = _musb;
+ else
+ return;
+ }
+
+ musb_base = musb->mregs;
+ if (is_host_enabled(musb)) {
+ musb_writew(musb_base, MUSB_FRAME, context.frame);
+ musb_writeb(musb_base, MUSB_TESTMODE, context.testmode);
+ musb_write_ulpi_buscontrol(musb->mregs, context.busctl);
+ }
+ musb_writeb(musb_base, MUSB_POWER, context.power);
+ musb_writew(musb_base, MUSB_INTRTXE, context.intrtxe);
+ musb_writew(musb_base, MUSB_INTRRXE, context.intrrxe);
+ musb_writeb(musb_base, MUSB_INTRUSBE, context.intrusbe);
+ musb_writeb(musb_base, MUSB_DEVCTL, context.devctl);
+
+ for (i = 0; i < musb->config->num_eps; ++i) {
+ struct musb_hw_ep *hw_ep;
+
+ musb_writeb(musb_base, MUSB_INDEX, i);
+ hw_ep = &musb->endpoints[i];
+ if (!hw_ep)
+ continue;
+
+ epio = hw_ep->regs;
+ if (!epio)
+ continue;
+
+ musb_writew(epio, MUSB_TXMAXP,
+ context.index_regs[i].txmaxp);
+ musb_writew(epio, MUSB_TXCSR,
+ context.index_regs[i].txcsr);
+ musb_writew(epio, MUSB_RXMAXP,
+ context.index_regs[i].rxmaxp);
+ musb_writew(epio, MUSB_RXCSR,
+ context.index_regs[i].rxcsr);
+
+ if (musb->dyn_fifo) {
+ musb_write_txfifosz(musb_base,
+ context.index_regs[i].txfifosz);
+ musb_write_rxfifosz(musb_base,
+ context.index_regs[i].rxfifosz);
+ musb_write_txfifoadd(musb_base,
+ context.index_regs[i].txfifoadd);
+ musb_write_rxfifoadd(musb_base,
+ context.index_regs[i].rxfifoadd);
+ }
+
+ if (is_host_enabled(musb)) {
+ musb_writeb(epio, MUSB_TXTYPE,
+ context.index_regs[i].txtype);
+ musb_writeb(epio, MUSB_TXINTERVAL,
+ context.index_regs[i].txinterval);
+ musb_writeb(epio, MUSB_RXTYPE,
+ context.index_regs[i].rxtype);
+ musb_writeb(epio, MUSB_RXINTERVAL,
+
+ musb->context.index_regs[i].rxinterval);
+ musb_write_txfunaddr(musb_base, i,
+ context.index_regs[i].txfunaddr);
+ musb_write_txhubaddr(musb_base, i,
+ context.index_regs[i].txhubaddr);
+ musb_write_txhubport(musb_base, i,
+ context.index_regs[i].txhubport);
+
+ ep_target_regs =
+ musb_read_target_reg_base(i, musb_base);
+
+ musb_write_rxfunaddr(ep_target_regs,
+ context.index_regs[i].rxfunaddr);
+ musb_write_rxhubaddr(ep_target_regs,
+ context.index_regs[i].rxhubaddr);
+ musb_write_rxhubport(ep_target_regs,
+ context.index_regs[i].rxhubport);
+ }
+ }
+ musb_writeb(musb_base, MUSB_INDEX, context.index);
+#endif
+}
+
+static void musb_notify_idle(unsigned long _musb)
+{
+ struct musb *musb = (void *)_musb;
+ unsigned long flags;
+
+ u8 devctl;
+ dev_dbg(musb->controller, "musb_notify_idle %s",
+ otg_state_string(musb->xceiv->state));
+ spin_lock_irqsave(&musb->lock, flags);
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ switch (musb->xceiv->state) {
+ case OTG_STATE_A_WAIT_BCON:
+ if (devctl & MUSB_DEVCTL_BDEVICE) {
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ MUSB_DEV_MODE(musb);
+ } else {
+ musb->xceiv->state = OTG_STATE_A_IDLE;
+ MUSB_HST_MODE(musb);
+ }
+ if (cpu_is_u8500()) {
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
+ }
+ break;
+
+ case OTG_STATE_A_SUSPEND:
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+/* blocking notifier support */
+static int musb_otg_notifications(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ struct musb *musb = container_of(nb, struct musb, nb);
+
+ dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
+ event, otg_state_string(musb->xceiv->state));
+ switch (event) {
+
+ case USB_EVENT_PREPARE:
+ pm_runtime_get_sync(musb->controller);
+ ux500_restore_context(musb);
+ break;
+ case USB_EVENT_ID:
+ case USB_EVENT_RIDA:
+ dev_dbg(musb->controller, "ID GND\n");
+ if (is_otg_enabled(musb)) {
+ ux500_musb_set_vbus(musb, 1);
+ }
+ break;
+
+ case USB_EVENT_VBUS:
+ dev_dbg(musb->controller, "VBUS Connect\n");
+
+ break;
+/* case USB_EVENT_RIDB: FIXME, not yet managed */
+ case USB_EVENT_NONE:
+ dev_dbg(musb->controller, "VBUS Disconnect\n");
+ if (is_otg_enabled(musb) && musb->is_host)
+ ux500_musb_set_vbus(musb, 0);
+ else
+ musb->xceiv->state = OTG_STATE_B_IDLE;
+ break;
+ case USB_EVENT_CLEAN:
+ pm_runtime_mark_last_busy(musb->controller);
+ pm_runtime_put_autosuspend(musb->controller);
+ break;
+ default:
+ dev_dbg(musb->controller, "ID float\n");
+ return NOTIFY_DONE;
+ }
+ return NOTIFY_OK;
+}
+
+static void ux500_musb_set_vbus(struct musb *musb, int is_on)
+{
+ u8 devctl;
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ int ret = 1;
+ struct musb_hdrc_platform_data *plat = musb->controller->platform_data;
+#ifdef CONFIG_USB_OTG_20
+ int val = 0;
+#endif
+ /* HDRC controls CPEN, but beware current surges during device
+ * connect. They can trigger transient overcurrent conditions
+ * that must be ignored.
+ */
+#ifdef CONFIG_USB_OTG_20
+ val = musb_readb(musb->mregs, MUSB_MISC);
+ val |= 0x1C;
+ musb_writeb(musb->mregs, MUSB_MISC, val);
+#endif
+
+ /* Use EXTVBUS */
+ u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
+ if (plat->extvbus) {
+ busctl |= MUSB_ULPI_USE_EXTVBUS;
+ musb_write_ulpi_buscontrol(musb->mregs, busctl);
+ } else {
+ busctl &= ~MUSB_ULPI_USE_EXTVBUS;
+ musb_write_ulpi_buscontrol(musb->mregs, busctl);
+ }
+
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+
+ if (is_on) {
+ if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+ /* start the session */
+ devctl |= MUSB_DEVCTL_SESSION;
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+ /*
+ * Wait for the musb to set as A device to enable the
+ * VBUS
+ */
+ while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(musb->controller,
+ "configured as A device timeout");
+ ret = -EINVAL;
+ break;
+ }
+ }
+
+ } else {
+ musb->is_active = 1;
+ musb->xceiv->otg->default_a = 1;
+ musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ devctl |= MUSB_DEVCTL_SESSION;
+ MUSB_HST_MODE(musb);
+ }
+ } else {
+ musb->is_active = 0;
+
+ /* NOTE: we're skipping A_WAIT_VFALL -> A_IDLE and
+ * jumping right to B_IDLE...
+ */
+ musb->xceiv->otg->default_a = 0;
+ devctl &= ~MUSB_DEVCTL_SESSION;
+ MUSB_DEV_MODE(musb);
+ }
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+
+ dev_dbg(musb->controller, "VBUS %s, devctl %02x "
+ /* otg %3x conf %08x prcm %08x */ "\n",
+ otg_state_string(musb->xceiv->state),
+ musb_readb(musb->mregs, MUSB_DEVCTL));
+}
+
+static void ux500_musb_try_idle(struct musb *musb, unsigned long timeout)
+{
+ static unsigned long last_timer;
+
+ if (timeout == 0)
+ timeout = jiffies + msecs_to_jiffies(3);
+
+ /* Never idle if active, or when VBUS timeout is not set as host */
+ if (musb->is_active || ((musb->a_wait_bcon == 0)
+ && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ dev_dbg(musb->controller, "%s active, deleting timer\n",
+ otg_state_string(musb->xceiv->state));
+ del_timer(&notify_timer);
+ last_timer = jiffies;
+ return;
+ }
+
+ if (time_after(last_timer, timeout)) {
+ if (!timer_pending(&notify_timer))
+ last_timer = timeout;
+ else {
+ dev_dbg(musb->controller, "Longer idle timer "
+ "already pending, ignoring\n");
+ return;
+ }
+ }
+ last_timer = timeout;
+
+ dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
+ otg_state_string(musb->xceiv->state),
+ (unsigned long)jiffies_to_msecs(timeout - jiffies));
+ mod_timer(&notify_timer, timeout);
+}
+
+static void ux500_musb_enable(struct musb *musb)
+{
+ ux500_store_context(musb);
+}
+
+static struct usb_ep *ux500_musb_configure_endpoints(struct musb *musb,
+ u8 type, struct usb_endpoint_descriptor *desc)
+{
+ struct usb_ep *ep = NULL;
+ struct usb_gadget *gadget = &musb->g;
+ char name[4];
+
+ if (USB_ENDPOINT_XFER_INT == type) {
+ list_for_each_entry(ep, &gadget->ep_list, ep_list) {
+ if (ep->maxpacket == 512)
+ continue;
+ if (NULL == ep->driver_data) {
+ strncpy(name, (ep->name + 3), 4);
+ if (USB_DIR_IN & desc->bEndpointAddress)
+ if (strcmp("in", name) == 0)
+ return ep;
+ }
+ }
+ }
+ return ep;
+}
+
static int ux500_musb_init(struct musb *musb)
{
+ int status;
+
musb->xceiv = usb_get_transceiver();
if (!musb->xceiv) {
pr_err("HS USB OTG: no transceiver configured\n");
return -ENODEV;
}
+ pm_runtime_get_noresume(musb->controller);
+ musb->nb.notifier_call = musb_otg_notifications;
+ status = usb_register_notifier(musb->xceiv, &musb->nb);
+
+ if (status < 0) {
+ dev_dbg(musb->controller, "notification register failed\n");
+ goto err1;
+ }
+
+ setup_timer(&notify_timer, musb_notify_idle, (unsigned long) musb);
return 0;
+err1:
+ pm_runtime_disable(musb->controller);
+ return status;
}
+/**
+ * ux500_musb_exit() - unregister the platform USB driver.
+ * @musb: struct musb pointer.
+ *
+ * This function unregisters the USB controller.
+ */
static int ux500_musb_exit(struct musb *musb)
{
usb_put_transceiver(musb->xceiv);
@@ -56,8 +484,21 @@ static int ux500_musb_exit(struct musb *musb)
static const struct musb_platform_ops ux500_ops = {
.init = ux500_musb_init,
.exit = ux500_musb_exit,
+
+ .set_vbus = ux500_musb_set_vbus,
+ .try_idle = ux500_musb_try_idle,
+
+ .enable = ux500_musb_enable,
+ .configure_endpoints = ux500_musb_configure_endpoints,
};
+/**
+ * ux500_probe() - Allocate the resources.
+ * @pdev: struct platform_device.
+ *
+ * This function allocates the required memory for the
+ * structures and initialize interrupts.
+ */
static int __devinit ux500_probe(struct platform_device *pdev)
{
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
@@ -122,12 +563,12 @@ static int __devinit ux500_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register musb device\n");
goto err4;
}
+ pm_runtime_enable(&pdev->dev);
return 0;
-
err4:
- clk_disable(clk);
-
+ if (cpu_is_u5500())
+ clk_disable(clk);
err3:
clk_put(clk);
@@ -147,43 +588,99 @@ static int __devexit ux500_remove(struct platform_device *pdev)
platform_device_del(glue->musb);
platform_device_put(glue->musb);
- clk_disable(glue->clk);
+ if (cpu_is_u5500())
+ clk_disable(glue->clk);
clk_put(glue->clk);
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
kfree(glue);
return 0;
}
#ifdef CONFIG_PM
+/**
+ * ux500_suspend() - Handles the platform suspend.
+ * @dev: struct device
+ *
+ * This function gets triggered when the platform
+ * is going to suspend
+ */
static int ux500_suspend(struct device *dev)
{
struct ux500_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
usb_phy_set_suspend(musb->xceiv, 1);
- clk_disable(glue->clk);
+ if (cpu_is_u5500())
+ /*
+ * Since this clock is in the APE domain, it will
+ * automatically be disabled on suspend.
+ * (And enabled on resume automatically.)
+ */
+ clk_disable(glue->clk);
+ dev_dbg(dev, "ux500_suspend\n");
return 0;
}
+/**
+ * ux500_resume() - Handles the platform resume.
+ * @dev: struct device
+ *
+ * This function gets triggered when the platform
+ * is going to resume
+ */
static int ux500_resume(struct device *dev)
{
struct ux500_glue *glue = dev_get_drvdata(dev);
struct musb *musb = glue_to_musb(glue);
- int ret;
+
+ if (cpu_is_u5500())
+ /* No point in propagating errors on resume */
+ (void) clk_enable(glue->clk);
+ dev_dbg(dev, "ux500_resume\n");
+
+ usb_phy_set_suspend(musb->xceiv, 0);
+
+ return 0;
+}
+#ifdef CONFIG_UX500_SOC_DB8500
+static int ux500_musb_runtime_resume(struct device *dev)
+{
+ struct ux500_glue *glue = dev_get_drvdata(dev);
+ int ret;
+
+ if (cpu_is_u5500())
+ return 0;
ret = clk_enable(glue->clk);
if (ret) {
- dev_err(dev, "failed to enable clock\n");
+ dev_dbg(dev, "Unable to enable clk\n");
return ret;
}
+ dev_dbg(dev, "ux500_musb_runtime_resume\n");
+ return 0;
+}
- usb_phy_set_suspend(musb->xceiv, 0);
+static int ux500_musb_runtime_suspend(struct device *dev)
+{
+ struct ux500_glue *glue = dev_get_drvdata(dev);
+ if (cpu_is_u5500())
+ return 0;
+
+ clk_disable(glue->clk);
+ dev_dbg(dev, "ux500_musb_runtime_suspend\n");
return 0;
}
-
+#endif
static const struct dev_pm_ops ux500_pm_ops = {
+#ifdef CONFIG_UX500_SOC_DB8500
+ SET_RUNTIME_PM_OPS(ux500_musb_runtime_suspend,
+ ux500_musb_runtime_resume, NULL)
+#endif
.suspend = ux500_suspend,
.resume = ux500_resume,
};
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index d05c7fbbb70..7bf0c289ef5 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -32,6 +32,11 @@
#include <linux/pfn.h>
#include <mach/usb.h>
#include "musb_core.h"
+#undef DBG
+#undef WARNING
+#undef INFO
+#include <linux/usb/composite.h>
+#define Ux500_USB_DMA_MIN_TRANSFER_SIZE 512
struct ux500_dma_channel {
struct dma_channel channel;
@@ -64,14 +69,14 @@ void ux500_dma_callback(void *private_data)
struct musb *musb = hw_ep->musb;
unsigned long flags;
- dev_dbg(musb->controller, "DMA rx transfer done on hw_ep=%d\n",
+ dev_dbg(musb->controller, "DMA tx transfer done on hw_ep=%d\n",
hw_ep->epnum);
spin_lock_irqsave(&musb->lock, flags);
ux500_channel->channel.actual_len = ux500_channel->cur_len;
ux500_channel->channel.status = MUSB_DMA_STATUS_FREE;
musb_dma_completion(musb, hw_ep->epnum,
- ux500_channel->is_tx);
+ ux500_channel->is_tx);
spin_unlock_irqrestore(&musb->lock, flags);
}
@@ -134,6 +139,15 @@ static bool ux500_configure_channel(struct dma_channel *channel,
return true;
}
+/**
+ * ux500_dma_controller_allocate() - allocates the DMA channels
+ * @c: pointer to DMA controller
+ * @hw_ep: pointer to endpoint
+ * @is_tx: transmit or receive direction
+ *
+ * This function allocates the DMA channel and initializes
+ * the channel
+*/
static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c,
struct musb_hw_ep *hw_ep, u8 is_tx)
{
@@ -172,7 +186,13 @@ static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c,
return &(ux500_channel->channel);
}
-
+/**
+ * ux500_dma_channel_release() - releases the DMA channel
+ * @channel: channel to be released
+ *
+ * This function releases the DMA channel
+ *
+*/
static void ux500_dma_channel_release(struct dma_channel *channel)
{
struct ux500_dma_channel *ux500_channel = channel->private_data;
@@ -190,26 +210,71 @@ static void ux500_dma_channel_release(struct dma_channel *channel)
static int ux500_dma_is_compatible(struct dma_channel *channel,
u16 maxpacket, void *buf, u32 length)
{
- if ((maxpacket & 0x3) ||
- ((int)buf & 0x3) ||
- (length < 512) ||
- (length & 0x3))
- return false;
- else
- return true;
+ struct ux500_dma_channel *ux500_channel = channel->private_data;
+ struct musb_hw_ep *hw_ep = ux500_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
+ struct usb_descriptor_header **descriptors;
+ struct usb_function *f;
+ struct usb_gadget *gadget = &musb->g;
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ if (length < Ux500_USB_DMA_MIN_TRANSFER_SIZE)
+ return 0;
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (!strcmp(f->name, "cdc_ethernet") ||
+ !strcmp(f->name, "rndis") ||
+ !strcmp(f->name, "mtp") ||
+ !strcmp(f->name, "phonet") ||
+ !strcmp(f->name, "adb")) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+
+ if ((*descriptors)->bDescriptorType !=
+ USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)
+ *descriptors;
+ if (ep->bEndpointAddress ==
+ ux500_channel->hw_ep->epnum)
+ return 0;
+ }
+ }
+ }
+
+ return 1;
}
+/**
+ * ux500_dma_channel_program() - Configures the channel and initiates transfer
+ * @channel: pointer to DMA channel
+ * @packet_sz: packet size
+ * @mode: mode
+ * @dma_addr: physical address of memory
+ * @len: length
+ *
+ * This function configures the channel and initiates the DMA transfer
+*/
static int ux500_dma_channel_program(struct dma_channel *channel,
u16 packet_sz, u8 mode,
dma_addr_t dma_addr, u32 len)
{
int ret;
+ struct ux500_dma_channel *ux500_dma_channel = channel->private_data;
BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
channel->status == MUSB_DMA_STATUS_BUSY);
- if (!ux500_dma_is_compatible(channel, packet_sz, (void *)dma_addr, len))
- return false;
+ if (len < Ux500_USB_DMA_MIN_TRANSFER_SIZE)
+ return 0;
+ if (!ux500_dma_channel->is_tx && len < packet_sz)
+ return 0;
channel->status = MUSB_DMA_STATUS_BUSY;
channel->actual_len = 0;
@@ -220,6 +285,12 @@ static int ux500_dma_channel_program(struct dma_channel *channel,
return ret;
}
+/**
+ * ux500_dma_channel_abort() - aborts the DMA transfer
+ * @channel: pointer to DMA channel.
+ *
+ * This function aborts the DMA transfer.
+*/
static int ux500_dma_channel_abort(struct dma_channel *channel)
{
struct ux500_dma_channel *ux500_channel = channel->private_data;
@@ -254,6 +325,12 @@ static int ux500_dma_channel_abort(struct dma_channel *channel)
return 0;
}
+/**
+ * ux500_dma_controller_stop() - releases all the channels and frees the DMA pipes
+ * @c: pointer to DMA controller
+ *
+ * This function frees all of the logical channels and frees the DMA pipes
+*/
static int ux500_dma_controller_stop(struct dma_controller *c)
{
struct ux500_dma_controller *controller = container_of(c,
@@ -285,6 +362,15 @@ static int ux500_dma_controller_stop(struct dma_controller *c)
return 0;
}
+
+/**
+ * ux500_dma_controller_start() - creates the logical channels pool and registers callbacks
+ * @c: pointer to DMA Controller
+ *
+ * This function requests the logical channels from the DMA driver and creates
+ * logical channels based on event lines and also registers the callbacks which
+ * are invoked after data transfer in the transmit or receive direction.
+*/
static int ux500_dma_controller_start(struct dma_controller *c)
{
struct ux500_dma_controller *controller = container_of(c,
@@ -356,6 +442,12 @@ static int ux500_dma_controller_start(struct dma_controller *c)
return 0;
}
+/**
+ * dma_controller_destroy() - deallocates the DMA controller
+ * @c: pointer to dma controller.
+ *
+ * This function deallocates the DMA controller.
+*/
void dma_controller_destroy(struct dma_controller *c)
{
struct ux500_dma_controller *controller = container_of(c,
@@ -364,6 +456,15 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(controller);
}
+/**
+ * dma_controller_create() - creates the dma controller and initializes callbacks
+ *
+ * @musb: pointer to mentor core driver data instance|
+ * @base: base address of musb registers.
+ *
+ * This function creates the DMA controller and initializes the callbacks
+ * that are invoked from the Mentor IP core.
+*/
struct dma_controller *__init
dma_controller_create(struct musb *musb, void __iomem *base)
{
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 5c87db06b59..168f5b0364f 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -108,6 +108,15 @@ config AB8500_USB
This transceiver supports high and full speed devices plus,
in host mode, low speed.
+config AB5500_USB
+ tristate "AB5500 USB Transceiver Driver"
+ depends on AB5500_CORE
+ select USB_OTG_UTILS
+ help
+ Enable this to support the USB OTG transceiver in AB5500 chip.
+ This transceiver supports high and full speed devices plus,
+ in host mode, low speed.
+
config FSL_USB2_OTG
bool "Freescale USB OTG Transceiver Driver"
depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 && USB_SUSPEND
diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile
index 41aa5098b13..e227d9add96 100644
--- a/drivers/usb/otg/Makefile
+++ b/drivers/usb/otg/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_USB_ULPI) += ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o
obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o
obj-$(CONFIG_AB8500_USB) += ab8500-usb.o
+obj-$(CONFIG_AB5500_USB) += ab5500-usb.o
fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o
obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o
obj-$(CONFIG_USB_MV_OTG) += mv_otg.o
diff --git a/drivers/usb/otg/ab5500-usb.c b/drivers/usb/otg/ab5500-usb.c
new file mode 100644
index 00000000000..e8c06e694a6
--- /dev/null
+++ b/drivers/usb/otg/ab5500-usb.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * Author: Avinash Kumar <avinash.kumar@stericsson.com> for ST-Ericsson
+ * Author: Ravi Kant SINGH <ravikant.singh@stericsson.com> for ST-Ericsson
+ * Author: Supriya s KARANTH <supriya.karanth@stericsson.com> for ST-Ericsson
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mfd/abx500/ab5500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <mach/usb.h>
+#include <linux/kernel_stat.h>
+#include <mach/gpio.h>
+#include <mach/reboot_reasons.h>
+#include <linux/pm_qos.h>
+
+/* AB5500 USB macros
+ */
+#define AB5500_MAIN_WATCHDOG_ENABLE 0x1
+#define AB5500_MAIN_WATCHDOG_KICK 0x2
+#define AB5500_MAIN_WATCHDOG_DISABLE 0x0
+#define AB5500_USB_ADP_ENABLE 0x1
+#define AB5500_WATCHDOG_DELAY 10
+#define AB5500_WATCHDOG_DELAY_US 100
+#define AB5500_PHY_DELAY_US 100
+#define AB5500_MAIN_WDOG_CTRL_REG 0x01
+#define AB5500_USB_LINE_STAT_REG 0x80
+#define AB5500_USB_PHY_CTRL_REG 0x8A
+#define AB5500_MAIN_WATCHDOG_ENABLE 0x1
+#define AB5500_MAIN_WATCHDOG_KICK 0x2
+#define AB5500_MAIN_WATCHDOG_DISABLE 0x0
+#define AB5500_SYS_CTRL2_BLOCK 0x2
+
+/* UsbLineStatus register bit masks */
+#define AB5500_USB_LINK_STATUS_MASK_V1 0x78
+#define AB5500_USB_LINK_STATUS_MASK_V2 0xF8
+
+#define USB_PROBE_DELAY 1000 /* 1 seconds */
+#define USB_LIMIT (200) /* If we have more than 200 irqs per second */
+
+static struct pm_qos_request usb_pm_qos_latency;
+static bool usb_pm_qos_is_latency_0;
+
+#define PUBLIC_ID_BACKUPRAM1 (U5500_BACKUPRAM1_BASE + 0x0FC0)
+#define MAX_USB_SERIAL_NUMBER_LEN 31
+
+/* UsbLineStatus register - usb types */
+enum ab5500_usb_link_status {
+ USB_LINK_NOT_CONFIGURED,
+ USB_LINK_STD_HOST_NC,
+ USB_LINK_STD_HOST_C_NS,
+ USB_LINK_STD_HOST_C_S,
+ USB_LINK_HOST_CHG_NM,
+ USB_LINK_HOST_CHG_HS,
+ USB_LINK_HOST_CHG_HS_CHIRP,
+ USB_LINK_DEDICATED_CHG,
+ USB_LINK_ACA_RID_A,
+ USB_LINK_ACA_RID_B,
+ USB_LINK_ACA_RID_C_NM,
+ USB_LINK_ACA_RID_C_HS,
+ USB_LINK_ACA_RID_C_HS_CHIRP,
+ USB_LINK_HM_IDGND,
+ USB_LINK_OTG_HOST_NO_CURRENT,
+ USB_LINK_NOT_VALID_LINK,
+ USB_LINK_PHY_EN_NO_VBUS_NO_IDGND,
+ USB_LINK_STD_UPSTREAM_NO_VBUS_NO_IDGND,
+ USB_LINK_HM_IDGND_V2
+};
+
+/**
+ * ab5500_usb_mode - Different states of ab usb_chip
+ *
+ * Used for USB cable plug-in state machine
+ */
+enum ab5500_usb_mode {
+ USB_IDLE,
+ USB_DEVICE,
+ USB_HOST,
+ USB_DEDICATED_CHG,
+};
+struct ab5500_usb {
+ struct otg_transceiver otg;
+ struct device *dev;
+ int irq_num_id_fall;
+ int irq_num_vbus_rise;
+ int irq_num_vbus_fall;
+ int irq_num_link_status;
+ unsigned vbus_draw;
+ struct delayed_work dwork;
+ struct work_struct phy_dis_work;
+ unsigned long link_status_wait;
+ int rev;
+ int usb_cs_gpio;
+ enum ab5500_usb_mode mode;
+ struct clk *sysclk;
+ struct regulator *v_ape;
+ struct abx500_usbgpio_platform_data *usb_gpio;
+ struct delayed_work work_usb_workaround;
+ bool phy_enabled;
+};
+
+static int ab5500_usb_irq_setup(struct platform_device *pdev,
+ struct ab5500_usb *ab);
+static int ab5500_usb_boot_detect(struct ab5500_usb *ab);
+static int ab5500_usb_link_status_update(struct ab5500_usb *ab);
+
+static void ab5500_usb_phy_enable(struct ab5500_usb *ab, bool sel_host);
+static void ab5500_usb_phy_disable(struct ab5500_usb *ab, bool sel_host);
+
+static inline struct ab5500_usb *xceiv_to_ab(struct otg_transceiver *x)
+{
+ return container_of(x, struct ab5500_usb, otg);
+}
+
+/**
+ * ab5500_usb_wd_workaround() - Kick the watch dog timer
+ *
+ * This function used to Kick the watch dog timer
+ */
+static void ab5500_usb_wd_workaround(struct ab5500_usb *ab)
+{
+ abx500_set_register_interruptible(ab->dev,
+ AB5500_SYS_CTRL2_BLOCK,
+ AB5500_MAIN_WDOG_CTRL_REG,
+ AB5500_MAIN_WATCHDOG_ENABLE);
+
+ udelay(AB5500_WATCHDOG_DELAY_US);
+
+ abx500_set_register_interruptible(ab->dev,
+ AB5500_SYS_CTRL2_BLOCK,
+ AB5500_MAIN_WDOG_CTRL_REG,
+ (AB5500_MAIN_WATCHDOG_ENABLE
+ | AB5500_MAIN_WATCHDOG_KICK));
+
+ udelay(AB5500_WATCHDOG_DELAY_US);
+
+ abx500_set_register_interruptible(ab->dev,
+ AB5500_SYS_CTRL2_BLOCK,
+ AB5500_MAIN_WDOG_CTRL_REG,
+ AB5500_MAIN_WATCHDOG_DISABLE);
+
+ udelay(AB5500_WATCHDOG_DELAY_US);
+}
+
+static void ab5500_usb_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+ struct delayed_work *work_usb_workaround = to_delayed_work(work);
+ struct ab5500_usb *ab = container_of(work_usb_workaround,
+ struct ab5500_usb, work_usb_workaround);
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB5500_USBOTG, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > USB_LIMIT) {
+
+ if (!usb_pm_qos_is_latency_0) {
+
+ pm_qos_add_request(&usb_pm_qos_latency,
+ PM_QOS_CPU_DMA_LATENCY, 0);
+ usb_pm_qos_is_latency_0 = true;
+ }
+ } else {
+
+ if (usb_pm_qos_is_latency_0) {
+
+ pm_qos_remove_request(&usb_pm_qos_latency);
+ usb_pm_qos_is_latency_0 = false;
+ }
+ }
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &ab->work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+}
+
+static void ab5500_usb_phy_enable(struct ab5500_usb *ab, bool sel_host)
+{
+ int ret = 0;
+ /* Workaround for spurious interrupt to be checked with Hardware Team*/
+ if (ab->phy_enabled == true)
+ return;
+ ab->phy_enabled = true;
+
+ ab->usb_gpio->enable();
+ clk_enable(ab->sysclk);
+ regulator_enable(ab->v_ape);
+
+ /* TODO: Remove ux500_resotore_context and handle similar to ab8500 */
+ ux500_restore_context(NULL);
+ ret = gpio_direction_output(ab->usb_cs_gpio, 0);
+ if (ret < 0) {
+ dev_err(ab->dev, "usb_cs_gpio: gpio direction failed\n");
+ gpio_free(ab->usb_cs_gpio);
+ return;
+ }
+ gpio_set_value(ab->usb_cs_gpio, 1);
+ if (sel_host) {
+ schedule_delayed_work_on(0,
+ &ab->work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+ }
+}
+
+static void ab5500_usb_phy_disable(struct ab5500_usb *ab, bool sel_host)
+{
+ /* Workaround for spurious interrupt to be checked with Hardware Team*/
+ if (ab->phy_enabled == false)
+ return;
+ ab->phy_enabled = false;
+
+ /* Needed to disable the phy.*/
+ ab5500_usb_wd_workaround(ab);
+ clk_disable(ab->sysclk);
+ regulator_disable(ab->v_ape);
+ ab->usb_gpio->disable();
+ gpio_set_value(ab->usb_cs_gpio, 0);
+
+ if (sel_host) {
+ if (usb_pm_qos_is_latency_0) {
+
+ pm_qos_remove_request(&usb_pm_qos_latency);
+ usb_pm_qos_is_latency_0 = false;
+ }
+ cancel_delayed_work_sync(&ab->work_usb_workaround);
+ }
+}
+
+#define ab5500_usb_peri_phy_en(ab) ab5500_usb_phy_enable(ab, false)
+#define ab5500_usb_peri_phy_dis(ab) ab5500_usb_phy_disable(ab, false)
+#define ab5500_usb_host_phy_en(ab) ab5500_usb_phy_enable(ab, true)
+#define ab5500_usb_host_phy_dis(ab) ab5500_usb_phy_disable(ab, true)
+
+/* Work created after an link status update handler*/
+static int ab5500_usb_link_status_update(struct ab5500_usb *ab)
+{
+ u8 val = 0;
+ enum ab5500_usb_link_status lsts;
+ enum usb_xceiv_events event = USB_EVENT_NONE;
+
+ (void)abx500_get_register_interruptible(ab->dev,
+ AB5500_BANK_USB, AB5500_USB_LINE_STAT_REG, &val);
+
+ if (ab->rev >= AB5500_2_0)
+ lsts = (val & AB5500_USB_LINK_STATUS_MASK_V2) >> 3;
+ else
+ lsts = (val & AB5500_USB_LINK_STATUS_MASK_V1) >> 3;
+
+ switch (lsts) {
+
+ case USB_LINK_STD_HOST_NC:
+ case USB_LINK_STD_HOST_C_NS:
+ case USB_LINK_STD_HOST_C_S:
+ case USB_LINK_HOST_CHG_NM:
+ case USB_LINK_HOST_CHG_HS:
+ case USB_LINK_HOST_CHG_HS_CHIRP:
+
+ event = USB_EVENT_VBUS;
+ ab5500_usb_peri_phy_en(ab);
+
+ break;
+ case USB_LINK_DEDICATED_CHG:
+ /* TODO: vbus_draw */
+ event = USB_EVENT_CHARGER;
+ break;
+
+ case USB_LINK_HM_IDGND:
+ if (ab->rev >= AB5500_2_0)
+ return -1;
+
+
+ ab5500_usb_host_phy_en(ab);
+
+ ab->otg.default_a = true;
+ event = USB_EVENT_ID;
+
+ break;
+ case USB_LINK_PHY_EN_NO_VBUS_NO_IDGND:
+ ab5500_usb_peri_phy_dis(ab);
+
+ break;
+ case USB_LINK_STD_UPSTREAM_NO_VBUS_NO_IDGND:
+ ab5500_usb_host_phy_dis(ab);
+
+ break;
+
+ case USB_LINK_HM_IDGND_V2:
+ if (!(ab->rev >= AB5500_2_0))
+ return -1;
+
+
+ ab5500_usb_host_phy_en(ab);
+
+ ab->otg.default_a = true;
+ event = USB_EVENT_ID;
+
+ break;
+ default:
+ break;
+ }
+
+ atomic_notifier_call_chain(&ab->otg.notifier, event, &ab->vbus_draw);
+
+ return 0;
+}
+
+static void ab5500_usb_delayed_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct ab5500_usb *ab = container_of(dwork, struct ab5500_usb, dwork);
+
+ ab5500_usb_link_status_update(ab);
+}
+
+/**
+ * This function is used to signal the completion of
+ * USB Link status register update
+ */
+static irqreturn_t ab5500_usb_link_status_irq(int irq, void *data)
+{
+ struct ab5500_usb *ab = (struct ab5500_usb *) data;
+ ab5500_usb_link_status_update(ab);
+
+ return IRQ_HANDLED;
+}
+
+
+
+
+static void ab5500_usb_irq_free(struct ab5500_usb *ab)
+{
+ if (ab->irq_num_link_status)
+ free_irq(ab->irq_num_link_status, ab);
+}
+
+/**
+ * ab5500_usb_irq_setup : register USB callback handlers for ab5500
+ * @mode: value for mode.
+ *
+ * This function is used to register USB callback handlers for ab5500.
+ */
+static int ab5500_usb_irq_setup(struct platform_device *pdev,
+ struct ab5500_usb *ab)
+{
+ int ret = 0;
+ int irq, err;
+
+ if (!ab->dev)
+ return -EINVAL;
+
+
+ irq = platform_get_irq_byname(pdev, "Link_Update");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Link Update irq not found\n");
+ err = irq;
+ goto irq_fail;
+ }
+ ab->irq_num_link_status = irq;
+
+ ret = request_threaded_irq(ab->irq_num_link_status,
+ NULL, ab5500_usb_link_status_irq,
+ IRQF_NO_SUSPEND | IRQF_SHARED,
+ "usb-link-status-update", ab);
+ if (ret < 0) {
+ printk(KERN_ERR "failed to set the callback"
+ " handler for usb charge"
+ " detect done\n");
+ err = ret;
+ goto irq_fail;
+ }
+
+ ab5500_usb_wd_workaround(ab);
+ return 0;
+
+irq_fail:
+ ab5500_usb_irq_free(ab);
+ return err;
+}
+
+/* Sys interfaces */
+static ssize_t
+serial_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u32 bufer[5];
+ void __iomem *backup_ram = NULL;
+ backup_ram = ioremap(PUBLIC_ID_BACKUPRAM1, 0x14);
+
+ if (backup_ram) {
+ bufer[0] = readl(backup_ram);
+ bufer[1] = readl(backup_ram + 4);
+ bufer[2] = readl(backup_ram + 8);
+ bufer[3] = readl(backup_ram + 0x0c);
+ bufer[4] = readl(backup_ram + 0x10);
+
+ snprintf(buf, MAX_USB_SERIAL_NUMBER_LEN+1,
+ "%.8X%.8X%.8X%.8X%.8X",
+ bufer[0], bufer[1], bufer[2], bufer[3], bufer[4]);
+
+ iounmap(backup_ram);
+ } else
+ dev_err(dev, "$$\n");
+
+ return strlen(buf);
+}
+
+
+static DEVICE_ATTR(serial_number, 0644, serial_number_show, NULL);
+
+static struct attribute *ab5500_usb_attributes[] = {
+ &dev_attr_serial_number.attr,
+ NULL
+};
+static const struct attribute_group ab5500_attr_group = {
+ .attrs = ab5500_usb_attributes,
+};
+
+static int ab5500_create_sysfsentries(struct ab5500_usb *ab)
+{
+ int err;
+
+ err = sysfs_create_group(&ab->dev->kobj, &ab5500_attr_group);
+ if (err)
+ sysfs_remove_group(&ab->dev->kobj, &ab5500_attr_group);
+
+ return err;
+}
+
+/**
+ * ab5500_usb_boot_detect : detect the USB cable during boot time.
+ * @mode: value for mode.
+ *
+ * This function is used to detect the USB cable during boot time.
+ */
+static int ab5500_usb_boot_detect(struct ab5500_usb *ab)
+{
+ int usb_status = 0;
+ enum ab5500_usb_link_status lsts;
+ if (!ab->dev)
+ return -EINVAL;
+
+ (void)abx500_get_register_interruptible(ab->dev,
+ AB5500_BANK_USB, AB5500_USB_LINE_STAT_REG, &usb_status);
+
+ if (ab->rev >= AB5500_2_0)
+ lsts = (usb_status & AB5500_USB_LINK_STATUS_MASK_V2) >> 3;
+ else
+ lsts = (usb_status & AB5500_USB_LINK_STATUS_MASK_V1) >> 3;
+
+ switch (lsts) {
+
+ case USB_LINK_STD_HOST_NC:
+ case USB_LINK_STD_HOST_C_NS:
+ case USB_LINK_STD_HOST_C_S:
+ case USB_LINK_HOST_CHG_NM:
+ case USB_LINK_HOST_CHG_HS:
+ case USB_LINK_HOST_CHG_HS_CHIRP:
+ /*
+ * If Power on key was not pressed then enter charge only
+ * mode and dont enumerate
+ */
+ if ((!(ab5500_get_turn_on_status() &
+ (P_ON_KEY1_EVENT | P_ON_KEY2_EVENT))) &&
+ (prcmu_get_reset_code() ==
+ SW_RESET_COLDSTART)) {
+ dev_dbg(ab->dev, "USB entered charge only mode");
+ return 0;
+ }
+ ab5500_usb_peri_phy_en(ab);
+
+ break;
+
+ case USB_LINK_HM_IDGND:
+ case USB_LINK_HM_IDGND_V2:
+ ab5500_usb_host_phy_en(ab);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ab5500_usb_set_power(struct otg_transceiver *otg, unsigned mA)
+{
+ struct ab5500_usb *ab;
+
+ if (!otg)
+ return -ENODEV;
+
+ ab = xceiv_to_ab(otg);
+
+ ab->vbus_draw = mA;
+
+ atomic_notifier_call_chain(&ab->otg.notifier,
+ USB_EVENT_VBUS, &ab->vbus_draw);
+ return 0;
+}
+
+static int ab5500_usb_set_suspend(struct otg_transceiver *x, int suspend)
+{
+ /* TODO */
+ return 0;
+}
+
+static int ab5500_usb_set_host(struct otg_transceiver *otg,
+ struct usb_bus *host)
+{
+ struct ab5500_usb *ab;
+
+ if (!otg)
+ return -ENODEV;
+
+ ab = xceiv_to_ab(otg);
+
+ /* Some drivers call this function in atomic context.
+ * Do not update ab5500 registers directly till this
+ * is fixed.
+ */
+
+ if (!host) {
+ ab->otg.host = NULL;
+ schedule_work(&ab->phy_dis_work);
+ } else {
+ ab->otg.host = host;
+ }
+
+ return 0;
+}
+
+static int ab5500_usb_set_peripheral(struct otg_transceiver *otg,
+ struct usb_gadget *gadget)
+{
+ struct ab5500_usb *ab;
+
+ if (!otg)
+ return -ENODEV;
+
+ ab = xceiv_to_ab(otg);
+
+ /* Some drivers call this function in atomic context.
+ * Do not update ab5500 registers directly till this
+ * is fixed.
+ */
+
+ if (!gadget) {
+ ab->otg.gadget = NULL;
+ schedule_work(&ab->phy_dis_work);
+ } else {
+ ab->otg.gadget = gadget;
+ }
+
+ return 0;
+}
+
+static int __devinit ab5500_usb_probe(struct platform_device *pdev)
+{
+ struct ab5500_usb *ab;
+ struct abx500_usbgpio_platform_data *usb_pdata =
+ pdev->dev.platform_data;
+ int err;
+ int ret = -1;
+ ab = kzalloc(sizeof *ab, GFP_KERNEL);
+ if (!ab)
+ return -ENOMEM;
+
+ ab->dev = &pdev->dev;
+ ab->otg.dev = ab->dev;
+ ab->otg.label = "ab5500";
+ ab->otg.state = OTG_STATE_B_IDLE;
+ ab->otg.set_host = ab5500_usb_set_host;
+ ab->otg.set_peripheral = ab5500_usb_set_peripheral;
+ ab->otg.set_suspend = ab5500_usb_set_suspend;
+ ab->otg.set_power = ab5500_usb_set_power;
+ ab->usb_gpio = usb_pdata;
+ ab->mode = USB_IDLE;
+
+ platform_set_drvdata(pdev, ab);
+
+ ATOMIC_INIT_NOTIFIER_HEAD(&ab->otg.notifier);
+
+ /* v1: Wait for link status to become stable.
+ * all: Updates form set_host and set_peripheral as they are atomic.
+ */
+ INIT_DELAYED_WORK(&ab->dwork, ab5500_usb_delayed_work);
+
+ INIT_DELAYED_WORK_DEFERRABLE(&ab->work_usb_workaround,
+ ab5500_usb_load);
+
+ err = otg_set_transceiver(&ab->otg);
+ if (err)
+ dev_err(&pdev->dev, "Can't register transceiver\n");
+
+ ab->usb_cs_gpio = ab->usb_gpio->usb_cs;
+
+ ab->rev = abx500_get_chip_id(ab->dev);
+
+ ab->sysclk = clk_get(ab->dev, "sysclk");
+ if (IS_ERR(ab->sysclk)) {
+ ret = PTR_ERR(ab->sysclk);
+ ab->sysclk = NULL;
+ return ret;
+ }
+
+ ab->v_ape = regulator_get(ab->dev, "v-ape");
+ if (!ab->v_ape) {
+ dev_err(ab->dev, "Could not get v-ape supply\n");
+
+ return -EINVAL;
+ }
+
+ ab5500_usb_irq_setup(pdev, ab);
+
+ ret = gpio_request(ab->usb_cs_gpio, "usb-cs");
+ if (ret < 0)
+ dev_err(&pdev->dev, "usb gpio request fail\n");
+
+ /* Aquire GPIO alternate config struct for USB */
+ err = ab->usb_gpio->get(ab->dev);
+ if (err < 0)
+ goto fail1;
+
+ err = ab5500_usb_boot_detect(ab);
+ if (err < 0)
+ goto fail1;
+
+ err = ab5500_create_sysfsentries(ab);
+ if (err < 0)
+ dev_err(ab->dev, "usb create sysfs entries failed\n");
+
+ return 0;
+
+fail1:
+ ab5500_usb_irq_free(ab);
+ kfree(ab);
+ return err;
+}
+
+static int __devexit ab5500_usb_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver ab5500_usb_driver = {
+ .driver = {
+ .name = "ab5500-usb",
+ .owner = THIS_MODULE,
+ },
+ .probe = ab5500_usb_probe,
+ .remove = __devexit_p(ab5500_usb_remove),
+};
+
+static int __init ab5500_usb_init(void)
+{
+ return platform_driver_register(&ab5500_usb_driver);
+}
+subsys_initcall(ab5500_usb_init);
+
+static void __exit ab5500_usb_exit(void)
+{
+ platform_driver_unregister(&ab5500_usb_driver);
+}
+module_exit(ab5500_usb_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/otg/ab8500-usb.c b/drivers/usb/otg/ab8500-usb.c
index a84af677dc5..49c5d31c150 100644
--- a/drivers/usb/otg/ab8500-usb.c
+++ b/drivers/usb/otg/ab8500-usb.c
@@ -29,47 +29,117 @@
#include <linux/notifier.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/dbx500-prcmu.h>
+#include <linux/kernel_stat.h>
+#include <linux/pm_qos.h>
#define AB8500_MAIN_WD_CTRL_REG 0x01
#define AB8500_USB_LINE_STAT_REG 0x80
#define AB8500_USB_PHY_CTRL_REG 0x8A
+#define AB8500_VBUS_CTRL_REG 0x82
+#define AB8500_IT_SOURCE2_REG 0x01
+#define AB8500_IT_SOURCE20_REG 0x13
+#define AB8500_SRC_INT_USB_HOST 0x04
+#define AB8500_SRC_INT_USB_DEVICE 0x80
#define AB8500_BIT_OTG_STAT_ID (1 << 0)
#define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0)
#define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1)
#define AB8500_BIT_WD_CTRL_ENABLE (1 << 0)
#define AB8500_BIT_WD_CTRL_KICK (1 << 1)
+#define AB8500_BIT_VBUS_ENABLE (1 << 0)
#define AB8500_V1x_LINK_STAT_WAIT (HZ/10)
#define AB8500_WD_KICK_DELAY_US 100 /* usec */
#define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */
+#define AB8500_V20_31952_DISABLE_DELAY_US 100 /* usec */
#define AB8500_WD_V10_DISABLE_DELAY_MS 100 /* ms */
+/* Registers in bank 0x11 */
+#define AB8500_BANK12_ACCESS 0x00
+
+/* Registers in bank 0x12 */
+#define AB8500_USB_PHY_TUNE1 0x05
+#define AB8500_USB_PHY_TUNE2 0x06
+#define AB8500_USB_PHY_TUNE3 0x07
+
+static struct pm_qos_request usb_pm_qos_latency;
+static bool usb_pm_qos_is_latency_0;
+
+#define USB_PROBE_DELAY 1000 /* 1 seconds */
+#define USB_LIMIT (200) /* If we have more than 200 irqs per second */
+
+#define PUBLIC_ID_BACKUPRAM1 (U8500_BACKUPRAM1_BASE + 0x0FC0)
+#define MAX_USB_SERIAL_NUMBER_LEN 31
+#define AB8505_USB_LINE_STAT_REG 0x94
+
/* Usb line status register */
enum ab8500_usb_link_status {
- USB_LINK_NOT_CONFIGURED = 0,
- USB_LINK_STD_HOST_NC,
- USB_LINK_STD_HOST_C_NS,
- USB_LINK_STD_HOST_C_S,
- USB_LINK_HOST_CHG_NM,
- USB_LINK_HOST_CHG_HS,
- USB_LINK_HOST_CHG_HS_CHIRP,
- USB_LINK_DEDICATED_CHG,
- USB_LINK_ACA_RID_A,
- USB_LINK_ACA_RID_B,
- USB_LINK_ACA_RID_C_NM,
- USB_LINK_ACA_RID_C_HS,
- USB_LINK_ACA_RID_C_HS_CHIRP,
- USB_LINK_HM_IDGND,
- USB_LINK_RESERVED,
- USB_LINK_NOT_VALID_LINK
+ USB_LINK_NOT_CONFIGURED_8500 = 0,
+ USB_LINK_STD_HOST_NC_8500,
+ USB_LINK_STD_HOST_C_NS_8500,
+ USB_LINK_STD_HOST_C_S_8500,
+ USB_LINK_HOST_CHG_NM_8500,
+ USB_LINK_HOST_CHG_HS_8500,
+ USB_LINK_HOST_CHG_HS_CHIRP_8500,
+ USB_LINK_DEDICATED_CHG_8500,
+ USB_LINK_ACA_RID_A_8500,
+ USB_LINK_ACA_RID_B_8500,
+ USB_LINK_ACA_RID_C_NM_8500,
+ USB_LINK_ACA_RID_C_HS_8500,
+ USB_LINK_ACA_RID_C_HS_CHIRP_8500,
+ USB_LINK_HM_IDGND_8500,
+ USB_LINK_RESERVED_8500,
+ USB_LINK_NOT_VALID_LINK_8500,
+};
+
+enum ab8505_usb_link_status {
+ USB_LINK_NOT_CONFIGURED_8505 = 0,
+ USB_LINK_STD_HOST_NC_8505,
+ USB_LINK_STD_HOST_C_NS_8505,
+ USB_LINK_STD_HOST_C_S_8505,
+ USB_LINK_CDP_8505,
+ USB_LINK_RESERVED0_8505,
+ USB_LINK_RESERVED1_8505,
+ USB_LINK_DEDICATED_CHG_8505,
+ USB_LINK_ACA_RID_A_8505,
+ USB_LINK_ACA_RID_B_8505,
+ USB_LINK_ACA_RID_C_NM_8505,
+ USB_LINK_RESERVED2_8505,
+ USB_LINK_RESERVED3_8505,
+ USB_LINK_HM_IDGND_8505,
+ USB_LINK_CHARGERPORT_NOT_OK_8505,
+ USB_LINK_CHARGER_DM_HIGH_8505,
+ USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8505,
+ USB_LINK_STD_UPSTREAM_NO_IDGNG_NO_VBUS_8505,
+ USB_LINK_STD_UPSTREAM_8505,
+ USB_LINK_CHARGER_SE1_8505,
+ USB_LINK_CARKIT_CHGR_1_8505,
+ USB_LINK_CARKIT_CHGR_2_8505,
+ USB_LINK_ACA_DOCK_CHGR_8505,
+ USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8505,
+ USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8505,
+ USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8505,
+ USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8505,
+ USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505,
+};
+
+enum ab8500_usb_mode {
+ USB_IDLE = 0,
+ USB_PERIPHERAL,
+ USB_HOST,
+ USB_DEDICATED_CHG
};
struct ab8500_usb {
struct usb_phy phy;
struct device *dev;
+ struct ab8500 *ab8500;
int irq_num_id_rise;
int irq_num_id_fall;
int irq_num_vbus_rise;
@@ -79,7 +149,13 @@ struct ab8500_usb {
struct delayed_work dwork;
struct work_struct phy_dis_work;
unsigned long link_status_wait;
- int rev;
+ enum ab8500_usb_mode mode;
+ struct clk *sysclk;
+ struct regulator *v_ape;
+ struct regulator *v_musb;
+ struct regulator *v_ulpi;
+ struct delayed_work work_usb_workaround;
+ bool sysfs_flag;
};
static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x)
@@ -102,10 +178,8 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
(AB8500_BIT_WD_CTRL_ENABLE
| AB8500_BIT_WD_CTRL_KICK));
- if (ab->rev > 0x10) /* v1.1 v2.0 */
+ if (!is_ab8500_1p0_or_earlier(ab->ab8500))
udelay(AB8500_WD_V11_DISABLE_DELAY_US);
- else /* v1.0 */
- msleep(AB8500_WD_V10_DISABLE_DELAY_MS);
abx500_set_register_interruptible(ab->dev,
AB8500_SYS_CTRL2_BLOCK,
@@ -113,146 +187,412 @@ static void ab8500_usb_wd_workaround(struct ab8500_usb *ab)
0);
}
-static void ab8500_usb_phy_ctrl(struct ab8500_usb *ab, bool sel_host,
+static void ab8500_usb_load(struct work_struct *work)
+{
+ int cpu;
+ unsigned int num_irqs = 0;
+ static unsigned int old_num_irqs = UINT_MAX;
+ struct delayed_work *work_usb_workaround = to_delayed_work(work);
+ struct ab8500_usb *ab = container_of(work_usb_workaround,
+ struct ab8500_usb, work_usb_workaround);
+
+ for_each_online_cpu(cpu)
+ num_irqs += kstat_irqs_cpu(IRQ_DB8500_USBOTG, cpu);
+
+ if ((num_irqs > old_num_irqs) &&
+ (num_irqs - old_num_irqs) > USB_LIMIT) {
+
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "usb", 125);
+ if (!usb_pm_qos_is_latency_0) {
+
+ pm_qos_add_request(&usb_pm_qos_latency,
+ PM_QOS_CPU_DMA_LATENCY, 0);
+ usb_pm_qos_is_latency_0 = true;
+ }
+ } else {
+
+ if (usb_pm_qos_is_latency_0) {
+
+ pm_qos_remove_request(&usb_pm_qos_latency);
+ usb_pm_qos_is_latency_0 = false;
+ }
+
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "usb", 25);
+ }
+ old_num_irqs = num_irqs;
+
+ schedule_delayed_work_on(0,
+ &ab->work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+}
+
+static void ab8500_usb_regulator_ctrl(struct ab8500_usb *ab, bool sel_host,
bool enable)
{
- u8 ctrl_reg;
- abx500_get_register_interruptible(ab->dev,
- AB8500_USB,
- AB8500_USB_PHY_CTRL_REG,
- &ctrl_reg);
- if (sel_host) {
- if (enable)
- ctrl_reg |= AB8500_BIT_PHY_CTRL_HOST_EN;
- else
- ctrl_reg &= ~AB8500_BIT_PHY_CTRL_HOST_EN;
+ int ret = 0, volt = 0;
+
+ if (enable) {
+ regulator_enable(ab->v_ape);
+ if (!is_ab8500_2p0_or_earlier(ab->ab8500)) {
+ ret = regulator_set_voltage(ab->v_ulpi,
+ 1300000, 1350000);
+ if (ret < 0)
+ dev_err(ab->dev, "Failed to set the Vintcore"
+ " to 1.3V, ret=%d\n", ret);
+ ret = regulator_set_optimum_mode(ab->v_ulpi,
+ 28000);
+ if (ret < 0)
+ dev_err(ab->dev, "Failed to set optimum mode"
+ " (ret=%d)\n", ret);
+
+ }
+ regulator_enable(ab->v_ulpi);
+ if (!is_ab8500_2p0_or_earlier(ab->ab8500)) {
+ volt = regulator_get_voltage(ab->v_ulpi);
+ if ((volt != 1300000) && (volt != 1350000))
+ dev_err(ab->dev, "Vintcore is not"
+ " set to 1.3V"
+ " volt=%d\n", volt);
+ }
+ regulator_enable(ab->v_musb);
+
} else {
- if (enable)
- ctrl_reg |= AB8500_BIT_PHY_CTRL_DEVICE_EN;
- else
- ctrl_reg &= ~AB8500_BIT_PHY_CTRL_DEVICE_EN;
+ regulator_disable(ab->v_musb);
+ regulator_disable(ab->v_ulpi);
+ regulator_disable(ab->v_ape);
}
+}
- abx500_set_register_interruptible(ab->dev,
+
+static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host)
+{
+ u8 bit;
+ bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN :
+ AB8500_BIT_PHY_CTRL_DEVICE_EN;
+
+ clk_enable(ab->sysclk);
+
+ ab8500_usb_regulator_ctrl(ab, sel_host, true);
+
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ (char *)dev_name(ab->dev), 100);
+
+ schedule_delayed_work_on(0,
+ &ab->work_usb_workaround,
+ msecs_to_jiffies(USB_PROBE_DELAY));
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
AB8500_USB,
AB8500_USB_PHY_CTRL_REG,
- ctrl_reg);
+ bit,
+ bit);
- /* Needed to enable the phy.*/
- if (enable)
- ab8500_usb_wd_workaround(ab);
}
-#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_ctrl(ab, true, true)
-#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_ctrl(ab, true, false)
-#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_ctrl(ab, false, true)
-#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_ctrl(ab, false, false)
+static void ab8500_usb_wd_linkstatus(struct ab8500_usb *ab,u8 bit)
+{
+ /* Workaround for v2.0 bug # 31952 */
+ if (is_ab8500_2p0(ab->ab8500)) {
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ bit,
+ bit);
+ udelay(AB8500_V20_31952_DISABLE_DELAY_US);
+ }
+}
-static int ab8500_usb_link_status_update(struct ab8500_usb *ab)
+static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host)
{
- u8 reg;
- enum ab8500_usb_link_status lsts;
- void *v = NULL;
- enum usb_phy_events event;
+ u8 bit;
+ bit = sel_host ? AB8500_BIT_PHY_CTRL_HOST_EN :
+ AB8500_BIT_PHY_CTRL_DEVICE_EN;
+
+ ab8500_usb_wd_linkstatus(ab,bit);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ bit,
+ 0);
+
+ /* Needed to disable the phy.*/
+ ab8500_usb_wd_workaround(ab);
+
+ clk_disable(ab->sysclk);
+
+ ab8500_usb_regulator_ctrl(ab, sel_host, false);
+
+ prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
+ (char *)dev_name(ab->dev), 50);
+
+ if (!sel_host) {
+
+ cancel_delayed_work_sync(&ab->work_usb_workaround);
+ prcmu_qos_update_requirement(PRCMU_QOS_ARM_OPP,
+ "usb", 25);
+ }
+}
+
+#define ab8500_usb_host_phy_en(ab) ab8500_usb_phy_enable(ab, true)
+#define ab8500_usb_host_phy_dis(ab) ab8500_usb_phy_disable(ab, true)
+#define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false)
+#define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false)
- abx500_get_register_interruptible(ab->dev,
- AB8500_USB,
- AB8500_USB_LINE_STAT_REG,
- &reg);
+static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
+ enum ab8505_usb_link_status lsts)
+{
+ enum usb_phy_events event=0;
- lsts = (reg >> 3) & 0x0F;
+ dev_dbg(ab->dev, "ab8505_usb_link_status_update %d\n", lsts);
switch (lsts) {
- case USB_LINK_NOT_CONFIGURED:
- case USB_LINK_RESERVED:
- case USB_LINK_NOT_VALID_LINK:
- /* TODO: Disable regulators. */
- ab8500_usb_host_phy_dis(ab);
- ab8500_usb_peri_phy_dis(ab);
- ab->phy.state = OTG_STATE_B_IDLE;
+ case USB_LINK_ACA_RID_B_8505:
+ event = USB_EVENT_RIDB;
+ case USB_LINK_NOT_CONFIGURED_8505:
+ case USB_LINK_RESERVED0_8505:
+ case USB_LINK_RESERVED1_8505:
+ case USB_LINK_RESERVED2_8505:
+ case USB_LINK_RESERVED3_8505:
+ if (ab->mode == USB_PERIPHERAL)
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_CLEAN,
+ &ab->vbus_draw);
+ ab->mode = USB_IDLE;
ab->phy.otg->default_a = false;
ab->vbus_draw = 0;
- event = USB_EVENT_NONE;
+ if (event != USB_EVENT_RIDB)
+ event = USB_EVENT_NONE;
break;
- case USB_LINK_STD_HOST_NC:
- case USB_LINK_STD_HOST_C_NS:
- case USB_LINK_STD_HOST_C_S:
- case USB_LINK_HOST_CHG_NM:
- case USB_LINK_HOST_CHG_HS:
- case USB_LINK_HOST_CHG_HS_CHIRP:
- if (ab->phy.otg->gadget) {
- /* TODO: Enable regulators. */
+ case USB_LINK_ACA_RID_C_NM_8505:
+ event = USB_EVENT_RIDC;
+ case USB_LINK_STD_HOST_NC_8505:
+ case USB_LINK_STD_HOST_C_NS_8505:
+ case USB_LINK_STD_HOST_C_S_8505:
+ if (ab->mode == USB_HOST) {
+ ab->mode = USB_PERIPHERAL;
+ ab8500_usb_host_phy_dis(ab);
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_CLEAN,
+ &ab->vbus_draw);
ab8500_usb_peri_phy_en(ab);
- v = ab->phy.otg->gadget;
}
- event = USB_EVENT_VBUS;
+ if (ab->mode == USB_IDLE) {
+ ab->mode = USB_PERIPHERAL;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+ ab8500_usb_peri_phy_en(ab);
+ }
+ if (event != USB_EVENT_RIDC)
+ event = USB_EVENT_VBUS;
break;
-
- case USB_LINK_HM_IDGND:
- if (ab->phy.otg->host) {
- /* TODO: Enable regulators. */
+ case USB_LINK_ACA_RID_A_8505:
+ event = USB_EVENT_RIDA;
+ case USB_LINK_HM_IDGND_8505:
+ if (ab->mode == USB_PERIPHERAL) {
+ ab->mode = USB_HOST;
+ ab8500_usb_peri_phy_dis(ab);
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+ ab8500_usb_host_phy_en(ab);
+ }
+ if (ab->mode == USB_IDLE) {
+ ab->mode = USB_HOST;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
ab8500_usb_host_phy_en(ab);
- v = ab->phy.otg->host;
}
- ab->phy.state = OTG_STATE_A_IDLE;
ab->phy.otg->default_a = true;
- event = USB_EVENT_ID;
+ if (event != USB_EVENT_RIDA)
+ event = USB_EVENT_ID;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ event,
+ &ab->vbus_draw);
break;
- case USB_LINK_ACA_RID_A:
- case USB_LINK_ACA_RID_B:
- /* TODO */
- case USB_LINK_ACA_RID_C_NM:
- case USB_LINK_ACA_RID_C_HS:
- case USB_LINK_ACA_RID_C_HS_CHIRP:
- case USB_LINK_DEDICATED_CHG:
- /* TODO: vbus_draw */
- event = USB_EVENT_CHARGER;
+ case USB_LINK_DEDICATED_CHG_8505:
+ ab->mode = USB_DEDICATED_CHG;
+ event = USB_EVENT_CHARGER;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ event,
+ &ab->vbus_draw);
break;
- }
-
- atomic_notifier_call_chain(&ab->phy.notifier, event, v);
+ default:
+ break;
+ }
return 0;
}
-static void ab8500_usb_delayed_work(struct work_struct *work)
+static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
+ enum ab8500_usb_link_status lsts)
{
- struct ab8500_usb *ab = container_of(work, struct ab8500_usb,
- dwork.work);
+ enum usb_phy_events event=0;
- ab8500_usb_link_status_update(ab);
+ dev_dbg(ab->dev, "ab8500_usb_link_status_update %d\n", lsts);
+
+ switch (lsts) {
+ case USB_LINK_ACA_RID_B_8500:
+ event = USB_EVENT_RIDB;
+ case USB_LINK_NOT_CONFIGURED_8500:
+ case USB_LINK_RESERVED_8500:
+ case USB_LINK_NOT_VALID_LINK_8500:
+ if (ab->mode == USB_PERIPHERAL)
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_CLEAN,
+ &ab->vbus_draw);
+ ab->mode = USB_IDLE;
+ ab->phy.otg->default_a = false;
+ ab->vbus_draw = 0;
+ if (event != USB_EVENT_RIDB)
+ event = USB_EVENT_NONE;
+ break;
+ case USB_LINK_ACA_RID_C_NM_8500:
+ case USB_LINK_ACA_RID_C_HS_8500:
+ case USB_LINK_ACA_RID_C_HS_CHIRP_8500:
+ event = USB_EVENT_RIDC;
+ case USB_LINK_STD_HOST_NC_8500:
+ case USB_LINK_STD_HOST_C_NS_8500:
+ case USB_LINK_STD_HOST_C_S_8500:
+ case USB_LINK_HOST_CHG_NM_8500:
+ case USB_LINK_HOST_CHG_HS_8500:
+ case USB_LINK_HOST_CHG_HS_CHIRP_8500:
+ if (ab->mode == USB_HOST) {
+ ab->mode = USB_PERIPHERAL;
+ ab8500_usb_host_phy_dis(ab);
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+ ab8500_usb_peri_phy_en(ab);
+ }
+ if (ab->mode == USB_IDLE) {
+ ab->mode = USB_PERIPHERAL;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+ ab8500_usb_peri_phy_en(ab);
+ }
+ if (event != USB_EVENT_RIDC)
+ event = USB_EVENT_VBUS;
+ break;
+
+ case USB_LINK_ACA_RID_A_8500:
+ event = USB_EVENT_RIDA;
+ case USB_LINK_HM_IDGND_8500:
+ if (ab->mode == USB_PERIPHERAL) {
+ ab->mode = USB_HOST;
+ ab8500_usb_peri_phy_dis(ab);
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+ ab8500_usb_host_phy_en(ab);
+ }
+ if (ab->mode == USB_IDLE) {
+ ab->mode = USB_HOST;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+ ab8500_usb_host_phy_en(ab);
+ }
+ ab->phy.otg->default_a = true;
+ if (event != USB_EVENT_RIDA)
+ event = USB_EVENT_ID;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ event,
+ &ab->vbus_draw);
+ break;
+
+ case USB_LINK_DEDICATED_CHG_8500:
+ ab->mode = USB_DEDICATED_CHG;
+ event = USB_EVENT_CHARGER;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ event,
+ &ab->vbus_draw);
+ break;
+ }
+ return 0;
}
-static irqreturn_t ab8500_usb_v1x_common_irq(int irq, void *data)
+static int abx500_usb_link_status_update(struct ab8500_usb *ab)
{
- struct ab8500_usb *ab = (struct ab8500_usb *) data;
+ u8 reg;
+ int ret = 0;
- /* Wait for link status to become stable. */
- schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+ if (!(ab->sysfs_flag)) {
+ if (is_ab8500(ab->ab8500)) {
+ enum ab8500_usb_link_status lsts;
- return IRQ_HANDLED;
+ abx500_get_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_LINE_STAT_REG,
+ &reg);
+ lsts = (reg >> 3) & 0x0F;
+ ret = ab8500_usb_link_status_update(ab, lsts);
+ }
+ if (is_ab8505(ab->ab8500)) {
+ enum ab8505_usb_link_status lsts;
+
+ abx500_get_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8505_USB_LINE_STAT_REG,
+ &reg);
+ lsts = (reg >> 3) & 0x1F;
+ ret = ab8505_usb_link_status_update(ab, lsts);
+ }
+ }
+ return ret;
+}
+
+static void ab8500_usb_delayed_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct ab8500_usb *ab = container_of(dwork, struct ab8500_usb, dwork);
+ abx500_usb_link_status_update(ab);
}
-static irqreturn_t ab8500_usb_v1x_vbus_fall_irq(int irq, void *data)
+static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data)
{
struct ab8500_usb *ab = (struct ab8500_usb *) data;
+ enum usb_phy_events event = USB_EVENT_NONE;
/* Link status will not be updated till phy is disabled. */
- ab8500_usb_peri_phy_dis(ab);
-
- /* Wait for link status to become stable. */
- schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+ if (ab->mode == USB_HOST) {
+ ab->phy.otg->default_a = false;
+ ab->vbus_draw = 0;
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ event, &ab->vbus_draw);
+ ab8500_usb_host_phy_dis(ab);
+ }
+ if (ab->mode == USB_PERIPHERAL) {
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ event, &ab->vbus_draw);
+ ab8500_usb_peri_phy_dis(ab);
+ }
+ if (is_ab8500_2p0(ab->ab8500)) {
+ if (ab->mode == USB_DEDICATED_CHG) {
+ ab8500_usb_wd_linkstatus(ab, AB8500_BIT_PHY_CTRL_DEVICE_EN);
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN,
+ 0);
+ }
+ }
return IRQ_HANDLED;
}
-static irqreturn_t ab8500_usb_v20_irq(int irq, void *data)
+static irqreturn_t ab8500_usb_v20_link_status_irq(int irq, void *data)
{
struct ab8500_usb *ab = (struct ab8500_usb *) data;
- ab8500_usb_link_status_update(ab);
+ abx500_usb_link_status_update(ab);
return IRQ_HANDLED;
}
@@ -267,8 +607,42 @@ static void ab8500_usb_phy_disable_work(struct work_struct *work)
if (!ab->phy.otg->gadget)
ab8500_usb_peri_phy_dis(ab);
+
}
+static unsigned ab8500_eyediagram_workaroud(struct ab8500_usb *ab, unsigned mA)
+{
+ /* AB V2 has eye diagram issues when drawing more
+ * than 100mA from VBUS.So setting charging current
+ * to 100mA in case of standard host
+ */
+ if (is_ab8500_2p0_or_earlier(ab->ab8500))
+ if (mA > 100)
+ mA = 100;
+
+ return mA;
+}
+
+#ifdef CONFIG_USB_OTG_20
+static int ab8500_usb_start_srp(struct usb_phy *phy, unsigned mA)
+{
+ struct ab8500_usb *ab;
+
+ if (!phy)
+ return -ENODEV;
+
+ ab = phy_to_ab(phy);
+
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_PREPARE,
+ &ab->vbus_draw);
+
+ ab8500_usb_peri_phy_en(ab);
+
+ return 0;
+}
+#endif
+
static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA)
{
struct ab8500_usb *ab;
@@ -278,18 +652,15 @@ static int ab8500_usb_set_power(struct usb_phy *phy, unsigned mA)
ab = phy_to_ab(phy);
+ mA = ab8500_eyediagram_workaroud(ab, mA);
+
ab->vbus_draw = mA;
- if (mA)
- atomic_notifier_call_chain(&ab->phy.notifier,
- USB_EVENT_ENUMERATED, ab->phy.otg->gadget);
+ atomic_notifier_call_chain(&ab->phy.notifier,
+ USB_EVENT_VBUS, &ab->vbus_draw);
return 0;
}
-/* TODO: Implement some way for charging or other drivers to read
- * ab->vbus_draw.
- */
-
static int ab8500_usb_set_suspend(struct usb_phy *x, int suspend)
{
/* TODO */
@@ -306,25 +677,13 @@ static int ab8500_usb_set_peripheral(struct usb_otg *otg,
ab = phy_to_ab(otg->phy);
+ ab->phy.otg->gadget = gadget;
/* Some drivers call this function in atomic context.
* Do not update ab8500 registers directly till this
* is fixed.
*/
-
- if (!gadget) {
- /* TODO: Disable regulators. */
- otg->gadget = NULL;
+ if (!gadget)
schedule_work(&ab->phy_dis_work);
- } else {
- otg->gadget = gadget;
- otg->phy->state = OTG_STATE_B_IDLE;
-
- /* Phy will not be enabled if cable is already
- * plugged-in. Schedule to enable phy.
- * Use same delay to avoid any race condition.
- */
- schedule_delayed_work(&ab->dwork, ab->link_status_wait);
- }
return 0;
}
@@ -338,22 +697,93 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
ab = phy_to_ab(otg->phy);
+ ab->phy.otg->host = host;
+
/* Some drivers call this function in atomic context.
* Do not update ab8500 registers directly till this
* is fixed.
*/
-
- if (!host) {
- /* TODO: Disable regulators. */
- otg->host = NULL;
+ if (!host)
schedule_work(&ab->phy_dis_work);
- } else {
- otg->host = host;
- /* Phy will not be enabled if cable is already
- * plugged-in. Schedule to enable phy.
- * Use same delay to avoid any race condition.
- */
- schedule_delayed_work(&ab->dwork, ab->link_status_wait);
+
+ return 0;
+}
+/**
+ * ab8500_usb_boot_detect : detect the USB cable during boot time.
+ * @device: value for device.
+ *
+ * This function is used to detect the USB cable during boot time.
+ */
+static int ab8500_usb_boot_detect(struct ab8500_usb *ab)
+{
+ /* Disabling PHY before selective enable or disable */
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN);
+
+ udelay(100);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_DEVICE_EN,
+ 0);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_HOST_EN,
+ AB8500_BIT_PHY_CTRL_HOST_EN);
+
+ udelay(100);
+
+ abx500_mask_and_set_register_interruptible(ab->dev,
+ AB8500_USB,
+ AB8500_USB_PHY_CTRL_REG,
+ AB8500_BIT_PHY_CTRL_HOST_EN,
+ 0);
+
+ return 0;
+}
+
+static void ab8500_usb_regulator_put(struct ab8500_usb *ab)
+{
+
+ if (ab->v_ape)
+ regulator_put(ab->v_ape);
+
+ if (ab->v_ulpi)
+ regulator_put(ab->v_ulpi);
+
+ if (ab->v_musb)
+ regulator_put(ab->v_musb);
+}
+
+static int ab8500_usb_regulator_get(struct ab8500_usb *ab)
+{
+ int err;
+
+ ab->v_ape = regulator_get(ab->dev, "v-ape");
+ if (IS_ERR(ab->v_ape)) {
+ dev_err(ab->dev, "Could not get v-ape supply\n");
+ err = PTR_ERR(ab->v_ape);
+ return err;
+ }
+
+ ab->v_ulpi = regulator_get(ab->dev, "vddulpivio18");
+ if (IS_ERR(ab->v_ulpi)) {
+ dev_err(ab->dev, "Could not get vddulpivio18 supply\n");
+ err = PTR_ERR(ab->v_ulpi);
+ return err;
+ }
+
+ ab->v_musb = regulator_get(ab->dev, "musb_1v8");
+ if (IS_ERR(ab->v_musb)) {
+ dev_err(ab->dev, "Could not get musb_1v8 supply\n");
+ err = PTR_ERR(ab->v_musb);
+ return err;
}
return 0;
@@ -361,126 +791,179 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
static void ab8500_usb_irq_free(struct ab8500_usb *ab)
{
- if (ab->rev < 0x20) {
+ if (ab->irq_num_id_rise)
free_irq(ab->irq_num_id_rise, ab);
+
+ if (ab->irq_num_id_fall)
free_irq(ab->irq_num_id_fall, ab);
+
+ if (ab->irq_num_vbus_rise)
free_irq(ab->irq_num_vbus_rise, ab);
+
+ if (ab->irq_num_vbus_fall)
free_irq(ab->irq_num_vbus_fall, ab);
- } else {
+
+ if (ab->irq_num_link_status)
free_irq(ab->irq_num_link_status, ab);
- }
}
-static int ab8500_usb_v1x_res_setup(struct platform_device *pdev,
+static int ab8500_usb_irq_setup(struct platform_device *pdev,
struct ab8500_usb *ab)
{
int err;
+ int irq;
+
+ if (!is_ab8500_1p0_or_earlier(ab->ab8500)) {
+ irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS");
+ if (irq < 0) {
+ err = irq;
+ dev_err(&pdev->dev, "Link status irq not found\n");
+ goto irq_fail;
+ }
- ab->irq_num_id_rise = platform_get_irq_byname(pdev, "ID_WAKEUP_R");
- if (ab->irq_num_id_rise < 0) {
- dev_err(&pdev->dev, "ID rise irq not found\n");
- return ab->irq_num_id_rise;
- }
- err = request_threaded_irq(ab->irq_num_id_rise, NULL,
- ab8500_usb_v1x_common_irq,
- IRQF_NO_SUSPEND | IRQF_SHARED,
- "usb-id-rise", ab);
- if (err < 0) {
- dev_err(ab->dev, "request_irq failed for ID rise irq\n");
- goto fail0;
+ err = request_threaded_irq(irq, NULL,
+ ab8500_usb_v20_link_status_irq,
+ IRQF_NO_SUSPEND | IRQF_SHARED,
+ "usb-link-status", ab);
+ if (err < 0) {
+ dev_err(ab->dev,
+ "request_irq failed for link status irq\n");
+ return err;
+ }
+ ab->irq_num_link_status = irq;
}
- ab->irq_num_id_fall = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
- if (ab->irq_num_id_fall < 0) {
+ irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F");
+ if (irq < 0) {
+ err = irq;
dev_err(&pdev->dev, "ID fall irq not found\n");
return ab->irq_num_id_fall;
}
- err = request_threaded_irq(ab->irq_num_id_fall, NULL,
- ab8500_usb_v1x_common_irq,
+ err = request_threaded_irq(irq, NULL,
+ ab8500_usb_disconnect_irq,
IRQF_NO_SUSPEND | IRQF_SHARED,
"usb-id-fall", ab);
if (err < 0) {
dev_err(ab->dev, "request_irq failed for ID fall irq\n");
- goto fail1;
+ goto irq_fail;
}
+ ab->irq_num_id_fall = irq;
- ab->irq_num_vbus_rise = platform_get_irq_byname(pdev, "VBUS_DET_R");
- if (ab->irq_num_vbus_rise < 0) {
- dev_err(&pdev->dev, "VBUS rise irq not found\n");
- return ab->irq_num_vbus_rise;
- }
- err = request_threaded_irq(ab->irq_num_vbus_rise, NULL,
- ab8500_usb_v1x_common_irq,
- IRQF_NO_SUSPEND | IRQF_SHARED,
- "usb-vbus-rise", ab);
- if (err < 0) {
- dev_err(ab->dev, "request_irq failed for Vbus rise irq\n");
- goto fail2;
- }
-
- ab->irq_num_vbus_fall = platform_get_irq_byname(pdev, "VBUS_DET_F");
- if (ab->irq_num_vbus_fall < 0) {
+ irq = platform_get_irq_byname(pdev, "VBUS_DET_F");
+ if (irq < 0) {
+ err = irq;
dev_err(&pdev->dev, "VBUS fall irq not found\n");
- return ab->irq_num_vbus_fall;
+ goto irq_fail;
}
- err = request_threaded_irq(ab->irq_num_vbus_fall, NULL,
- ab8500_usb_v1x_vbus_fall_irq,
+ err = request_threaded_irq(irq, NULL,
+ ab8500_usb_disconnect_irq,
IRQF_NO_SUSPEND | IRQF_SHARED,
"usb-vbus-fall", ab);
if (err < 0) {
dev_err(ab->dev, "request_irq failed for Vbus fall irq\n");
- goto fail3;
+ goto irq_fail;
}
+ ab->irq_num_vbus_fall = irq;
return 0;
-fail3:
- free_irq(ab->irq_num_vbus_rise, ab);
-fail2:
- free_irq(ab->irq_num_id_fall, ab);
-fail1:
- free_irq(ab->irq_num_id_rise, ab);
-fail0:
+
+irq_fail:
+ ab8500_usb_irq_free(ab);
return err;
}
-static int ab8500_usb_v2_res_setup(struct platform_device *pdev,
- struct ab8500_usb *ab)
+/* Sys interfaces */
+static ssize_t
+serial_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- int err;
+ u32 bufer[5];
+ void __iomem *backup_ram = NULL;
- ab->irq_num_link_status = platform_get_irq_byname(pdev,
- "USB_LINK_STATUS");
- if (ab->irq_num_link_status < 0) {
- dev_err(&pdev->dev, "Link status irq not found\n");
- return ab->irq_num_link_status;
- }
+ backup_ram = ioremap(PUBLIC_ID_BACKUPRAM1, 0x14);
- err = request_threaded_irq(ab->irq_num_link_status, NULL,
- ab8500_usb_v20_irq,
- IRQF_NO_SUSPEND | IRQF_SHARED,
- "usb-link-status", ab);
- if (err < 0) {
- dev_err(ab->dev,
- "request_irq failed for link status irq\n");
- return err;
- }
+ if (backup_ram) {
+ bufer[0] = readl(backup_ram);
+ bufer[1] = readl(backup_ram + 4);
+ bufer[2] = readl(backup_ram + 8);
+ bufer[3] = readl(backup_ram + 0x0c);
+ bufer[4] = readl(backup_ram + 0x10);
- return 0;
+ snprintf(buf, MAX_USB_SERIAL_NUMBER_LEN+1,
+ "%.8X%.8X%.8X%.8X%.8X",
+ bufer[0], bufer[1], bufer[2], bufer[3], bufer[4]);
+
+ iounmap(backup_ram);
+ } else
+ dev_err(dev, "$$\n");
+
+ return strlen(buf);
+}
+
+static DEVICE_ATTR(serial_number, 0644, serial_number_show, NULL);
+
+static ssize_t
+boot_time_device_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ab8500_usb *ab = dev_get_drvdata(dev);
+ u8 val = ab->sysfs_flag;
+
+ snprintf(buf, 2, "%d", val);
+
+ return strlen(buf);
+}
+
+static ssize_t
+boot_time_device_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct ab8500_usb *ab = dev_get_drvdata(dev);
+
+ ab->sysfs_flag = false;
+
+ abx500_usb_link_status_update(ab);
+
+ return n;
+}
+static DEVICE_ATTR(boot_time_device, 0644,
+ boot_time_device_show, boot_time_device_store);
+
+
+static struct attribute *ab8500_usb_attributes[] = {
+ &dev_attr_serial_number.attr,
+ &dev_attr_boot_time_device.attr,
+ NULL
+};
+static const struct attribute_group ab8500_attr_group = {
+ .attrs = ab8500_usb_attributes,
+};
+
+static int ab8500_create_sysfsentries(struct ab8500_usb *ab)
+{
+ int err;
+
+ err = sysfs_create_group(&ab->dev->kobj, &ab8500_attr_group);
+ if (err)
+ sysfs_remove_group(&ab->dev->kobj, &ab8500_attr_group);
+
+ return err;
}
static int __devinit ab8500_usb_probe(struct platform_device *pdev)
{
struct ab8500_usb *ab;
+ struct ab8500 *ab8500;
struct usb_otg *otg;
int err;
int rev;
+ int ret = -1;
+ ab8500 = dev_get_drvdata(pdev->dev.parent);
rev = abx500_get_chip_id(&pdev->dev);
- if (rev < 0) {
- dev_err(&pdev->dev, "Chip id read failed\n");
- return rev;
- } else if (rev < 0x10) {
- dev_err(&pdev->dev, "Unsupported AB8500 chip\n");
+
+ if (is_ab8500_1p1_or_earlier(ab8500)) {
+ dev_err(&pdev->dev, "Unsupported AB8500 chip rev=%d\n", rev);
return -ENODEV;
}
@@ -495,20 +978,24 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev)
}
ab->dev = &pdev->dev;
- ab->rev = rev;
+ ab->ab8500 = ab8500;
ab->phy.dev = ab->dev;
ab->phy.otg = otg;
ab->phy.label = "ab8500";
ab->phy.set_suspend = ab8500_usb_set_suspend;
ab->phy.set_power = ab8500_usb_set_power;
- ab->phy.state = OTG_STATE_UNDEFINED;
+ ab->phy.state = OTG_STATE_B_IDLE;
otg->phy = &ab->phy;
otg->set_host = ab8500_usb_set_host;
otg->set_peripheral = ab8500_usb_set_peripheral;
+#ifdef CONFIG_USB_OTG_20
+ ab->otg.start_srp = ab8500_usb_start_srp;
+#endif
+ ab->sysfs_flag = true;
platform_set_drvdata(pdev, ab);
-
+ dev_set_drvdata(ab->dev, ab);
ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier);
/* v1: Wait for link status to become stable.
@@ -519,27 +1006,98 @@ static int __devinit ab8500_usb_probe(struct platform_device *pdev)
/* all: Disable phy when called from set_host and set_peripheral */
INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
- if (ab->rev < 0x20) {
- err = ab8500_usb_v1x_res_setup(pdev, ab);
- ab->link_status_wait = AB8500_V1x_LINK_STAT_WAIT;
- } else {
- err = ab8500_usb_v2_res_setup(pdev, ab);
+ INIT_DELAYED_WORK_DEFERRABLE(&ab->work_usb_workaround,
+ ab8500_usb_load);
+ err = ab8500_usb_regulator_get(ab);
+ if (err)
+ goto fail0;
+
+ ab->sysclk = clk_get(ab->dev, "sysclk");
+ if (IS_ERR(ab->sysclk)) {
+ err = PTR_ERR(ab->sysclk);
+ goto fail1;
}
+ err = ab8500_usb_irq_setup(pdev, ab);
if (err < 0)
- goto fail0;
+ goto fail2;
err = usb_set_transceiver(&ab->phy);
if (err) {
dev_err(&pdev->dev, "Can't register transceiver\n");
- goto fail1;
+ goto fail3;
}
- dev_info(&pdev->dev, "AB8500 usb driver initialized\n");
+ /* Write Phy tuning values */
+ if (!is_ab8500_2p0_or_earlier(ab->ab8500)) {
+ /* Enable the PBT/Bank 0x12 access */
+ ret = abx500_set_register_interruptible(ab->dev,
+ AB8500_DEVELOPMENT,
+ AB8500_BANK12_ACCESS,
+ 0x01);
+ if (ret < 0)
+ printk(KERN_ERR "Failed to enable bank12"
+ " access ret=%d\n", ret);
+
+ ret = abx500_set_register_interruptible(ab->dev,
+ AB8500_DEBUG,
+ AB8500_USB_PHY_TUNE1,
+ 0xC8);
+ if (ret < 0)
+ printk(KERN_ERR "Failed to set PHY_TUNE1"
+ " register ret=%d\n", ret);
+
+ ret = abx500_set_register_interruptible(ab->dev,
+ AB8500_DEBUG,
+ AB8500_USB_PHY_TUNE2,
+ 0x00);
+ if (ret < 0)
+ printk(KERN_ERR "Failed to set PHY_TUNE2"
+ " register ret=%d\n", ret);
+
+ ret = abx500_set_register_interruptible(ab->dev,
+ AB8500_DEBUG,
+ AB8500_USB_PHY_TUNE3,
+ 0x78);
+
+ if (ret < 0)
+ printk(KERN_ERR "Failed to set PHY_TUNE3"
+ " regester ret=%d\n", ret);
+
+ /* Switch to normal mode/disable Bank 0x12 access */
+ ret = abx500_set_register_interruptible(ab->dev,
+ AB8500_DEVELOPMENT,
+ AB8500_BANK12_ACCESS,
+ 0x00);
+
+ if (ret < 0)
+ printk(KERN_ERR "Failed to switch bank12"
+ " access ret=%d\n", ret);
+ }
+ /* Needed to enable ID detection. */
+ ab8500_usb_wd_workaround(ab);
+
+ prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
+ (char *)dev_name(ab->dev), 50);
+ dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev);
+
+ prcmu_qos_add_requirement(PRCMU_QOS_ARM_OPP, "usb", 25);
+
+ err = ab8500_usb_boot_detect(ab);
+ if (err < 0)
+ goto fail3;
+
+ err = ab8500_create_sysfsentries(ab);
+ if (err)
+ goto fail3;
return 0;
-fail1:
+fail3:
ab8500_usb_irq_free(ab);
+fail2:
+ clk_put(ab->sysclk);
+fail1:
+ ab8500_usb_regulator_put(ab);
fail0:
kfree(otg);
kfree(ab);
@@ -558,8 +1116,14 @@ static int __devexit ab8500_usb_remove(struct platform_device *pdev)
usb_set_transceiver(NULL);
- ab8500_usb_host_phy_dis(ab);
- ab8500_usb_peri_phy_dis(ab);
+ if (ab->mode == USB_HOST)
+ ab8500_usb_host_phy_dis(ab);
+ else if (ab->mode == USB_PERIPHERAL)
+ ab8500_usb_peri_phy_dis(ab);
+
+ clk_put(ab->sysclk);
+
+ ab8500_usb_regulator_put(ab);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 37096246c93..14e568be223 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -349,6 +349,22 @@ config IMX2_WDT
To compile this driver as a module, choose M here: the
module will be called imx2_wdt.
+config UX500_WATCHDOG
+ bool "ST-Ericsson Ux500 watchdog"
+ depends on UX500_SOC_DB8500 || UX500_SOC_DB5500
+ default y
+ help
+ Say Y here to include Watchdog timer support for the
+ watchdog existing in the prcmu of ST-Ericsson Ux500 series platforms.
+ This watchdog is used to reset the system and thus cannot be
+ compiled as a module.
+
+config UX500_WATCHDOG_DEBUG
+ bool "ST-Ericsson Ux500 watchdog DEBUG"
+ depends on (UX500_SOC_DB8500 || UX500_SOC_DB5500) && DEBUG_FS
+ help
+ Say Y here to add various debugfs entries in wdog/
+
# AVR32 Architecture
config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index e8f479a1640..738a0f3ad21 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
+obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c
index 7c741dc987b..96ae2465c25 100644
--- a/drivers/watchdog/mpcore_wdt.c
+++ b/drivers/watchdog/mpcore_wdt.c
@@ -35,11 +35,13 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/cpufreq.h>
+#include <linux/kexec.h>
#include <asm/smp_twd.h>
struct mpcore_wdt {
- unsigned long timer_alive;
+ cpumask_t timer_alive;
struct device *dev;
void __iomem *base;
int irq;
@@ -50,6 +52,8 @@ struct mpcore_wdt {
static struct platform_device *mpcore_wdt_pdev;
static DEFINE_SPINLOCK(wdt_lock);
+static DEFINE_PER_CPU(unsigned long, mpcore_wdt_rate);
+
#define TIMER_MARGIN 60
static int mpcore_margin = TIMER_MARGIN;
module_param(mpcore_margin, int, 0);
@@ -70,6 +74,8 @@ MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
"set to 1 to ignore reboots, 0 to reboot (default="
__MODULE_STRING(ONLY_TESTING) ")");
+#define MPCORE_WDT_PERIPHCLK_PRESCALER 2
+
/*
* This is the interrupt handler. Note that we only use this
* in testing mode, so don't actually do a reboot here.
@@ -102,9 +108,8 @@ static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
spin_lock(&wdt_lock);
/* Assume prescale is set to 256 */
- count = __raw_readl(wdt->base + TWD_WDOG_COUNTER);
- count = (0xFFFFFFFFU - count) * (HZ / 5);
- count = (count / 256) * mpcore_margin;
+ count = per_cpu(mpcore_wdt_rate, smp_processor_id()) / 256;
+ count = count*mpcore_margin;
/* Reload the counter */
writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
@@ -112,6 +117,56 @@ static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
spin_unlock(&wdt_lock);
}
+static void mpcore_wdt_set_rate(unsigned long new_rate)
+{
+ unsigned long count;
+ unsigned long long rate_tmp;
+ unsigned long old_rate;
+
+ spin_lock(&wdt_lock);
+ old_rate = per_cpu(mpcore_wdt_rate, smp_processor_id());
+ per_cpu(mpcore_wdt_rate, smp_processor_id()) = new_rate;
+
+ if (mpcore_wdt_dev) {
+ struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
+ count = readl(wdt->base + TWD_WDOG_COUNTER);
+ /* The goal: count = count * (new_rate/old_rate); */
+ rate_tmp = (unsigned long long)count * new_rate;
+ do_div(rate_tmp, old_rate);
+ count = rate_tmp;
+ writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
+ wdt->perturb = wdt->perturb ? 0 : 1;
+ }
+ spin_unlock(&wdt_lock);
+}
+
+static void mpcore_wdt_update_cpu_frequency_on_cpu(void *data)
+{
+ struct cpufreq_freqs *freq = data;
+ mpcore_wdt_set_rate((freq->new * 1000) /
+ MPCORE_WDT_PERIPHCLK_PRESCALER);
+}
+
+static int mpcore_wdt_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_freqs *freq = data;
+
+ if (event == CPUFREQ_RESUMECHANGE ||
+ (event == CPUFREQ_PRECHANGE && freq->new > freq->old) ||
+ (event == CPUFREQ_POSTCHANGE && freq->new < freq->old))
+ smp_call_function_single(freq->cpu,
+ mpcore_wdt_update_cpu_frequency_on_cpu,
+ freq, 1);
+
+ return 0;
+}
+
+static struct notifier_block mpcore_wdt_cpufreq_notifier_block = {
+ .notifier_call = mpcore_wdt_cpufreq_notifier,
+};
+
+
static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
{
spin_lock(&wdt_lock);
@@ -146,6 +201,20 @@ static int mpcore_wdt_set_heartbeat(int t)
return 0;
}
+static int mpcore_wdt_stop_notifier(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
+ printk(KERN_INFO "Stopping watchdog on non-crashing core %u\n",
+ smp_processor_id());
+ mpcore_wdt_stop(wdt);
+ return NOTIFY_STOP;
+}
+
+static struct notifier_block mpcore_wdt_stop_block = {
+ .notifier_call = mpcore_wdt_stop_notifier,
+};
+
/*
* /dev/watchdog handling
*/
@@ -153,7 +222,7 @@ static int mpcore_wdt_open(struct inode *inode, struct file *file)
{
struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
- if (test_and_set_bit(0, &wdt->timer_alive))
+ if (cpumask_test_and_set_cpu(smp_processor_id(), &wdt->timer_alive))
return -EBUSY;
if (nowayout)
@@ -161,6 +230,9 @@ static int mpcore_wdt_open(struct inode *inode, struct file *file)
file->private_data = wdt;
+ atomic_notifier_chain_register(&crash_percpu_notifier_list,
+ &mpcore_wdt_stop_block);
+
/*
* Activate timer
*/
@@ -184,7 +256,7 @@ static int mpcore_wdt_release(struct inode *inode, struct file *file)
"unexpected close, not stopping watchdog!\n");
mpcore_wdt_keepalive(wdt);
}
- clear_bit(0, &wdt->timer_alive);
+ cpumask_clear_cpu(smp_processor_id(), &wdt->timer_alive);
wdt->expect_close = 0;
return 0;
}
@@ -427,6 +499,8 @@ static struct platform_driver mpcore_wdt_driver = {
static int __init mpcore_wdt_init(void)
{
+ int i;
+
/*
* Check that the margin value is within it's range;
* if not reset to the default
@@ -437,6 +511,18 @@ static int __init mpcore_wdt_init(void)
TIMER_MARGIN);
}
+ cpufreq_register_notifier(&mpcore_wdt_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+
+ for_each_online_cpu(i)
+ per_cpu(mpcore_wdt_rate, i) =
+ (cpufreq_get(i) * 1000) / MPCORE_WDT_PERIPHCLK_PRESCALER;
+
+ for_each_online_cpu(i)
+ pr_info("mpcore_wdt: rate for core %d is %lu.%02luMHz.\n", i,
+ per_cpu(mpcore_wdt_rate, i) / 1000000,
+ (per_cpu(mpcore_wdt_rate, i) / 10000) % 100);
+
pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
mpcore_noboot, mpcore_margin, nowayout);
diff --git a/drivers/watchdog/ux500_wdt.c b/drivers/watchdog/ux500_wdt.c
new file mode 100644
index 00000000000..a1e8c2dbf10
--- /dev/null
+++ b/drivers/watchdog/ux500_wdt.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * Heavily based upon geodewdt.c
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/dbx500-prcmu.h>
+
+#define WATCHDOG_TIMEOUT 600 /* 10 minutes */
+
+#define WDT_FLAGS_OPEN 1
+#define WDT_FLAGS_ORPHAN 2
+
+static unsigned long wdt_flags;
+
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+ "Watchdog timeout in seconds. default="
+ __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Watchdog cannot be stopped once started (default="
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+static u8 wdog_id;
+static bool wdt_en;
+static bool wdt_auto_off = false;
+static bool safe_close;
+
+static int ux500_wdt_open(struct inode *inode, struct file *file)
+{
+ if (!timeout)
+ return -ENODEV;
+
+ if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags))
+ return -EBUSY;
+
+ if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags))
+ __module_get(THIS_MODULE);
+
+ prcmu_enable_a9wdog(wdog_id);
+ wdt_en = true;
+
+ return nonseekable_open(inode, file);
+}
+
+static int ux500_wdt_release(struct inode *inode, struct file *file)
+{
+ if (safe_close) {
+ prcmu_disable_a9wdog(wdog_id);
+ module_put(THIS_MODULE);
+ } else {
+ pr_crit("Unexpected close - watchdog is not stopping.\n");
+ prcmu_kick_a9wdog(wdog_id);
+
+ set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);
+ }
+
+ clear_bit(WDT_FLAGS_OPEN, &wdt_flags);
+ safe_close = false;
+ return 0;
+}
+
+static ssize_t ux500_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (!len)
+ return len;
+
+ if (!nowayout) {
+ size_t i;
+ safe_close = false;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+
+ if (c == 'V')
+ safe_close = true;
+ }
+ }
+
+ prcmu_kick_a9wdog(wdog_id);
+
+ return len;
+}
+
+static long ux500_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ int interval;
+
+ static const struct watchdog_info ident = {
+ .options = WDIOF_SETTIMEOUT |
+ WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+ .firmware_version = 1,
+ .identity = "Ux500 WDT",
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+
+ case WDIOC_SETOPTIONS:
+ {
+ int options;
+ int ret = -EINVAL;
+
+ if (get_user(options, p))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ prcmu_disable_a9wdog(wdog_id);
+ wdt_en = false;
+ ret = 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ prcmu_enable_a9wdog(wdog_id);
+ wdt_en = true;
+ ret = 0;
+ }
+
+ return ret;
+ }
+ case WDIOC_KEEPALIVE:
+ return prcmu_kick_a9wdog(wdog_id);
+
+ case WDIOC_SETTIMEOUT:
+ if (get_user(interval, p))
+ return -EFAULT;
+
+ if (cpu_is_u8500()) {
+ /* 28 bit resolution in ms, becomes 268435.455 s */
+ if (interval > 268435 || interval < 0)
+ return -EINVAL;
+ } else if (cpu_is_u5500()) {
+ /* 32 bit resolution in ms, becomes 4294967.295 s */
+ if (interval > 4294967 || interval < 0)
+ return -EINVAL;
+ } else
+ return -EINVAL;
+
+ timeout = interval;
+ prcmu_disable_a9wdog(wdog_id);
+ prcmu_load_a9wdog(wdog_id, timeout * 1000);
+ prcmu_enable_a9wdog(wdog_id);
+
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+static const struct file_operations ux500_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = ux500_wdt_write,
+ .unlocked_ioctl = ux500_wdt_ioctl,
+ .open = ux500_wdt_open,
+ .release = ux500_wdt_release,
+};
+
+static struct miscdevice ux500_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ux500_wdt_fops,
+};
+
+#ifdef CONFIG_UX500_WATCHDOG_DEBUG
+enum wdog_dbg {
+ WDOG_DBG_CONFIG,
+ WDOG_DBG_LOAD,
+ WDOG_DBG_KICK,
+ WDOG_DBG_EN,
+ WDOG_DBG_DIS,
+};
+
+static ssize_t wdog_dbg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long val;
+ int err;
+ enum wdog_dbg v = (enum wdog_dbg)((struct seq_file *)
+ (file->private_data))->private;
+
+ switch(v) {
+ case WDOG_DBG_CONFIG:
+ err = kstrtoul_from_user(user_buf, count, 0, &val);
+
+ if (!err) {
+ wdt_auto_off = val != 0;
+ (void) prcmu_config_a9wdog(1,
+ wdt_auto_off);
+ }
+ else {
+ pr_err("ux500_wdt:dbg: unknown value\n");
+ }
+ break;
+ case WDOG_DBG_LOAD:
+ err = kstrtoul_from_user(user_buf, count, 0, &val);
+
+ if (!err) {
+ timeout = val;
+ /* Convert seconds to ms */
+ prcmu_disable_a9wdog(wdog_id);
+ prcmu_load_a9wdog(wdog_id, timeout * 1000);
+ prcmu_enable_a9wdog(wdog_id);
+ }
+ else {
+ pr_err("ux500_wdt:dbg: unknown value\n");
+ }
+ break;
+ case WDOG_DBG_KICK:
+ (void) prcmu_kick_a9wdog(wdog_id);
+ break;
+ case WDOG_DBG_EN:
+ wdt_en = true;
+ (void) prcmu_enable_a9wdog(wdog_id);
+ break;
+ case WDOG_DBG_DIS:
+ wdt_en = false;
+ (void) prcmu_disable_a9wdog(wdog_id);
+ break;
+ }
+
+ return count;
+}
+
+static int wdog_dbg_read(struct seq_file *s, void *p)
+{
+ enum wdog_dbg v = (enum wdog_dbg)s->private;
+
+ switch(v) {
+ case WDOG_DBG_CONFIG:
+ seq_printf(s,"wdog is on id %d, auto off on sleep: %s\n",
+ (int)wdog_id,
+ wdt_auto_off ? "enabled": "disabled");
+ break;
+ case WDOG_DBG_LOAD:
+ /* In 1s */
+ seq_printf(s, "wdog load is: %d s\n",
+ timeout);
+ break;
+ case WDOG_DBG_KICK:
+ break;
+ case WDOG_DBG_EN:
+ case WDOG_DBG_DIS:
+ seq_printf(s, "wdog is %sabled\n",
+ wdt_en ? "en" : "dis");
+ break;
+ }
+ return 0;
+}
+
+static int wdog_dbg_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file, wdog_dbg_read, inode->i_private);
+}
+
+static const struct file_operations wdog_dbg_fops = {
+ .open = wdog_dbg_open,
+ .write = wdog_dbg_write,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int __init wdog_dbg_init(void)
+{
+ struct dentry *wdog_dir;
+
+ wdog_dir = debugfs_create_dir("wdog", NULL);
+ if (IS_ERR_OR_NULL(wdog_dir))
+ goto fail;
+
+ if (IS_ERR_OR_NULL(debugfs_create_u8("id",
+ S_IWUGO | S_IRUGO, wdog_dir,
+ &wdog_id)))
+ goto fail;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("config",
+ S_IWUGO | S_IRUGO, wdog_dir,
+ (void *)WDOG_DBG_CONFIG,
+ &wdog_dbg_fops)))
+ goto fail;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("load",
+ S_IWUGO | S_IRUGO, wdog_dir,
+ (void *)WDOG_DBG_LOAD,
+ &wdog_dbg_fops)))
+ goto fail;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("kick",
+ S_IWUGO, wdog_dir,
+ (void *)WDOG_DBG_KICK,
+ &wdog_dbg_fops)))
+ goto fail;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("enable",
+ S_IWUGO | S_IRUGO, wdog_dir,
+ (void *)WDOG_DBG_EN,
+ &wdog_dbg_fops)))
+ goto fail;
+
+ if (IS_ERR_OR_NULL(debugfs_create_file("disable",
+ S_IWUGO | S_IRUGO, wdog_dir,
+ (void *)WDOG_DBG_DIS,
+ &wdog_dbg_fops)))
+ goto fail;
+
+ return 0;
+fail:
+ pr_err("ux500:wdog: Failed to initialize wdog dbg\n");
+ debugfs_remove_recursive(wdog_dir);
+
+ return -EFAULT;
+}
+
+#else
+static inline int __init wdog_dbg_init(void)
+{
+ return 0;
+}
+#endif
+
+static int __init ux500_wdt_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ /* Number of watch dogs */
+ prcmu_config_a9wdog(1, wdt_auto_off);
+ /* convert to ms */
+ prcmu_load_a9wdog(wdog_id, timeout * 1000);
+
+ ret = misc_register(&ux500_wdt_miscdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register misc\n");
+ return ret;
+ }
+
+ ret = wdog_dbg_init();
+ if (ret < 0)
+ goto fail;
+
+ dev_info(&pdev->dev, "initialized\n");
+
+ return 0;
+fail:
+ misc_deregister(&ux500_wdt_miscdev);
+ return ret;
+}
+
+static int __exit ux500_wdt_remove(struct platform_device *dev)
+{
+ prcmu_disable_a9wdog(wdog_id);
+ wdt_en = false;
+ misc_deregister(&ux500_wdt_miscdev);
+ return 0;
+}
+#ifdef CONFIG_PM
+static int ux500_wdt_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ if (wdt_en && cpu_is_u5500()) {
+ prcmu_disable_a9wdog(wdog_id);
+ return 0;
+ }
+
+ if (wdt_en && !wdt_auto_off) {
+ prcmu_disable_a9wdog(wdog_id);
+ prcmu_config_a9wdog(1, true);
+
+ prcmu_load_a9wdog(wdog_id, timeout * 1000);
+ prcmu_enable_a9wdog(wdog_id);
+ }
+ return 0;
+}
+
+static int ux500_wdt_resume(struct platform_device *pdev)
+{
+ if (wdt_en && cpu_is_u5500()) {
+ prcmu_load_a9wdog(wdog_id, timeout * 1000);
+ prcmu_enable_a9wdog(wdog_id);
+ return 0;
+ }
+
+ if (wdt_en && !wdt_auto_off) {
+ prcmu_disable_a9wdog(wdog_id);
+ prcmu_config_a9wdog(1, wdt_auto_off);
+
+ prcmu_load_a9wdog(wdog_id, timeout * 1000);
+ prcmu_enable_a9wdog(wdog_id);
+ }
+ return 0;
+}
+
+#else
+#define ux500_wdt_suspend NULL
+#define ux500_wdt_resume NULL
+#endif
+static struct platform_driver ux500_wdt_driver = {
+ .remove = __exit_p(ux500_wdt_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ux500_wdt",
+ },
+ .suspend = ux500_wdt_suspend,
+ .resume = ux500_wdt_resume,
+};
+
+static int __init ux500_wdt_init(void)
+{
+ return platform_driver_probe(&ux500_wdt_driver, ux500_wdt_probe);
+}
+module_init(ux500_wdt_init);
+
+MODULE_AUTHOR("Jonas Aaberg <jonas.aberg@stericsson.com>");
+MODULE_DESCRIPTION("Ux500 Watchdog Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/include/Kbuild b/include/Kbuild
index 8d226bfa269..506f6d7dba7 100644
--- a/include/Kbuild
+++ b/include/Kbuild
@@ -10,3 +10,4 @@ header-y += video/
header-y += drm/
header-y += xen/
header-y += scsi/
+header-y += trace/
diff --git a/include/linux/ab5500-vibra.h b/include/linux/ab5500-vibra.h
new file mode 100644
index 00000000000..e85048dd801
--- /dev/null
+++ b/include/linux/ab5500-vibra.h
@@ -0,0 +1,57 @@
+/*----------------------------------------------------------------------------
+ * ab5500-vibra.h header file for ab5500 vibrator driver
+ *
+ * Copyright (C) 2011 ST-Ericsson SA.
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ *
+ */
+
+#ifndef __AB5500_VIBRA_H__
+#define __AB5500_VIBRA_H__
+
+enum ab5500_vibra_type {
+ AB5500_VIB_ROTARY,
+ AB5500_VIB_LINEAR,
+};
+
+/* Vibrator Voltage */
+#define AB5500_VIB_VOLT_MIN (0x00) /* 1.3 Volt */
+#define AB5500_VIB_VOLT_MAX (0x0A) /* 3.5 Volt */
+#define AB5500_VIB_VOLT_STEP (0x01) /* 0.2 Volt */
+
+
+/* Linear Vibrator Resonance Frequncy */
+#define AB5500_VIB_RFREQ_100HZ (0xFB)
+#define AB5500_VIB_RFREQ_150HZ (0x52)
+#define AB5500_VIB_RFREQ_196HZ (0x03)
+
+/* Vibrator pulse duration in milliseconds */
+enum ab5500_vibra_pulse {
+ AB5500_VIB_PULSE_OFF,
+ AB5500_VIB_PULSE_20ms,
+ AB5500_VIB_PULSE_75ms,
+ AB5500_VIB_PULSE_130ms,
+ AB5500_VIB_PULSE_170ms,
+};
+
+/**
+ * struct ab5500_vibra_platform_data
+ * @voltage: Vibra output voltage
+ * @res_freq: Linear vibra resonance freq.
+ * @type: Vibra HW type
+ * @pulse: Vibra pulse duration in ms
+ * @eol_voltage: EOL voltage in mV
+ */
+struct ab5500_vibra_platform_data {
+ u8 voltage;
+ u8 res_freq;
+ u8 magnitude;
+ enum ab5500_vibra_type type;
+ enum ab5500_vibra_pulse pulse;
+ int eol_voltage;
+};
+
+#endif /* __AB5500_VIBRA_H__ */
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 8d54f79457b..affea2229c4 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -128,3 +128,23 @@ struct amba_device name##_device = { \
module_driver(__amba_drv, amba_driver_register, amba_driver_unregister)
#endif
+
+#ifdef CONFIG_SUSPEND
+extern int amba_pm_suspend(struct device *dev);
+extern int amba_pm_resume(struct device *dev);
+#else
+#define amba_pm_suspend NULL
+#define amba_pm_resume NULL
+#endif
+
+#ifdef CONFIG_HIBERNATE_CALLBACKS
+extern int amba_pm_freeze(struct device *dev);
+extern int amba_pm_thaw(struct device *dev);
+extern int amba_pm_poweroff(struct device *dev);
+extern int amba_pm_restore(struct device *dev);
+#else
+#define amba_pm_freeze NULL
+#define amba_pm_thaw NULL
+#define amba_pm_poweroff NULL
+#define amba_pm_restore NULL
+#endif
diff --git a/include/linux/boottime.h b/include/linux/boottime.h
new file mode 100644
index 00000000000..9836c5b3175
--- /dev/null
+++ b/include/linux/boottime.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009-2010
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * boottime is a tool for collecting start-up timing
+ * information and can together with boot loader support
+ * display a total system start-up time.
+ *
+ */
+
+#ifndef LINUX_BOOTTIME_H
+#define LINUX_BOOTTIME_H
+
+#ifdef CONFIG_BOOTTIME
+#include <linux/kernel.h>
+
+/**
+ * struct boottime_timer - Callbacks for generic timer.
+ * @init: Function to call at boottime initialization
+ * @get_time: Returns the number of us since start-up
+ * Preferable this is based upon a free running timer.
+ * This is the only required entry.
+ * @finalize: Called before init is executed and boottime is done.
+ */
+struct boottime_timer {
+ int (*init)(void);
+ unsigned long (*get_time)(void);
+ void (*finalize)(void);
+};
+
+/**
+ * boottime_mark_wtime()
+ * Add a sample point with a given time. Useful for adding data collected
+ * by for example a boot loader.
+ * @name: The name of the sample point
+ * @time: The time in us when this point was reached
+ */
+void __init boottime_mark_wtime(char *name, unsigned long time);
+
+/**
+ * boottime_mark()
+ * Add a sample point with the current time.
+ * @name: The name of this sample point
+ */
+void __init boottime_mark(char *name);
+
+/**
+ * boottime_mark_symbolic()
+ * Add a sample point where the name is a symbolic function
+ * and %pF is needed to get the correct function name.
+ * @name: function name.
+ */
+void __init boottime_mark_symbolic(void *name);
+
+/**
+ * boottime_activate()
+ * Activates boottime and register callbacks.
+ * @bt: struct with callbacks.
+ */
+void __ref boottime_activate(struct boottime_timer *bt);
+
+/**
+ * boottime_deactivate()
+ * This function is called when the kernel boot is done.
+ * (before "free init memory" is called)
+ */
+void __init boottime_deactivate(void);
+
+/**
+ * boottime_system_up()
+ * A function is called when the basics of the kernel
+ * is up and running.
+ */
+void __init boottime_system_up(void);
+
+#else
+
+#define boottime_mark_wtime(name, time)
+#define boottime_mark(name)
+#define boottime_mark_symbolic(name)
+#define boottime_activate(bt)
+#define boottime_deactivate()
+#define boottime_system_up()
+#endif
+
+#endif /* LINUX_BOOTTIME_H */
diff --git a/include/linux/clksrc-db5500-mtimer.h b/include/linux/clksrc-db5500-mtimer.h
new file mode 100644
index 00000000000..3112c7f2709
--- /dev/null
+++ b/include/linux/clksrc-db5500-mtimer.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+#ifndef __CLKSRC_DB5500_MTIMER_H
+#define __CLKSRC_DB5500_MTIMER_H
+
+#include <linux/io.h>
+
+#ifdef CONFIG_CLKSRC_DB5500_MTIMER
+void db5500_mtimer_init(void __iomem *base);
+#else
+static inline void db5500_mtimer_init(void __iomem *base) {}
+#endif
+
+#endif
diff --git a/include/linux/cpufreq-dbx500.h b/include/linux/cpufreq-dbx500.h
new file mode 100644
index 00000000000..15c59c3b1c8
--- /dev/null
+++ b/include/linux/cpufreq-dbx500.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+#ifndef __CPUFREQ_DBX500_H
+#define __CPUFREQ_DBX500_H
+
+#include <linux/cpufreq.h>
+
+int dbx500_cpufreq_get_limits(int cpu, int r,
+ unsigned int *min, unsigned int *max);
+
+int dbx500_cpufreq_percent2freq(int percent);
+
+#endif
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index b60f6ba01d0..faf1d179032 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -200,6 +200,7 @@ extern int __cpufreq_driver_getavg(struct cpufreq_policy *policy,
int cpufreq_register_governor(struct cpufreq_governor *governor);
void cpufreq_unregister_governor(struct cpufreq_governor *governor);
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max);
/*********************************************************************
* CPUFREQ DRIVER INTERFACE *
@@ -337,6 +338,7 @@ static inline unsigned int cpufreq_quick_get_max(unsigned int cpu)
}
#endif
+int cpufreq_update_freq(int cpu, unsigned int min, unsigned int max);
/*********************************************************************
* CPUFREQ DEFAULT GOVERNOR *
diff --git a/arch/arm/plat-nomadik/include/plat/gpio-nomadik.h b/include/linux/gpio/nomadik.h
index 9605bf227df..3e8b7f16fb7 100644
--- a/arch/arm/plat-nomadik/include/plat/gpio-nomadik.h
+++ b/include/linux/gpio/nomadik.h
@@ -29,6 +29,7 @@
#define NMK_GPIO_SLPC 0x1c
#define NMK_GPIO_AFSLA 0x20
#define NMK_GPIO_AFSLB 0x24
+#define NMK_GPIO_LOWEMI 0x28
#define NMK_GPIO_RIMSC 0x40
#define NMK_GPIO_FIMSC 0x44
diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h
index 82b29ae6ebb..6cb4d26bf60 100644
--- a/include/linux/hwmon.h
+++ b/include/linux/hwmon.h
@@ -14,12 +14,18 @@
#ifndef _HWMON_H_
#define _HWMON_H_
+#include <linux/notifier.h>
+
struct device;
struct device *hwmon_device_register(struct device *dev);
void hwmon_device_unregister(struct device *dev);
+int hwmon_notifier_register(struct notifier_block *nb);
+int hwmon_notifier_unregister(struct notifier_block *nb);
+void hwmon_notify(unsigned long val, void *v);
+
/* Scale user input to sensible values */
static inline int SENSORS_LIMIT(long value, long low, long high)
{
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 0d7d6a1b172..9943c5dd618 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -120,6 +120,7 @@ struct kimage {
/* kexec interface functions */
extern void machine_kexec(struct kimage *image);
+extern void machine_crash_swreset(void);
extern int machine_kexec_prepare(struct kimage *image);
extern void machine_kexec_cleanup(struct kimage *image);
extern asmlinkage long sys_kexec_load(unsigned long entry,
@@ -170,6 +171,7 @@ unsigned long paddr_vmcoreinfo_note(void);
extern struct kimage *kexec_image;
extern struct kimage *kexec_crash_image;
+extern struct atomic_notifier_head crash_percpu_notifier_list;
#ifndef kexec_flush_icache_page
#define kexec_flush_icache_page(page)
diff --git a/include/linux/led-lm3530.h b/include/linux/led-lm3530.h
index eeae6e74247..7b5ae415d10 100644
--- a/include/linux/led-lm3530.h
+++ b/include/linux/led-lm3530.h
@@ -58,6 +58,12 @@
#define LM3530_ALS_IMPD_700Ohm (0x0E)
#define LM3530_ALS_IMPD_667Ohm (0x0F)
+/*
+ * If lm3530 does not use a gpio for HWEN, set LM3530_NO_HWEN_GPIO
+ * for hw_en_gpio in lm3530_platform data member
+ */
+#define LM3530_NO_HWEN_GPIO -1
+
enum lm3530_mode {
LM3530_BL_MODE_MANUAL = 0, /* "man" */
LM3530_BL_MODE_ALS, /* "als" */
@@ -94,6 +100,7 @@ struct lm3530_pwm_data {
* @als_vmax: als input voltage calibrated for min brightness in mV
* @brt_val: brightness value (0-255)
* @pwm_data: PWM control functions (only valid when the mode is PWM)
+ * @hw_en_gpio: GPIO line for LM3530 HWEN
*/
struct lm3530_platform_data {
enum lm3530_mode mode;
@@ -116,6 +123,8 @@ struct lm3530_platform_data {
u8 brt_val;
struct lm3530_pwm_data pwm_data;
+
+ int hw_en_gpio;
};
#endif /* _LINUX_LED_LM3530_H__ */
diff --git a/include/linux/leds-ab5500.h b/include/linux/leds-ab5500.h
new file mode 100644
index 00000000000..9ba9ac61d90
--- /dev/null
+++ b/include/linux/leds-ab5500.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 ST-Ericsson SA.
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Simple driver for HVLED in ST-Ericsson AB5500 Analog baseband Controller
+ *
+ * Author: Shreshtha Kumar SAHU <shreshthakumar.sahu@stericsson.com>
+ */
+
+#define AB5500_HVLED0 0
+#define AB5500_HVLED1 1
+#define AB5500_HVLED2 2
+#define AB5500_HVLEDS_MAX 3
+
+enum ab5500_fade_delay {
+ AB5500_FADE_DELAY_BYPASS = 0,
+ AB5500_FADE_DELAY_HALFSEC,
+ AB5500_FADE_DELAY_ONESEC,
+ AB5500_FADE_DELAY_TWOSEC
+};
+
+struct ab5500_led_conf {
+ char *name;
+ u8 led_id;
+ u8 max_current;
+ u8 fade_hi;
+ u8 fade_lo;
+ bool led_on;
+};
+
+struct ab5500_hvleds_platform_data {
+ bool hw_fade;
+ struct ab5500_led_conf leds[AB5500_HVLEDS_MAX];
+};
diff --git a/include/linux/leds_pwm.h b/include/linux/leds_pwm.h
index 33a07116748..5128ffa43c3 100644
--- a/include/linux/leds_pwm.h
+++ b/include/linux/leds_pwm.h
@@ -11,6 +11,9 @@ struct led_pwm {
u8 active_low;
unsigned max_brightness;
unsigned pwm_period_ns;
+ unsigned int lth_brightness;
+ unsigned int dutycycle_steps;
+ unsigned int period_steps;
};
struct led_pwm_platform_data {
diff --git a/include/linux/mfd/ab8500/denc-regs.h b/include/linux/mfd/ab8500/denc-regs.h
new file mode 100644
index 00000000000..a6683ca7470
--- /dev/null
+++ b/include/linux/mfd/ab8500/denc-regs.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson AB8500 DENC related registers
+ *
+ * Author: Marcus Tunnissen <marcel.tuennissen@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef __AB8500_DENC_H
+#define __AB8500_DENC_H
+
+#define AB8500_VAL2REG(__reg, __fld, __val) \
+ (((__val) << __reg##_##__fld##_SHIFT) & __reg##_##__fld##_MASK)
+#define AB8500_REG2VAL(__reg, __fld, __val) \
+ (((__val) & __reg##_##__fld##_MASK) >> __reg##_##__fld##_SHIFT)
+
+#define AB8500_CTRL3 0x00000200
+#define AB8500_CTRL3_TH_SD_ENA_SHIFT 3
+#define AB8500_CTRL3_TH_SD_ENA_MASK 0x00000008
+#define AB8500_CTRL3_TH_SD_ENA(__x) \
+ AB8500_VAL2REG(AB8500_CTRL3, TH_SD_ENA, __x)
+#define AB8500_CTRL3_RESET_DENC_N_SHIFT 2
+#define AB8500_CTRL3_RESET_DENC_N_MASK 0x00000004
+#define AB8500_CTRL3_RESET_DENC_N(__x) \
+ AB8500_VAL2REG(AB8500_CTRL3, RESET_DENC_N, __x)
+#define AB8500_CTRL3_RESET_AUD_N_SHIFT 1
+#define AB8500_CTRL3_RESET_AUD_N_MASK 0x00000002
+#define AB8500_CTRL3_RESET_AUD_N(__x) \
+ AB8500_VAL2REG(AB8500_CTRL3, RESET_AUD_N, __x)
+#define AB8500_CTRL3_CLK_32K_OUT2_IS_SHIFT 0
+#define AB8500_CTRL3_CLK_32K_OUT2_IS_MASK 0x00000001
+#define AB8500_CTRL3_CLK_32K_OUT2_IS(__x) \
+ AB8500_VAL2REG(AB8500_CTRL3, CLK_32K_OUT2_IS, __x)
+#define AB8500_SYS_ULP_CLK_CONF 0x0000020A
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_PD_ENA_SHIFT 7
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_PD_ENA_MASK 0x00000080
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_PD_ENA(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_PD_ENA, __x)
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_BUF_ENA_SHIFT 6
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_BUF_ENA_MASK 0x00000040
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_BUF_ENA(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_BUF_ENA, __x)
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_STRE_SHIFT 5
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_STRE_MASK 0x00000020
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_STRE(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, ULP_CLK_STRE, __x)
+#define AB8500_SYS_ULP_CLK_CONF_TVOUT_CLK_INV_SHIFT 4
+#define AB8500_SYS_ULP_CLK_CONF_TVOUT_CLK_INV_MASK 0x00000010
+#define AB8500_SYS_ULP_CLK_CONF_TVOUT_CLK_INV(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, TVOUT_CLK_INV, __x)
+#define AB8500_SYS_ULP_CLK_CONF_TVOUT_CLK_DE_IN_SHIFT 3
+#define AB8500_SYS_ULP_CLK_CONF_TVOUT_CLK_DE_IN_MASK 0x00000008
+#define AB8500_SYS_ULP_CLK_CONF_TVOUT_CLK_DE_IN(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, TVOUT_CLK_DE_IN, __x)
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_STRE_SHIFT 2
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_STRE_MASK 0x00000004
+#define AB8500_SYS_ULP_CLK_CONF_CLK_27MHZ_STRE(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, CLK_27MHZ_STRE, __x)
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF_SHIFT 0
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF_MASK 0x00000003
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF_NO_FUNC 0
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF_AS_OUTPUT 1
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF_AS_INPUT 2
+#define AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF(__x) \
+ AB8500_VAL2REG(AB8500_SYS_ULP_CLK_CONF, ULP_CLK_CONF, \
+ AB8500_SYS_ULP_CLK_CONF_ULP_CLK_CONF_##__x)
+#define AB8500_SYS_CLK_CTRL 0x0000020C
+#define AB8500_SYS_CLK_CTRL_USB_CLK_VALID_SHIFT 2
+#define AB8500_SYS_CLK_CTRL_USB_CLK_VALID_MASK 0x00000004
+#define AB8500_SYS_CLK_CTRL_USB_CLK_VALID(__x) \
+ AB8500_VAL2REG(AB8500_SYS_CLK_CTRL, USB_CLK_VALID, __x)
+#define AB8500_SYS_CLK_CTRL_TVOUT_CLK_VALID_SHIFT 1
+#define AB8500_SYS_CLK_CTRL_TVOUT_CLK_VALID_MASK 0x00000002
+#define AB8500_SYS_CLK_CTRL_TVOUT_CLK_VALID(__x) \
+ AB8500_VAL2REG(AB8500_SYS_CLK_CTRL, TVOUT_CLK_VALID, __x)
+#define AB8500_SYS_CLK_CTRL_TVOUT_PLL_ENA_SHIFT 0
+#define AB8500_SYS_CLK_CTRL_TVOUT_PLL_ENA_MASK 0x00000001
+#define AB8500_SYS_CLK_CTRL_TVOUT_PLL_ENA(__x) \
+ AB8500_VAL2REG(AB8500_SYS_CLK_CTRL, TVOUT_PLL_ENA, __x)
+#define AB8500_REGU_MISC1 0x00000380
+#define AB8500_REGU_MISC1_V_TVOUT_LP_SHIFT 7
+#define AB8500_REGU_MISC1_V_TVOUT_LP_MASK 0x00000080
+#define AB8500_REGU_MISC1_V_TVOUT_LP(__x) \
+ AB8500_VAL2REG(AB8500_REGU_MISC1, V_TVOUT_LP, __x)
+#define AB8500_REGU_MISC1_V_INT_CORE_12_LP_SHIFT 6
+#define AB8500_REGU_MISC1_V_INT_CORE_12_LP_MASK 0x00000040
+#define AB8500_REGU_MISC1_V_INT_CORE_12_LP(__x) \
+ AB8500_VAL2REG(AB8500_REGU_MISC1, V_INT_CORE_12_LP, __x)
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_SHIFT 3
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_MASK 0x00000038
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_2V 0
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_225V 1
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_25V 2
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_275V 3
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_3V 4
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_325V 5
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL_1_35V 6
+#define AB8500_REGU_MISC1_V_INT_CORE_12_SEL(__x) \
+ AB8500_VAL2REG(AB8500_REGU_MISC1, V_INT_CORE_12_SEL, \
+ AB8500_REGU_MISC1_V_INT_CORE_12_SEL_##__x)
+#define AB8500_REGU_MISC1_V_INT_CORE_12_ENA_SHIFT 2
+#define AB8500_REGU_MISC1_V_INT_CORE_12_ENA_MASK 0x00000004
+#define AB8500_REGU_MISC1_V_INT_CORE_12_ENA(__x) \
+ AB8500_VAL2REG(AB8500_REGU_MISC1, V_INT_CORE_12_ENA, __x)
+#define AB8500_REGU_MISC1_V_TVOUT_ENA_SHIFT 1
+#define AB8500_REGU_MISC1_V_TVOUT_ENA_MASK 0x00000002
+#define AB8500_REGU_MISC1_V_TVOUT_ENA(__x) \
+ AB8500_VAL2REG(AB8500_REGU_MISC1, V_TVOUT_ENA, __x)
+#define AB8500_VAUX12_REGU 0x00000409
+#define AB8500_VAUX12_REGU_VAUX_1_SHIFT 2
+#define AB8500_VAUX12_REGU_VAUX_1_MASK 0x0000000C
+#define AB8500_VAUX12_REGU_VAUX_1_DISABLE 0
+#define AB8500_VAUX12_REGU_VAUX_1_FORCE_HP 1
+#define AB8500_VAUX12_REGU_VAUX_1_BY_CTRL_REG 2
+#define AB8500_VAUX12_REGU_VAUX_1_FORCE_LP 3
+#define AB8500_VAUX12_REGU_VAUX_1(__x) \
+ AB8500_VAL2REG(AB8500_VAUX12_REGU, VAUX_1, \
+ AB8500_VAUX12_REGU_VAUX_1_##__x)
+#define AB8500_VAUX12_REGU_VAUX_2_SHIFT 0
+#define AB8500_VAUX12_REGU_VAUX_2_MASK 0x00000003
+#define AB8500_VAUX12_REGU_VAUX_2_DISABLE 0
+#define AB8500_VAUX12_REGU_VAUX_2_FORCE_HP 1
+#define AB8500_VAUX12_REGU_VAUX_2_BY_CTRL_REG 2
+#define AB8500_VAUX12_REGU_VAUX_2_FORCE_LP 3
+#define AB8500_VAUX12_REGU_VAUX_2(__x) \
+ AB8500_VAL2REG(AB8500_VAUX12_REGU, VAUX_2, \
+ AB8500_VAUX12_REGU_VAUX_2_##__x)
+#define AB8500_VAUX1_SEL 0x0000041F
+#define AB8500_VAUX1_SEL_VAL_SHIFT 0
+#define AB8500_VAUX1_SEL_VAL_MASK 0x0000000F
+#define AB8500_VAUX1_SEL_VAL_1_1V 0
+#define AB8500_VAUX1_SEL_VAL_1_2V 1
+#define AB8500_VAUX1_SEL_VAL_1_3V 2
+#define AB8500_VAUX1_SEL_VAL_1_4V 3
+#define AB8500_VAUX1_SEL_VAL_1_5V 4
+#define AB8500_VAUX1_SEL_VAL_1_8V 5
+#define AB8500_VAUX1_SEL_VAL_1_85V 6
+#define AB8500_VAUX1_SEL_VAL_1_9V 7
+#define AB8500_VAUX1_SEL_VAL_2_5V 8
+#define AB8500_VAUX1_SEL_VAL_2_65V 9
+#define AB8500_VAUX1_SEL_VAL_2_7V 10
+#define AB8500_VAUX1_SEL_VAL_2_75V 11
+#define AB8500_VAUX1_SEL_VAL_2_8V 12
+#define AB8500_VAUX1_SEL_VAL_2_9V 13
+#define AB8500_VAUX1_SEL_VAL_3_0V 14
+#define AB8500_VAUX1_SEL_VAL_3_3V 15
+#define AB8500_VAUX1_SEL_VAL(__x) \
+ AB8500_VAL2REG(AB8500_VAUX1_SEL, VAL, AB8500_VAUX1_SEL_VAL_##__x)
+#define AB8500_DENC_CONF0 0x00000600
+#define AB8500_DENC_CONF0_STD_SHIFT 6
+#define AB8500_DENC_CONF0_STD_MASK 0x000000C0
+#define AB8500_DENC_CONF0_STD_PAL_BDGHI 0
+#define AB8500_DENC_CONF0_STD_PAL_N 1
+#define AB8500_DENC_CONF0_STD_NTSC_M 2
+#define AB8500_DENC_CONF0_STD_PAL_M 3
+#define AB8500_DENC_CONF0_STD(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF0, STD, AB8500_DENC_CONF0_STD_##__x)
+#define AB8500_DENC_CONF0_SYNC_SHIFT 3
+#define AB8500_DENC_CONF0_SYNC_MASK 0x00000038
+#define AB8500_DENC_CONF0_SYNC_F_BASED_SLAVE 1
+#define AB8500_DENC_CONF0_SYNC_AUTO_TEST 7
+#define AB8500_DENC_CONF0_SYNC(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF0, SYNC, AB8500_DENC_CONF0_SYNC_##__x)
+#define AB8500_DENC_CONF1 0x00000601
+#define AB8500_DENC_CONF1_BLK_LI_SHIFT 7
+#define AB8500_DENC_CONF1_BLK_LI_MASK 0x00000080
+#define AB8500_DENC_CONF1_BLK_LI_PARTIAL 0
+#define AB8500_DENC_CONF1_BLK_LI_FULL 1
+#define AB8500_DENC_CONF1_BLK_LI(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF1, BLK_LI, \
+ AB8500_DENC_CONF1_BLK_LI_##__x)
+#define AB8500_DENC_CONF1_FLT_SHIFT 5
+#define AB8500_DENC_CONF1_FLT_MASK 0x00000060
+#define AB8500_DENC_CONF1_FLT_1_1MHZ 0
+#define AB8500_DENC_CONF1_FLT_1_3MHZ 1
+#define AB8500_DENC_CONF1_FLT_1_6MHZ 2
+#define AB8500_DENC_CONF1_FLT_1_9MHZ 3
+#define AB8500_DENC_CONF1_FLT(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF1, FLT, AB8500_DENC_CONF1_FLT_##__x)
+#define AB8500_DENC_CONF1_CO_KI_SHIFT 3
+#define AB8500_DENC_CONF1_CO_KI_MASK 0x00000008
+#define AB8500_DENC_CONF1_CO_KI(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF1, CO_KI, __x)
+#define AB8500_DENC_CONF1_SETUP_MAIN_SHIFT 2
+#define AB8500_DENC_CONF1_SETUP_MAIN_MASK 0x00000004
+#define AB8500_DENC_CONF1_SETUP_MAIN_BLACK_EQ_BLANK 0
+#define AB8500_DENC_CONF1_SETUP_MAIN_BLACK_GT_BLANK 1
+#define AB8500_DENC_CONF1_SETUP_MAIN(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF1, SETUP_MAIN, \
+ AB8500_DENC_CONF1_SETUP_MAIN_##__x)
+#define AB8500_DENC_CONF1_CC_SHIFT 0
+#define AB8500_DENC_CONF1_CC_MASK 0x00000003
+#define AB8500_DENC_CONF1_CC_NONE 0
+#define AB8500_DENC_CONF1_CC_FIELD_1 1
+#define AB8500_DENC_CONF1_CC_FIELD_2 2
+#define AB8500_DENC_CONF1_CC_ALL 3
+#define AB8500_DENC_CONF1_CC(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF1, CC, AB8500_DENC_CONF1_CC_##__x)
+#define AB8500_DENC_CONF2 0x00000602
+#define AB8500_DENC_CONF2_N_INTRL_SHIFT 7
+#define AB8500_DENC_CONF2_N_INTRL_MASK 0x00000080
+#define AB8500_DENC_CONF2_N_INTRL(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF2, N_INTRL, __x)
+#define AB8500_DENC_CONF2_EN_RST_SHIFT 6
+#define AB8500_DENC_CONF2_EN_RST_MASK 0x00000040
+#define AB8500_DENC_CONF2_EN_RST(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF2, EN_RST, __x)
+#define AB8500_DENC_CONF2_BURST_EN_SHIFT 5
+#define AB8500_DENC_CONF2_BURST_EN_MASK 0x00000020
+#define AB8500_DENC_CONF2_BURST_EN(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF2, BURST_EN, __x)
+#define AB8500_DENC_CONF2_SEL_RST_SHIFT 4
+#define AB8500_DENC_CONF2_SEL_RST_MASK 0x00000010
+#define AB8500_DENC_CONF2_SEL_RST_USE_HW_VAL 0
+#define AB8500_DENC_CONF2_SEL_RST_USE_PROG_VAL 1
+#define AB8500_DENC_CONF2_SEL_RST(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF2, SEL_RST, \
+ AB8500_DENC_CONF2_SEL_RST_##__x)
+#define AB8500_DENC_CONF2_RST_OSC_BUF_SHIFT 2
+#define AB8500_DENC_CONF2_RST_OSC_BUF_MASK 0x00000004
+#define AB8500_DENC_CONF2_RST_OSC_BUF(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF2, RST_OSC_BUF, __x)
+#define AB8500_DENC_CONF2_VAL_RST_SHIFT 0
+#define AB8500_DENC_CONF2_VAL_RST_MASK 0x00000003
+#define AB8500_DENC_CONF2_VAL_RST_ALL_LINES 0
+#define AB8500_DENC_CONF2_VAL_RST_EVERY_2ND_FIELD 1
+#define AB8500_DENC_CONF2_VAL_RST_EVERY_4TH_FIELD 2
+#define AB8500_DENC_CONF2_VAL_RST_EVERY_8TH_FIELD 3
+#define AB8500_DENC_CONF2_VAL_RST(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF2, VAL_RST, \
+ AB8500_DENC_CONF2_VAL_RST_##__x)
+#define AB8500_DENC_CONF6 0x00000606
+#define AB8500_DENC_CONF6_SOFT_RESET_SHIFT 7
+#define AB8500_DENC_CONF6_SOFT_RESET_MASK 0x00000080
+#define AB8500_DENC_CONF6_SOFT_RESET(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF6, SOFT_RESET, __x)
+#define AB8500_DENC_CONF6_JUMP_SHIFT 6
+#define AB8500_DENC_CONF6_JUMP_MASK 0x00000040
+#define AB8500_DENC_CONF6_JUMP(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF6, JUMP, __x)
+#define AB8500_DENC_CONF6_DEC_NINC_SHIFT 5
+#define AB8500_DENC_CONF6_DEC_NINC_MASK 0x00000020
+#define AB8500_DENC_CONF6_DEC_NINC(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF6, DEC_NINC, __x)
+#define AB8500_DENC_CONF6_FREE_JUMP_SHIFT 4
+#define AB8500_DENC_CONF6_FREE_JUMP_MASK 0x00000010
+#define AB8500_DENC_CONF6_FREE_JUMP(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF6, FREE_JUMP, __x)
+#define AB8500_DENC_CONF6_MAX_DYN_SHIFT 0
+#define AB8500_DENC_CONF6_MAX_DYN_MASK 0x00000001
+#define AB8500_DENC_CONF6_MAX_DYN(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF6, MAX_DYN, __x)
+#define AB8500_DENC_CONF8 0x00000608
+#define AB8500_DENC_CONF8_PH_RST_MODE_SHIFT 6
+#define AB8500_DENC_CONF8_PH_RST_MODE_MASK 0x000000C0
+#define AB8500_DENC_CONF8_PH_RST_MODE_DISABLED 0
+#define AB8500_DENC_CONF8_PH_RST_MODE_UPDATE_FROM_PHASE_BUF 1
+#define AB8500_DENC_CONF8_PH_RST_MODE_UPDATE_FROM_INC_DFS 2
+#define AB8500_DENC_CONF8_PH_RST_MODE_RESET 3
+#define AB8500_DENC_CONF8_PH_RST_MODE(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF8, PH_RST_MODE, \
+ AB8500_DENC_CONF8_PH_RST_MODE_##__x)
+#define AB8500_DENC_CONF8_VAL_422_MUX_SHIFT 4
+#define AB8500_DENC_CONF8_VAL_422_MUX_MASK 0x00000010
+#define AB8500_DENC_CONF8_VAL_422_MUX_TEST 0
+#define AB8500_DENC_CONF8_VAL_422_MUX_ACTIVE 1
+#define AB8500_DENC_CONF8_VAL_422_MUX(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF8, VAL_422_MUX, \
+ AB8500_DENC_CONF8_VAL_422_MUX_##__x)
+#define AB8500_DENC_CONF8_BLK_ALL_SHIFT 3
+#define AB8500_DENC_CONF8_BLK_ALL_MASK 0x00000008
+#define AB8500_DENC_CONF8_BLK_ALL(__x) \
+ AB8500_VAL2REG(AB8500_DENC_CONF8, BLK_ALL, __x)
+#define AB8500_TVOUT_CTRL 0x00000680
+#define AB8500_TVOUT_CTRL_TV_LOAD_RC_SHIFT 6
+#define AB8500_TVOUT_CTRL_TV_LOAD_RC_MASK 0x00000040
+#define AB8500_TVOUT_CTRL_TV_LOAD_RC(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL, TV_LOAD_RC, __x)
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_SHIFT 3
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_MASK 0x00000038
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_0_5S 0
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_1S 0
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_1_5S 0
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_2S 0
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_2_5S 0
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME_3S 0
+#define AB8500_TVOUT_CTRL_PLUG_TV_TIME(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL, PLUG_TV_TIME, \
+ AB8500_TVOUT_CTRL_PLUG_TV_TIME_##__x)
+#define AB8500_TVOUT_CTRL_TV_PLUG_ON_SHIFT 2
+#define AB8500_TVOUT_CTRL_TV_PLUG_ON_MASK 0x00000004
+#define AB8500_TVOUT_CTRL_TV_PLUG_ON(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL, TV_PLUG_ON, __x)
+#define AB8500_TVOUT_CTRL_DAC_CTRL0_SHIFT 1
+#define AB8500_TVOUT_CTRL_DAC_CTRL0_MASK 0x00000002
+#define AB8500_TVOUT_CTRL_DAC_CTRL0(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL, DAC_CTRL0, __x)
+#define AB8500_TVOUT_CTRL_DAC_CTRL1_SHIFT 0
+#define AB8500_TVOUT_CTRL_DAC_CTRL1_MASK 0x00000001
+#define AB8500_TVOUT_CTRL_DAC_CTRL1(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL, DAC_CTRL1, __x)
+#define AB8500_TVOUT_CTRL2 0x00000681
+#define AB8500_TVOUT_CTRL2_SWAP_DDR_DATA_IN_SHIFT 1
+#define AB8500_TVOUT_CTRL2_SWAP_DDR_DATA_IN_MASK 0x00000002
+#define AB8500_TVOUT_CTRL2_SWAP_DDR_DATA_IN(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL2, SWAP_DDR_DATA_IN, __x)
+#define AB8500_TVOUT_CTRL2_DENC_DDR_SHIFT 0
+#define AB8500_TVOUT_CTRL2_DENC_DDR_MASK 0x00000001
+#define AB8500_TVOUT_CTRL2_DENC_DDR(__x) \
+ AB8500_VAL2REG(AB8500_TVOUT_CTRL2, DENC_DDR, __x)
+#define AB8500_IT_MASK1 0x00000E40
+#define AB8500_IT_MASK1_PON_KEY1_DBR_SHIFT 7
+#define AB8500_IT_MASK1_PON_KEY1_DBR_MASK 0x00000080
+#define AB8500_IT_MASK1_PON_KEY1_DBR(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, PON_KEY1_DBR, __x)
+#define AB8500_IT_MASK1_PON_KEY1_DBF_SHIFT 6
+#define AB8500_IT_MASK1_PON_KEY1_DBF_MASK 0x00000040
+#define AB8500_IT_MASK1_PON_KEY1_DBF(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, PON_KEY1_DBF, __x)
+#define AB8500_IT_MASK1_PON_KEY2_DBR_SHIFT 5
+#define AB8500_IT_MASK1_PON_KEY2_DBR_MASK 0x00000020
+#define AB8500_IT_MASK1_PON_KEY2_DBR(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, PON_KEY2_DBR, __x)
+#define AB8500_IT_MASK1_PON_KEY2_DBF_SHIFT 4
+#define AB8500_IT_MASK1_PON_KEY2_DBF_MASK 0x00000010
+#define AB8500_IT_MASK1_PON_KEY2_DBF(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, PON_KEY2_DBF, __x)
+#define AB8500_IT_MASK1_TEMP_WARN_SHIFT 3
+#define AB8500_IT_MASK1_TEMP_WARN_MASK 0x00000008
+#define AB8500_IT_MASK1_TEMP_WARN(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, TEMP_WARN, __x)
+#define AB8500_IT_MASK1_PLUG_TV_DET_SHIFT 2
+#define AB8500_IT_MASK1_PLUG_TV_DET_MASK 0x00000004
+#define AB8500_IT_MASK1_PLUG_TV_DET(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, PLUG_TV_DET, __x)
+#define AB8500_IT_MASK1_UNPLUG_TV_DET_SHIFT 1
+#define AB8500_IT_MASK1_UNPLUG_TV_DET_MASK 0x00000002
+#define AB8500_IT_MASK1_UNPLUG_TV_DET(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, UNPLUG_TV_DET, __x)
+#define AB8500_IT_MASK1_MAIN_EXT_CH_NOK_SHIFT 0
+#define AB8500_IT_MASK1_MAIN_EXT_CH_NOK_MASK 0x00000001
+#define AB8500_IT_MASK1_MAIN_EXT_CH_NOK(__x) \
+ AB8500_VAL2REG(AB8500_IT_MASK1, MAIN_EXT_CH_NOK, __x)
+#define AB8500_REV 0x00001080
+#define AB8500_REV_FULL_MASK_SHIFT 4
+#define AB8500_REV_FULL_MASK_MASK 0x000000F0
+#define AB8500_REV_FULL_MASK(__x) \
+ AB8500_VAL2REG(AB8500_REV, FULL_MASK, __x)
+#define AB8500_REV_METAL_FIX_SHIFT 0
+#define AB8500_REV_METAL_FIX_MASK 0x0000000F
+#define AB8500_REV_METAL_FIX(__x) \
+ AB8500_VAL2REG(AB8500_REV, METAL_FIX, __x)
+
+#endif /* __AB8500_DENC_H */
diff --git a/include/linux/mfd/ab8500/denc.h b/include/linux/mfd/ab8500/denc.h
new file mode 100644
index 00000000000..25a09a2c2bd
--- /dev/null
+++ b/include/linux/mfd/ab8500/denc.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * AB8500 tvout driver interface
+ *
+ * Author: Marcel Tunnissen <marcel.tuennissen@stericsson.com>
+ * for ST-Ericsson.
+ *
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+#ifndef __AB8500_DENC__H__
+#define __AB8500_DENC__H__
+
+#include <linux/platform_device.h>
+
+struct ab8500_denc_platform_data {
+ /* Platform info */
+ bool ddr_enable;
+ bool ddr_little_endian;
+};
+
+enum ab8500_denc_TV_std {
+ TV_STD_PAL_BDGHI,
+ TV_STD_PAL_N,
+ TV_STD_PAL_M,
+ TV_STD_NTSC_M,
+};
+
+enum ab8500_denc_cr_filter_bandwidth {
+ TV_CR_NTSC_LOW_DEF_FILTER,
+ TV_CR_PAL_LOW_DEF_FILTER,
+ TV_CR_NTSC_HIGH_DEF_FILTER,
+ TV_CR_PAL_HIGH_DEF_FILTER,
+};
+
+enum ab8500_denc_phase_reset_mode {
+ TV_PHASE_RST_MOD_DISABLE,
+ TV_PHASE_RST_MOD_FROM_PHASE_BUF,
+ TV_PHASE_RST_MOD_FROM_INC_DFS,
+ TV_PHASE_RST_MOD_RST,
+};
+
+enum ab8500_denc_plug_time {
+ TV_PLUG_TIME_0_5S,
+ TV_PLUG_TIME_1S,
+ TV_PLUG_TIME_1_5S,
+ TV_PLUG_TIME_2S,
+ TV_PLUG_TIME_2_5S,
+ TV_PLUG_TIME_3S,
+};
+
+struct ab8500_denc_conf {
+ /* register settings for DENC_configuration */
+ bool act_output;
+ enum ab8500_denc_TV_std TV_std;
+ bool progressive;
+ bool test_pattern;
+ bool partial_blanking;
+ bool blank_all;
+ bool black_level_setup;
+ enum ab8500_denc_cr_filter_bandwidth cr_filter;
+ bool suppress_col;
+ enum ab8500_denc_phase_reset_mode phase_reset_mode;
+ bool dac_enable;
+ bool act_dc_output;
+};
+
+struct platform_device *ab8500_denc_get_device(void);
+void ab8500_denc_put_device(struct platform_device *pdev);
+
+void ab8500_denc_reset(struct platform_device *pdev, bool hard);
+void ab8500_denc_power_up(struct platform_device *pdev);
+void ab8500_denc_power_down(struct platform_device *pdev);
+
+void ab8500_denc_conf(struct platform_device *pdev,
+ struct ab8500_denc_conf *conf);
+void ab8500_denc_conf_plug_detect(struct platform_device *pdev,
+ bool enable, bool load_RC,
+ enum ab8500_denc_plug_time time);
+void ab8500_denc_mask_int_plug_det(struct platform_device *pdev, bool plug,
+ bool unplug);
+#endif /* __AB8500_DENC__H__ */
diff --git a/include/linux/mfd/ab8500/pwmleds.h b/include/linux/mfd/ab8500/pwmleds.h
new file mode 100644
index 00000000000..e31658280a0
--- /dev/null
+++ b/include/linux/mfd/ab8500/pwmleds.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright ST-Ericsson 2012.
+ *
+ * Author: Naga Radhesh <naga.radheshy@stericsson.com>
+ * Licensed under GPLv2.
+ */
+#ifndef _AB8500_PWMLED_H
+#define _AB8500_PWMLED_H
+
+struct ab8500_led_pwm {
+ int pwm_id;
+ int blink_en;
+};
+
+struct ab8500_pwmled_platform_data {
+ int num_pwm;
+ struct ab8500_led_pwm *leds;
+};
+
+#endif
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h
index ee96cd51d8b..1eb4c001471 100644
--- a/include/linux/mfd/abx500.h
+++ b/include/linux/mfd/abx500.h
@@ -33,6 +33,7 @@ struct device;
#define AB5500_1_0 0x20
#define AB5500_1_1 0x21
#define AB5500_2_0 0x24
+#define AB5500_2_1 0x25
/*
* AB3100, EVENTA1, A2 and A3 event register flags
@@ -444,6 +445,13 @@ int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
int abx500_get_chip_id(struct device *dev);
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
+void abx500_dump_all_banks(void);
+
+#define abx500_get abx500_get_register_interruptible
+#define abx500_set abx500_set_register_interruptible
+#define abx500_get_page abx500_get_register_page_interruptible
+#define abx500_set_page abx500_set_register_page_interruptible
+#define abx500_mask_and_set abx500_mask_and_set_register_interruptible
struct abx500_ops {
int (*get_chip_id) (struct device *);
@@ -454,6 +462,7 @@ struct abx500_ops {
int (*mask_and_set_register) (struct device *, u8, u8, u8, u8);
int (*event_registers_startup_state_get) (struct device *, u8 *);
int (*startup_irq_enabled) (struct device *, unsigned int);
+ void (*dump_all_banks) (struct device *);
};
int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
diff --git a/include/linux/mfd/abx500/ab5500-bm.h b/include/linux/mfd/abx500/ab5500-bm.h
new file mode 100644
index 00000000000..05ebc8c3840
--- /dev/null
+++ b/include/linux/mfd/abx500/ab5500-bm.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright ST-Ericsson 2011.
+ *
+ * Author: Arun Murthy <arun.murthy@stericsson.com>
+ * Licensed under GPLv2.
+ */
+
+#ifndef _AB5500_BM_H
+#define _AB5500_BM_H
+
+#define AB5500_MCB 0x2F
+/*
+ * USB/ULPI register offsets
+ * Bank : 0x5
+ */
+#define AB5500_USB_LINE_STATUS 0x80
+#define AB5500_USB_PHY_STATUS 0x89
+#define AB5500_CHGFSM_CHARGER_DETECT 0xBF
+#define AB5500_CHGFSM_USB_BTEMP_CURR_LIM 0xAD
+#define AB5500_USB_LINE_CTRL2 0x82
+#define AB5500_USB_OTG_CTRL 0x87
+
+/*
+ * Charger / control register offfsets
+ * Bank : 0x0B
+ */
+#define AB5500_CVBUSM 0x11
+#define AB5500_LEDT 0x12
+#define AB5500_VSRC 0x13
+#define AB5500_ICSR 0x14
+#define AB5500_OCSRV 0x15
+#define AB5500_CVREC 0x16
+#define AB5500_CREVS 0x17
+#define AB5500_CCTRL 0x18
+#define AB5500_TBDATA 0x19
+#define AB5500_CPWM 0x1A
+#define AB5500_DCIOCURRENT 0x1B
+#define AB5500_USB_HS_CURR_LIM 0x1C
+#define AB5500_WALL_HS_CURR_LIM 0x1D
+
+/*
+ * FG, Battcom and ACC registers offsets
+ * Bank : 0x0C
+ */
+#define AB5500_FG_CH0 0x00
+#define AB5500_FG_CH1 0x01
+#define AB5500_FG_CH2 0x02
+#define AB5500_FG_DIS_CH0 0x03
+#define AB5500_FG_DIS_CH1 0x04
+#define AB5500_FG_DIS_CH2 0x05
+#define AB5500_FGDIS_COUNT0 0x06
+#define AB5500_FGDIS_COUNT1 0x07
+#define AB5500_FG_VAL_COUNT0 0x08
+#define AB5500_FG_VAL_COUNT1 0x09
+#define AB5500_FGDIR_READ0 0x0A
+#define AB5500_FGDIR_READ1 0x0B
+#define AB5500_FG_CONTROL_A 0x0C
+#define AB5500_FG_CONTROL_B 0x0F
+#define AB5500_FG_CONTROL_C 0x10
+#define AB5500_FG_DIS 0x0D
+#define AB5500_FG_EOC 0x0E
+#define AB5500_FG_CB 0x0F
+#define AB5500_FG_CC 0x10
+#define AB5500_UIOR 0x1A
+#define AB5500_UART 0x1B
+#define AB5500_URI 0x1C
+#define AB5500_UART_RQ 0x1D
+#define AB5500_ACC_DETECT1 0x20
+#define AB5500_ACC_DETECT2 0x21
+#define AB5500_ACC_DETECTCTRL 0x23
+#define AB5500_ACC_AVCTRL 0x24
+#define AB5500_ACC_DETECT3_DEG_LITCH_TIME 0x30
+#define AB5500_ACC_DETECT3_KEY_PRESS_TIME 0x31
+#define AB5500_ACC_DETECT3_LONG_KEY_TIME 0x32
+#define AB5500_ACC_DETECT3_TIME_READ_MS 0x33
+#define AB5500_ACC_DETECT3_TIME_READ_LS 0x34
+#define AB5500_ACC_DETECT3_CONTROL 0x35
+#define AB5500_ACC_DETECT3_LEVEL 0x36
+#define AB5500_ACC_DETECT3_TIMER_READ_CTL 0x37
+
+/*
+ * Interrupt register offsets
+ * Bank : 0x0E
+ */
+#define AB5500_IT_SOURCE8 0x28
+#define AB5500_IT_SOURCE9 0x29
+
+/* BatCtrl Current Source Constants */
+#define BAT_CTRL_7U_ENA (0x01 << 0)
+#define BAT_CTRL_15U_ENA (0x01 << 1)
+#define BAT_CTRL_30U_ENA (0x01 << 2)
+#define BAT_CTRL_60U_ENA (0x01 << 3)
+#define BAT_CTRL_120U_ENA (0x01 << 4)
+#define BAT_CTRL_CMP_ENA 0x04
+#define FORCE_BAT_CTRL_CMP_HIGH 0x08
+#define BAT_CTRL_PULL_UP_ENA 0x10
+
+/* Battery type */
+#define BATTERY_UNKNOWN 0
+
+#ifdef CONFIG_AB5500_BM
+struct ab5500_btemp *ab5500_btemp_get(void);
+int ab5500_btemp_get_batctrl_temp(struct ab5500_btemp *btemp);
+void ab5500_fg_reinit(void);
+#else
+static inline struct ab5500_btemp *ab5500_btemp_get(void)
+{
+ return 0;
+}
+static inline int ab5500_btemp_get_batctrl_temp(struct ab5500_btemp *btemp)
+{
+ return 0;
+}
+static inline void ab5500_fg_reinit(void) {}
+#endif
+#endif /* _AB5500_BM_H */
diff --git a/include/linux/mfd/abx500/ab5500-gpadc.h b/include/linux/mfd/abx500/ab5500-gpadc.h
new file mode 100644
index 00000000000..8bddafc7f79
--- /dev/null
+++ b/include/linux/mfd/abx500/ab5500-gpadc.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 ST-Ericsson SA
+ * Licensed under GPLv2.
+ *
+ * Author: Vijaya Kumar K <vijay.kilari@stericsson.com>
+ */
+
+#ifndef _AB5500_GPADC_H
+#define _AB5500_GPADC_H
+
+/*
+ * GPADC source:
+ * The BTEMP_BALL and PCB_TEMP are same. They differ if the
+ * battery supports internal NTC resistor connected to BDATA
+ * line. In this case, the BTEMP_BALL correspondss to BDATA
+ * of GPADC as per AB5500 product spec.
+ */
+
+#define BTEMP_BALL 0
+#define ACC_DETECT2 1
+#define ACC_DETECT3 2
+#define MAIN_BAT_V 3
+#define MAIN_BAT_V_TXON 4
+#define VBUS_V 5
+#define USB_CHARGER_C 6
+#define BK_BAT_V 7
+#define DIE_TEMP 8
+#define PCB_TEMP 9
+#define XTAL_TEMP 10
+#define USB_ID 11
+#define BAT_CTRL 12
+/* VBAT with TXON only min trigger */
+#define MAIN_BAT_V_TXON_TRIG_MIN 13
+/* VBAT with TX off only min trigger */
+#define MAIN_BAT_V_TRIG_MIN 14
+#define GPADC0_V 15
+#define VIBRA_KELVIN 16
+
+/*
+ * Frequency of auto adc conversion
+ */
+#define MS1000 0x0
+#define MS500 0x1
+#define MS200 0x2
+#define MS100 0x3
+#define MS10 0x4
+
+struct ab5500_gpadc;
+
+/*
+ * struct adc_auto_input - AB5500 GPADC auto trigger
+ * @adc_mux Mux input
+ * @freq freq of conversion
+ * @min min value for trigger
+ * @max max value for trigger
+ * @auto_adc_callback notification callback
+ */
+struct adc_auto_input {
+ u8 mux;
+ u8 freq;
+ int min;
+ int max;
+ int (*auto_adc_callback)(int mux);
+};
+
+struct ab5500_gpadc *ab5500_gpadc_get(const char *name);
+int ab5500_gpadc_convert(struct ab5500_gpadc *gpadc, u8 input);
+int ab5500_gpadc_convert_auto(struct ab5500_gpadc *gpadc,
+ struct adc_auto_input *auto_input);
+
+#endif /* _AB5500_GPADC_H */
diff --git a/include/linux/mfd/abx500/ab5500.h b/include/linux/mfd/abx500/ab5500.h
index 54f820ed73b..bf990010048 100644
--- a/include/linux/mfd/abx500/ab5500.h
+++ b/include/linux/mfd/abx500/ab5500.h
@@ -24,6 +24,10 @@ enum ab5500_devid {
AB5500_DEVID_VIDEO,
AB5500_DEVID_DBIECI,
AB5500_DEVID_ONSWA,
+ AB5500_DEVID_CHARGALG,
+ AB5500_DEVID_BTEMP,
+ AB5500_DEVID_TEMPMON,
+ AB5500_DEVID_ACCDET,
AB5500_NUM_DEVICES,
};
@@ -92,8 +96,9 @@ enum ab5500_banks_addr {
#define AB5500_IT_SOURCE21_REG 0x35
#define AB5500_IT_SOURCE22_REG 0x36
#define AB5500_IT_SOURCE23_REG 0x37
+#define AB5500_IT_SOURCE24_REG 0x38
-#define AB5500_NUM_IRQ_REGS 23
+#define AB5500_NUM_IRQ_REGS 25
/**
* struct ab5500
@@ -118,6 +123,7 @@ struct ab5500 {
char chip_name[32];
u8 chip_id;
struct mutex irq_lock;
+ u32 num_event_reg;
u32 abb_events;
u8 mask[AB5500_NUM_IRQ_REGS];
u8 oldmask[AB5500_NUM_IRQ_REGS];
@@ -129,12 +135,31 @@ struct ab5500 {
#endif
};
+#ifndef CONFIG_AB5500_CORE
+static inline int ab5500_clock_rtc_enable(int num, bool enable)
+{
+ return -ENOSYS;
+}
+#else
+extern int ab5500_clock_rtc_enable(int num, bool enable);
+#endif
+
+/* Forward Declaration */
+struct ab5500_regulator_platform_data;
+
struct ab5500_platform_data {
struct {unsigned int base; unsigned int count; } irq;
void *dev_data[AB5500_NUM_DEVICES];
+ size_t dev_data_sz[AB5500_NUM_DEVICES];
struct abx500_init_settings *init_settings;
unsigned int init_settings_sz;
bool pm_power_off;
+ struct ab5500_regulator_platform_data *regulator;
+ struct ab5500_usbgpio_platform_data *usb;
+ struct abx500_accdet_platform_data *accdet;
};
+struct ab5500_ponkey_platform_data {
+ u8 shutdown_secs;
+};
#endif /* MFD_AB5500_H */
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h
index 44310c98ee6..c92c297fd3f 100644
--- a/include/linux/mfd/abx500/ab8500-bm.h
+++ b/include/linux/mfd/abx500/ab8500-bm.h
@@ -395,6 +395,7 @@ struct ab8500_bm_data {
int gnd_lift_resistance;
const struct ab8500_maxim_parameters *maxi;
const struct ab8500_bm_capacity_levels *cap_levels;
+ const struct abx500_battery_type *bat_type;
const struct ab8500_bm_charger_parameters *chg_params;
const struct ab8500_fg_parameters *fg_params;
};
diff --git a/include/linux/mfd/abx500/ab8500-gpio.h b/include/linux/mfd/abx500/ab8500-gpio.h
index 2387c207ea8..67ac32c1dcb 100644
--- a/include/linux/mfd/abx500/ab8500-gpio.h
+++ b/include/linux/mfd/abx500/ab8500-gpio.h
@@ -8,6 +8,8 @@
#ifndef _AB8500_GPIO_H
#define _AB8500_GPIO_H
+#include <mach/gpio.h>
+
/*
* Platform data to register a block: only the initial gpio/irq number.
* Array sizes are large enough to contain all AB8500 and AB9540 GPIO
@@ -18,6 +20,82 @@ struct ab8500_gpio_platform_data {
int gpio_base;
u32 irq_base;
u8 config_reg[8];
+ u8 config_direction[7];
+ u8 config_pullups[7];
+};
+
+enum ab8500_pin {
+ AB8500_PIN_GPIO1 = AB8500_GPIO_BASE,
+ AB8500_PIN_GPIO2,
+ AB8500_PIN_GPIO3,
+ AB8500_PIN_GPIO4,
+ AB8500_PIN_GPIO5,
+ AB8500_PIN_GPIO6,
+ AB8500_PIN_GPIO7,
+ AB8500_PIN_GPIO8,
+ AB8500_PIN_GPIO9,
+ AB8500_PIN_GPIO10,
+ AB8500_PIN_GPIO11,
+ AB8500_PIN_GPIO12,
+ AB8500_PIN_GPIO13,
+ AB8500_PIN_GPIO14,
+ AB8500_PIN_GPIO15,
+ AB8500_PIN_GPIO16,
+ AB8500_PIN_GPIO17,
+ AB8500_PIN_GPIO18,
+ AB8500_PIN_GPIO19,
+ AB8500_PIN_GPIO20,
+ AB8500_PIN_GPIO21,
+ AB8500_PIN_GPIO22,
+ AB8500_PIN_GPIO23,
+ AB8500_PIN_GPIO24,
+ AB8500_PIN_GPIO25,
+ AB8500_PIN_GPIO26,
+ AB8500_PIN_GPIO27,
+ AB8500_PIN_GPIO28,
+ AB8500_PIN_GPIO29,
+ AB8500_PIN_GPIO30,
+ AB8500_PIN_GPIO31,
+ AB8500_PIN_GPIO32,
+ AB8500_PIN_GPIO33,
+ AB8500_PIN_GPIO34,
+ AB8500_PIN_GPIO35,
+ AB8500_PIN_GPIO36,
+ AB8500_PIN_GPIO37,
+ AB8500_PIN_GPIO38,
+ AB8500_PIN_GPIO39,
+ AB8500_PIN_GPIO40,
+ AB8500_PIN_GPIO41,
+ AB8500_PIN_GPIO42,
+ /* AB9540 GPIO extends support provided by AB8500 */
+ AB9540_PIN_GPIO43,
+ AB9540_PIN_GPIO44,
+ AB9540_PIN_GPIO45,
+ AB9540_PIN_GPIO46,
+ AB9540_PIN_GPIO47,
+ AB9540_PIN_GPIO48,
+ AB9540_PIN_GPIO49,
+ AB9540_PIN_GPIO50,
+ AB9540_PIN_GPIO51,
+ AB9540_PIN_GPIO52,
+ AB9540_PIN_GPIO53,
+ /*
+ * AB9540_PIN_GPIO60 is configured, in the AB9540 GPIO registers, where
+ * AB9540_PIN_GPIO54 would be expected. AB9540_PIN_GPIO54 to
+ * AB9540_PIN_GPIO59 do not exist and no reserved space has been left
+ * for them in the registers. Therefore the enum goes directly from
+ * AB9540_PIN_GPIO53 to AB9540_PIN_GPIO60.
+ */
+ AB9540_PIN_GPIO60,
};
+int ab8500_config_pulldown(struct device *dev,
+ enum ab8500_pin gpio, bool enable);
+
+int ab8500_gpio_config_select(struct device *dev,
+ enum ab8500_pin gpio, bool gpio_select);
+
+int ab8500_gpio_config_get_select(struct device *dev,
+ enum ab8500_pin gpio, bool *gpio_select);
+
#endif /* _AB8500_GPIO_H */
diff --git a/include/linux/mfd/abx500/ab8500-sysctrl.h b/include/linux/mfd/abx500/ab8500-sysctrl.h
index 10eb50973c3..ebf12e793db 100644
--- a/include/linux/mfd/abx500/ab8500-sysctrl.h
+++ b/include/linux/mfd/abx500/ab8500-sysctrl.h
@@ -37,6 +37,11 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
return ab8500_sysctrl_write(reg, bits, 0);
}
+/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
+struct ab8500_sysctrl_platform_data {
+ u8 initial_req_buf_config[8];
+};
+
/* Registers */
#define AB8500_TURNONSTATUS 0x100
#define AB8500_RESETSTATUS 0x101
diff --git a/include/linux/mfd/abx500/ab8500.h b/include/linux/mfd/abx500/ab8500.h
index fccc3002f27..4aee57c9853 100644
--- a/include/linux/mfd/abx500/ab8500.h
+++ b/include/linux/mfd/abx500/ab8500.h
@@ -211,6 +211,9 @@ enum ab8500_version {
#define AB8500_NUM_IRQ_REGS 14
#define AB9540_NUM_IRQ_REGS 17
+/* Forward declaration */
+struct ab8500_charger;
+
/**
* struct ab8500 - ab8500 internal structure
* @dev: parent device
@@ -229,12 +232,12 @@ enum ab8500_version {
* @mask_size: Actual number of valid entries in mask[], oldmask[] and
* irq_reg_offset
* @irq_reg_offset: Array of offsets into IRQ registers
+ * @charger: pointer to the charger driver device information.
*/
struct ab8500 {
struct device *dev;
struct mutex lock;
struct mutex irq_lock;
-
int irq_base;
int irq;
enum ab8500_version version;
@@ -251,29 +254,52 @@ struct ab8500 {
u8 *oldmask;
int mask_size;
const int *irq_reg_offset;
+
+ struct ab8500_charger *charger;
};
-struct regulator_reg_init;
-struct regulator_init_data;
+struct ab8500_regulator_platform_data;
+struct ab8500_accdet_platform_data;
+struct ab8500_denc_platform_data;
+struct ab8500_audio_platform_data;
struct ab8500_gpio_platform_data;
+struct ab8500_sysctrl_platform_data;
/**
* struct ab8500_platform_data - AB8500 platform data
+ * @pm_power_off: Should machine pm power off hook be registered or not
+ * @thermal_power_off_pending: Set if there was a thermal alarm
+ * @thermal_set_time_sec: Time of the thermal alarm
+ * @thermal_time_out: Time out before the thermal alarm should be ignored
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
* @init: board-specific initialization after detection of ab8500
- * @num_regulator_reg_init: number of regulator init registers
- * @regulator_reg_init: regulator init registers
- * @num_regulator: number of regulators
* @regulator: machine-specific constraints for regulators
+ * @accdet: machine-specific Accessory detection data
+ * @battery: machine-specific battery management data
+ * @charger: machine-specific charger data
+ * @btemp: machine-specific battery temp data
+ * @pwmled: machine-specific pwmled data
*/
struct ab8500_platform_data {
int irq_base;
+ bool pm_power_off;
+ bool thermal_power_off_pending;
+ long thermal_set_time_sec;
+ long thermal_time_out;
void (*init) (struct ab8500 *);
- int num_regulator_reg_init;
- struct ab8500_regulator_reg_init *regulator_reg_init;
- int num_regulator;
- struct regulator_init_data *regulator;
+ struct ab8500_regulator_platform_data *regulator;
+ struct abx500_accdet_platform_data *accdet;
+ struct abx500_bm_data *battery;
+ struct ab8500_denc_platform_data *denc;
+ struct ab8500_audio_platform_data *audio;
+ struct abx500_charger_platform_data *charger;
+ struct abx500_btemp_platform_data *btemp;
+ struct abx500_fg_platform_data *fg;
+ struct abx500_chargalg_platform_data *chargalg;
struct ab8500_gpio_platform_data *gpio;
+ struct abx500_usbgpio_platform_data *usb;
+ struct ab8500_sysctrl_platform_data *sysctrl;
+ struct ab8500_pwmled_platform_data *pwmled;
};
extern int __devinit ab8500_init(struct ab8500 *ab8500,
@@ -324,4 +350,12 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
}
+#ifdef CONFIG_AB8500_DEBUG
+void ab8500_dump_all_banks(struct device *dev);
+void ab8500_debug_register_interrupt(int line);
+#else
+static inline void ab8500_dump_all_banks(struct device *dev) {}
+static inline void ab8500_debug_register_interrupt(int line) {}
+#endif
+
#endif /* MFD_AB8500_H */
diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h
index 5a049dfaf15..681c8f99bf1 100644
--- a/include/linux/mfd/db5500-prcmu.h
+++ b/include/linux/mfd/db5500-prcmu.h
@@ -8,42 +8,169 @@
#ifndef __MFD_DB5500_PRCMU_H
#define __MFD_DB5500_PRCMU_H
-static inline int prcmu_resetout(u8 resoutn, u8 state)
+#ifdef CONFIG_MFD_DB5500_PRCMU
+
+void db5500_prcmu_early_init(void);
+int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state);
+int db5500_prcmu_set_display_clocks(void);
+int db5500_prcmu_disable_dsipll(void);
+int db5500_prcmu_enable_dsipll(void);
+int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
+int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
+void db5500_prcmu_enable_wakeups(u32 wakeups);
+int db5500_prcmu_request_clock(u8 clock, bool enable);
+void db5500_prcmu_config_abb_event_readout(u32 abb_events);
+void db5500_prcmu_get_abb_event_buffer(void __iomem **buf);
+int prcmu_resetout(u8 resoutn, u8 state);
+int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk,
+ bool keep_ap_pll);
+u8 db5500_prcmu_get_power_state_result(void);
+int db5500_prcmu_config_esram0_deep_sleep(u8 state);
+void db5500_prcmu_system_reset(u16 reset_code);
+u16 db5500_prcmu_get_reset_code(void);
+#ifdef CONFIG_UX500_SOC_DB5500
+void prcmu_modem_req(void);
+void prcmu_modem_rel(void);
+void prcmu_ape_ack(void);
+#endif
+bool db5500_prcmu_is_modem_requested(void);
+void db5500_prcmu_modem_reset(void);
+int db5500_prcmu_set_arm_opp(u8 opp);
+int db5500_prcmu_get_arm_opp(void);
+int db5500_prcmu_set_ape_opp(u8 opp);
+int db5500_prcmu_get_ape_opp(void);
+int db5500_prcmu_set_ddr_opp(u8 opp);
+int db5500_prcmu_get_ddr_opp(void);
+
+u32 db5500_prcmu_read(unsigned int reg);
+void db5500_prcmu_write(unsigned int reg, u32 value);
+void db5500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value);
+
+static inline unsigned long prcmu_clock_rate(u8 clock)
{
return 0;
}
-static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state)
+static inline long prcmu_round_clock_rate(u8 clock, unsigned long rate)
+{
+ return 0;
+}
+
+static inline int prcmu_set_clock_rate(u8 clock, unsigned long rate)
{
return 0;
}
+int db5500_prcmu_get_hotdog(void);
+int db5500_prcmu_config_hotdog(u8 threshold);
+int db5500_prcmu_config_hotmon(u8 low, u8 high);
+int db5500_prcmu_start_temp_sense(u16 cycles32k);
+int db5500_prcmu_stop_temp_sense(void);
+
+int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off);
+int db5500_prcmu_enable_a9wdog(u8 id);
+int db5500_prcmu_disable_a9wdog(u8 id);
+int db5500_prcmu_kick_a9wdog(u8 id);
+int db5500_prcmu_load_a9wdog(u8 id, u32 timeout);
+
+#else /* !CONFIG_UX500_SOC_DB5500 */
+
+static inline void db5500_prcmu_early_init(void) {}
+
+static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ return -ENOSYS;
+}
+
+static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+{
+ return -ENOSYS;
+}
static inline int db5500_prcmu_request_clock(u8 clock, bool enable)
{
return 0;
}
+static inline unsigned long db5500_prcmu_clock_rate(u8 clock)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_set_display_clocks(void)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_disable_dsipll(void)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_enable_dsipll(void)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state)
+{
+ return 0;
+}
+
+static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {}
+
+static inline long db5500_prcmu_round_clock_rate(u8 clock, unsigned long rate)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_set_clock_rate(u8 clock, unsigned long rate)
+{
+ return 0;
+}
+
+static inline int prcmu_resetout(u8 resoutn, u8 state)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state)
+{
+ return 0;
+}
+
+static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {}
+static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {}
+
static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk,
bool keep_ap_pll)
{
return 0;
}
-static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state)
+static inline u8 db5500_prcmu_get_power_state_result(void)
{
return 0;
}
+static inline void db5500_prcmu_system_reset(u16 reset_code) {}
+
static inline u16 db5500_prcmu_get_reset_code(void)
{
return 0;
}
-static inline bool db5500_prcmu_is_ac_wake_requested(void)
+static inline void db5500_prcmu_modem_reset(void) {}
+static inline bool db5500_prcmu_is_modem_requested(void)
{
return 0;
}
+#ifdef CONFIG_UX500_SOC_DB5500
+static void prcmu_ape_ack(void) {}
+static void prcmu_modem_req(void) {}
+static void prcmu_modem_rel(void) {}
+#endif
+
static inline int db5500_prcmu_set_arm_opp(u8 opp)
{
return 0;
@@ -54,48 +181,80 @@ static inline int db5500_prcmu_get_arm_opp(void)
return 0;
}
-static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {}
-
-static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {}
+static inline int db5500_prcmu_set_ape_opp(u8 opp)
+{
+ return 0;
+}
-static inline void db5500_prcmu_system_reset(u16 reset_code) {}
+static inline int db5500_prcmu_get_ape_opp(void)
+{
+ return 0;
+}
-static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {}
+static inline int db5500_prcmu_set_ddr_opp(u8 opp)
+{
+ return 0;
+}
-#ifdef CONFIG_MFD_DB5500_PRCMU
+static inline int db5500_prcmu_get_ddr_opp(void)
+{
+ return 0;
+}
-void db5500_prcmu_early_init(void);
-int db5500_prcmu_set_display_clocks(void);
-int db5500_prcmu_disable_dsipll(void);
-int db5500_prcmu_enable_dsipll(void);
-int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size);
-int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size);
+static inline u32 db5500_prcmu_read(unsigned int reg)
+{
+ return 0;
+}
-#else /* !CONFIG_UX500_SOC_DB5500 */
+static inline void db5500_prcmu_write(unsigned int reg, u32 value) {}
-static inline void db5500_prcmu_early_init(void) {}
+static inline void db5500_prcmu_write_masked(unsigned int reg, u32 mask,
+ u32 value) {}
-static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)
+static inline int db5500_prcmu_get_hotdog(void)
{
return -ENOSYS;
}
+static inline int db5500_prcmu_config_hotdog(u8 threshold)
+{
+ return 0;
+}
-static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
+static inline int db5500_prcmu_config_hotmon(u8 low, u8 high)
{
- return -ENOSYS;
+ return 0;
}
-static inline int db5500_prcmu_set_display_clocks(void)
+static inline int db5500_prcmu_start_temp_sense(u16 cycles32k)
+{
+ return 0;
+}
+static inline int db5500_prcmu_stop_temp_sense(void)
{
return 0;
}
-static inline int db5500_prcmu_disable_dsipll(void)
+static inline int db5500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
return 0;
}
-static inline int db5500_prcmu_enable_dsipll(void)
+static inline int db5500_prcmu_enable_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_disable_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_kick_a9wdog(u8 id)
+{
+ return 0;
+}
+
+static inline int db5500_prcmu_load_a9wdog(u8 id, u32 timeout)
{
return 0;
}
diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h
index b3a43b1263f..705af54b89f 100644
--- a/include/linux/mfd/db8500-prcmu.h
+++ b/include/linux/mfd/db8500-prcmu.h
@@ -30,6 +30,11 @@
#define DB8500_PRCM_DSI_SW_RESET_DSI1_SW_RESETN BIT(1)
#define DB8500_PRCM_DSI_SW_RESET_DSI2_SW_RESETN BIT(2)
+#define DB8500_PRCM_DSI_GLITCHFREE_EN 0x534
+#define DB8500_PRCM_DSI_GLITCHFREE_EN_DSI0_BYTE_CLK BIT(0)
+#define DB8500_PRCM_DSI_GLITCHFREE_EN_DSI1_BYTE_CLK BIT(8)
+#define DB8500_PRCM_DSI_GLITCHFREE_EN_DSI2_BYTE_CLK BIT(16)
+
/* This portion previously known as <mach/prcmu-fw-defs_v1.h> */
/**
@@ -181,84 +186,6 @@ enum hw_acc_state {
};
/**
- * enum mbox_2_arm_stat - Status messages definition for mbox_arm
- * @BOOT_TO_EXECUTEOK: The apBoot to apExecute state transition has been
- * completed
- * @DEEPSLEEPOK: The apExecute to apDeepSleep state transition has been
- * completed
- * @SLEEPOK: The apExecute to apSleep state transition has been completed
- * @IDLEOK: The apExecute to apIdle state transition has been completed
- * @SOFTRESETOK: The A9 watchdog/ SoftReset state has been completed
- * @SOFTRESETGO : The A9 watchdog/SoftReset state is on going
- * @BOOT_TO_EXECUTE: The apBoot to apExecute state transition is on going
- * @EXECUTE_TO_DEEPSLEEP: The apExecute to apDeepSleep state transition is on
- * going
- * @DEEPSLEEP_TO_EXECUTE: The apDeepSleep to apExecute state transition is on
- * going
- * @DEEPSLEEP_TO_EXECUTEOK: The apDeepSleep to apExecute state transition has
- * been completed
- * @EXECUTE_TO_SLEEP: The apExecute to apSleep state transition is on going
- * @SLEEP_TO_EXECUTE: The apSleep to apExecute state transition is on going
- * @SLEEP_TO_EXECUTEOK: The apSleep to apExecute state transition has been
- * completed
- * @EXECUTE_TO_IDLE: The apExecute to apIdle state transition is on going
- * @IDLE_TO_EXECUTE: The apIdle to apExecute state transition is on going
- * @IDLE_TO_EXECUTEOK: The apIdle to apExecute state transition has been
- * completed
- * @INIT_STATUS: Status init
- */
-enum ap_pwrsttr_status {
- BOOT_TO_EXECUTEOK = 0xFF,
- DEEPSLEEPOK = 0xFE,
- SLEEPOK = 0xFD,
- IDLEOK = 0xFC,
- SOFTRESETOK = 0xFB,
- SOFTRESETGO = 0xFA,
- BOOT_TO_EXECUTE = 0xF9,
- EXECUTE_TO_DEEPSLEEP = 0xF8,
- DEEPSLEEP_TO_EXECUTE = 0xF7,
- DEEPSLEEP_TO_EXECUTEOK = 0xF6,
- EXECUTE_TO_SLEEP = 0xF5,
- SLEEP_TO_EXECUTE = 0xF4,
- SLEEP_TO_EXECUTEOK = 0xF3,
- EXECUTE_TO_IDLE = 0xF2,
- IDLE_TO_EXECUTE = 0xF1,
- IDLE_TO_EXECUTEOK = 0xF0,
- RDYTODS_RETURNTOEXE = 0xEF,
- NORDYTODS_RETURNTOEXE = 0xEE,
- EXETOSLEEP_RETURNTOEXE = 0xED,
- EXETOIDLE_RETURNTOEXE = 0xEC,
- INIT_STATUS = 0xEB,
-
- /*error messages */
- INITERROR = 0x00,
- PLLARMLOCKP_ER = 0x01,
- PLLDDRLOCKP_ER = 0x02,
- PLLSOCLOCKP_ER = 0x03,
- PLLSOCK1LOCKP_ER = 0x04,
- ARMWFI_ER = 0x05,
- SYSCLKOK_ER = 0x06,
- I2C_NACK_DATA_ER = 0x07,
- BOOT_ER = 0x08,
- I2C_STATUS_ALWAYS_1 = 0x0A,
- I2C_NACK_REG_ADDR_ER = 0x0B,
- I2C_NACK_DATA0123_ER = 0x1B,
- I2C_NACK_ADDR_ER = 0x1F,
- CURAPPWRSTISNOT_BOOT = 0x20,
- CURAPPWRSTISNOT_EXECUTE = 0x21,
- CURAPPWRSTISNOT_SLEEPMODE = 0x22,
- CURAPPWRSTISNOT_CORRECTFORIT10 = 0x23,
- FIFO4500WUISNOT_WUPEVENT = 0x24,
- PLL32KLOCKP_ER = 0x29,
- DDRDEEPSLEEPOK_ER = 0x2A,
- ROMCODEREADY_ER = 0x50,
- WUPBEFOREDS = 0x51,
- DDRCONFIG_ER = 0x52,
- WUPBEFORESLEEP = 0x53,
- WUPBEFOREIDLE = 0x54
-}; /* earlier called as mbox_2_arm_stat */
-
-/**
* enum dvfs_stat - DVFS status messages definition
* @DVFS_GO: A state transition DVFS is on going
* @DVFS_ARM100OPPOK: The state transition DVFS has been completed for 100OPP
@@ -559,6 +486,7 @@ void db8500_prcmu_config_abb_event_readout(u32 abb_events);
void db8500_prcmu_get_abb_event_buffer(void __iomem **buf);
int db8500_prcmu_config_esram0_deep_sleep(u8 state);
u16 db8500_prcmu_get_reset_code(void);
+u32 db8500_prcmu_get_reset_status(void);
bool db8500_prcmu_is_ac_wake_requested(void);
int db8500_prcmu_set_arm_opp(u8 opp);
int db8500_prcmu_get_arm_opp(void);
@@ -571,6 +499,8 @@ u32 db8500_prcmu_read(unsigned int reg);
void db8500_prcmu_write(unsigned int reg, u32 value);
void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value);
+void db8500_prcmu_vc(bool enable);
+
#else /* !CONFIG_MFD_DB8500_PRCMU */
static inline void db8500_prcmu_early_init(void) {}
@@ -740,6 +670,11 @@ static inline u16 db8500_prcmu_get_reset_code(void)
return 0;
}
+static inline u32 db8500_prcmu_get_reset_status(void)
+{
+ return 0;
+}
+
static inline int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
return 0;
@@ -790,6 +725,8 @@ static inline void db8500_prcmu_write(unsigned int reg, u32 value) {}
static inline void db8500_prcmu_write_masked(unsigned int reg, u32 mask,
u32 value) {}
+void db8500_prcmu_vc(bool enable) {}
+
#endif /* !CONFIG_MFD_DB8500_PRCMU */
#endif /* __MFD_DB8500_PRCMU_H */
diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h
index d7674eb7305..818209b84c0 100644
--- a/include/linux/mfd/dbx500-prcmu.h
+++ b/include/linux/mfd/dbx500-prcmu.h
@@ -276,7 +276,7 @@ static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk,
static inline u8 prcmu_get_power_state_result(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_power_state_result();
else
return db8500_prcmu_get_power_state_result();
}
@@ -332,7 +332,7 @@ static inline bool prcmu_pending_irq(void)
static inline int prcmu_set_epod(u16 epod_id, u8 epod_state)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_epod(epod_id, epod_state);
else
return db8500_prcmu_set_epod(epod_id, epod_state);
}
@@ -387,14 +387,14 @@ int prcmu_set_clock_rate(u8 clock, unsigned long rate);
static inline int prcmu_set_ddr_opp(u8 opp)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_ddr_opp(opp);
else
return db8500_prcmu_set_ddr_opp(opp);
}
static inline int prcmu_get_ddr_opp(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_ddr_opp();
else
return db8500_prcmu_get_ddr_opp();
}
@@ -402,7 +402,7 @@ static inline int prcmu_get_ddr_opp(void)
static inline int prcmu_set_arm_opp(u8 opp)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_arm_opp(opp);
else
return db8500_prcmu_set_arm_opp(opp);
}
@@ -410,7 +410,7 @@ static inline int prcmu_set_arm_opp(u8 opp)
static inline int prcmu_get_arm_opp(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_arm_opp();
else
return db8500_prcmu_get_arm_opp();
}
@@ -418,7 +418,7 @@ static inline int prcmu_get_arm_opp(void)
static inline int prcmu_set_ape_opp(u8 opp)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_set_ape_opp(opp);
else
return db8500_prcmu_set_ape_opp(opp);
}
@@ -426,7 +426,7 @@ static inline int prcmu_set_ape_opp(u8 opp)
static inline int prcmu_get_ape_opp(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_get_ape_opp();
else
return db8500_prcmu_get_ape_opp();
}
@@ -447,12 +447,20 @@ static inline u16 prcmu_get_reset_code(void)
return db8500_prcmu_get_reset_code();
}
+static inline u32 prcmu_get_reset_status(void)
+{
+ if (cpu_is_u8500())
+ return db8500_prcmu_get_reset_status();
+ else
+ return 0;
+}
+
void prcmu_ac_wake_req(void);
void prcmu_ac_sleep_req(void);
static inline void prcmu_modem_reset(void)
{
if (cpu_is_u5500())
- return;
+ return db5500_prcmu_modem_reset();
else
return db8500_prcmu_modem_reset();
}
@@ -460,7 +468,7 @@ static inline void prcmu_modem_reset(void)
static inline bool prcmu_is_ac_wake_requested(void)
{
if (cpu_is_u5500())
- return db5500_prcmu_is_ac_wake_requested();
+ return db5500_prcmu_is_modem_requested();
else
return db8500_prcmu_is_ac_wake_requested();
}
@@ -492,7 +500,7 @@ static inline int prcmu_enable_dsipll(void)
static inline int prcmu_config_esram0_deep_sleep(u8 state)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_esram0_deep_sleep(state);
else
return db8500_prcmu_config_esram0_deep_sleep(state);
}
@@ -500,7 +508,7 @@ static inline int prcmu_config_esram0_deep_sleep(u8 state)
static inline int prcmu_config_hotdog(u8 threshold)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_hotdog(threshold);
else
return db8500_prcmu_config_hotdog(threshold);
}
@@ -508,7 +516,7 @@ static inline int prcmu_config_hotdog(u8 threshold)
static inline int prcmu_config_hotmon(u8 low, u8 high)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_hotmon(low, high);
else
return db8500_prcmu_config_hotmon(low, high);
}
@@ -516,7 +524,7 @@ static inline int prcmu_config_hotmon(u8 low, u8 high)
static inline int prcmu_start_temp_sense(u16 cycles32k)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_start_temp_sense(cycles32k);
else
return db8500_prcmu_start_temp_sense(cycles32k);
}
@@ -524,7 +532,7 @@ static inline int prcmu_start_temp_sense(u16 cycles32k)
static inline int prcmu_stop_temp_sense(void)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_stop_temp_sense();
else
return db8500_prcmu_stop_temp_sense();
}
@@ -532,7 +540,7 @@ static inline int prcmu_stop_temp_sense(void)
static inline u32 prcmu_read(unsigned int reg)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_read(reg);
else
return db8500_prcmu_read(reg);
}
@@ -540,7 +548,7 @@ static inline u32 prcmu_read(unsigned int reg)
static inline void prcmu_write(unsigned int reg, u32 value)
{
if (cpu_is_u5500())
- return;
+ db5500_prcmu_write(reg, value);
else
db8500_prcmu_write(reg, value);
}
@@ -548,7 +556,7 @@ static inline void prcmu_write(unsigned int reg, u32 value)
static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
{
if (cpu_is_u5500())
- return;
+ db5500_prcmu_write_masked(reg, mask, value);
else
db8500_prcmu_write_masked(reg, mask, value);
}
@@ -556,7 +564,7 @@ static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value)
static inline int prcmu_enable_a9wdog(u8 id)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_enable_a9wdog(id);
else
return db8500_prcmu_enable_a9wdog(id);
}
@@ -564,7 +572,7 @@ static inline int prcmu_enable_a9wdog(u8 id)
static inline int prcmu_disable_a9wdog(u8 id)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_disable_a9wdog(id);
else
return db8500_prcmu_disable_a9wdog(id);
}
@@ -572,7 +580,7 @@ static inline int prcmu_disable_a9wdog(u8 id)
static inline int prcmu_kick_a9wdog(u8 id)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_kick_a9wdog(id);
else
return db8500_prcmu_kick_a9wdog(id);
}
@@ -580,7 +588,7 @@ static inline int prcmu_kick_a9wdog(u8 id)
static inline int prcmu_load_a9wdog(u8 id, u32 timeout)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_load_a9wdog(id, timeout);
else
return db8500_prcmu_load_a9wdog(id, timeout);
}
@@ -588,10 +596,17 @@ static inline int prcmu_load_a9wdog(u8 id, u32 timeout)
static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
{
if (cpu_is_u5500())
- return -EINVAL;
+ return db5500_prcmu_config_a9wdog(num, sleep_auto_off);
else
return db8500_prcmu_config_a9wdog(num, sleep_auto_off);
}
+
+static inline void prcmu_vc(bool enable)
+{
+ if (cpu_is_u8500())
+ db8500_prcmu_vc(enable);
+}
+
#else
static inline void __init prcmu_early_init(void) {}
@@ -689,6 +704,11 @@ static inline u16 prcmu_get_reset_code(void)
return 0;
}
+static inline u32 prcmu_get_reset_status(void)
+{
+ return 0;
+}
+
static inline void prcmu_ac_wake_req(void) {}
static inline void prcmu_ac_sleep_req(void) {}
@@ -756,6 +776,8 @@ static inline void prcmu_write(unsigned int reg, u32 value) {}
static inline void prcmu_write_masked(unsigned int reg, u32 mask, u32 value) {}
+static inline void prcmu_vc(bool enable) {}
+
#endif
static inline void prcmu_set(unsigned int reg, u32 bits)
@@ -866,6 +888,7 @@ int prcmu_qos_add_notifier(int prcmu_qos_class,
struct notifier_block *notifier);
int prcmu_qos_remove_notifier(int prcmu_qos_class,
struct notifier_block *notifier);
+void prcmu_qos_voice_call_override(bool enable);
#else
@@ -909,7 +932,7 @@ static inline int prcmu_qos_remove_notifier(int prcmu_qos_class,
{
return 0;
}
-
+static inline void prcmu_qos_voice_call_override(bool enable) {}
#endif
#endif /* __MACH_PRCMU_H */
diff --git a/include/linux/mfd/stmpe.h b/include/linux/mfd/stmpe.h
index 8516fd1eaab..f8d5b4d5843 100644
--- a/include/linux/mfd/stmpe.h
+++ b/include/linux/mfd/stmpe.h
@@ -117,7 +117,7 @@ struct matrix_keymap_data;
* @no_autorepeat: disable key autorepeat
*/
struct stmpe_keypad_platform_data {
- struct matrix_keymap_data *keymap_data;
+ const struct matrix_keymap_data *keymap_data;
unsigned int debounce_ms;
unsigned int scan_count;
bool no_autorepeat;
diff --git a/include/linux/mfd/tc35892.h b/include/linux/mfd/tc35892.h
new file mode 100644
index 00000000000..8c5385c2191
--- /dev/null
+++ b/include/linux/mfd/tc35892.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License, version 2
+ */
+
+#ifndef __LINUX_MFD_TC35892_H
+#define __LINUX_MFD_TC35892_H
+
+#include <linux/device.h>
+
+#define TC35892_RSTCTRL_IRQRST (1 << 4)
+#define TC35892_RSTCTRL_TIMRST (1 << 3)
+#define TC35892_RSTCTRL_ROTRST (1 << 2)
+#define TC35892_RSTCTRL_KBDRST (1 << 1)
+#define TC35892_RSTCTRL_GPIRST (1 << 0)
+
+
+#define TC35892_MANFCODE 0x80
+#define TC35892_MANFCODE_MAGIC 0x03
+#define TC35892_VERSION 0x81
+#define TC35892_RSTCTRL 0x82
+#define TC35892_EXTRSTN 0x83
+#define TC35892_RSTINTCLR 0x84
+#define TC35892_CLKMODE 0x88
+#define TC35892_CLKCFG 0x89
+#define TC35892_CLKEN 0x8A
+#define TC35892_IRQST 0x91
+
+#define TC35892_DRIVE0_L 0xA0
+#define TC35892_DRIVE0_H 0xA1
+#define TC35892_DRIVE1_L 0xA2
+#define TC35892_DRIVE1_H 0xA3
+#define TC35892_DRIVE2_L 0xA4
+#define TC35892_DRIVE2_H 0XA5
+#define TC35892_DRIVE3 0xA6
+#define TC35892_IOCFG 0xA7
+
+#define TC35892_IOPC0_L 0xAA
+#define TC35892_IOPC0_H 0xAB
+#define TC35892_IOPC1_L 0xAC
+#define TC35892_IOPC1_H 0xAD
+#define TC35892_IOPC2_L 0xAE
+#define TC35892_IOPC2_H 0xAF
+
+#define TC35892_GPIODATA0 0xC0
+#define TC35892_GPIOMASK0 0xC1
+#define TC35892_GPIODATA1 0xC2
+#define TC35892_GPIOMASK1 0xC3
+#define TC35892_GPIODATA2 0xC4
+#define TC35892_GPIOMASK2 0xC5
+#define TC35892_GPIODIR0 0xC6
+#define TC35892_GPIODIR1 0xC7
+#define TC35892_GPIODIR2 0xC8
+#define TC35892_GPIOIS0 0xC9
+#define TC35892_GPIOIS1 0xCA
+#define TC35892_GPIOIS2 0xCB
+#define TC35892_GPIOIBE0 0xCC
+#define TC35892_GPIOIBE1 0xCD
+#define TC35892_GPIOIBE2 0xCE
+#define TC35892_GPIOIEV0 0xCF
+#define TC35892_GPIOIEV1 0xD0
+#define TC35892_GPIOIEV2 0xD1
+#define TC35892_GPIOIE0 0xD2
+#define TC35892_GPIOIE1 0xD3
+#define TC35892_GPIOIE2 0xD4
+#define TC35892_GPIORIS0 0xD6
+#define TC35892_GPIORIS1 0xD7
+#define TC35892_GPIORIS2 0xD8
+#define TC35892_GPIOMIS0 0xD9
+#define TC35892_GPIOMIS1 0xDA
+#define TC35892_GPIOMIS2 0xDB
+#define TC35892_GPIOIC0 0xDC
+#define TC35892_GPIOIC1 0xDD
+#define TC35892_GPIOIC2 0xDE
+#define TC35892_GPIOODM0 0xE0
+#define TC35892_GPIOODE0 0xE1
+#define TC35892_GPIOODM1 0xE2
+#define TC35892_GPIOODE1 0xE3
+#define TC35892_GPIOODM2 0xE4
+#define TC35892_GPIOODE2 0xE5
+
+#define TC35892_GPIOSYNC0 0xE6
+#define TC35892_GPIOSYNC1 0xE7
+#define TC35892_GPIOSYNC2 0xE8
+
+#define TC35892_GPIOWAKE0 0xE9
+#define TC35892_GPIOWAKE1 0xEA
+#define TC35892_GPIOWAKE2 0xEB
+
+#define TC35892_INT_GPIIRQ 0
+#define TC35892_INT_TI0IRQ 1
+#define TC35892_INT_TI1IRQ 2
+#define TC35892_INT_TI2IRQ 3
+#define TC35892_INT_ROTIRQ 5
+#define TC35892_INT_KBDIRQ 6
+#define TC35892_INT_PORIRQ 7
+
+#define TC35892_NR_INTERNAL_IRQS 8
+#define TC35892_INT_GPIO(x) (TC35892_NR_INTERNAL_IRQS + (x))
+
+struct tc35892 {
+ struct mutex lock;
+ struct device *dev;
+ struct i2c_client *i2c;
+
+ int irq_base;
+ int num_gpio;
+ struct tc35892_platform_data *pdata;
+};
+
+extern int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data);
+extern int tc35892_reg_read(struct tc35892 *tc35892, u8 reg);
+extern int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length,
+ u8 *values);
+extern int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length,
+ const u8 *values);
+extern int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val);
+
+/**
+ * struct tc35892_gpio_platform_data - TC35892 GPIO platform data
+ * @gpio_base: first gpio number assigned to TC35892. A maximum of
+ * %TC35892_NR_GPIOS GPIOs will be allocated.
+ * @setup: callback for board-specific initialization
+ * @remove: callback for board-specific teardown
+ */
+struct tc35892_gpio_platform_data {
+ int gpio_base;
+ void (*setup)(struct tc35892 *tc35892, unsigned gpio_base);
+ void (*remove)(struct tc35892 *tc35892, unsigned gpio_base);
+};
+
+/**
+ * struct tc35892_platform_data - TC35892 platform data
+ * @irq_base: base IRQ number. %TC35892_NR_IRQS irqs will be used.
+ * @gpio: GPIO-specific platform data
+ */
+struct tc35892_platform_data {
+ int irq_base;
+ struct tc35892_gpio_platform_data *gpio;
+};
+
+#define TC35892_NR_GPIOS 24
+#define TC35892_NR_IRQS TC35892_INT_GPIO(TC35892_NR_GPIOS)
+
+#endif
diff --git a/include/linux/mfd/tc3589x.h b/include/linux/mfd/tc3589x.h
index 3acb3a8e3af..5c5f37d2e95 100644
--- a/include/linux/mfd/tc3589x.h
+++ b/include/linux/mfd/tc3589x.h
@@ -31,20 +31,43 @@ enum tx3589x_block {
#define TC3589x_EVTCODE_FIFO 0x10
#define TC3589x_KBDMFS 0x8F
-#define TC3589x_IRQST 0x91
-
-#define TC3589x_MANFCODE_MAGIC 0x03
#define TC3589x_MANFCODE 0x80
+#define TC3589x_MANFCODE_MAGIC 0x03
#define TC3589x_VERSION 0x81
-#define TC3589x_IOCFG 0xA7
+#define TC3589x_RSTCTRL 0x82
+#define TC3589x_EXTRSTN 0x83
+#define TC3589x_RSTINTCLR 0x84
#define TC3589x_CLKMODE 0x88
#define TC3589x_CLKCFG 0x89
#define TC3589x_CLKEN 0x8A
+#define TC3589x_IRQST 0x91
-#define TC3589x_RSTCTRL 0x82
-#define TC3589x_EXTRSTN 0x83
-#define TC3589x_RSTINTCLR 0x84
+#define TC3589x_DRIVE0_L 0xA0
+#define TC3589x_DRIVE0_H 0xA1
+#define TC3589x_DRIVE1_L 0xA2
+#define TC3589x_DRIVE1_H 0xA3
+#define TC3589x_DRIVE2_L 0xA4
+#define TC3589x_DRIVE2_H 0XA5
+#define TC3589x_DRIVE3 0xA6
+#define TC3589x_IOCFG 0xA7
+
+#define TC3589x_IOPC0_L 0xAA
+#define TC3589x_IOPC0_H 0xAB
+#define TC3589x_IOPC1_L 0xAC
+#define TC3589x_IOPC1_H 0xAD
+#define TC3589x_IOPC2_L 0xAE
+#define TC3589x_IOPC2_H 0xAF
+
+#define TC3589x_GPIODATA0 0xC0
+#define TC3589x_GPIOMASK0 0xC1
+#define TC3589x_GPIODATA1 0xC2
+#define TC3589x_GPIOMASK1 0xC3
+#define TC3589x_GPIODATA2 0xC4
+#define TC3589x_GPIOMASK2 0xC5
+#define TC3589x_GPIODIR0 0xC6
+#define TC3589x_GPIODIR1 0xC7
+#define TC3589x_GPIODIR2 0xC8
/* Pull up/down configuration registers */
#define TC3589x_IOCFG 0xA7
@@ -75,17 +98,12 @@ enum tx3589x_block {
#define TC3589x_GPIOIC0 0xDC
#define TC3589x_GPIOIC1 0xDD
#define TC3589x_GPIOIC2 0xDE
-
-#define TC3589x_GPIODATA0 0xC0
-#define TC3589x_GPIOMASK0 0xc1
-#define TC3589x_GPIODATA1 0xC2
-#define TC3589x_GPIOMASK1 0xc3
-#define TC3589x_GPIODATA2 0xC4
-#define TC3589x_GPIOMASK2 0xC5
-
-#define TC3589x_GPIODIR0 0xC6
-#define TC3589x_GPIODIR1 0xC7
-#define TC3589x_GPIODIR2 0xC8
+#define TC3589x_GPIOODM0 0xE0
+#define TC3589x_GPIOODE0 0xE1
+#define TC3589x_GPIOODM1 0xE2
+#define TC3589x_GPIOODE1 0xE3
+#define TC3589x_GPIOODM2 0xE4
+#define TC3589x_GPIOODE2 0xE5
#define TC3589x_GPIOSYNC0 0xE6
#define TC3589x_GPIOSYNC1 0xE7
@@ -95,13 +113,6 @@ enum tx3589x_block {
#define TC3589x_GPIOWAKE1 0xEA
#define TC3589x_GPIOWAKE2 0xEB
-#define TC3589x_GPIOODM0 0xE0
-#define TC3589x_GPIOODE0 0xE1
-#define TC3589x_GPIOODM1 0xE2
-#define TC3589x_GPIOODE1 0xE3
-#define TC3589x_GPIOODM2 0xE4
-#define TC3589x_GPIOODE2 0xE5
-
#define TC3589x_INT_GPIIRQ 0
#define TC3589x_INT_TI0IRQ 1
#define TC3589x_INT_TI1IRQ 2
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index cbde4b7e675..015eb334ce5 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -292,6 +292,11 @@ struct mmc_host {
int detect_change; /* card detect flag */
struct mmc_hotplug hotplug;
+ struct delayed_work resume; /* deferred resume work */
+ unsigned int pm_state; /* used for deferred resume */
+#define MMC_HOST_DEFERRED_RESUME (1 << 0)
+#define MMC_HOST_NEEDS_RESUME (1 << 1)
+
const struct mmc_bus_ops *bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */
@@ -340,6 +345,7 @@ static inline void *mmc_priv(struct mmc_host *host)
extern int mmc_suspend_host(struct mmc_host *);
extern int mmc_resume_host(struct mmc_host *);
+extern void mmc_resume_host_sync(struct mmc_host *);
extern int mmc_power_save_host(struct mmc_host *host);
extern int mmc_power_restore_host(struct mmc_host *host);
@@ -429,4 +435,15 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
return host->ios.clock;
}
#endif
+
+static inline int mmc_host_deferred_resume(struct mmc_host *host)
+{
+ return host->pm_state & MMC_HOST_DEFERRED_RESUME;
+}
+
+static inline int mmc_host_needs_resume(struct mmc_host *host)
+{
+ return host->pm_state & MMC_HOST_NEEDS_RESUME;
+}
+
#endif /* LINUX_MMC_HOST_H */
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index 7c775751392..792ed4a9240 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -28,4 +28,14 @@ int pwm_enable(struct pwm_device *pwm);
*/
void pwm_disable(struct pwm_device *pwm);
+/*
+ * pwm_config_blink - configure PWM blinking
+ */
+int pwm_config_blink(struct pwm_device *pwm, int duty_ns, int period_ns);
+
+/*
+ * pwm_blink_ctrl - Enable/Disable PWM blinking
+ */
+int pwm_blink_ctrl(struct pwm_device *pwm , int enable);
+
#endif /* __LINUX_PWM_H */
diff --git a/include/linux/regulator/ab5500.h b/include/linux/regulator/ab5500.h
new file mode 100644
index 00000000000..04673d3494f
--- /dev/null
+++ b/include/linux/regulator/ab5500.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef __LINUX_REGULATOR_AB5500_H
+#define __LINUX_REGULATOR_AB5500_H
+
+enum ab5500_regulator_id {
+ AB5500_LDO_G,
+ AB5500_LDO_H,
+ AB5500_LDO_K,
+ AB5500_LDO_L,
+ AB5500_LDO_VDIGMIC,
+ AB5500_LDO_SIM,
+ AB5500_BIAS1,
+ AB5500_BIAS2,
+ AB5500_NUM_REGULATORS,
+};
+
+struct regulator_init_data;
+
+struct ab5500_regulator_data {
+ bool off_is_lowpower;
+};
+
+struct ab5500_regulator_platform_data {
+ struct regulator_init_data *regulator;
+ struct ab5500_regulator_data *data;
+ int num_regulator;
+};
+
+#endif
diff --git a/include/linux/regulator/ab8500-debug.h b/include/linux/regulator/ab8500-debug.h
new file mode 100644
index 00000000000..01655fc7fc1
--- /dev/null
+++ b/include/linux/regulator/ab8500-debug.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Authors: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ */
+
+#ifndef __LINUX_MFD_AB8500_REGULATOR_DEBUG_H
+#define __LINUX_MFD_AB8500_REGULATOR_DEBUG_H
+
+#ifdef CONFIG_REGULATOR_AB8500_DEBUG
+/* AB8500 debug force/restore functions */
+void ab8500_regulator_debug_force(void);
+void ab8500_regulator_debug_restore(void);
+#else
+static inline void ab8500_regulator_debug_force(void) {}
+static inline void ab8500_regulator_debug_restore(void) {}
+#endif
+
+#endif
diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h
index 7bd73bbdfd1..9f50d640ead 100644
--- a/include/linux/regulator/ab8500.h
+++ b/include/linux/regulator/ab8500.h
@@ -10,6 +10,8 @@
#ifndef __LINUX_MFD_AB8500_REGULATOR_H
#define __LINUX_MFD_AB8500_REGULATOR_H
+#include <linux/platform_device.h>
+
/* AB8500 regulators */
enum ab8500_regulator_id {
AB8500_LDO_AUX1,
@@ -17,12 +19,13 @@ enum ab8500_regulator_id {
AB8500_LDO_AUX3,
AB8500_LDO_INTCORE,
AB8500_LDO_TVOUT,
- AB8500_LDO_USB,
AB8500_LDO_AUDIO,
AB8500_LDO_ANAMIC1,
AB8500_LDO_ANAMIC2,
AB8500_LDO_DMIC,
AB8500_LDO_ANA,
+ AB8500_SYSCLKREQ_2,
+ AB8500_SYSCLKREQ_4,
AB8500_NUM_REGULATORS,
};
@@ -48,13 +51,15 @@ enum ab9540_regulator_id {
/* AB8500 and AB9540 register initialization */
struct ab8500_regulator_reg_init {
int id;
+ u8 mask;
u8 value;
};
-#define INIT_REGULATOR_REGISTER(_id, _value) \
- { \
- .id = _id, \
- .value = _value, \
+#define INIT_REGULATOR_REGISTER(_id, _mask, _value) \
+ { \
+ .id = _id, \
+ .mask = _mask, \
+ .value = _value, \
}
/* AB8500 registers */
@@ -86,7 +91,6 @@ enum ab8500_regulator_reg {
AB8500_REGUCTRL2SPARE,
AB8500_REGUCTRLDISCH,
AB8500_REGUCTRLDISCH2,
- AB8500_VSMPS1SEL1,
AB8500_NUM_REGULATOR_REGISTERS,
};
@@ -139,4 +143,35 @@ enum ab9540_regulator_reg {
AB9540_NUM_REGULATOR_REGISTERS,
};
+/* AB8500 external regulators */
+struct ab8500_ext_regulator_cfg {
+ bool hwreq; /* requires hw mode or high power mode */
+};
+
+enum ab8500_ext_regulator_id {
+ AB8500_EXT_SUPPLY1,
+ AB8500_EXT_SUPPLY2,
+ AB8500_EXT_SUPPLY3,
+ AB8500_NUM_EXT_REGULATORS,
+};
+
+/* AB8500 regulator platform data */
+struct ab8500_regulator_platform_data {
+ int num_reg_init;
+ struct ab8500_regulator_reg_init *reg_init;
+ int num_regulator;
+ struct regulator_init_data *regulator;
+ int num_ext_regulator;
+ struct regulator_init_data *ext_regulator;
+};
+
+/* AB8500 external regulator functions (internal) */
+#ifdef CONFIG_REGULATOR_AB8500_EXT
+__devinit int ab8500_ext_regulator_init(struct platform_device *pdev);
+__devexit int ab8500_ext_regulator_exit(struct platform_device *pdev);
+#else
+inline __devinit int ab8500_ext_regulator_init(struct platform_device *pdev) {}
+inline __devexit int ab8500_ext_regulator_exit(struct platform_device *pdev) {}
+#endif
+
#endif
diff --git a/include/linux/regulator/db5500-prcmu.h b/include/linux/regulator/db5500-prcmu.h
new file mode 100644
index 00000000000..fee68795867
--- /dev/null
+++ b/include/linux/regulator/db5500-prcmu.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ *
+ * Author: Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
+ *
+ * Interface to power domain regulators on DB5500
+ */
+
+#ifndef __DB5500_REGULATOR_H__
+#define __DB5500_REGULATOR_H__
+
+#include <linux/regulator/dbx500-prcmu.h>
+
+/* Number of DB5500 regulators and regulator enumeration */
+enum db5500_regulator_id {
+ DB5500_REGULATOR_VAPE,
+ DB5500_REGULATOR_SWITCH_SGA,
+ DB5500_REGULATOR_SWITCH_HVA,
+ DB5500_REGULATOR_SWITCH_SIA,
+ DB5500_REGULATOR_SWITCH_DISP,
+ DB5500_REGULATOR_SWITCH_ESRAM12,
+ DB5500_NUM_REGULATORS
+};
+
+#endif
diff --git a/include/linux/regulator/dbx500-prcmu.h b/include/linux/regulator/dbx500-prcmu.h
new file mode 100644
index 00000000000..2ecb34c56aa
--- /dev/null
+++ b/include/linux/regulator/dbx500-prcmu.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) ST Ericsson SA 2011
+ *
+ * License Terms: GNU General Public License v2
+ *
+ */
+#ifndef __LINUX_REGULATOR_DBX500_H
+#define __LINUX_REGULATOR_DBX500_H
+
+struct ux500_regulator;
+
+#ifdef CONFIG_REGULATOR
+/*
+ * NOTE! The device will be connected to the correct regulator by this
+ * new framework. A list with connections will match up dev_name(dev)
+ * to the specific regulator. This follows the same principle as the
+ * normal regulator framework.
+ *
+ * This framework shall only be used in special cases when a regulator
+ * has to be enabled/disabled in atomic context.
+ */
+
+/**
+ * ux500_regulator_get()
+ *
+ * @dev: Drivers device struct
+ *
+ * Returns a ux500_regulator struct. Shall be used as argument for
+ * ux500_regulator_atomic_enable/disable calls.
+ * Return ERR_PTR(-EINVAL) upon no matching regulator found.
+ */
+struct ux500_regulator *__must_check ux500_regulator_get(struct device *dev);
+
+/**
+ * ux500_regulator_atomic_enable()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ *
+ * The enable/disable functions keep an internal counter, so every
+ * enable must be paired with an disable in order to turn off regulator.
+ */
+int ux500_regulator_atomic_enable(struct ux500_regulator *regulator);
+
+/**
+ * ux500_regulator_atomic_disable()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ *
+ */
+int ux500_regulator_atomic_disable(struct ux500_regulator *regulator);
+
+/**
+ * ux500_regulator_put()
+ *
+ * @regulator: Regulator handle, provided from ux500_regulator_get.
+ */
+void ux500_regulator_put(struct ux500_regulator *regulator);
+
+#else
+
+static inline struct ux500_regulator *__must_check
+ux500_regulator_get(struct device *dev)
+{
+ return ERR_PTR(-EINVAL);
+}
+
+static inline int
+ux500_regulator_atomic_enable(struct ux500_regulator *regulator)
+{
+ return -EINVAL;
+}
+
+static inline int
+ux500_regulator_atomic_disable(struct ux500_regulator *regulator)
+{
+ return -EINVAL;
+}
+
+static inline void ux500_regulator_put(struct ux500_regulator *regulator)
+{
+}
+#endif /* CONFIG_REGULATOR */
+
+#ifdef CONFIG_REGULATOR_DEBUG
+void ux500_regulator_suspend_debug(void);
+void ux500_regulator_resume_debug(void);
+#else
+static inline void ux500_regulator_suspend_debug(void) { }
+static inline void ux500_regulator_resume_debug(void) { }
+#endif
+
+#endif
diff --git a/include/linux/ste_timed_vibra.h b/include/linux/ste_timed_vibra.h
new file mode 100644
index 00000000000..24edf89aade
--- /dev/null
+++ b/include/linux/ste_timed_vibra.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Grzegorz Sygieda <grzegorz.sygieda@tieto.com> for ST-Ericsson.
+ * Krzysztof Antonowicz <krzysztof.antonowicz@tieto.com> for ST-Ericsson.
+ *
+ * License Terms: GNU General Public License v2
+ */
+
+#ifndef _STE_TIMED_VIBRA_H_
+#define _STE_TIMED_VIBRA_H
+
+/* Vibrator states */
+enum ste_timed_vibra_states {
+ STE_VIBRA_IDLE = 0,
+ STE_VIBRA_BOOST,
+ STE_VIBRA_ON,
+ STE_VIBRA_OFF,
+};
+
+typedef void (*timed_vibra_control_fp)(
+ unsigned char speed_left_pos,
+ unsigned char speed_left_neg,
+ unsigned char speed_right_pos,
+ unsigned char speed_right_neg);
+
+/*
+ * Vibrator platform data structure
+ * For details check ste_timed_vibra docbook
+ */
+struct ste_timed_vibra_platform_data {
+ bool is_linear_vibra;
+ unsigned int boost_level; /* p1 */
+ unsigned int boost_time; /* p2 */
+ unsigned int on_level; /* p3 */
+ unsigned int off_level; /* p4 */
+ unsigned int off_time; /* p5 */
+ timed_vibra_control_fp timed_vibra_control;
+};
+
+#endif /* _STE_TIMED_VIBRA_H_ */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 73b68d1f2cb..3645e63e029 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -331,6 +331,10 @@ struct usb_bus {
u8 otg_port; /* 0, or number of OTG/HNP port */
unsigned is_b_host:1; /* true during some HNP roleswitches */
unsigned b_hnp_enable:1; /* OTG: did A-Host enable HNP? */
+#ifdef CONFIG_USB_OTG
+ unsigned hnp_support:1; /* OTG: HNP is supported on OTG port */
+ struct delayed_work hnp_polling;/* OTG: HNP polling work */
+#endif
unsigned sg_tablesize; /* 0 or largest number of sg list entries */
int devnum_next; /* Next open device number in
@@ -372,7 +376,15 @@ struct usb_bus {
* limit. Because the arrays need to add a bit for hub status data, we
* do 31, so plus one evens out to four bytes.
*/
+
+#ifdef CONFIG_ARCH_U8500
+/**
+* On U8500 platform we support 16 ports only
+*/
+#define USB_MAXCHILDREN (16)
+#else
#define USB_MAXCHILDREN (31)
+#endif
struct usb_tt;
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index af21f311591..1ed2af81975 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -152,6 +152,12 @@
#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */
+#ifdef CONFIG_USB_OTG
+/* OTG 2.0 spec 6.2 and 6.3 sections */
+#define OTG_STATUS_SELECTOR 0xF000
+#define THOST_REQ_POLL 1500 /* 1000 - 2000 msec */
+#define HOST_REQUEST_FLAG 0
+#endif
/* Bit array elements as returned by the USB_REQ_GET_STATUS request. */
#define USB_DEV_STAT_U1_ENABLED 2 /* transition into U1 state */
#define USB_DEV_STAT_U2_ENABLED 3 /* transition into U2 state */
@@ -651,8 +657,10 @@ struct usb_qualifier_descriptor {
struct usb_otg_descriptor {
__u8 bLength;
__u8 bDescriptorType;
-
- __u8 bmAttributes; /* support for HNP, SRP, etc */
+#ifdef CONFIG_USB_OTG
+ __u8 bmAttributes; /* support for HNP, SRP, ADP etc */
+ __le16 bcdOTG;
+#endif
} __attribute__ ((packed));
/* from usb_otg_descriptor.bmAttributes */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 9517466abab..164c9a03052 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -476,6 +476,8 @@ struct usb_gadget_ops {
int (*start)(struct usb_gadget_driver *,
int (*bind)(struct usb_gadget *));
int (*stop)(struct usb_gadget_driver *);
+ struct usb_ep* (*configure_ep)(struct usb_gadget *, u8 type,
+ struct usb_endpoint_descriptor *);
};
/**
@@ -534,6 +536,11 @@ struct usb_gadget {
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
+#ifdef CONFIG_USB_OTG_20
+ unsigned host_request:1;
+ unsigned otg_hnp_reqd:1;
+ unsigned otg_srp_reqd:1;
+#endif
const char *name;
struct device dev;
};
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 38ab3f46346..3583bd09d38 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -41,6 +41,11 @@ enum usb_phy_events {
USB_EVENT_ID, /* id was grounded */
USB_EVENT_CHARGER, /* usb dedicated charger */
USB_EVENT_ENUMERATED, /* gadget driver enumerated */
+ USB_EVENT_RIDA,
+ USB_EVENT_RIDB,
+ USB_EVENT_RIDC,
+ USB_EVENT_PREPARE, /* restore context and clocks */
+ USB_EVENT_CLEAN, /* disable clocks */
};
struct usb_phy;
diff --git a/include/trace/Kbuild b/include/trace/Kbuild
new file mode 100644
index 00000000000..7e8b704d610
--- /dev/null
+++ b/include/trace/Kbuild
@@ -0,0 +1 @@
+header-y += stm.h
diff --git a/include/trace/stm.h b/include/trace/stm.h
new file mode 100644
index 00000000000..de3ed1dc381
--- /dev/null
+++ b/include/trace/stm.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * ST-Ericsson STM Trace driver
+ *
+ * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
+ * Philippe Langlais <philippe.langlais@stericsson.com> for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2.
+ */
+
+#ifndef STM_H
+#define STM_H
+
+#include <linux/types.h>
+
+#define STM_DEV_NAME "stm"
+
+/* One single channel mapping */
+struct stm_channel {
+ union {
+ __u8 no_stamp8;
+ __u16 no_stamp16;
+ __u32 no_stamp32;
+ __u64 no_stamp64;
+ };
+ union {
+ __u8 stamp8;
+ __u16 stamp16;
+ __u32 stamp32;
+ __u64 stamp64;
+ };
+};
+
+/* Possible trace modes */
+#define STM_SW_LOSSLESS 0 /* Software mode: lossless data but intrusive */
+#define STM_HW_LOSSY 1 /* Hardware mode: lossy data but less intrusive */
+
+/* Possible clock setting */
+enum clock_div {
+ STM_CLOCK_DIV2 = 0,
+ STM_CLOCK_DIV4,
+ STM_CLOCK_DIV6,
+ STM_CLOCK_DIV8,
+ STM_CLOCK_DIV10,
+ STM_CLOCK_DIV12,
+ STM_CLOCK_DIV14,
+ STM_CLOCK_DIV16,
+};
+
+/* ioctl commands */
+#define STM_CONNECTION _IOW('t', 0, enum stm_connection_type)
+#define STM_DISABLE _IO('t', 1)
+#define STM_GET_NB_MAX_CHANNELS _IOR('t', 2, int)
+#define STM_GET_NB_FREE_CHANNELS _IOR('t', 3, int)
+#define STM_GET_CHANNEL_NO _IOR('t', 4, int)
+#define STM_SET_CLOCK_DIV _IOW('t', 5, enum clock_div)
+#define STM_GET_CTRL_REG _IOR('t', 6, int)
+#define STM_ENABLE_SRC _IOWR('t', 7, int)
+#define STM_GET_FREE_CHANNEL _IOW('t', 8, int)
+#define STM_RELEASE_CHANNEL _IOW('t', 9, int)
+#define STM_SET_MODE _IOWR('t', 10, int)
+#define STM_GET_MODE _IOR('t', 11, int)
+
+enum stm_connection_type {
+ STM_DISCONNECT = 0,
+ STM_DEFAULT_CONNECTION = 1,
+ STM_STE_MODEM_ON_MIPI34_NONE_ON_MIPI60 = 2,
+ STM_STE_APE_ON_MIPI34_NONE_ON_MIPI60 = 3,
+ STM_STE_MODEM_ON_MIPI34_APE_ON_MIPI60 = 4,
+ STM_STE_MODEM_ON_MICROSD = 5,
+ STM_STE_APE_ON_MICROSD = 6,
+ STM_STE_INVALID_CONNECTION = 0xff
+};
+
+#ifdef __KERNEL__
+
+struct stm_platform_data {
+ u32 regs_phys_base;
+ u32 channels_phys_base;
+ u32 id_mask;
+ u32 masters_enabled;
+ const s16 *channels_reserved;
+ int channels_reserved_sz;
+ int (*stm_connection)(enum stm_connection_type);
+};
+
+/* Channels base address */
+extern volatile struct stm_channel __iomem *stm_channels;
+
+/* Provides stm_trace_XX() and stm_tracet_XX() trace API */
+#define DEFLLTFUN(size) \
+static inline void stm_trace_##size(int channel, __u##size data) \
+{ \
+ stm_channels[channel].no_stamp##size = data; \
+} \
+static inline void stm_tracet_##size(int channel, __u##size data) \
+{ \
+ stm_channels[channel].stamp##size = data; \
+} \
+
+DEFLLTFUN(8);
+DEFLLTFUN(16);
+DEFLLTFUN(32);
+DEFLLTFUN(64);
+
+/*
+ * Trace a buffer on a given channel
+ * with auto time stamping on the last byte(s) only
+ */
+int stm_trace_buffer_onchannel(int channel, const void *data, size_t length);
+/*
+ * Trace a buffer on a dynamically allocated channel
+ * with auto time stamping on the last byte(s) only
+ * Dynamic channel are allocated in the 128 highest channels
+ */
+int stm_trace_buffer(const void *data, size_t length);
+
+/* printk equivalent for STM */
+int stm_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+#if defined(CONFIG_STM_PRINTK)
+#define stm_dup_printk(buf, length) \
+ stm_trace_buffer_onchannel(CONFIG_STM_PRINTK_CHANNEL, buf, length)
+
+#else
+static inline int stm_dup_printk(char *buf, size_t size)
+{
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_STM_TRACE_PRINTK)
+static inline int stm_trace_printk_buf(
+ unsigned long ip, const char *buf, size_t size)
+{
+ stm_trace_32(CONFIG_STM_TRACE_PRINTK_CHANNEL, ip);
+ return stm_trace_buffer_onchannel(CONFIG_STM_TRACE_PRINTK_CHANNEL,
+ buf, size);
+}
+
+static inline int stm_trace_bprintk_buf(
+ unsigned long ip, const char *fmt, const void *buf, size_t size)
+{
+ stm_trace_64(CONFIG_STM_TRACE_BPRINTK_CHANNEL, ((u64)ip<<32)+(u32)fmt);
+ return stm_trace_buffer_onchannel(CONFIG_STM_TRACE_PRINTK_CHANNEL,
+ buf, size);
+}
+#else
+static inline int stm_trace_printk_buf(
+ unsigned long ip, const char *buf, size_t size)
+{
+ return 0;
+}
+
+static inline int stm_trace_bprintk_buf(
+ unsigned long ip, const char *fmt, const void *buf, size_t size)
+{
+ return 0;
+}
+#endif
+
+#if defined(CONFIG_STM_FTRACE)
+static inline void stm_ftrace(unsigned long ip, unsigned long parent_ip)
+{
+ stm_tracet_64(CONFIG_STM_FTRACE_CHANNEL, (((__u64)ip)<<32) + parent_ip);
+}
+#else
+static inline void stm_ftrace(unsigned long ip, unsigned long parent_ip)
+{
+}
+#endif
+
+#if defined(CONFIG_STM_CTX_SWITCH)
+static inline void stm_sched_switch(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+ stm_trace_64(CONFIG_STM_CTX_SWITCH_CHANNEL,
+ (((__u64)prev_pid)<<32) + next_pid);
+ stm_tracet_64(CONFIG_STM_CTX_SWITCH_CHANNEL, (((__u64)next_cpu)<<32)
+ + (prev_prio<<24) + (prev_state<<16)
+ + (next_prio<<8) + next_state);
+}
+#else
+static inline void stm_sched_switch(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+}
+#endif
+
+#if defined(CONFIG_STM_WAKEUP)
+static inline void stm_sched_wakeup(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+ stm_trace_64(CONFIG_STM_WAKEUP_CHANNEL,
+ (((__u64)prev_pid)<<32) + next_pid);
+ stm_tracet_64(CONFIG_STM_WAKEUP_CHANNEL, (((__u64)next_cpu)<<32)
+ + (prev_prio<<24) + (prev_state<<16)
+ + (next_prio<<8) + next_state);
+}
+#else
+static inline void stm_sched_wakeup(u32 prev_pid, u8 prev_prio, u8 prev_state,
+ u32 next_pid, u8 next_prio, u8 next_state, u32 next_cpu)
+{
+}
+#endif
+
+#if defined(CONFIG_STM_STACK_TRACE)
+static inline void stm_stack_trace(unsigned long *callers)
+{
+ while (*(callers + 1) != ULONG_MAX) {
+ stm_trace_32(CONFIG_STM_STACK_TRACE_CHANNEL, *callers++);
+ }
+ /* Time stamp the latest */
+ stm_tracet_32(CONFIG_STM_STACK_TRACE_CHANNEL, *callers);
+}
+#else
+static inline void stm_stack_trace(unsigned long *callers)
+{
+}
+#endif
+
+/* Alloc/Free STM channel */
+int stm_alloc_channel(int offset);
+void stm_free_channel(int channel);
+
+#endif /* __KERNEL__ */
+
+#endif /* STM_H */
diff --git a/init/Kconfig b/init/Kconfig
index 6cfd71d0646..c2786394546 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1311,6 +1311,15 @@ config PROFILING
Say Y here to enable the extended profiling support mechanisms used
by profilers such as OProfile.
+config BOOTTIME
+ bool "Boot time measurments"
+ default n
+ help
+ Adds sysfs entries (boottime/) with start-up timing information.
+ If CONFIG_DEBUG_FS is enabled, detailed information about the
+ boot time, including system load during boot can be extraced.
+ This information can be visualised with help of the bootgraph script.
+
#
# Place an empty function call at each tracepoint site. Can be
# dynamically changed for a probe function.
diff --git a/init/Makefile b/init/Makefile
index 0bf677aa087..6b77be3855f 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -9,6 +9,7 @@ else
obj-$(CONFIG_BLK_DEV_INITRD) += initramfs.o
endif
obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
+obj-$(CONFIG_BOOTTIME) += boottime.o
mounts-y := do_mounts.o
mounts-$(CONFIG_BLK_DEV_RAM) += do_mounts_rd.o
diff --git a/init/boottime.c b/init/boottime.c
new file mode 100644
index 00000000000..ff0a8f0381b
--- /dev/null
+++ b/init/boottime.c
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009-2010
+ *
+ * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ *
+ * boottime is a tool for collecting start-up timing
+ * information and can together with boot loader support
+ * display a total system start-up time.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/spinlock.h>
+#include <linux/boottime.h>
+#include <linux/kernel_stat.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+/*
+ * BOOTTIME_MAX_NAME_LEN is defined in arch/arm/include/asm/setup.h to 64.
+ * No crisis if they don't match.
+ */
+#ifndef BOOTTIME_MAX_NAME_LEN
+#define BOOTTIME_MAX_NAME_LEN 64
+#endif
+
+/*
+ * We have a few static entries, since it is good to have measure points
+ * before the system is up and running properly
+ */
+#define NUM_STATIC_BOOTTIME_ENTRIES 32
+
+struct boottime_list {
+ struct list_head list;
+ char name[BOOTTIME_MAX_NAME_LEN];
+ /* Time in us since power on, possible including boot loader. */
+ unsigned long time;
+ bool cpu_load;
+ struct kernel_cpustat cpu_usage[NR_CPUS];
+};
+
+enum boottime_filter_type {
+ BOOTTIME_FILTER_OUT_ZERO,
+ BOOTTIME_FILTER_OUT_LESS_100,
+ BOOTTIME_FILTER_NOTHING,
+};
+
+enum boottime_symbolic_print {
+ BOOTTIME_SYMBOLIC_PRINT,
+ BOOTTIME_NORMAL_PRINT,
+};
+
+enum boottime_cpu_load {
+ BOOTTIME_CPU_LOAD,
+ BOOTTIME_NO_CPU_LOAD,
+};
+
+static LIST_HEAD(boottime_list);
+static __initdata DEFINE_SPINLOCK(boottime_list_lock);
+static __initdata struct boottime_timer boottime_timer;
+static __initdata int num_const_boottime_list;
+static struct boottime_list const_boottime_list[NUM_STATIC_BOOTTIME_ENTRIES];
+static unsigned long time_kernel_done;
+static unsigned long time_bootloader_done;
+static __initdata bool system_up;
+static bool boottime_done;
+
+int __attribute__((weak)) boottime_arch_startup(void)
+{
+ return 0;
+}
+
+int __attribute__((weak)) boottime_bootloader_idle(void)
+{
+ return 0;
+}
+
+static void __init boottime_mark_core(char *name,
+ unsigned long time,
+ enum boottime_symbolic_print symbolic,
+ enum boottime_cpu_load cpu_load)
+{
+ struct boottime_list *b;
+ unsigned long flags = 0;
+ int i;
+
+ if (system_up) {
+ b = kmalloc(sizeof(struct boottime_list), GFP_KERNEL);
+ if (!b) {
+ printk(KERN_ERR
+ "boottime: failed to allocate memory!\n");
+ return;
+ }
+
+ } else {
+ if (num_const_boottime_list < NUM_STATIC_BOOTTIME_ENTRIES) {
+ b = &const_boottime_list[num_const_boottime_list];
+ num_const_boottime_list++;
+ } else {
+ printk(KERN_ERR
+ "boottime: too many early measure points!\n");
+ return;
+ }
+ }
+
+ INIT_LIST_HEAD(&b->list);
+
+ if (symbolic == BOOTTIME_SYMBOLIC_PRINT)
+ snprintf(b->name, BOOTTIME_MAX_NAME_LEN, "%pF", name);
+ else
+ strncpy(b->name, name, BOOTTIME_MAX_NAME_LEN);
+
+ b->name[BOOTTIME_MAX_NAME_LEN - 1] = '\0';
+ b->time = time;
+ b->cpu_load = cpu_load;
+
+ if (cpu_load == BOOTTIME_CPU_LOAD && system_up)
+ for_each_possible_cpu(i) {
+ b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] =
+ kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM];
+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] =
+ kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] =
+ kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT];
+ b->cpu_usage[i].cpustat[CPUTIME_IRQ] =
+ kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
+ /*
+ * TODO: Make sure that user, nice, softirq, steal
+ * and guest are not used during boot
+ */
+ }
+ else
+ b->cpu_load = BOOTTIME_NO_CPU_LOAD;
+
+ if (system_up) {
+ spin_lock_irqsave(&boottime_list_lock, flags);
+ list_add(&b->list, &boottime_list);
+ spin_unlock_irqrestore(&boottime_list_lock, flags);
+ } else {
+ list_add(&b->list, &boottime_list);
+ }
+}
+
+void __init boottime_mark_wtime(char *name, unsigned long time)
+{
+ boottime_mark_core(name, time,
+ BOOTTIME_NORMAL_PRINT,
+ BOOTTIME_NO_CPU_LOAD);
+}
+
+void __ref boottime_mark_symbolic(void *name)
+{
+
+ if (boottime_done)
+ return;
+
+ if (boottime_timer.get_time)
+ boottime_mark_core((char *) name,
+ boottime_timer.get_time(),
+ BOOTTIME_SYMBOLIC_PRINT,
+ BOOTTIME_CPU_LOAD);
+}
+
+void __init boottime_mark(char *name)
+{
+ if (boottime_timer.get_time)
+ boottime_mark_core(name,
+ boottime_timer.get_time(),
+ BOOTTIME_NORMAL_PRINT,
+ BOOTTIME_CPU_LOAD);
+}
+
+void __init boottime_activate(struct boottime_timer *bt)
+{
+ struct boottime_list *b;
+ int res = 0;
+ unsigned long flags;
+
+ if (bt == NULL) {
+ printk(KERN_ERR
+ "boottime: error: bad configured\n");
+ return;
+ }
+
+ if (bt->get_time == NULL) {
+ printk(KERN_ERR
+ "boottime: error: you must provide a get_time() function\n");
+ return;
+ }
+ memcpy(&boottime_timer, bt, sizeof(struct boottime_timer));
+
+ if (boottime_timer.init)
+ res = boottime_timer.init();
+
+ if (res) {
+ printk(KERN_ERR "boottime: initialization failed\n");
+ return;
+ }
+
+ if (boottime_arch_startup())
+ printk(KERN_ERR
+ "boottime: arch specfic initialization failed\n");
+
+ spin_lock_irqsave(&boottime_list_lock, flags);
+
+ if (!list_empty(&boottime_list)) {
+
+ b = list_first_entry(&boottime_list, struct boottime_list,
+ list);
+ if (b)
+ time_bootloader_done = b->time;
+ }
+
+ spin_unlock_irqrestore(&boottime_list_lock, flags);
+}
+
+void __init boottime_system_up(void)
+{
+ system_up = true;
+}
+
+void __init boottime_deactivate(void)
+{
+ struct boottime_list *b;
+ unsigned long flags;
+
+ boottime_mark("execute_init+0x0/0x0");
+
+ boottime_done = true;
+
+ spin_lock_irqsave(&boottime_list_lock, flags);
+ b = list_first_entry(&boottime_list, struct boottime_list, list);
+ spin_unlock_irqrestore(&boottime_list_lock, flags);
+
+ time_kernel_done = b->time;
+
+ if (boottime_timer.finalize)
+ boottime_timer.finalize();
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void boottime_debugfs_load(struct seq_file *s,
+ struct boottime_list *b,
+ struct boottime_list *p)
+{
+ int i;
+ unsigned long total_p, total_b;
+ unsigned long system_total, idle_total, irq_total, iowait_total;
+ unsigned long system_load, idle_load, irq_load, iowait_load;
+
+ for_each_possible_cpu(i) {
+ total_b = (b->cpu_usage[i].cpustat[CPUTIME_SYSTEM] +
+ b->cpu_usage[i].cpustat[CPUTIME_IDLE] +
+ b->cpu_usage[i].cpustat[CPUTIME_IOWAIT] +
+ b->cpu_usage[i].cpustat[CPUTIME_IRQ]);
+
+ total_p = (p->cpu_usage[i].cpustat[CPUTIME_SYSTEM] +
+ p->cpu_usage[i].cpustat[CPUTIME_IDLE] +
+ p->cpu_usage[i].cpustat[CPUTIME_IOWAIT] +
+ p->cpu_usage[i].cpustat[CPUTIME_IRQ]);
+
+ if (total_b == total_p)
+ continue;
+
+ system_total = b->cpu_usage[i].cpustat[CPUTIME_SYSTEM]
+ - p->cpu_usage[i].cpustat[CPUTIME_SYSTEM];
+ idle_total = b->cpu_usage[i].cpustat[CPUTIME_IDLE]
+ - p->cpu_usage[i].cpustat[CPUTIME_IDLE];
+ irq_total = b->cpu_usage[i].cpustat[CPUTIME_IRQ]
+ - p->cpu_usage[i].cpustat[CPUTIME_IRQ];
+ iowait_total = b->cpu_usage[i].cpustat[CPUTIME_IOWAIT]
+ - p->cpu_usage[i].cpustat[CPUTIME_IOWAIT];
+
+ system_load = (100 * system_total / (total_b - total_p));
+ idle_load = (100 * idle_total / (total_b - total_p));
+ irq_load = (100 * irq_total / (total_b - total_p));
+ iowait_load = (100 * iowait_total / (total_b - total_p));
+
+ seq_printf(s,
+ " cpu%d system: %lu%% idle: %lu%% iowait: %lu%% irq: %lu%%",
+ i,
+ system_load,
+ idle_load,
+ iowait_load,
+ irq_load);
+ }
+ seq_printf(s, "\n");
+}
+
+static void boottime_debugfs_print(struct seq_file *s,
+ struct boottime_list *b,
+ struct boottime_list *p)
+{
+ seq_printf(s, "[%5lu.%06lu] calling %s\n",
+ p->time / 1000000,
+ (p->time % 1000000),
+ p->name);
+ seq_printf(s, "[%5lu.%06lu] initcall %s returned 0 after %ld msecs.",
+ b->time / 1000000,
+ (b->time % 1000000),
+ p->name, (b->time - p->time) / 1000);
+
+ if (p->cpu_load == BOOTTIME_NO_CPU_LOAD ||
+ b->cpu_load == BOOTTIME_NO_CPU_LOAD) {
+ seq_printf(s, "\n");
+ return;
+ }
+
+ boottime_debugfs_load(s, b, p);
+}
+
+static int boottime_debugfs_bootgraph_show(struct seq_file *s, void *iter)
+{
+ struct boottime_list *b, *p = NULL, *old_p = NULL;
+ enum boottime_filter_type filter = (int)s->private;
+
+ list_for_each_entry_reverse(b, &boottime_list, list) {
+ if (p) {
+ if (!(filter == BOOTTIME_FILTER_OUT_ZERO &&
+ (b->time - p->time) / 1000 == 0)
+ && !(filter == BOOTTIME_FILTER_OUT_LESS_100 &&
+ (b->time - p->time) < 100 * 1000))
+ boottime_debugfs_print(s, b, p);
+ old_p = p;
+ }
+ p = b;
+ }
+
+ if (filter == BOOTTIME_FILTER_NOTHING && p)
+ boottime_debugfs_print(s, p, p);
+
+ if (p)
+ seq_printf(s, "[%5lu.%06lu] Freeing init memory: 0K\n",
+ p->time / 1000000, p->time % 1000000);
+ return 0;
+}
+
+static int boottime_debugfs_summary_show(struct seq_file *s, void *data)
+{
+ struct boottime_list *b, b_zero;
+
+ if (time_bootloader_done)
+ seq_printf(s, "bootloader: %ld msecs\n",
+ time_bootloader_done / 1000);
+
+ seq_printf(s, "kernel: %ld msecs\ntotal: %ld msecs\n",
+ (time_kernel_done - time_bootloader_done) / 1000,
+ time_kernel_done / 1000);
+ seq_printf(s, "kernel:");
+ b = list_first_entry(&boottime_list,
+ struct boottime_list, list);
+ memset(&b_zero, 0, sizeof(struct boottime_list));
+ boottime_debugfs_load(s, b, &b_zero);
+
+ if (time_bootloader_done)
+ seq_printf(s,
+ "bootloader: cpu0 system: %d%% idle: %d%% iowait: 0%% irq: 0%%\n",
+ 100 - boottime_bootloader_idle(),
+ boottime_bootloader_idle());
+ return 0;
+}
+
+static int boottime_debugfs_bootgraph_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file,
+ boottime_debugfs_bootgraph_show,
+ inode->i_private);
+}
+
+static int boottime_debugfs_summary_open(struct inode *inode,
+ struct file *file)
+{
+ return single_open(file,
+ boottime_debugfs_summary_show,
+ inode->i_private);
+}
+
+static const struct file_operations boottime_debugfs_bootgraph_operations = {
+ .open = boottime_debugfs_bootgraph_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations boottime_debugfs_summary_operations = {
+ .open = boottime_debugfs_summary_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+void boottime_debugfs_init(void)
+{
+ struct dentry *dir;
+
+ dir = debugfs_create_dir("boottime", NULL);
+
+ (void) debugfs_create_file("bootgraph", S_IFREG | S_IRUGO,
+ dir, (void *)BOOTTIME_FILTER_NOTHING,
+ &boottime_debugfs_bootgraph_operations);
+ (void) debugfs_create_file("bootgraph_all_except0", S_IFREG | S_IRUGO,
+ dir, (void *)BOOTTIME_FILTER_OUT_ZERO,
+ &boottime_debugfs_bootgraph_operations);
+ (void) debugfs_create_file("bootgraph_larger100",
+ S_IFREG | S_IRUGO,
+ dir, (void *)BOOTTIME_FILTER_OUT_LESS_100,
+ &boottime_debugfs_bootgraph_operations);
+ (void) debugfs_create_file("summary", S_IFREG | S_IRUGO,
+ dir, NULL,
+ &boottime_debugfs_summary_operations);
+}
+#else
+#define boottime_debugfs_init(x)
+#endif
+
+static ssize_t show_bootloader(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%ld\n", time_bootloader_done / 1000);
+}
+
+static ssize_t show_kernel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%ld\n",
+ (time_kernel_done - time_bootloader_done) / 1000);
+}
+
+DEVICE_ATTR(kernel, 0444, show_kernel, NULL);
+DEVICE_ATTR(bootloader, 0444, show_bootloader, NULL);
+
+static struct attribute *boottime_sysfs_entries[] = {
+ &dev_attr_kernel.attr,
+ &dev_attr_bootloader.attr,
+ NULL
+};
+
+static struct attribute_group boottime_attr_grp = {
+ .name = NULL,
+ .attrs = boottime_sysfs_entries,
+};
+
+static int __init boottime_init(void)
+{
+ struct kobject *boottime_kobj;
+
+ boottime_kobj = kobject_create_and_add("boottime", NULL);
+ if (!boottime_kobj) {
+ printk(KERN_ERR "boottime: out of memory!\n");
+ return -ENOMEM;
+ }
+
+ if (sysfs_create_group(boottime_kobj, &boottime_attr_grp) < 0) {
+ kobject_put(boottime_kobj);
+ printk(KERN_ERR "boottime: Failed creating sysfs group\n");
+ return -ENOMEM;
+ }
+
+ boottime_debugfs_init();
+
+ return 0;
+}
+
+late_initcall(boottime_init);
diff --git a/init/main.c b/init/main.c
index 44b2433334c..90c0067c647 100644
--- a/init/main.c
+++ b/init/main.c
@@ -68,6 +68,7 @@
#include <linux/shmem_fs.h>
#include <linux/slab.h>
#include <linux/perf_event.h>
+#include <linux/boottime.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -678,6 +679,8 @@ int __init_or_module do_one_initcall(initcall_t fn)
int count = preempt_count();
int ret;
+ boottime_mark_symbolic(fn);
+
if (initcall_debug)
ret = do_one_initcall_debug(fn);
else
@@ -802,6 +805,7 @@ static noinline int init_post(void)
{
/* need to finish all async __init code before freeing the memory */
async_synchronize_full();
+ boottime_deactivate();
free_initmem();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
@@ -857,6 +861,7 @@ static int __init kernel_init(void * unused)
do_pre_smp_initcalls();
lockup_detector_init();
+ boottime_system_up();
smp_init();
sched_init_smp();
@@ -879,6 +884,7 @@ static int __init kernel_init(void * unused)
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
+ boottime_mark("mount+0x0/0x0");
prepare_namespace();
}
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 3914c1e03cf..468bcbc5db4 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -266,7 +266,7 @@ void handle_nested_irq(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction *action;
- irqreturn_t action_ret;
+ irqreturn_t action_ret = IRQ_NONE;
might_sleep();
@@ -281,7 +281,11 @@ void handle_nested_irq(unsigned int irq)
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);
raw_spin_unlock_irq(&desc->lock);
- action_ret = action->thread_fn(action->irq, action->dev_id);
+ do {
+ action_ret |= action->thread_fn(action->irq, action->dev_id);
+ action = action->next;
+ } while (action);
+
if (!noirqdebug)
note_interrupt(irq, desc, action_ret);
diff --git a/kernel/kexec.c b/kernel/kexec.c
index 4e2e472f6ae..799064323aa 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -48,6 +48,8 @@ u32 vmcoreinfo_note[VMCOREINFO_NOTE_SIZE/4];
size_t vmcoreinfo_size;
size_t vmcoreinfo_max_size = sizeof(vmcoreinfo_data);
+ATOMIC_NOTIFIER_HEAD(crash_percpu_notifier_list);
+
/* Location of the reserved area for the crash kernel */
struct resource crashk_res = {
.name = "Crash kernel",
@@ -1080,6 +1082,7 @@ asmlinkage long compat_sys_kexec_load(unsigned long entry,
void crash_kexec(struct pt_regs *regs)
{
+ struct pt_regs fixed_regs;
/* Take the kexec_mutex here to prevent sys_kexec_load
* running on one cpu from replacing the crash kernel
* we are using after a panic on a different cpu.
@@ -1090,13 +1093,20 @@ void crash_kexec(struct pt_regs *regs)
*/
if (mutex_trylock(&kexec_mutex)) {
if (kexec_crash_image) {
- struct pt_regs fixed_regs;
crash_setup_regs(&fixed_regs, regs);
crash_save_vmcoreinfo();
machine_crash_shutdown(&fixed_regs);
machine_kexec(kexec_crash_image);
}
+#ifdef CONFIG_CRASH_SWRESET
+ else {
+ crash_setup_regs(&fixed_regs, regs);
+ crash_save_vmcoreinfo();
+ machine_crash_shutdown(&fixed_regs);
+ machine_crash_swreset();
+ }
+#endif
mutex_unlock(&kexec_mutex);
}
}
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 396d262b8fd..e53c663a5e9 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/suspend.h>
+#include <linux/kthread.h>
#include <linux/syscore_ops.h>
#include <trace/events/power.h>
@@ -35,6 +36,10 @@ const char *const pm_states[PM_SUSPEND_MAX] = {
static const struct platform_suspend_ops *suspend_ops;
+static struct completion second_cpu_complete = {1,
+ __WAIT_QUEUE_HEAD_INITIALIZER((second_cpu_complete).wait)
+};
+
/**
* suspend_set_ops - Set the global suspend method table.
* @ops: Suspend operations to use.
@@ -157,9 +162,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
if (suspend_test(TEST_PLATFORM))
goto Platform_wake;
+
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS))
- goto Enable_cpus;
+ goto Platform_wake;
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
@@ -177,9 +183,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
- Enable_cpus:
- enable_nonboot_cpus();
-
Platform_wake:
if (suspend_ops->wake)
suspend_ops->wake();
@@ -257,6 +260,18 @@ static void suspend_finish(void)
pm_restore_console();
}
+static int plug_secondary_cpus(void *data)
+{
+ if (!(suspend_test(TEST_FREEZER) ||
+ suspend_test(TEST_DEVICES) ||
+ suspend_test(TEST_PLATFORM)))
+ enable_nonboot_cpus();
+
+ complete(&second_cpu_complete);
+
+ return 0;
+}
+
/**
* enter_state - Do common work needed to enter system sleep state.
* @state: System sleep state to enter.
@@ -268,6 +283,7 @@ static void suspend_finish(void)
static int enter_state(suspend_state_t state)
{
int error;
+ struct task_struct *cpu_task;
if (!valid_state(state))
return -ENODEV;
@@ -275,6 +291,14 @@ static int enter_state(suspend_state_t state)
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
+ /*
+ * Assure that previous started thread is completed before
+ * attempting to suspend again.
+ */
+ error = wait_for_completion_timeout(&second_cpu_complete,
+ msecs_to_jiffies(500));
+ WARN_ON(error == 0);
+
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.\n");
@@ -296,6 +320,11 @@ static int enter_state(suspend_state_t state)
pr_debug("PM: Finishing wakeup.\n");
suspend_finish();
Unlock:
+
+ cpu_task = kthread_run(plug_secondary_cpus,
+ NULL, "cpu-plug");
+ BUG_ON(IS_ERR(cpu_task));
+
mutex_unlock(&pm_mutex);
return error;
}
diff --git a/kernel/printk.c b/kernel/printk.c
index b663c2c95d3..c538c187618 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -41,6 +41,7 @@
#include <linux/cpu.h>
#include <linux/notifier.h>
#include <linux/rculist.h>
+#include <trace/stm.h>
#include <asm/uaccess.h>
@@ -56,6 +57,10 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...)
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+#ifdef CONFIG_PRINTK_LL
+extern void printascii(char *);
+#endif
+
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
@@ -884,6 +889,10 @@ asmlinkage int vprintk(const char *fmt, va_list args)
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
+#ifdef CONFIG_PRINTK_LL
+ printascii(printk_buf);
+#endif
+
p = printk_buf;
/* Read log level and handle special printk prefix */
@@ -905,6 +914,9 @@ asmlinkage int vprintk(const char *fmt, va_list args)
}
}
+ /* Send printk buffer to MIPI STM trace hardware too if enable */
+ stm_dup_printk(printk_buf, printed_len);
+
/*
* Copy the output into log_buf. If the caller didn't provide
* the appropriate log prefix, we insert them here
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 2a22255c101..dba8f56afc2 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -38,6 +38,7 @@
#include <linux/poll.h>
#include <linux/nmi.h>
#include <linux/fs.h>
+#include <trace/stm.h>
#include "trace.h"
#include "trace_output.h"
@@ -965,7 +966,7 @@ void tracing_reset_current_online_cpus(void)
tracing_reset_online_cpus(&global_trace);
}
-#define SAVED_CMDLINES 128
+#define SAVED_CMDLINES 2048
#define NO_CMDLINE_MAP UINT_MAX
static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
static unsigned map_cmdline_to_pid[SAVED_CMDLINES];
@@ -1293,6 +1294,8 @@ trace_function(struct trace_array *tr,
if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event);
+
+ stm_ftrace(ip, parent_ip);
}
void
@@ -1387,6 +1390,8 @@ static void __ftrace_trace_stack(struct ring_buffer *buffer,
if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event);
+ stm_stack_trace(trace.entries);
+
out:
/* Again, don't let gcc optimize things here */
barrier();
@@ -1557,6 +1562,8 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
ftrace_trace_stack(buffer, flags, 6, pc);
}
+ stm_trace_bprintk_buf(ip, fmt, trace_buf, sizeof(u32) * len);
+
out_unlock:
arch_spin_unlock(&trace_buf_lock);
local_irq_restore(flags);
@@ -1633,6 +1640,8 @@ int trace_array_vprintk(struct trace_array *tr,
ftrace_trace_stack(buffer, irq_flags, 6, pc);
}
+ stm_trace_printk_buf(ip, trace_buf, len);
+
out_unlock:
arch_spin_unlock(&trace_buf_lock);
raw_local_irq_restore(irq_flags);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 29111da1d10..7dd750a3443 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <asm/setup.h>
+#include <trace/stm.h>
#include "trace_output.h"
@@ -1712,6 +1713,8 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip)
trace_nowake_buffer_unlock_commit(buffer, event, flags, pc);
+ stm_ftrace(ip, parent_ip);
+
out:
atomic_dec(&per_cpu(ftrace_test_event_disable, cpu));
preempt_enable_notrace();
diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c
index 7e62c0a1845..a136fd86533 100644
--- a/kernel/trace/trace_sched_switch.c
+++ b/kernel/trace/trace_sched_switch.c
@@ -11,6 +11,7 @@
#include <linux/uaccess.h>
#include <linux/ftrace.h>
#include <trace/events/sched.h>
+#include <trace/stm.h>
#include "trace.h"
@@ -47,6 +48,10 @@ tracing_sched_switch_trace(struct trace_array *tr,
if (!filter_check_discard(call, entry, buffer, event))
trace_buffer_unlock_commit(buffer, event, flags, pc);
+
+ stm_sched_switch(entry->prev_pid, entry->prev_prio, entry->prev_state,
+ entry->next_pid, entry->next_prio, entry->next_state,
+ entry->next_cpu);
}
static void
@@ -103,6 +108,11 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
if (!filter_check_discard(call, entry, buffer, event))
ring_buffer_unlock_commit(buffer, event);
+
+ stm_sched_wakeup(entry->prev_pid, entry->prev_prio, entry->prev_state,
+ entry->next_pid, entry->next_prio, entry->next_state,
+ entry->next_cpu);
+
ftrace_trace_stack(tr->buffer, flags, 6, pc);
ftrace_trace_userstack(tr->buffer, flags, pc);
}
diff --git a/scripts/setlocalversion b/scripts/setlocalversion
index bd6dca8a0ab..f090355733e 100755
--- a/scripts/setlocalversion
+++ b/scripts/setlocalversion
@@ -10,23 +10,37 @@
#
usage() {
- echo "Usage: $0 [--save-scmversion] [srctree]" >&2
+ echo "Usage: $0 [--save-scmversion] [-s srctree] [-t ref_tag]" >&2
exit 1
}
scm_only=false
srctree=.
-if test "$1" = "--save-scmversion"; then
- scm_only=true
- shift
-fi
-if test $# -gt 0; then
- srctree=$1
+match_option=--exact-match
+
+while [ $# -ne 0 ]; do
+ if test "$1" = "--save-scmversion"; then
+ scm_only=true
+ elif test "$1" = "-s"; then
+ shift
+ if test $# -ne 0 -a -d "$1"; then
+ srctree=$1
+ else
+ usage
+ fi
+ elif test "$1" = "-t"; then
+ shift
+ if [ $# -ne 0 ]; then
+ match=" --tags --match "$1
+ rev_refs="--refs refs/tags/"$1
+ else
+ usage
+ fi
+ else
+ usage
+ fi
shift
-fi
-if test $# -gt 0 -o ! -d "$srctree"; then
- usage
-fi
+done
scm_version()
{
@@ -47,8 +61,8 @@ scm_version()
# If we are at a tagged commit (like "v2.6.30-rc6"), we ignore
# it, because this version is defined in the top level Makefile.
- if [ -z "`git describe --exact-match 2>/dev/null`" ]; then
-
+ if git name-rev --tags $rev_refs HEAD | \
+ grep -E '^HEAD[[:space:]]+(.*~[0-9]*|undefined)$' > /dev/null; then
# If only the short version is requested, don't bother
# running further git commands
if $short; then
@@ -57,7 +71,7 @@ scm_version()
fi
# If we are past a tagged commit (like
# "v2.6.30-rc5-302-g72357d5"), we pretty print it.
- if atag="`git describe 2>/dev/null`"; then
+ if atag="`git describe $match 2>/dev/null`"; then
echo "$atag" | awk -F- '{printf("-%05d-%s", $(NF-1),$(NF))}'
# If we don't have a tag at all we print -g{commitish}.