diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-03-05 11:07:47 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-03-05 11:07:47 +1100 |
commit | d08a89d457e04ba6f5b8d0922f35fa9e506c60bd (patch) | |
tree | cc5c446d36cd882bf49600174986dc8312a9f325 /arch | |
parent | 67905b2d7db1a202aa33c8d4f634555dce25f5ca (diff) | |
parent | d99c6ab416c69b1788ac1ad5288dd86e776225ae (diff) |
Merge remote branch 'msm/for-next'
Diffstat (limited to 'arch')
160 files changed, 48316 insertions, 674 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 11383acad1c1..a1624bb40df1 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -616,14 +616,15 @@ config ARCH_PXA config ARCH_MSM bool "Qualcomm MSM" - select CPU_V6 + select GENERIC_GPIO select GENERIC_TIME select GENERIC_CLOCKEVENTS help - Support for Qualcomm MSM7K based systems. This runs on the ARM11 - apps processor of the MSM7K and depends on a shared memory - interface to the ARM9 modem processor which runs the baseband stack - and controls some vital subsystems (clock and power control, etc). + Support for Qualcomm MSM/QSD based systems. This runs on the + apps processor of the MSM/QSD and depends on a shared memory + interface to the modem processor which runs the baseband + stack and controls some vital subsystems + (clock and power control, etc). config ARCH_SHMOBILE bool "Renesas SH-Mobile" diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig index f780086befd7..57012e82e3cd 100644 --- a/arch/arm/mach-msm/Kconfig +++ b/arch/arm/mach-msm/Kconfig @@ -1,7 +1,189 @@ if ARCH_MSM -comment "MSM Board Type" - depends on ARCH_MSM +choice + prompt "MSM SoC Type" + default ARCH_MSM7X00A + +config ARCH_MSM7X01A + bool "MSM7x00A / MSM7x01A" + select ARCH_MSM_ARM11 + select CPU_V6 + +config ARCH_MSM7X27 + bool "MSM7x27" + select ARCH_MSM_ARM11 + select CPU_V6 + +config ARCH_MSM7X30 + bool "MSM7x30" + select ARCH_MSM_SCORPION + select CPU_V7 + +config ARCH_QSD8X50 + bool "QSD8X50" + select ARCH_MSM_SCORPION + select VERIFY_PERMISSION_FAULT + select CPU_V7 + +endchoice + +config ARCH_MSM_ARM11 + bool +config ARCH_MSM_SCORPION + bool + + +menu "MSM Board Selection" + +config MACH_HALIBUT + select CPU_V6 + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "Halibut Board (QCT SURF7201A)" + help + Support for the Qualcomm SURF7201A eval board. + +config MACH_MSM7201A_SURF + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "MSM7201A SURF" + help + Support for the Qualcomm MSM7201A SURF eval board. + +config MACH_MSM7201A_FFA + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "MSM7201A FFA" + help + Support for the Qualcomm MSM7201A FFA eval board. + +config MACH_TROUT + select CPU_V6 + depends on ARCH_MSM7X01A + depends on MSM_STACKED_MEMORY + default y + bool "HTC Dream (aka trout)" + help + Support for the HTC Dream, T-Mobile G1, Android ADP1 devices. + +config MACH_MSM7X27_SURF + depends on ARCH_MSM7X27 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27 SURF" + help + Support for the Qualcomm MSM7x27 SURF eval board. + +config MACH_MSM7X27_FFA + depends on ARCH_MSM7X27 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x27 FFA" + help + Support for the Qualcomm MSM7x27 FFA eval board. + +config MACH_MSM7X30_SURF + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x30 SURF" + help + Support for the Qualcomm MSM7x30 SURF eval board. + +config MACH_MSM7X30_FFA + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x30 FFA" + help + Support for the Qualcomm MSM7x30 FFA eval board. + +config MACH_MSM7X30_FLUID + depends on ARCH_MSM7X30 + depends on !MSM_STACKED_MEMORY + default y + bool "MSM7x30 FLUID" + help + Support for the Qualcomm MSM7x30 FLUID eval board. + +config MACH_SAPPHIRE + depends on ARCH_MSM7X01A + default n + bool "Sapphire" + +config MACH_QSD8X50_SURF + depends on ARCH_QSD8X50 + depends on MSM_STACKED_MEMORY + default y + bool "QSD8x50 SURF" + help + Support for the Qualcomm QSD8x50 SURF eval board. + +config MACH_QSD8X50_FFA + depends on ARCH_QSD8X50 + depends on MSM_STACKED_MEMORY + default y + bool "QSD8x50 FFA" + help + Support for the Qualcomm QSD8x50 FFA eval board. + +config MACH_QSD8X50_COMET + depends on ARCH_QSD8X50 + depends on MSM_STACKED_MEMORY + default n + bool "QSD8x50 Comet" + help + Support for the Qualcomm Comet eval board. + +config MACH_QSD8X50_GRAPEFRUIT + depends on ARCH_QSD8X50 + depends on MSM_STACKED_MEMORY + default n + bool "QSD8x50 Grapefruit" + help + Support for the Qualcomm Grapefruit eval board. + +config MACH_QSD8X50_ST1 + depends on ARCH_QSD8X50 + depends on MSM_STACKED_MEMORY + default n + bool "QSD8x50 ST1" + help + Support for the Qualcomm ST1. + +endmenu + +config MSM_STACKED_MEMORY + bool "Stacked Memory" + default y + help + This option is used to indicate the presence of on-die stacked + memory. When present this memory bank is used for a high speed + shared memory interface. When not present regular RAM is used. + +config MSM_AMSS_VERSION + int + default 6210 if MSM_AMSS_VERSION_6210 + default 6220 if MSM_AMSS_VERSION_6220 + default 6225 if MSM_AMSS_VERSION_6225 + +choice + prompt "AMSS modem firmware version" + + default MSM_AMSS_VERSION_6225 + + config MSM_AMSS_VERSION_6210 + bool "6.2.10" + + config MSM_AMSS_VERSION_6220 + bool "6.2.20" + + config MSM_AMSS_VERSION_6225 + bool "6.2.20 + New ADSP" +endchoice config MSM_DEBUG_UART int @@ -27,17 +209,488 @@ choice bool "UART3" endchoice -config MACH_HALIBUT - depends on ARCH_MSM +choice + prompt "Default Timer" + default MSM7X00A_USE_GP_TIMER + + config MSM7X00A_USE_GP_TIMER + bool "GP Timer" + help + Low resolution timer that allows power collapse from idle. + + config MSM7X00A_USE_DG_TIMER + bool "DG Timer" + help + High resolution timer. +endchoice + +choice + prompt "Suspend sleep mode" + default MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + help + Allows overriding the sleep mode used. Leave at power + collapse suspend unless the arm9 image has problems. + + config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + bool "Power collapse suspend" + help + Lowest sleep state. Returns through reset vector. + + config MSM7X00A_SLEEP_MODE_POWER_COLLAPSE + bool "Power collapse" + help + Sleep state that returns through reset vector. + + config MSM7X00A_SLEEP_MODE_APPS_SLEEP + bool "Apps Sleep" + + config MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + bool "Ramp down cpu clock and wait for interrupt" + + config MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT + bool "Wait for interrupt" +endchoice + +config MSM7X00A_SLEEP_MODE + int + default 0 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + default 1 if MSM7X00A_SLEEP_MODE_POWER_COLLAPSE + default 2 if MSM7X00A_SLEEP_MODE_APPS_SLEEP + default 3 if MSM7X00A_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + default 4 if MSM7X00A_SLEEP_WAIT_FOR_INTERRUPT + +choice + prompt "Idle sleep mode" + default MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + help + Allows overriding the sleep mode used from idle. Leave at power + collapse suspend unless the arm9 image has problems. + + config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + bool "Power collapse suspend" + help + Lowest sleep state. Returns through reset vector. + + config MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + bool "Power collapse" + help + Sleep state that returns through reset vector. + + config MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP + bool "Apps Sleep" + + config MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + bool "Ramp down cpu clock and wait for interrupt" + + config MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT + bool "Wait for interrupt" +endchoice + +config MSM7X00A_IDLE_SLEEP_MODE + int + default 0 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE_SUSPEND + default 1 if MSM7X00A_IDLE_SLEEP_MODE_POWER_COLLAPSE + default 2 if MSM7X00A_IDLE_SLEEP_MODE_APPS_SLEEP + default 3 if MSM7X00A_IDLE_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT + default 4 if MSM7X00A_IDLE_SLEEP_WAIT_FOR_INTERRUPT + +config MSM7X00A_IDLE_SLEEP_MIN_TIME + int "Minimum idle time before sleep" + default 20000000 + help + Minimum idle time in nanoseconds before entering low power mode. + +config MSM7X00A_IDLE_SPIN_TIME + int "Idle spin time before cpu ramp down" + default 80000 + help + Spin time in nanoseconds before ramping down cpu clock and entering + any low power state. + +menuconfig MSM_IDLE_STATS + bool "Collect idle statistics" default y - bool "Halibut Board (QCT SURF7201A)" help - Support for the Qualcomm SURF7201A eval board. + Collect idle statistics and export them in proc/msm_pm_stats. -config MACH_TROUT +if MSM_IDLE_STATS + +config MSM_IDLE_STATS_FIRST_BUCKET + int "First bucket time" + default 62500 + help + Upper time limit in nanoseconds of first bucket. + +config MSM_IDLE_STATS_BUCKET_SHIFT + int "Bucket shift" + default 2 + +config MSM_IDLE_STATS_BUCKET_COUNT + int "Bucket count" + default 10 + +config MSM_SUSPEND_STATS_FIRST_BUCKET + int "First bucket time for suspend" + default 1000000000 + help + Upper time limit in nanoseconds of first bucket of the + histogram. This is for collecting statistics on suspend. + +endif # MSM_IDLE_STATS + +config MSM_JTAG_V7 + depends on CPU_V7 + default y if DEBUG_KERNEL + bool "JTAG debug support" + help + Add additional support for JTAG kernel debugging. + +config HTC_HEADSET + tristate "HTC 2 Wire detection driver" + default n + help + Provides support for detecting HTC 2 wire devices, such as wired + headset, on the trout platform. Can be used with the msm serial + debugger, but not with serial console. + +config TROUT_BATTCHG + depends on MACH_TROUT && POWER_SUPPLY default y - bool "HTC Dream (aka trout)" + bool "Trout battery / charger driver" + +config HTC_PWRSINK + depends on MSM_SMD + default n + bool "HTC Power Sink Driver" + +config QSD_SVS + bool "QSD Static Voltage Scaling" + depends on (MACH_QSD8X50_SURF || MACH_QSD8X50_FFA || MACH_QSD8X50_COMET) + default y + select TPS65023 help - Support for the HTC Dream, T-Mobile G1, Android ADP1 devices. + Enables static voltage scaling using the TPS65023 PMIC. + +config QSD_PMIC_DEFAULT_DCDC1 + int "PMIC default output voltage" + depends on (MACH_QSD8X50_SURF || MACH_QSD8X50_FFA || MACH_QSD8X50_COMET) + default 1250 + help + This is the PMIC voltage at Linux kernel boot. + +config MSM_FIQ_SUPPORT + default y + bool "Enable installation of an FIQ handler." + +config MSM_SMD + default y + bool "MSM Shared Memory Driver (SMD)" + help + Support for the shared memory interface between the apps + processor and the baseband processor. Provides access to + the "shared heap", as well as virtual serial channels + used to communicate with various services on the baseband + processor. + +choice + prompt "MSM Shared memory interface version" + depends on MSM_SMD + default MSM_SMD_PKG3 if ARCH_MSM_ARM11 + default MSM_SMD_PKG4 if ARCH_MSM_SCORPION + + config MSM_SMD_PKG3 + bool "Package 3" + + config MSM_SMD_PKG4 + bool "Package 4" +endchoice + +config MSM_SMD_DEBUG + depends on MSM_SMD + default y + bool "MSM SMD debug support" + help + Support for debugging the SMD for communication + between the ARM9 and ARM11 + +config MSM_N_WAY_SMD + depends on (MSM_SMD && (ARCH_MSM_SCORPION || ARCH_MSM7X27)) + default y + bool "MSM N-WAY SMD support" + help + Supports APPS-QDSP SMD communication along with + normal APPS-MODEM SMD communication. + +config MSM_N_WAY_SMSM + depends on (MSM_SMD && (ARCH_MSM_SCORPION || ARCH_MSM7X27)) + default y + bool "MSM N-WAY SMSM support" + help + Supports APPS-QDSP SMSM communication along with + normal APPS-MODEM SMSM communication. + +config MSM_RESET_MODEM + tristate "Reset Modem Driver" + depends on MSM_SMD + default m + help + Allows the user to reset the modem through a device node. + +config MSM_SMD_LOGGING + depends on MSM_SMD + default y + bool "MSM Shared Memory Logger" + help + This option exposes the shared memory logger at /dev/smem_log + and a debugfs node named smem_log. + + If in doubt, say yes. + +config MSM_SMD_NMEA + bool "NMEA GPS Driver" + depends on MSM_SMD + default y + help + Enable this to support the NMEA GPS device. + + If in doubt, say yes. + +config MSM_SMD_TTY + bool "SMD TTY Driver" + depends on MSM_SMD + default y + help + Provides TTY interfaces to interact with the modem. + + If in doubt, say yes. + +config MSM_SMD_QMI + bool "SMD QMI Driver" + depends on MSM_SMD + default y + help + Manages network data connections. + + If in doubt, say yes. + +config MSM_SMD_CTL + bool "SMD Control Driver" + depends on MSM_SMD + default y + help + Provides a binary SMD non-muxed control port interface. + + If in doubt, say yes. + +config MSM_ONCRPCROUTER + depends on MSM_SMD + default y + bool "MSM ONCRPC router support" + help + Support for the MSM ONCRPC router for communication between + the ARM9 and ARM11 + +config MSM_ONCRPCROUTER_DEBUG + depends on MSM_ONCRPCROUTER + default y + bool "MSM debug ONCRPC router support" + help + Support for debugging the ONCRPC router for communication + between the ARM9 and ARM11 + +config MSM_RPCSERVERS + depends on MSM_ONCRPCROUTER + select RTC_HCTOSYS + default y + bool "Kernel side RPC server bundle" + help + none + +config MSM_RPC_PING + depends on MSM_ONCRPCROUTER && DEBUG_FS + default m + bool "MSM rpc ping" + help + Implements MSM rpc ping test module. + +config MSM_RPC_PROC_COMM_TEST + depends on DEBUG_FS + default m + bool "MSM rpc proc comm test" + help + Implements MSM rpc proc comm test module. + +config MSM_RPC_OEM_RAPI + depends on MSM_ONCRPCROUTER + default m + bool "MSM oem rapi" + help + Implements MSM oem rapi client module. + +config MSM_RPCSERVER_HANDSET + depends on MSM_ONCRPCROUTER + default y + bool "Handset events RPC server" + help + Support for receiving handset events like headset detect, + headset switch and clamshell state. + +config MSM_DALRPC + bool "DAL RPC support" + depends on ARCH_MSM_SCORPION + default y + help + Supports RPC calls to DAL devices on remote processor cores. + +config MSM_DALRPC_TEST + tristate "DAL RPC test module" + depends on (MSM_DALRPC && DEBUG_FS) + default m + help + Exercises DAL RPC calls to QDSP6. + +config MSM_CPU_FREQ_SCREEN + depends on (HAS_EARLYSUSPEND && !CPU_FREQ_MSM) + default n + bool "Enable simple cpu frequency scaling" + help + Simple cpufreq scaling based on screen ON/OFF. + +if MSM_CPU_FREQ_SCREEN + +config MSM_CPU_FREQ_SCREEN_OFF + int "Screen off cpu frequency" + default 245760 + +config MSM_CPU_FREQ_SCREEN_ON + int "Screen on cpu frequency" + default 384000 + +endif # MSM_CPU_FREQ_SCREEN + +if CPU_FREQ_MSM + +config MSM_CPU_FREQ_SET_MIN_MAX + bool "Set Min/Max CPU frequencies." + default n + help + Allow setting min and max CPU frequencies. Sysfs can be used + to override these values. + +config MSM_CPU_FREQ_MAX + int "Max CPU Frequency" + depends on MSM_CPU_FREQ_SET_MIN_MAX + default 384000 + +config MSM_CPU_FREQ_MIN + int "Min CPU Frequency" + depends on MSM_CPU_FREQ_SET_MIN_MAX + default 245760 + +endif # CPU_FREQ_MSM + +config MSM_CPU_AVS + bool "Enable Adaptive Voltage Scaling (AVS)" + depends on (ARCH_MSM_SCORPION && QSD_SVS) + depends on ARCH_QSD8X50 + default y + help + This enables the Adaptive Voltage Scaling feature of + Qualcomm ARMv7 CPUs. It adjusts the voltage for each frequency + based on feedback from three ring oscillators in the CPU. + +config MSM_VREG_SWITCH_INVERTED + bool "Reverse vreg switch polarity" + default n + help + Reverses the enable and disable for vreg switch. + +config MSM_DMA_TEST + tristate "MSM DMA test module" + default m + help + Intended to be compiled as a module. Provides a device node + and ioctls for testing the MSM dma system. + +config WIFI_CONTROL_FUNC + bool "Enable WiFi control function abstraction" + help + Enables Power/Reset/Carddetect function abstraction + +config WIFI_MEM_PREALLOC + depends on WIFI_CONTROL_FUNC + bool "Preallocate memory for WiFi buffers" + help + Preallocates memory buffers for WiFi driver + +config QSD_AUDIO + bool "QSD audio" + depends on (ARCH_MSM_SCORPION && MSM_DALRPC) + default y + help + Provides PCM, MP3, and AAC audio playback. + +config AUDIO_AAC_PLUS + depends on (MSM_ADSP || QSD_AUDIO) + bool "AAC+ Audio" + default y + help + Provides AAC+ decoding + +config AUDIO_ENHANCED_AAC_PLUS + depends on AUDIO_AAC_PLUS + bool "Enhanced AAC+ Audio" + default y + help + Provides Enhanced AAC+ decoding + +config SURF_FFA_GPIO_KEYPAD + bool "MSM SURF/FFA GPIO keypad" + depends on INPUT_GPIO = "y" + default y + help + Select if the GPIO keypad is attached. + +config CLOCK_BASED_SLEEP_LIMIT + default y + bool "Set sleep limitation based on clock usage" + help + The application processor checks for enabled clocks and + decides accordingly the sleep limitation which it informs + the modem to use. + +config MSM_ADM_OFF_AT_POWER_COLLAPSE + bool "Turn off ADM clock during power collapse" + default n + help + The application processor turns off the ADM clock before + entering power collapse and turns it back on after exiting + power collapse. + +config MSM_SLEEP_TIME_OVERRIDE + bool "Allow overriding suspend/sleep time with PM module parameter" + default y + help + Enable the module parameter sleep_time_override. Specified + in units of seconds, it overwrites the normal sleep time of + suspend. The feature is required for automated power management + testing. + +choice + prompt "Power management timeout action" + default MSM_PM_TIMEOUT_HALT + help + Selects the Application Processor's action when Power Management + times out waiting for Modem's handshake. + + config MSM_PM_TIMEOUT_HALT + bool "Halt the Application Processor" + + config MSM_PM_TIMEOUT_RESET_MODEM + bool "Reset the Modem Processor" + + config MSM_PM_TIMEOUT_RESET_CHIP + bool "Reset the entire chip" +endchoice endif diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile index 91e6f5c95dc1..3f1c74dbb131 100644 --- a/arch/arm/mach-msm/Makefile +++ b/arch/arm/mach-msm/Makefile @@ -1,9 +1,72 @@ -obj-y += io.o idle.o irq.o timer.o dma.o +obj-y += io.o irq.o timer.o dma.o obj-y += devices.o obj-y += proc_comm.o +obj-y += vreg.o mpp.o obj-y += vreg.o -obj-y += clock.o clock-7x01a.o +obj-y += clock.o clock-pcom.o +obj-y += gpio.o generic_gpio.o +obj-y += nand_partitions.o +obj-y += remote_spinlock.o modem_notifier.o +obj-y += rpc_hsusb.o +obj-y += socinfo.o +obj-y += cpufreq.o +obj-y += nohlt.o +obj-y += pmic.o +obj-y += internal_power_rail.o -obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o +obj-$(CONFIG_ARCH_MSM_ARM11) += acpuclock.o +obj-$(CONFIG_ARCH_MSM_SCORPION) += acpuclock-8x50.o +obj-$(CONFIG_MSM_CPU_AVS) += avs.o avs_hw.o +obj-$(CONFIG_CPU_V6) += idle-v6.o +obj-$(CONFIG_CPU_V7) += idle-v7.o +obj-$(CONFIG_MSM_JTAG_V7) += jtag-v7.o + +obj-$(CONFIG_ARCH_QSD8X50) += sirc.o +obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o +obj-$(CONFIG_MSM_SMD) += smd.o +obj-$(CONFIG_MSM_SMD_LOGGING) += smem_log.o +obj-$(CONFIG_MSM_SMD_TTY) += smd_tty.o +obj-$(CONFIG_MSM_SMD_QMI) += smd_qmi.o +obj-$(CONFIG_MSM_SMD_CTL) += smd_ctl2.o +obj-$(CONFIG_MSM_SMD_NMEA) += smd_nmea.o +obj-$(CONFIG_DEBUG_FS) += pmic_debugfs.o +obj-$(CONFIG_MSM_RESET_MODEM) += reset_modem.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_device.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_servers.o +obj-$(CONFIG_MSM_ONCRPCROUTER) += smd_rpcrouter_clients.o +obj-$(CONFIG_MSM_RPC_PING) += ping_mdm_rpc_client.o +obj-$(CONFIG_MSM_RPC_PROC_COMM_TEST) += proc_comm_test.o +obj-$(CONFIG_MSM_RPC_OEM_RAPI) += oem_rapi_client.o +obj-$(CONFIG_MSM_RPCSERVERS) += rpc_server_dog_keepalive.o +obj-$(CONFIG_MSM_DALRPC) += dal.o +obj-$(CONFIG_MSM_DALRPC_TEST) += dal_remotetest.o +obj-$(CONFIG_MSM_RPCSERVER_HANDSET) += rpc_server_handset.o +ifdef CONFIG_MSM_N_WAY_SMSM + obj-$(CONFIG_PM) += pm2.o +else + obj-$(CONFIG_PM) += pm.o +endif +obj-$(CONFIG_MSM_DMA_TEST) += dma_test.o +obj-$(CONFIG_SURF_FFA_GPIO_KEYPAD) += keypad-surf-ffa.o obj-$(CONFIG_MACH_TROUT) += board-dream.o +obj-$(CONFIG_MACH_QSD8X50_SURF) += board-qsd8x50.o +obj-$(CONFIG_MACH_QSD8X50_FFA) += board-qsd8x50.o +obj-$(CONFIG_MACH_QSD8X50_COMET) += board-comet.o +obj-$(CONFIG_TROUT_H2W) += board-trout-h2w.o +obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o +obj-$(CONFIG_TROUT_PWRSINK) += htc_pwrsink.o +obj-$(CONFIG_ARCH_MSM7X27) += board-msm7x27.o +obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o clock-7x30.o + +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-keypad.o board-sapphire-panel.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-mmc.o board-sapphire-wifi.o +obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire-rfkill.o msm_vibrator.o + +obj-$(CONFIG_TROUT_BATTCHG) += htc_battery.o + +obj-$(CONFIG_HTC_PWRSINK) += htc_pwrsink.o +obj-$(CONFIG_HTC_HEADSET) += htc_headset.o +obj-$(CONFIG_PMIC8058) += pmic8058-gpio.o pmic8058-mpp.o diff --git a/arch/arm/mach-msm/Makefile.boot b/arch/arm/mach-msm/Makefile.boot index 24dfbf8c07c4..9acaeef05ea9 100644 --- a/arch/arm/mach-msm/Makefile.boot +++ b/arch/arm/mach-msm/Makefile.boot @@ -1,3 +1,21 @@ +ifeq ($(CONFIG_ARCH_MSM_SCORPION),y) +ifeq ($(CONFIG_MSM_STACKED_MEMORY), y) + zreladdr-y := 0x20008000 +params_phys-y := 0x20000100 +initrd_phys-y := 0x24000000 +else # !CONFIG_MSM_STACKED_MEMORY + zreladdr-y := 0x00208000 +params_phys-y := 0x00200100 +initrd_phys-y := 0x01200000 +endif # CONFIG_MSM_STACKED_MEMORY +else # !CONFIG_ARCH_MSM_SCORPION +ifeq ($(CONFIG_MSM_STACKED_MEMORY), y) zreladdr-y := 0x10008000 params_phys-y := 0x10000100 initrd_phys-y := 0x10800000 +else # !CONFIG_MSM_STACKED_MEMORY + zreladdr-y := 0x00208000 +params_phys-y := 0x00200100 +initrd_phys-y := 0x0A000000 +endif # CONFIG_MSM_STACKED_MEMORY +endif # CONFIG_ARCH_MSM_SCORPION diff --git a/arch/arm/mach-msm/acpuclock-8x50.c b/arch/arm/mach-msm/acpuclock-8x50.c new file mode 100644 index 000000000000..dde6900b0f0a --- /dev/null +++ b/arch/arm/mach-msm/acpuclock-8x50.c @@ -0,0 +1,723 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/cpufreq.h> + +#include <mach/board.h> +#include <mach/msm_iomap.h> + +#include "acpuclock.h" +#include "avs.h" +#include "clock.h" + +#define SHOT_SWITCH 4 +#define HOP_SWITCH 5 +#define SIMPLE_SLEW 6 +#define COMPLEX_SLEW 7 + +#define SPSS_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) +#define SPSS_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) + +/* Scorpion PLL registers */ +#define SCPLL_CTL_ADDR (MSM_SCPLL_BASE + 0x4) +#define SCPLL_STATUS_ADDR (MSM_SCPLL_BASE + 0x18) +#define SCPLL_FSM_CTL_EXT_ADDR (MSM_SCPLL_BASE + 0x10) + +enum { + ACPU_PLL_TCXO = -1, + ACPU_PLL_0 = 0, + ACPU_PLL_1, + ACPU_PLL_2, + ACPU_PLL_3, + ACPU_PLL_END, +}; + +struct clkctl_acpu_speed { + unsigned int use_for_scaling; + unsigned int acpuclk_khz; + int pll; + unsigned int acpuclk_src_sel; + unsigned int acpuclk_src_div; + unsigned int ahbclk_khz; + unsigned int ahbclk_div; + unsigned int axiclk_khz; + unsigned int sc_core_src_sel_mask; + unsigned int sc_l_value; + int vdd; + unsigned long lpj; /* loops_per_jiffy */ +}; + +struct clkctl_acpu_speed acpu_freq_tbl[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 0, 0, 14000, 0, 0, 1000}, + { 0, 128000, ACPU_PLL_1, 1, 5, 0, 0, 14000, 2, 0, 1000}, + { 1, 245760, ACPU_PLL_0, 4, 0, 0, 0, 29000, 0, 0, 1000}, + { 1, 384000, ACPU_PLL_3, 0, 0, 0, 0, 58000, 1, 0xA, 1000}, + { 0, 422400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xB, 1000}, + { 0, 460800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xC, 1000}, + { 0, 499200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xD, 1025}, + { 0, 537600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xE, 1050}, + { 1, 576000, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0xF, 1050}, + { 0, 614400, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x10, 1075}, + { 0, 652800, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x11, 1100}, + { 0, 691200, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x12, 1125}, + { 0, 729600, ACPU_PLL_3, 0, 0, 0, 0, 117000, 1, 0x13, 1150}, + { 1, 768000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x14, 1150}, + { 0, 806400, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x15, 1175}, + { 0, 844800, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x16, 1200}, + { 0, 883200, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x17, 1225}, + { 0, 921600, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x18, 1250}, + { 0, 960000, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x19, 1250}, + { 1, 998400, ACPU_PLL_3, 0, 0, 0, 0, 128000, 1, 0x1A, 1250}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[20]; + +static void __init cpufreq_table_init(void) +{ + unsigned int i; + unsigned int freq_cnt = 0; + + /* Construct the freq_table table from acpu_freq_tbl since the + * freq_table values need to match frequencies specified in + * acpu_freq_tbl and acpu_freq_tbl needs to be fixed up during init. + */ + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0 + && freq_cnt < ARRAY_SIZE(freq_table)-1; i++) { + if (acpu_freq_tbl[i].use_for_scaling) { + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency + = acpu_freq_tbl[i].acpuclk_khz; + freq_cnt++; + } + } + + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].acpuclk_khz != 0); + + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END; + + pr_info("%d scaling frequencies supported.\n", freq_cnt); +} +#endif + +struct clock_state { + struct clkctl_acpu_speed *current_speed; + struct mutex lock; + uint32_t acpu_switch_time_us; + uint32_t max_speed_delta_khz; + uint32_t vdd_switch_time_us; + unsigned long power_collapse_khz; + unsigned long wait_for_irq_khz; + unsigned int max_vdd; + int (*acpu_set_vdd) (int mvolts); +}; + +static struct clock_state drv_state = { 0 }; + +unsigned long clk_get_max_axi_khz(void) +{ + return 128000; +} +EXPORT_SYMBOL(clk_get_max_axi_khz); + +static void scpll_set_freq(uint32_t lval, unsigned freq_switch) +{ + uint32_t regval; + + if (lval > 33) + lval = 33; + if (lval < 10) + lval = 10; + + /* wait for any calibrations or frequency switches to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x3) + ; + + /* write the new L val and switch mode */ + regval = readl(SCPLL_FSM_CTL_EXT_ADDR); + regval &= ~(0x3f << 3); + regval |= (lval << 3); + if (freq_switch == SIMPLE_SLEW) + regval |= (0x1 << 9); + + regval &= ~(0x3 << 0); + regval |= (freq_switch << 0); + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + dmb(); + + /* put in normal mode */ + regval = readl(SCPLL_CTL_ADDR); + regval |= 0x7; + writel(regval, SCPLL_CTL_ADDR); + + dmb(); + + /* wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* status bit seems to clear early, using + * 100us to handle the worst case. */ + udelay(100); +} + +static void scpll_apps_enable(bool state) +{ + uint32_t regval; + + /* Wait for any frequency switches to finish. */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* put the pll in standby mode */ + regval = readl(SCPLL_CTL_ADDR); + regval &= ~(0x7); + regval |= (0x2); + writel(regval, SCPLL_CTL_ADDR); + + dmb(); + + if (state) { + /* put the pll in normal mode */ + regval = readl(SCPLL_CTL_ADDR); + regval |= (0x7); + writel(regval, SCPLL_CTL_ADDR); + udelay(200); + } else { + /* put the pll in power down mode */ + regval = readl(SCPLL_CTL_ADDR); + regval &= ~(0x7); + writel(regval, SCPLL_CTL_ADDR); + } + udelay(drv_state.vdd_switch_time_us); +} + +static void scpll_init(void) +{ + uint32_t regval; +#define L_VAL_384MHZ 0xA +#define L_VAL_768MHZ 0x14 + + /* power down scpll */ + writel(0x0, SCPLL_CTL_ADDR); + + dmb(); + + /* set bypassnl, put into standby */ + writel(0x00400002, SCPLL_CTL_ADDR); + + /* set bypassnl, reset_n, full calibration */ + writel(0x00600004, SCPLL_CTL_ADDR); + + /* Ensure register write to initiate calibration has taken + effect before reading status flag */ + dmb(); + + /* wait for cal_all_done */ + while (readl(SCPLL_STATUS_ADDR) & 0x2) + ; + + /* Start: Set of experimentally derived steps + * to work around a h/w bug. */ + + /* Put the pll in normal mode */ + scpll_apps_enable(1); + + /* SHOT switch to 384 MHz */ + regval = readl(SCPLL_FSM_CTL_EXT_ADDR); + regval &= ~(0x3f << 3); + regval |= (L_VAL_384MHZ << 3); + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + regval &= ~0x7; + regval |= SHOT_SWITCH; + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + /* Wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* Status bit seems to clear early, using + * 800 microseconds for the worst case. */ + udelay(800); + + /* HOP switch to 768 MHz. */ + regval = readl(SCPLL_FSM_CTL_EXT_ADDR); + regval &= ~(0x3f << 3); + regval |= (L_VAL_768MHZ << 3); + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + regval &= ~0x7; + regval |= HOP_SWITCH; + writel(regval, SCPLL_FSM_CTL_EXT_ADDR); + + /* Wait for frequency switch to finish */ + while (readl(SCPLL_STATUS_ADDR) & 0x1) + ; + + /* Status bit seems to clear early, using + * 100 microseconds for the worst case. */ + udelay(100); + + /* End: Work around for h/w bug */ + + /* Power down scpll */ + scpll_apps_enable(0); +} + +static void config_pll(struct clkctl_acpu_speed *s) +{ + uint32_t regval; + + if (s->pll == ACPU_PLL_3) + scpll_set_freq(s->sc_l_value, HOP_SWITCH); + /* Configure the PLL divider mux if we plan to use it. */ + else if (s->sc_core_src_sel_mask == 0) { + /* get the current clock source selection */ + regval = readl(SPSS_CLK_SEL_ADDR) & 0x1; + + /* configure the other clock source, then switch to it, + * using the glitch free mux */ + switch (regval) { + case 0x0: + regval = readl(SPSS_CLK_CNTL_ADDR); + regval &= ~(0x7 << 4 | 0xf); + regval |= (s->acpuclk_src_sel << 4); + regval |= (s->acpuclk_src_div << 0); + writel(regval, SPSS_CLK_CNTL_ADDR); + + regval = readl(SPSS_CLK_SEL_ADDR); + regval |= 0x1; + writel(regval, SPSS_CLK_SEL_ADDR); + break; + + case 0x1: + regval = readl(SPSS_CLK_CNTL_ADDR); + regval &= ~(0x7 << 12 | 0xf << 8); + regval |= (s->acpuclk_src_sel << 12); + regval |= (s->acpuclk_src_div << 8); + writel(regval, SPSS_CLK_CNTL_ADDR); + + regval = readl(SPSS_CLK_SEL_ADDR); + regval &= ~0x1; + writel(regval, SPSS_CLK_SEL_ADDR); + break; + } + dmb(); + } + + regval = readl(SPSS_CLK_SEL_ADDR); + regval &= ~(0x3 << 1); + regval |= (s->sc_core_src_sel_mask << 1); + writel(regval, SPSS_CLK_SEL_ADDR); +} + +void config_switching_pll(void) +{ + uint32_t regval; + + /* Use AXI clock temporarily when we're changing + * scpll. PLL0 is faster, but it may not be available during + * early modem initialization, and we will only be using this + * a very short time (while scpll is reconfigured). + */ + + regval = readl(SPSS_CLK_SEL_ADDR); + regval &= ~(0x3 << 1); + regval |= (0x2 << 1); + writel(regval, SPSS_CLK_SEL_ADDR); +} + +static int acpuclk_set_vdd_level(int vdd) +{ + if (drv_state.acpu_set_vdd) + return drv_state.acpu_set_vdd(vdd); + else { + /* Assume that the PMIC supports scaling the processor + * to its maximum frequency at its default voltage. + */ + return 0; + } +} + +int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) +{ + struct clkctl_acpu_speed *tgt_s, *strt_s; + int rc = 0; + int freq_index = 0; + + if (reason == SETRATE_CPUFREQ) + mutex_lock(&drv_state.lock); + + strt_s = drv_state.current_speed; + + if (rate == (strt_s->acpuclk_khz * 1000)) + goto out; + + for (tgt_s = acpu_freq_tbl; tgt_s->acpuclk_khz != 0; tgt_s++) { + if (tgt_s->acpuclk_khz == (rate / 1000)) + break; + freq_index++; + } + + if (tgt_s->acpuclk_khz == 0) { + rc = -EINVAL; + goto out; + } + + if (reason == SETRATE_CPUFREQ) { +#ifdef CONFIG_MSM_CPU_AVS + /* Notify avs before changing frequency */ + rc = avs_adjust_freq(freq_index, 1); + if (rc) { + printk(KERN_ERR + "acpuclock: Unable to increase ACPU " + "vdd.\n"); + goto out; + } +#endif + /* Increase VDD if needed. */ + if (tgt_s->vdd > strt_s->vdd) { + rc = acpuclk_set_vdd_level(tgt_s->vdd); + if (rc) { + printk(KERN_ERR + "acpuclock: Unable to increase ACPU " + "vdd.\n"); + goto out; + } + } + } + + if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) { + config_pll(tgt_s); + } else if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll == ACPU_PLL_3) { + scpll_apps_enable(1); + config_pll(tgt_s); + } else if (strt_s->pll == ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) { + config_pll(tgt_s); + scpll_apps_enable(0); + } else { + config_switching_pll(); + config_pll(tgt_s); + } + + /* Update the driver state with the new clock freq */ + drv_state.current_speed = tgt_s; + + /* Re-adjust lpj for the new clock speed. */ + loops_per_jiffy = tgt_s->lpj; + + /* Nothing else to do for SWFI. */ + if (reason == SETRATE_SWFI) + goto out; + + if (strt_s->axiclk_khz != tgt_s->axiclk_khz) { + rc = ebi1_clk_set_min_rate(CLKVOTE_ACPUCLK, + tgt_s->axiclk_khz * 1000); + if (rc < 0) + pr_err("Setting AXI min rate failed!\n"); + } + + /* Nothing else to do for power collapse */ + if (reason == SETRATE_PC) + goto out; + +#ifdef CONFIG_MSM_CPU_AVS + /* notify avs after changing frequency */ + rc = avs_adjust_freq(freq_index, 0); + if (rc) + printk(KERN_ERR + "acpuclock: Unable to drop ACPU vdd.\n"); +#endif + + /* Drop VDD level if we can. */ + if (tgt_s->vdd < strt_s->vdd) { + rc = acpuclk_set_vdd_level(tgt_s->vdd); + if (rc) + printk(KERN_ERR + "acpuclock: Unable to drop ACPU vdd.\n"); + } +out: + if (reason == SETRATE_CPUFREQ) + mutex_unlock(&drv_state.lock); + return rc; +} + +static void __init acpuclk_init(void) +{ + struct clkctl_acpu_speed *speed; + uint32_t div, sel, regval; + int rc; + + /* Determine the source of the Scorpion clock. */ + regval = readl(SPSS_CLK_SEL_ADDR); + switch ((regval & 0x6) >> 1) { + case 0: /* raw source clock */ + case 3: /* low jitter PLL1 (768Mhz) */ + if (regval & 0x1) { + sel = ((readl(SPSS_CLK_CNTL_ADDR) >> 4) & 0x7); + div = ((readl(SPSS_CLK_CNTL_ADDR) >> 0) & 0xf); + } else { + sel = ((readl(SPSS_CLK_CNTL_ADDR) >> 12) & 0x7); + div = ((readl(SPSS_CLK_CNTL_ADDR) >> 8) & 0xf); + } + + /* Find the matching clock rate. */ + for (speed = acpu_freq_tbl; speed->acpuclk_khz != 0; speed++) { + if (speed->acpuclk_src_sel == sel && + speed->acpuclk_src_div == div) + break; + } + break; + + case 1: /* unbuffered scorpion pll (384Mhz to 998.4Mhz) */ + sel = ((readl(SCPLL_FSM_CTL_EXT_ADDR) >> 3) & 0x3f); + + /* Find the matching clock rate. */ + for (speed = acpu_freq_tbl; speed->acpuclk_khz != 0; speed++) { + if (speed->sc_l_value == sel && + speed->sc_core_src_sel_mask == 1) + break; + } + break; + + case 2: /* AXI bus clock (128Mhz) */ + default: + speed = &acpu_freq_tbl[4]; + } + + /* Initialize scpll only if it wasn't already initialized by the boot + * loader. If the CPU is already running on scpll, then the scpll was + * initialized by the boot loader. */ + if (speed->pll != ACPU_PLL_3) + scpll_init(); + + if (speed->acpuclk_khz == 0) { + printk(KERN_WARNING "Warning - ACPU clock reports invalid " + "speed\n"); + return; + } + + drv_state.current_speed = speed; + rc = ebi1_clk_set_min_rate(CLKVOTE_ACPUCLK, speed->axiclk_khz * 1000); + if (rc < 0) + pr_err("Setting AXI min rate failed!\n"); + + printk(KERN_INFO "ACPU running at %d KHz\n", speed->acpuclk_khz); +} + +unsigned long acpuclk_get_rate(void) +{ + return drv_state.current_speed->acpuclk_khz; +} + +uint32_t acpuclk_get_switch_time(void) +{ + return drv_state.acpu_switch_time_us; +} + +unsigned long acpuclk_power_collapse(void) +{ + int ret = acpuclk_get_rate(); + acpuclk_set_rate(drv_state.power_collapse_khz, SETRATE_PC); + return ret * 1000; +} + +unsigned long acpuclk_wait_for_irq(void) +{ + int ret = acpuclk_get_rate(); + acpuclk_set_rate(drv_state.wait_for_irq_khz, SETRATE_SWFI); + return ret * 1000; +} + +/* Spare register populated with efuse data on max ACPU freq. */ +#define CT_CSR_PHYS 0xA8700000 +#define TCSR_SPARE2_ADDR (ct_csr_base + 0x60) + +#define PLL0_M_VAL_ADDR (MSM_CLK_CTL_BASE + 0x308) + +static void __init acpu_freq_tbl_fixup(void) +{ + void __iomem *ct_csr_base; + uint32_t tcsr_spare2, pll0_m_val; + unsigned int max_acpu_khz, pll0_fixup; + unsigned int i; + + ct_csr_base = ioremap(CT_CSR_PHYS, PAGE_SIZE); + BUG_ON(ct_csr_base == NULL); + + tcsr_spare2 = readl(TCSR_SPARE2_ADDR); + + /* Check if the register is supported and meaningful. */ + if ((tcsr_spare2 & 0xF000) != 0xA000) { + pr_info("Efuse data on Max ACPU freq not present.\n"); + goto skip_efuse_fixup; + } + + switch (tcsr_spare2 & 0xF0) { + case 0x70: + max_acpu_khz = 768000; + break; + case 0x30: + case 0x00: + max_acpu_khz = 998400; + break; + case 0x10: + max_acpu_khz = 1267200; + break; + default: + pr_warning("Invalid efuse data (%x) on Max ACPU freq!\n", + tcsr_spare2); + goto skip_efuse_fixup; + } + + pr_info("Max ACPU freq from efuse data is %d KHz\n", max_acpu_khz); + + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0; i++) { + if (acpu_freq_tbl[i].acpuclk_khz > max_acpu_khz) { + acpu_freq_tbl[i].acpuclk_khz = 0; + break; + } + } + +skip_efuse_fixup: + iounmap(ct_csr_base); + BUG_ON(drv_state.max_vdd == 0); + + /* pll0_m_val will be 36 when PLL0 is run at 235MHz + * instead of the usual 245MHz. */ + pll0_m_val = readl(PLL0_M_VAL_ADDR) & 0x7FFFF; + pll0_fixup = (pll0_m_val == 36); + + for (i = 0; acpu_freq_tbl[i].acpuclk_khz != 0; i++) { + if (acpu_freq_tbl[i].pll == ACPU_PLL_0 + && acpu_freq_tbl[i].acpuclk_khz == 245760 + && pll0_fixup) { + acpu_freq_tbl[i].acpuclk_khz = 235930; + } + if (acpu_freq_tbl[i].vdd > drv_state.max_vdd) { + acpu_freq_tbl[i].acpuclk_khz = 0; + break; + } + } +} + +/* Initalize the lpj field in the acpu_freq_tbl. */ +static void __init lpj_init(void) +{ + int i; + const struct clkctl_acpu_speed *base_clk = drv_state.current_speed; + for (i = 0; acpu_freq_tbl[i].acpuclk_khz; i++) { + acpu_freq_tbl[i].lpj = cpufreq_scale(loops_per_jiffy, + base_clk->acpuclk_khz, + acpu_freq_tbl[i].acpuclk_khz); + } +} + +#ifdef CONFIG_MSM_CPU_AVS +static int __init acpu_avs_init(int (*set_vdd) (int), int khz) +{ + int i; + int freq_count = 0; + int freq_index = -1; + + for (i = 0; acpu_freq_tbl[i].acpuclk_khz; i++) { + freq_count++; + if (acpu_freq_tbl[i].acpuclk_khz == khz) + freq_index = i; + } + + return avs_init(set_vdd, freq_count, freq_index); +} +#endif + +void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) +{ + mutex_init(&drv_state.lock); + drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us; + drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz; + drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us; + drv_state.power_collapse_khz = clkdata->power_collapse_khz; + drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz; + drv_state.max_vdd = clkdata->max_vdd; + drv_state.acpu_set_vdd = clkdata->acpu_set_vdd; + + acpu_freq_tbl_fixup(); + acpuclk_init(); + lpj_init(); +#ifdef CONFIG_CPU_FREQ_MSM + cpufreq_table_init(); + cpufreq_frequency_table_get_attr(freq_table, smp_processor_id()); +#endif +#ifdef CONFIG_MSM_CPU_AVS + if (!acpu_avs_init(drv_state.acpu_set_vdd, + drv_state.current_speed->acpuclk_khz)) { + /* avs init successful. avs will handle voltage changes */ + drv_state.acpu_set_vdd = NULL; + } +#endif +} diff --git a/arch/arm/mach-msm/acpuclock.c b/arch/arm/mach-msm/acpuclock.c new file mode 100644 index 000000000000..7214a9235c30 --- /dev/null +++ b/arch/arm/mach-msm/acpuclock.c @@ -0,0 +1,915 @@ +/* arch/arm/mach-msm/acpuclock.c + * + * MSM architecture clock driver + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/mutex.h> +#include <linux/io.h> +#include <linux/sort.h> +#include <mach/remote_spinlock.h> +#include <mach/board.h> +#include <mach/msm_iomap.h> +#include <asm/mach-types.h> + +#include "proc_comm.h" +#include "smd_private.h" +#include "clock.h" +#include "acpuclock.h" +#include "socinfo.h" + +#define PERF_SWITCH_DEBUG 0 +#define PERF_SWITCH_STEP_DEBUG 0 + +#define A11S_CLK_CNTL_ADDR (MSM_CSR_BASE + 0x100) +#define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) +#define A11S_VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124) +#define PLLn_MODE(n) (MSM_CLK_CTL_BASE + 0x300 + 28 * (n)) +#define PLLn_L_VAL(n) (MSM_CLK_CTL_BASE + 0x304 + 28 * (n)) + +enum { + ACPU_PLL_TCXO = -1, + ACPU_PLL_0 = 0, + ACPU_PLL_1, + ACPU_PLL_2, + ACPU_PLL_3, + ACPU_PLL_END, +}; + +struct clock_state +{ + struct clkctl_acpu_speed *current_speed; + struct mutex lock; + uint32_t acpu_switch_time_us; + uint32_t max_speed_delta_khz; + uint32_t vdd_switch_time_us; + unsigned long power_collapse_khz; + unsigned long wait_for_irq_khz; + unsigned long max_axi_khz; +}; + +#define PLL_BASE 7 + +struct shared_pll_control { + uint32_t version; + struct { + /* Denotes if the PLL is ON. Technically, this can be read + * directly from the PLL registers, but this feild is here, + * so let's use it. + */ + uint32_t on; + /* One bit for each processor core. The application processor + * is allocated bit position 1. All other bits should be + * considered as votes from other processors. + */ + uint32_t votes; + } pll[PLL_BASE + ACPU_PLL_END]; +}; + +struct clkctl_acpu_speed { + unsigned int use_for_scaling; + unsigned int a11clk_khz; + int pll; + unsigned int a11clk_src_sel; + unsigned int a11clk_src_div; + unsigned int ahbclk_khz; + unsigned int ahbclk_div; + int vdd; + unsigned int axiclk_khz; + unsigned long lpj; /* loops_per_jiffy */ +/* Pointers in acpu_freq_tbl[] for max up/down steppings. */ + struct clkctl_acpu_speed *down[3]; + struct clkctl_acpu_speed *up[3]; +}; + +static remote_spinlock_t pll_lock; +static struct shared_pll_control *pll_control; +static struct clock_state drv_state = { 0 }; +static struct clkctl_acpu_speed *acpu_freq_tbl; + +static void __init acpuclk_init(void); + +/* + * ACPU freq tables used for different PLLs frequency combinations. The + * correct table is selected during init. + * + * Table stepping up/down entries are calculated during boot to choose the + * largest frequency jump that's less than max_speed_delta_khz on each PLL. + */ + +/* 7x01/7x25 normal with GSM capable modem */ +static struct clkctl_acpu_speed pll0_245_pll1_768_pll2_1056[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 }, + { 1, 122880, ACPU_PLL_0, 4, 1, 61440, 1, 3, 61440 }, + { 0, 128000, ACPU_PLL_1, 1, 5, 64000, 1, 3, 61440 }, + { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 61440 }, + { 1, 245760, ACPU_PLL_0, 4, 0, 81920, 2, 4, 61440 }, + { 1, 256000, ACPU_PLL_1, 1, 2, 128000, 1, 5, 128000 }, + { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 128000 }, + { 1, 384000, ACPU_PLL_1, 1, 1, 128000, 2, 6, 128000 }, + { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 128000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x01/7x25 normal with CDMA-only modem */ +static struct clkctl_acpu_speed pll0_196_pll1_768_pll2_1056[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 }, + { 1, 98304, ACPU_PLL_0, 4, 1, 49152, 1, 3, 24576 }, + { 0, 128000, ACPU_PLL_1, 1, 5, 64000, 1, 3, 24576 }, + { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 24576 }, + { 1, 196608, ACPU_PLL_0, 4, 0, 65536, 2, 4, 24576 }, + { 1, 256000, ACPU_PLL_1, 1, 2, 128000, 1, 5, 128000 }, + { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 128000 }, + { 1, 384000, ACPU_PLL_1, 1, 1, 128000, 2, 6, 128000 }, + { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 128000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x01/7x25 turbo with GSM capable modem */ +static struct clkctl_acpu_speed pll0_245_pll1_960_pll2_1056[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 }, + { 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 61440 }, + { 1, 122880, ACPU_PLL_0, 4, 1, 61440, 1, 3, 61440 }, + { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 61440 }, + { 1, 245760, ACPU_PLL_0, 4, 0, 81920, 2, 4, 61440 }, + { 1, 320000, ACPU_PLL_1, 1, 2, 107000, 2, 5, 120000 }, + { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 120000 }, + { 1, 480000, ACPU_PLL_1, 1, 1, 120000, 3, 6, 120000 }, + { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 122880 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x01/7x25 turbo with CDMA-only modem */ +static struct clkctl_acpu_speed pll0_196_pll1_960_pll2_1056[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 }, + { 1, 98304, ACPU_PLL_0, 4, 1, 49152, 1, 3, 24576 }, + { 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 24576 }, + { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 24576 }, + { 1, 196608, ACPU_PLL_0, 4, 0, 65536, 2, 4, 24576 }, + { 1, 320000, ACPU_PLL_1, 1, 2, 107000, 2, 5, 120000 }, + { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 120000 }, + { 1, 480000, ACPU_PLL_1, 1, 1, 120000, 3, 6, 120000 }, + { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 120000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x27 normal with GSM capable modem */ +static struct clkctl_acpu_speed pll0_245_pll1_960_pll2_1200[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 }, + { 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 61440 }, + { 1, 122880, ACPU_PLL_0, 4, 1, 61440, 1, 3, 61440 }, + { 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 61440 }, + { 1, 245760, ACPU_PLL_0, 4, 0, 122880, 1, 4, 61440 }, + { 1, 320000, ACPU_PLL_1, 1, 2, 160000, 1, 5, 122880 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 122880 }, + { 1, 480000, ACPU_PLL_1, 1, 1, 160000, 2, 6, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 122880 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x27 normal with CDMA-only modem */ +static struct clkctl_acpu_speed pll0_196_pll1_960_pll2_1200[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 }, + { 1, 98304, ACPU_PLL_0, 4, 1, 98304, 0, 3, 49152 }, + { 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 49152 }, + { 1, 196608, ACPU_PLL_0, 4, 0, 65536, 2, 4, 98304 }, + { 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 98304 }, + { 1, 320000, ACPU_PLL_1, 1, 2, 160000, 1, 5, 120000 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 120000 }, + { 1, 480000, ACPU_PLL_1, 1, 1, 160000, 2, 6, 120000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 120000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x27 normal with GSM capable modem - PLL0 and PLL1 swapped */ +static struct clkctl_acpu_speed pll0_960_pll1_245_pll2_1200[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 }, + { 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 61440 }, + { 1, 122880, ACPU_PLL_1, 1, 1, 61440, 1, 3, 61440 }, + { 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 61440 }, + { 1, 245760, ACPU_PLL_1, 1, 0, 122880, 1, 4, 61440 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 122880 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 122880 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 122880 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 122880 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +/* 7x27 normal with CDMA-only modem - PLL0 and PLL1 swapped */ +static struct clkctl_acpu_speed pll0_960_pll1_196_pll2_1200[] = { + { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 }, + { 1, 98304, ACPU_PLL_1, 1, 1, 98304, 0, 3, 49152 }, + { 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 49152 }, + { 1, 196608, ACPU_PLL_1, 1, 0, 65536, 2, 4, 98304 }, + { 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 98304 }, + { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 120000 }, + { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 120000 }, + { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 120000 }, + { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 120000 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, {0, 0, 0} } +}; + +#define PLL_196_MHZ 10 +#define PLL_245_MHZ 12 +#define PLL_491_MHZ 25 +#define PLL_768_MHZ 40 +#define PLL_960_MHZ 50 +#define PLL_1056_MHZ 55 +#define PLL_1200_MHZ 62 + +#define PLL_CONFIG(m0, m1, m2) { \ + PLL_##m0##_MHZ, PLL_##m1##_MHZ, PLL_##m2##_MHZ, \ + pll0_##m0##_pll1_##m1##_pll2_##m2 \ +} + +struct pll_freq_tbl_map { + unsigned int pll0_l; + unsigned int pll1_l; + unsigned int pll2_l; + struct clkctl_acpu_speed *tbl; +}; + +static struct pll_freq_tbl_map acpu_freq_tbl_list[] = { + PLL_CONFIG(196, 768, 1056), + PLL_CONFIG(245, 768, 1056), + PLL_CONFIG(196, 960, 1056), + PLL_CONFIG(245, 960, 1056), + PLL_CONFIG(196, 960, 1200), + PLL_CONFIG(245, 960, 1200), + PLL_CONFIG(960, 196, 1200), + PLL_CONFIG(960, 245, 1200), + { 0, 0, 0, 0 } +}; + +#ifdef CONFIG_CPU_FREQ_MSM +static struct cpufreq_frequency_table freq_table[20]; + +static void __init cpufreq_table_init(void) +{ + unsigned int i; + unsigned int freq_cnt = 0; + + /* Construct the freq_table table from acpu_freq_tbl since the + * freq_table values need to match frequencies specified in + * acpu_freq_tbl and acpu_freq_tbl needs to be fixed up during init. + */ + for (i = 0; acpu_freq_tbl[i].a11clk_khz != 0 + && freq_cnt < ARRAY_SIZE(freq_table)-1; i++) { + if (acpu_freq_tbl[i].use_for_scaling) { + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency + = acpu_freq_tbl[i].a11clk_khz; + freq_cnt++; + } + } + + /* freq_table not big enough to store all usable freqs. */ + BUG_ON(acpu_freq_tbl[i].a11clk_khz != 0); + + freq_table[freq_cnt].index = freq_cnt; + freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END; + + pr_info("%d scaling frequencies supported.\n", freq_cnt); +} +#endif + +unsigned long clk_get_max_axi_khz(void) +{ + return drv_state.max_axi_khz; +} +EXPORT_SYMBOL(clk_get_max_axi_khz); + +static int pc_pll_request(unsigned id, unsigned on) +{ + int res = 0; + on = !!on; + +#if PERF_SWITCH_DEBUG + if (on) + printk(KERN_DEBUG "Enabling PLL %d\n", id); + else + printk(KERN_DEBUG "Disabling PLL %d\n", id); +#endif + + if (id >= ACPU_PLL_END) + return -EINVAL; + + if (pll_control) { + remote_spin_lock(&pll_lock); + if (on) { + pll_control->pll[PLL_BASE + id].votes |= 2; + if (!pll_control->pll[PLL_BASE + id].on) { + writel(6, PLLn_MODE(id)); + udelay(50); + writel(7, PLLn_MODE(id)); + pll_control->pll[PLL_BASE + id].on = 1; + } + } else { + pll_control->pll[PLL_BASE + id].votes &= ~2; + if (pll_control->pll[PLL_BASE + id].on + && !pll_control->pll[PLL_BASE + id].votes) { + writel(0, PLLn_MODE(id)); + pll_control->pll[PLL_BASE + id].on = 0; + } + } + remote_spin_unlock(&pll_lock); + } else { + res = msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on); + if (res < 0) + return res; + else if ((int) id < 0) + return -EINVAL; + } + +#if PERF_SWITCH_DEBUG + if (on) + printk(KERN_DEBUG "PLL enabled\n"); + else + printk(KERN_DEBUG "PLL disabled\n"); +#endif + return res; +} + + +/*---------------------------------------------------------------------------- + * ARM11 'owned' clock control + *---------------------------------------------------------------------------*/ + +unsigned long acpuclk_power_collapse(void) { + int ret = acpuclk_get_rate(); + acpuclk_set_rate(drv_state.power_collapse_khz, SETRATE_PC); + return ret * 1000; +} + +unsigned long acpuclk_wait_for_irq(void) { + int ret = acpuclk_get_rate(); + acpuclk_set_rate(drv_state.wait_for_irq_khz, SETRATE_SWFI); + return ret * 1000; +} + +static int acpuclk_set_vdd_level(int vdd) +{ + uint32_t current_vdd; + + current_vdd = readl(A11S_VDD_SVS_PLEVEL_ADDR) & 0x07; + +#if PERF_SWITCH_DEBUG + printk(KERN_DEBUG "acpuclock: Switching VDD from %u -> %d\n", + current_vdd, vdd); +#endif + writel((1 << 7) | (vdd << 3), A11S_VDD_SVS_PLEVEL_ADDR); + udelay(drv_state.vdd_switch_time_us); + if ((readl(A11S_VDD_SVS_PLEVEL_ADDR) & 0x7) != vdd) { +#if PERF_SWITCH_DEBUG + printk(KERN_ERR "acpuclock: VDD set failed\n"); +#endif + return -EIO; + } + +#if PERF_SWITCH_DEBUG + printk(KERN_DEBUG "acpuclock: VDD switched\n"); +#endif + return 0; +} + +/* Set proper dividers for the given clock speed. */ +static void acpuclk_set_div(const struct clkctl_acpu_speed *hunt_s) { + uint32_t reg_clkctl, reg_clksel, clk_div, src_sel; + + reg_clksel = readl(A11S_CLK_SEL_ADDR); + + /* AHB_CLK_DIV */ + clk_div = (reg_clksel >> 1) & 0x03; + /* CLK_SEL_SRC1NO */ + src_sel = reg_clksel & 1; + + /* + * If the new clock divider is higher than the previous, then + * program the divider before switching the clock + */ + if (hunt_s->ahbclk_div > clk_div) { + reg_clksel &= ~(0x3 << 1); + reg_clksel |= (hunt_s->ahbclk_div << 1); + writel(reg_clksel, A11S_CLK_SEL_ADDR); + } + + /* Program clock source and divider */ + reg_clkctl = readl(A11S_CLK_CNTL_ADDR); + reg_clkctl &= ~(0xFF << (8 * src_sel)); + reg_clkctl |= hunt_s->a11clk_src_sel << (4 + 8 * src_sel); + reg_clkctl |= hunt_s->a11clk_src_div << (0 + 8 * src_sel); + writel(reg_clkctl, A11S_CLK_CNTL_ADDR); + + /* Program clock source selection */ + reg_clksel ^= 1; + writel(reg_clksel, A11S_CLK_SEL_ADDR); + + /* + * If the new clock divider is lower than the previous, then + * program the divider after switching the clock + */ + if (hunt_s->ahbclk_div < clk_div) { + reg_clksel &= ~(0x3 << 1); + reg_clksel |= (hunt_s->ahbclk_div << 1); + writel(reg_clksel, A11S_CLK_SEL_ADDR); + } +} + +int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason) +{ + uint32_t reg_clkctl; + struct clkctl_acpu_speed *cur_s, *tgt_s, *strt_s; + int rc = 0; + unsigned int plls_enabled = 0, pll; + + if (reason == SETRATE_CPUFREQ) + mutex_lock(&drv_state.lock); + + strt_s = cur_s = drv_state.current_speed; + + WARN_ONCE(cur_s == NULL, "acpuclk_set_rate: not initialized\n"); + if (cur_s == NULL) { + rc = -ENOENT; + goto out; + } + + if (rate == (cur_s->a11clk_khz * 1000)) + goto out; + + for (tgt_s = acpu_freq_tbl; tgt_s->a11clk_khz != 0; tgt_s++) { + if (tgt_s->a11clk_khz == (rate / 1000)) + break; + } + + if (tgt_s->a11clk_khz == 0) { + rc = -EINVAL; + goto out; + } + + /* Choose the highest speed at or below 'rate' with same PLL. */ + if (reason != SETRATE_CPUFREQ + && tgt_s->a11clk_khz < cur_s->a11clk_khz) { + while (tgt_s->pll != ACPU_PLL_TCXO && tgt_s->pll != cur_s->pll) + tgt_s--; + } + + if (strt_s->pll != ACPU_PLL_TCXO) + plls_enabled |= 1 << strt_s->pll; + + if (reason == SETRATE_CPUFREQ) { + if (strt_s->pll != tgt_s->pll && tgt_s->pll != ACPU_PLL_TCXO) { + rc = pc_pll_request(tgt_s->pll, 1); + if (rc < 0) { + pr_err("PLL%d enable failed (%d)\n", + tgt_s->pll, rc); + goto out; + } + plls_enabled |= 1 << tgt_s->pll; + } + } + /* Need to do this when coming out of power collapse since some modem + * firmwares reset the VDD when the application processor enters power + * collapse. */ + if (reason == SETRATE_CPUFREQ || reason == SETRATE_PC) { + /* Increase VDD if needed. */ + if (tgt_s->vdd > cur_s->vdd) { + if ((rc = acpuclk_set_vdd_level(tgt_s->vdd)) < 0) { + printk(KERN_ERR "Unable to switch ACPU vdd\n"); + goto out; + } + } + } + + /* Set wait states for CPU inbetween frequency changes */ + reg_clkctl = readl(A11S_CLK_CNTL_ADDR); + reg_clkctl |= (100 << 16); /* set WT_ST_CNT */ + writel(reg_clkctl, A11S_CLK_CNTL_ADDR); + +#if PERF_SWITCH_DEBUG + printk(KERN_INFO "acpuclock: Switching from ACPU rate %u -> %u\n", + strt_s->a11clk_khz * 1000, tgt_s->a11clk_khz * 1000); +#endif + + while (cur_s != tgt_s) { + /* + * Always jump to target freq if within 256mhz, regulardless of + * PLL. If differnece is greater, use the predefinied + * steppings in the table. + */ + int d = abs((int)(cur_s->a11clk_khz - tgt_s->a11clk_khz)); + if (d > drv_state.max_speed_delta_khz) { + + if (tgt_s->a11clk_khz > cur_s->a11clk_khz) { + /* Step up: jump to target PLL as early as + * possible so indexing using TCXO (up[-1]) + * never occurs. */ + if (likely(cur_s->up[tgt_s->pll])) + cur_s = cur_s->up[tgt_s->pll]; + else + cur_s = cur_s->up[cur_s->pll]; + } else { + /* Step down: stay on current PLL as long as + * possible so indexing using TCXO (down[-1]) + * never occurs. */ + if (likely(cur_s->down[cur_s->pll])) + cur_s = cur_s->down[cur_s->pll]; + else + cur_s = cur_s->down[tgt_s->pll]; + } + + if (cur_s == NULL) { /* This should not happen. */ + pr_err("No stepping frequencies found. " + "strt_s:%u tgt_s:%u\n", + strt_s->a11clk_khz, tgt_s->a11clk_khz); + rc = -EINVAL; + goto out; + } + + } else { + cur_s = tgt_s; + } +#if PERF_SWITCH_STEP_DEBUG + printk(KERN_DEBUG "%s: STEP khz = %u, pll = %d\n", + __FUNCTION__, cur_s->a11clk_khz, cur_s->pll); +#endif + if (cur_s->pll != ACPU_PLL_TCXO + && !(plls_enabled & (1 << cur_s->pll))) { + rc = pc_pll_request(cur_s->pll, 1); + if (rc < 0) { + pr_err("PLL%d enable failed (%d)\n", + cur_s->pll, rc); + goto out; + } + plls_enabled |= 1 << cur_s->pll; + } + + acpuclk_set_div(cur_s); + drv_state.current_speed = cur_s; + /* Re-adjust lpj for the new clock speed. */ + loops_per_jiffy = cur_s->lpj; + udelay(drv_state.acpu_switch_time_us); + } + + /* Nothing else to do for SWFI. */ + if (reason == SETRATE_SWFI) + goto out; + + /* Change the AXI bus frequency if we can. */ + if (strt_s->axiclk_khz != tgt_s->axiclk_khz) { + rc = ebi1_clk_set_min_rate(CLKVOTE_ACPUCLK, + tgt_s->axiclk_khz * 1000); + if (rc < 0) + pr_err("Setting AXI min rate failed!\n"); + } + + /* Nothing else to do for power collapse if not 7x27. */ + if (reason == SETRATE_PC && !cpu_is_msm7x27()) + goto out; + + /* Disable PLLs we are not using anymore. */ + if (tgt_s->pll != ACPU_PLL_TCXO) + plls_enabled &= ~(1 << tgt_s->pll); + for (pll = ACPU_PLL_0; pll <= ACPU_PLL_2; pll++) + if (plls_enabled & (1 << pll)) { + rc = pc_pll_request(pll, 0); + if (rc < 0) { + pr_err("PLL%d disable failed (%d)\n", pll, rc); + goto out; + } + } + + /* Nothing else to do for power collapse. */ + if (reason == SETRATE_PC) + goto out; + + /* Drop VDD level if we can. */ + if (tgt_s->vdd < strt_s->vdd) { + if (acpuclk_set_vdd_level(tgt_s->vdd) < 0) + printk(KERN_ERR "acpuclock: Unable to drop ACPU vdd\n"); + } + +#if PERF_SWITCH_DEBUG + printk(KERN_DEBUG "%s: ACPU speed change complete\n", __FUNCTION__); +#endif +out: + if (reason == SETRATE_CPUFREQ) + mutex_unlock(&drv_state.lock); + return rc; +} + +static void __init acpuclk_init(void) +{ + struct clkctl_acpu_speed *speed; + uint32_t div, sel; + int rc; + + /* + * Determine the rate of ACPU clock + */ + + if (!(readl(A11S_CLK_SEL_ADDR) & 0x01)) { /* CLK_SEL_SRC1N0 */ + /* CLK_SRC0_SEL */ + sel = (readl(A11S_CLK_CNTL_ADDR) >> 12) & 0x7; + /* CLK_SRC0_DIV */ + div = (readl(A11S_CLK_CNTL_ADDR) >> 8) & 0x0f; + } else { + /* CLK_SRC1_SEL */ + sel = (readl(A11S_CLK_CNTL_ADDR) >> 4) & 0x07; + /* CLK_SRC1_DIV */ + div = readl(A11S_CLK_CNTL_ADDR) & 0x0f; + } + + for (speed = acpu_freq_tbl; speed->a11clk_khz != 0; speed++) { + if (speed->a11clk_src_sel == sel + && (speed->a11clk_src_div == div)) + break; + } + if (speed->a11clk_khz == 0) { + printk(KERN_WARNING "Warning - ACPU clock reports invalid speed\n"); + return; + } + + drv_state.current_speed = speed; + + rc = ebi1_clk_set_min_rate(CLKVOTE_ACPUCLK, speed->axiclk_khz * 1000); + if (rc < 0) + pr_err("Setting AXI min rate failed!\n"); + + printk(KERN_INFO "ACPU running at %d KHz\n", speed->a11clk_khz); +} + +unsigned long acpuclk_get_rate(void) +{ + WARN_ONCE(drv_state.current_speed == NULL, + "acpuclk_get_rate: not initialized\n"); + if (drv_state.current_speed) + return drv_state.current_speed->a11clk_khz; + else + return 0; +} + +uint32_t acpuclk_get_switch_time(void) +{ + return drv_state.acpu_switch_time_us; +} + +/*---------------------------------------------------------------------------- + * Clock driver initialization + *---------------------------------------------------------------------------*/ + +#define DIV2REG(n) ((n)-1) +#define REG2DIV(n) ((n)+1) +#define SLOWER_BY(div, factor) div = DIV2REG(REG2DIV(div) * factor) + +static void __init acpu_freq_tbl_fixup(void) +{ + unsigned long pll0_l, pll1_l, pll2_l; + int axi_160mhz = 0, axi_200mhz = 0; + struct pll_freq_tbl_map *lst; + struct clkctl_acpu_speed *t; + unsigned int pll0_needs_fixup = 0; + + /* Wait for the PLLs to be initialized and then read their frequency. + */ + do { + pll0_l = readl(PLLn_L_VAL(0)) & 0x3f; + cpu_relax(); + udelay(50); + } while (pll0_l == 0); + do { + pll1_l = readl(PLLn_L_VAL(1)) & 0x3f; + cpu_relax(); + udelay(50); + } while (pll1_l == 0); + do { + pll2_l = readl(PLLn_L_VAL(2)) & 0x3f; + cpu_relax(); + udelay(50); + } while (pll2_l == 0); + + printk(KERN_INFO "L val: PLL0: %d, PLL1: %d, PLL2: %d\n", + (int)pll0_l, (int)pll1_l, (int)pll2_l); + + /* Some configurations run PLL0 twice as fast. Instead of having + * separate tables for this case, we simply fix up the ACPU clock + * source divider since it's a simple fix up. + */ + if (pll0_l == PLL_491_MHZ) { + pll0_l = PLL_245_MHZ; + pll0_needs_fixup = 1; + } + + /* Select the right table to use. */ + for (lst = acpu_freq_tbl_list; lst->tbl != 0; lst++) { + if (lst->pll0_l == pll0_l && lst->pll1_l == pll1_l + && lst->pll2_l == pll2_l) { + acpu_freq_tbl = lst->tbl; + break; + } + } + + if (acpu_freq_tbl == NULL) { + pr_crit("Unknown PLL configuration!\n"); + BUG(); + } + + /* Fix up PLL0 source divider if necessary. Also, fix up the AXI to + * the max that's supported by the board (RAM used in board). + */ + axi_160mhz = (pll0_l == PLL_960_MHZ || pll1_l == PLL_960_MHZ); + axi_200mhz = (pll2_l == PLL_1200_MHZ); + for (t = &acpu_freq_tbl[0]; t->a11clk_khz != 0; t++) { + + if (pll0_needs_fixup && t->pll == ACPU_PLL_0) + SLOWER_BY(t->a11clk_src_div, 2); + if (axi_160mhz && drv_state.max_axi_khz >= 160000 + && t->ahbclk_khz > 128000) + t->axiclk_khz = 160000; + if (axi_200mhz && drv_state.max_axi_khz >= 200000 + && t->ahbclk_khz > 160000) + t->axiclk_khz = 200000; + } + + t--; + drv_state.max_axi_khz = t->axiclk_khz; + + /* The default 7x27 ACPU clock plan supports running the AXI bus at + * 200 MHz. So we don't classify it as Turbo mode. + */ + if (cpu_is_msm7x27()) + return; + + if (!axi_160mhz) + pr_info("Turbo mode not supported.\n"); + else if (t->axiclk_khz == 160000) + pr_info("Turbo mode supported and enabled.\n"); + else + pr_info("Turbo mode supported but not enabled.\n"); +} + +/* Initalize the lpj field in the acpu_freq_tbl. */ +static void __init lpj_init(void) +{ + int i; + const struct clkctl_acpu_speed *base_clk = drv_state.current_speed; + for (i = 0; acpu_freq_tbl[i].a11clk_khz; i++) { + acpu_freq_tbl[i].lpj = cpufreq_scale(loops_per_jiffy, + base_clk->a11clk_khz, + acpu_freq_tbl[i].a11clk_khz); + } +} + +static void __init precompute_stepping(void) +{ + int i, step_idx; + +#define cur_freq acpu_freq_tbl[i].a11clk_khz +#define step_freq acpu_freq_tbl[step_idx].a11clk_khz +#define cur_pll acpu_freq_tbl[i].pll +#define step_pll acpu_freq_tbl[step_idx].pll + + for (i = 0; acpu_freq_tbl[i].a11clk_khz; i++) { + + /* Calculate max "up" step for each destination PLL */ + step_idx = i + 1; + while (step_freq && (step_freq - cur_freq) + <= drv_state.max_speed_delta_khz) { + acpu_freq_tbl[i].up[step_pll] = + &acpu_freq_tbl[step_idx]; + step_idx++; + } + if (step_idx == (i + 1) && step_freq) { + pr_crit("Delta between freqs %u KHz and %u KHz is" + " too high!\n", cur_freq, step_freq); + BUG(); + } + + /* Calculate max "down" step for each destination PLL */ + step_idx = i - 1; + while (step_idx >= 0 && (cur_freq - step_freq) + <= drv_state.max_speed_delta_khz) { + acpu_freq_tbl[i].down[step_pll] = + &acpu_freq_tbl[step_idx]; + step_idx--; + } + if (step_idx == (i - 1) && i > 0) { + pr_crit("Delta between freqs %u KHz and %u KHz is" + " too high!\n", cur_freq, step_freq); + BUG(); + } + } +} + +static void __init print_acpu_freq_tbl(void) +{ + struct clkctl_acpu_speed *t; + short down_idx[3]; + short up_idx[3]; + int i, j; + +#define FREQ_IDX(freq_ptr) (freq_ptr - acpu_freq_tbl) + pr_info("Id CPU-KHz PLL DIV AHB-KHz ADIV AXI-KHz " + "D0 D1 D2 U0 U1 U2\n"); + + t = &acpu_freq_tbl[0]; + for (i = 0; t->a11clk_khz != 0; i++) { + + for (j = 0; j < 3; j++) { + down_idx[j] = t->down[j] ? FREQ_IDX(t->down[j]) : -1; + up_idx[j] = t->up[j] ? FREQ_IDX(t->up[j]) : -1; + } + + pr_info("%2d %7d %3d %3d %7d %4d %7d " + "%2d %2d %2d %2d %2d %2d\n", + i, t->a11clk_khz, t->pll, t->a11clk_src_div + 1, + t->ahbclk_khz, t->ahbclk_div + 1, t->axiclk_khz, + down_idx[0], down_idx[1], down_idx[2], + up_idx[0], up_idx[1], up_idx[2]); + + t++; + } +} + +static void msm7x25_acpu_pll_hw_bug_fix(void) +{ + unsigned int n; + + /* The 7625 has a hardware bug and in order to select PLL2 we + * must program PLL3. Use the same table, and just fix up the + * numbers on this target. */ + for (n = 0; acpu_freq_tbl[n].a11clk_khz != 0; n++) + if (acpu_freq_tbl[n].pll == ACPU_PLL_2) + acpu_freq_tbl[n].a11clk_src_sel = 3; +} + +static void shared_pll_control_init(void) +{ +#define PLL_REMOTE_SPINLOCK_ID 7 + unsigned smem_size; + remote_spin_lock_init(&pll_lock, PLL_REMOTE_SPINLOCK_ID); + pll_control = smem_get_entry(SMEM_CLKREGIM_SOURCES, &smem_size); + + if (!pll_control) + pr_err("Unable to find shared PLL control data structure!\n"); + /* There might be more PLLs than what the application processor knows + * about. But the index used for each PLL is guaranteed to remain the + * same. */ + else if (smem_size < sizeof(struct shared_pll_control)) + pr_err("Shared PLL control data structure too small!\n"); + else if (pll_control->version != 0xCCEE0001) + pr_err("Shared PLL control version mismatch!\n"); + else { + pr_info("Shared PLL control available.\n"); + return; + } + + pll_control = NULL; + pr_err("Falling back to proc_comm PLL control.\n"); +} + +void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *clkdata) +{ + pr_info("acpu_clock_init()\n"); + + mutex_init(&drv_state.lock); + drv_state.acpu_switch_time_us = clkdata->acpu_switch_time_us; + drv_state.max_speed_delta_khz = clkdata->max_speed_delta_khz; + drv_state.vdd_switch_time_us = clkdata->vdd_switch_time_us; + drv_state.power_collapse_khz = clkdata->power_collapse_khz; + drv_state.wait_for_irq_khz = clkdata->wait_for_irq_khz; + drv_state.max_axi_khz = clkdata->max_axi_khz; + acpu_freq_tbl_fixup(); + precompute_stepping(); + acpuclk_init(); + lpj_init(); + print_acpu_freq_tbl(); + if (cpu_is_msm7x25()) + msm7x25_acpu_pll_hw_bug_fix(); + if (cpu_is_msm7x27()) + shared_pll_control_init(); +#ifdef CONFIG_CPU_FREQ_MSM + cpufreq_table_init(); + cpufreq_frequency_table_get_attr(freq_table, smp_processor_id()); +#endif +} diff --git a/arch/arm/mach-msm/acpuclock.h b/arch/arm/mach-msm/acpuclock.h new file mode 100644 index 000000000000..ad7b4cd2399d --- /dev/null +++ b/arch/arm/mach-msm/acpuclock.h @@ -0,0 +1,39 @@ +/* arch/arm/mach-msm/acpuclock.h + * + * MSM architecture clock driver header + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_ACPUCLOCK_H +#define __ARCH_ARM_MACH_MSM_ACPUCLOCK_H + +#include <linux/list.h> + +enum setrate_reason { + SETRATE_CPUFREQ = 0, + SETRATE_SWFI, + SETRATE_PC, +}; + +int acpuclk_set_rate(unsigned long rate, enum setrate_reason reason); +unsigned long acpuclk_get_rate(void); +uint32_t acpuclk_get_switch_time(void); +unsigned long acpuclk_wait_for_irq(void); +unsigned long acpuclk_power_collapse(void); + + +#endif + diff --git a/arch/arm/mach-msm/avs.c b/arch/arm/mach-msm/avs.c new file mode 100644 index 000000000000..297d3e1a4d52 --- /dev/null +++ b/arch/arm/mach-msm/avs.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/kernel_stat.h> +#include <linux/workqueue.h> + +#include "avs.h" + +#define AVSDSCR_INPUT 0x01004860 /* magic # from circuit designer */ +#define TSCSR_INPUT 0x00000001 /* enable temperature sense */ + +#define TEMPRS 16 /* total number of temperature regions */ +#define GET_TEMPR() (avs_get_tscsr() >> 28) /* scale TSCSR[CTEMP] to regions */ + +struct mutex avs_lock; + +static struct avs_state_s +{ + u32 freq_cnt; /* Frequencies supported list */ + short *avs_v; /* Dyanmically allocated storage for + * 2D table of voltages over temp & + * freq. Used as a set of 1D tables. + * Each table is for a single temp. + * For usage see avs_get_voltage + */ + int (*set_vdd) (int); /* Function Ptr for setting voltage */ + int changing; /* Clock frequency is changing */ + u32 freq_idx; /* Current frequency index */ + int vdd; /* Current ACPU voltage */ +} avs_state; + +/* + * Update the AVS voltage vs frequency table, for current temperature + * Adjust based on the AVS delay circuit hardware status + */ +static void avs_update_voltage_table(short *vdd_table) +{ + u32 avscsr; + int cpu; + int vu; + int l2; + int i; + u32 cur_freq_idx; + short cur_voltage; + + cur_freq_idx = avs_state.freq_idx; + cur_voltage = avs_state.vdd; + + avscsr = avs_test_delays(); + AVSDEBUG("avscsr=%x, avsdscr=%x\n", avscsr, avs_get_avsdscr()); + + /* + * Read the results for the various unit's AVS delay circuits + * 2=> up, 1=>down, 0=>no-change + */ + cpu = ((avscsr >> 23) & 2) + ((avscsr >> 16) & 1); + vu = ((avscsr >> 28) & 2) + ((avscsr >> 21) & 1); + l2 = ((avscsr >> 29) & 2) + ((avscsr >> 22) & 1); + + if ((cpu == 3) || (vu == 3) || (l2 == 3)) { + printk(KERN_ERR "AVS: Dly Synth O/P error\n"); + } else if ((cpu == 2) || (l2 == 2) || (vu == 2)) { + /* + * even if one oscillator asks for up, increase the voltage, + * as its an indication we are running outside the + * critical acceptable range of v-f combination. + */ + AVSDEBUG("cpu=%d l2=%d vu=%d\n", cpu, l2, vu); + AVSDEBUG("Voltage up at %d\n", cur_freq_idx); + + if (cur_voltage >= VOLTAGE_MAX) + printk(KERN_ERR + "AVS: Voltage can not get high enough!\n"); + + /* Raise the voltage for all frequencies */ + for (i = 0; i < avs_state.freq_cnt; i++) { + vdd_table[i] = cur_voltage + VOLTAGE_STEP; + if (vdd_table[i] > VOLTAGE_MAX) + vdd_table[i] = VOLTAGE_MAX; + } + } else if ((cpu == 1) && (l2 == 1) && (vu == 1)) { + if ((cur_voltage - VOLTAGE_STEP >= VOLTAGE_MIN) && + (cur_voltage <= vdd_table[cur_freq_idx])) { + vdd_table[cur_freq_idx] = cur_voltage - VOLTAGE_STEP; + AVSDEBUG("Voltage down for %d and lower levels\n", + cur_freq_idx); + + /* clamp to this voltage for all lower levels */ + for (i = 0; i < cur_freq_idx; i++) { + if (vdd_table[i] > vdd_table[cur_freq_idx]) + vdd_table[i] = vdd_table[cur_freq_idx]; + } + } + } +} + +/* + * Return the voltage for the target performance freq_idx and optionally + * use AVS hardware to check the present voltage freq_idx + */ +static short avs_get_target_voltage(int freq_idx, bool update_table) +{ + unsigned cur_tempr = GET_TEMPR(); + unsigned temp_index = cur_tempr*avs_state.freq_cnt; + + /* Table of voltages vs frequencies for this temp */ + short *vdd_table = avs_state.avs_v + temp_index; + + if (update_table) + avs_update_voltage_table(vdd_table); + + return vdd_table[freq_idx]; +} + + +/* + * Set the voltage for the freq_idx and optionally + * use AVS hardware to update the voltage + */ +static int avs_set_target_voltage(int freq_idx, bool update_table) +{ + int rc = 0; + int new_voltage = avs_get_target_voltage(freq_idx, update_table); + if (avs_state.vdd != new_voltage) { + AVSDEBUG("AVS setting V to %d mV @%d\n", + new_voltage, freq_idx); + rc = avs_state.set_vdd(new_voltage); + if (rc) + return rc; + avs_state.vdd = new_voltage; + } + return rc; +} + +/* + * Notify avs of clk frquency transition begin & end + */ +int avs_adjust_freq(u32 freq_idx, int begin) +{ + int rc = 0; + + if (!avs_state.set_vdd) { + /* AVS not initialized */ + return 0; + } + + if (freq_idx >= avs_state.freq_cnt) { + AVSDEBUG("Out of range :%d\n", freq_idx); + return -EINVAL; + } + + mutex_lock(&avs_lock); + if ((begin && (freq_idx > avs_state.freq_idx)) || + (!begin && (freq_idx < avs_state.freq_idx))) { + /* Update voltage before increasing frequency & + * after decreasing frequency + */ + rc = avs_set_target_voltage(freq_idx, 0); + if (rc) + goto aaf_out; + + avs_state.freq_idx = freq_idx; + } + avs_state.changing = begin; +aaf_out: + mutex_unlock(&avs_lock); + + return rc; +} + + +static struct delayed_work avs_work; +static struct workqueue_struct *kavs_wq; +#define AVS_DELAY ((CONFIG_HZ * 50 + 999) / 1000) + +static void do_avs_timer(struct work_struct *work) +{ + int cur_freq_idx; + + mutex_lock(&avs_lock); + if (!avs_state.changing) { + /* Only adjust the voltage if clk is stable */ + cur_freq_idx = avs_state.freq_idx; + avs_set_target_voltage(cur_freq_idx, 1); + } + mutex_unlock(&avs_lock); + queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY); +} + + +static void __init avs_timer_init(void) +{ + INIT_DELAYED_WORK_DEFERRABLE(&avs_work, do_avs_timer); + queue_delayed_work_on(0, kavs_wq, &avs_work, AVS_DELAY); +} + +static void __exit avs_timer_exit(void) +{ + cancel_delayed_work(&avs_work); +} + +static int __init avs_work_init(void) +{ + kavs_wq = create_workqueue("avs"); + if (!kavs_wq) { + printk(KERN_ERR "AVS initialization failed\n"); + return -EFAULT; + } + avs_timer_init(); + + return 1; +} + +static void __exit avs_work_exit(void) +{ + avs_timer_exit(); + destroy_workqueue(kavs_wq); +} + +int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx) +{ + int i; + + mutex_init(&avs_lock); + + if (freq_cnt == 0) + return -EINVAL; + + avs_state.freq_cnt = freq_cnt; + + if (freq_idx >= avs_state.freq_cnt) + return -EINVAL; + + avs_state.avs_v = kmalloc(TEMPRS * avs_state.freq_cnt * + sizeof(avs_state.avs_v[0]), GFP_KERNEL); + + if (avs_state.avs_v == 0) + return -ENOMEM; + + for (i = 0; i < TEMPRS*avs_state.freq_cnt; i++) + avs_state.avs_v[i] = VOLTAGE_MAX; + + avs_reset_delays(AVSDSCR_INPUT); + avs_set_tscsr(TSCSR_INPUT); + + avs_state.set_vdd = set_vdd; + avs_state.changing = 0; + avs_state.freq_idx = -1; + avs_state.vdd = -1; + avs_adjust_freq(freq_idx, 0); + + avs_work_init(); + + return 0; +} + +void __exit avs_exit() +{ + avs_work_exit(); + + kfree(avs_state.avs_v); +} + + diff --git a/arch/arm/mach-msm/avs.h b/arch/arm/mach-msm/avs.h new file mode 100644 index 000000000000..daae78938297 --- /dev/null +++ b/arch/arm/mach-msm/avs.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef AVS_H +#define AVS_H + +#define VOLTAGE_MIN 1000 /* mV */ +#define VOLTAGE_MAX 1250 +#define VOLTAGE_STEP 25 + +int __init avs_init(int (*set_vdd)(int), u32 freq_cnt, u32 freq_idx); +void __exit avs_exit(void); + +int avs_adjust_freq(u32 freq_index, int begin); + +/* Routines exported from avs_hw.S */ +u32 avs_test_delays(void); +u32 avs_reset_delays(u32 avsdscr); +u32 avs_get_avscsr(void); +u32 avs_get_avsdscr(void); +u32 avs_get_tscsr(void); +void avs_set_tscsr(u32 to_tscsr); + +/*#define AVSDEBUG(x...) pr_info("AVS: " x);*/ +#define AVSDEBUG(...) + +#endif /* AVS_H */ diff --git a/arch/arm/mach-msm/avs_hw.S b/arch/arm/mach-msm/avs_hw.S new file mode 100644 index 000000000000..57521b60d8f0 --- /dev/null +++ b/arch/arm/mach-msm/avs_hw.S @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + .text + + .global avs_test_delays +avs_test_delays: + +/* Read r1=CPMR and enable Never Sleep for VSLPDLY */ + mrc p15, 7, r1, c15, c0, 5 + orr r12, r1, #3, 24 + mcr p15, 7, r12, c15, c0, 5 + +/* Read r2=CPACR and enable full access to CP10 and CP11 space */ + mrc p15, 0, r2, c1, c0, 2 + orr r12, r2, #(0xf << 20) + mcr p15, 0, r12, c1, c0, 2 + isb + +/* Read r3=FPEXC and or in FP enable, VFP/ASE enable = FPEXC[30]; */ + fmrx r3, fpexc + orr r12, r3, #1, 2 + fmxr fpexc, r12 + +/* + * Do floating-point operations to prime the VFP pipeline. Use + * fcpyd d0, d0 as a floating point nop. This avoids changing VFP + * state. + */ + fcpyd d0, d0 + fcpyd d0, d0 + fcpyd d0, d0 + +/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */ + mrc p15, 7, r0, c15, c1, 7 + +/* Restore FPEXC */ + fmxr fpexc, r3 + +/* Restore CPACR */ + MCR p15, 0, r2, c1, c0, 2 + +/* Restore CPMR */ + mcr p15, 7, r1, c15, c0, 5 + isb + + bx lr + + + + + .global avs_get_avscsr +/* Read r0=AVSCSR to get status from CPU, VFP, and L2 ring oscillators */ + +avs_get_avscsr: + mrc p15, 7, r0, c15, c1, 7 + bx lr + + .global avs_get_avsdscr +/* Read r0=AVSDSCR to get the AVS Delay Synthesizer control settings */ + +avs_get_avsdscr: + mrc p15, 7, r0, c15, c0, 6 + bx lr + + + + + .global avs_get_tscsr +/* Read r0=TSCSR to get temperature sensor control and status */ + +avs_get_tscsr: + mrc p15, 7, r0, c15, c1, 0 + bx lr + + .global avs_set_tscsr +/* Write TSCSR=r0 to set temperature sensor control and status */ + +avs_set_tscsr: + mcr p15, 7, r0, c15, c1, 0 + bx lr + + + + + + .global avs_reset_delays +avs_reset_delays: + +/* AVSCSR(0x61) to enable CPU, V and L2 AVS module */ + mov r3, #0x61 + mcr p15, 7, r3, c15, c1, 7 + +/* AVSDSCR(dly) to program delay */ + mcr p15, 7, r0, c15, c0, 6 + +/* Read r0=AVSDSCR */ + mrc p15, 7, r0, c15, c0, 6 + + bx lr + + .end + + diff --git a/arch/arm/mach-msm/board-comet.c b/arch/arm/mach-msm/board-comet.c new file mode 100644 index 000000000000..01333821f343 --- /dev/null +++ b/arch/arm/mach-msm/board-comet.c @@ -0,0 +1,488 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/bootmem.h> +#include <linux/i2c.h> +#include <linux/io.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include <mach/mmc.h> +#include <mach/vreg.h> +#include <mach/board.h> +#include <mach/sirc.h> +#include <linux/spi/spi.h> + +#include "devices.h" +#include "timer.h" +#include "pm.h" + +#define TOUCHPAD_SUSPEND 34 +#define TOUCHPAD_IRQ 42 + +#define MSM_PMEM_MDP_SIZE 0x800000 +#define MSM_FB_SIZE 0x500000 +#define MSM_AUDIO_SIZE 0x200000 + +#define MSM_SMI_BASE 0x2b00000 +#define MSM_SMI_SIZE 0x1500000 + +#define MSM_FB_BASE MSM_SMI_BASE +#define MSM_PMEM_GPU0_BASE (MSM_FB_BASE + MSM_FB_SIZE) +#define MSM_PMEM_GPU0_SIZE (MSM_SMI_SIZE - MSM_FB_SIZE) + +#define COMET_CPLD_START 0x70004000 +#define COMET_CPLD_PER_ENABLE 0x00000010 +#define COMET_CPLD_PER_RESET 0x00000018 +#define COMET_CPLD_STATUS 0x00000028 +#define COMET_CPLD_EXT_PER_ENABLE 0x00000030 +#define COMET_CPLD_I2C_ENABLE 0x00000038 +#define COMET_CPLD_EXT_PER_RESET 0x00000048 +#define COMET_CPLD_VERSION 0x00000058 + +#define COMET_CPLD_SIZE 0x00000060 +#define COMET_CPLD_STATUS_WVGA 0x0004 +#define COMET_CPLD_VERSION_MAJOR 0xFF00 +#define COMET_CPLD_PER_ENABLE_WVGA 0x0400 +#define COMET_CPLD_PER_ENABLE_LVDS 0x0200 +#define COMET_CPLD_PER_ENABLE_WXGA 0x0040 +#define COMET_CPLD_EXT_PER_ENABLE_WXGA 0x0080 + +static unsigned long vreg_sts, gpio_sts; +static struct vreg *vreg_mmc; +static int gp6_enabled; + +static int cpld_version; +static bool wvga_present; +static bool wxga_present; +static struct comet_cpld_t { + u16 per_reset_all_reset; + u16 ext_per_reset_all_reset; + u16 i2c_enable; + u16 per_enable_all; + u16 ext_per_enable_all; + u16 bt_reset_reg; + u16 bt_reset_mask; +} comet_cpld[] = { + [0] = { + .per_reset_all_reset = 0x00FF, + /* enable all peripherals except microphones and */ + /* reset line for i2c touchpad */ + .per_enable_all = 0xFFD8, + .bt_reset_reg = 0x0018, + .bt_reset_mask = 0x0001, + }, + [1] = { + .per_reset_all_reset = 0x00BF, + .ext_per_reset_all_reset = 0x0007, + .i2c_enable = 0x07F7, + /* enable all peripherals except microphones and */ + /* displays */ + .per_enable_all = 0xF9B8, + .ext_per_enable_all = 0x007D, + .bt_reset_reg = 0x0048, + .bt_reset_mask = 0x0004, + }, +}; +static struct comet_cpld_t *cpld_info; + +static struct resource smc911x_resources[] = { + [0] = { + .start = 0x84000000, + .end = 0x84000100, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(156), + .end = 156, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc911x_device = { + .name = "smc911x", + .id = 0, + .num_resources = ARRAY_SIZE(smc911x_resources), + .resource = smc911x_resources, +}; + +static void __iomem *comet_cpld_base(void) +{ + static void __iomem *comet_cpld_base_addr; + + if (!comet_cpld_base_addr) { + if (!request_mem_region(COMET_CPLD_START, COMET_CPLD_SIZE, + "cpld")) { + printk(KERN_ERR + "%s: request_mem_region for comet cpld failed\n", + __func__); + goto cpld_base_exit; + } + comet_cpld_base_addr = ioremap(COMET_CPLD_START, + COMET_CPLD_SIZE); + if (!comet_cpld_base_addr) { + release_mem_region(COMET_CPLD_START, + COMET_CPLD_SIZE); + printk(KERN_ERR "%s: Could not map comet cpld\n", + __func__); + } + } +cpld_base_exit: + return comet_cpld_base_addr; +} + +static struct platform_device *devices[] __initdata = { + &msm_device_smd, + &msm_device_dmov, + &smc911x_device, + &msm_device_nand, +}; + + +#define KBD_RST 35 +#define KBD_IRQ 144 + +static void kbd_gpio_release(void) +{ + gpio_free(KBD_IRQ); + gpio_free(KBD_RST); +} + +static int kbd_gpio_setup(void) +{ + int rc; + int respin = KBD_RST; + int irqpin = KBD_IRQ; + unsigned rescfg = + GPIO_CFG(respin, 0, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA); + unsigned irqcfg = + GPIO_CFG(irqpin, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA); + + rc = gpio_request(irqpin, "gpio_keybd_irq"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + irqpin, rc); + goto err_gpioconfig; + } + rc = gpio_request(respin, "gpio_keybd_reset"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + respin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(rescfg, GPIO_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + respin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(irqcfg, GPIO_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + irqpin, rc); + goto err_gpioconfig; + } + return rc; + +err_gpioconfig: + kbd_gpio_release(); + return rc; +} + +static void __init comet_init_irq(void) +{ + msm_init_irq(); + msm_init_sirc(); +} + +static void sdcc_gpio_init(void) +{ + /* SDC1 GPIOs */ + if (gpio_request(51, "sdc1_data_3")) + pr_err("failed to request gpio sdc1_data_3\n"); + if (gpio_request(52, "sdc1_data_2")) + pr_err("failed to request gpio sdc1_data_2\n"); + if (gpio_request(53, "sdc1_data_1")) + pr_err("failed to request gpio sdc1_data_1\n"); + if (gpio_request(54, "sdc1_data_0")) + pr_err("failed to request gpio sdc1_data_0\n"); + if (gpio_request(55, "sdc1_cmd")) + pr_err("failed to request gpio sdc1_cmd\n"); + if (gpio_request(56, "sdc1_clk")) + pr_err("failed to request gpio sdc1_clk\n"); + + /* SDC2 GPIOs */ + if (gpio_request(62, "sdc2_clk")) + pr_err("failed to request gpio sdc2_clk\n"); + if (gpio_request(63, "sdc2_cmd")) + pr_err("failed to request gpio sdc2_cmd\n"); + if (gpio_request(64, "sdc2_data_3")) + pr_err("failed to request gpio sdc2_data_3\n"); + if (gpio_request(65, "sdc2_data_2")) + pr_err("failed to request gpio sdc2_data_2\n"); + if (gpio_request(66, "sdc2_data_1")) + pr_err("failed to request gpio sdc2_data_1\n"); + if (gpio_request(67, "sdc2_data_0")) + pr_err("failed to request gpio sdc2_data_0\n"); + + /* SDC3 GPIOs */ + if (gpio_request(88, "sdc3_clk")) + pr_err("failed to request gpio sdc3_clk\n"); + if (gpio_request(89, "sdc3_cmd")) + pr_err("failed to request gpio sdc3_cmd\n"); + if (gpio_request(90, "sdc3_data_3")) + pr_err("failed to request gpio sdc3_data_3\n"); + if (gpio_request(91, "sdc3_data_2")) + pr_err("failed to request gpio sdc3_data_2\n"); + if (gpio_request(92, "sdc3_data_1")) + pr_err("failed to request gpio sdc3_data_1\n"); + if (gpio_request(93, "sdc3_data_0")) + pr_err("failed to request gpio sdc3_data_0\n"); + +} + +static unsigned sdcc_cfg_data[][6] = { + /* SDC1 configs */ + { + GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + }, + /* SDC2 configs */ + { + GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, + /* SDC3 configs */ + { + GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, +}; + +static void msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int i, rc; + + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return; + + if (enable) + set_bit(dev_id, &gpio_sts); + else + clear_bit(dev_id, &gpio_sts); + + for (i = 0; i < ARRAY_SIZE(sdcc_cfg_data[dev_id - 1]); i++) { + rc = gpio_tlmm_config(sdcc_cfg_data[dev_id - 1][i], + enable ? GPIO_ENABLE : GPIO_DISABLE); + if (rc) + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, sdcc_cfg_data[dev_id - 1][i], rc); + } +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + msm_sdcc_setup_gpio(pdev->id, !!vdd); + + if (vdd == 0) { + if (!vreg_sts) + return 0; + + clear_bit(pdev->id, &vreg_sts); + + if (!vreg_sts && !gp6_enabled) { + rc = vreg_disable(vreg_mmc); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + return 0; + } + + if (!vreg_sts && !gp6_enabled) { + rc = vreg_set_level(vreg_mmc, 2850); + if (!rc) + rc = vreg_enable(vreg_mmc); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + set_bit(pdev->id, &vreg_sts); + return 0; +} + +static struct mmc_platform_data comet_sdcc_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +}; + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].supported = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 8594, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].residency = 23740, + + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].supported = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 4594, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].residency = 23740, + + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].supported = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].suspend_enabled + = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].idle_enabled = 0, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 443, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].residency = 1098, + + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].supported = 1, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency = 2, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].residency = 0, +}; + +static void __init comet_init(void) +{ + char __iomem *cpld_base; + int per_enable; + int ext_per_enable; + + cpld_base = comet_cpld_base(); + + if (!cpld_base) + return; + + cpld_version = (readw(cpld_base + COMET_CPLD_VERSION) & + COMET_CPLD_VERSION_MAJOR) >> 8; + if (cpld_version >= 2) { + cpld_info = &comet_cpld[1]; + per_enable = cpld_info->per_enable_all; + wvga_present = (readw(cpld_base + COMET_CPLD_STATUS) + & COMET_CPLD_STATUS_WVGA) != 0; + wxga_present = !wvga_present; + ext_per_enable = cpld_info->ext_per_enable_all; + if (wvga_present) + per_enable |= COMET_CPLD_PER_ENABLE_WVGA; + else { + per_enable |= COMET_CPLD_PER_ENABLE_LVDS | + COMET_CPLD_PER_ENABLE_WXGA; + ext_per_enable |= COMET_CPLD_EXT_PER_ENABLE_WXGA; + } + writew(ext_per_enable, + cpld_base + COMET_CPLD_EXT_PER_ENABLE); + writew(cpld_info->i2c_enable, + cpld_base + COMET_CPLD_I2C_ENABLE); + writew(cpld_info->ext_per_reset_all_reset, + cpld_base + COMET_CPLD_EXT_PER_RESET); + } else { + cpld_info = &comet_cpld[0]; + wvga_present = 1; + wxga_present = 0; + per_enable = cpld_info->per_enable_all; + smc911x_resources[0].start = 0x90000000; + smc911x_resources[0].end = 0x90000100; + } + + writew(per_enable, + cpld_base + COMET_CPLD_PER_ENABLE); + writew(cpld_info->per_reset_all_reset, + cpld_base + COMET_CPLD_PER_RESET); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_pm_set_platform_data(msm_pm_data); +} + +static void __init comet_map_io(void) +{ + msm_map_comet_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +MACHINE_START(QSD8X50_COMET, "QCT QSD8x50 Comet") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x0, + .map_io = comet_map_io, + .init_irq = comet_init_irq, + .init_machine = comet_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-dream.c b/arch/arm/mach-msm/board-dream.c index 21afa8513168..96ac5c2256bf 100644 --- a/arch/arm/mach-msm/board-dream.c +++ b/arch/arm/mach-msm/board-dream.c @@ -34,8 +34,6 @@ static struct platform_device *devices[] __initdata = { &msm_device_uart3, &msm_device_smd, &msm_device_nand, - &msm_device_hsusb, - &msm_device_i2c, }; extern struct sys_timer msm_timer; @@ -78,7 +76,7 @@ static void __init trout_map_io(void) writeb(0x80, TROUT_CPLD_BASE + 0x00); #endif - msm_clock_init(); + msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a); } MACHINE_START(TROUT, "HTC Dream") diff --git a/arch/arm/mach-msm/board-halibut-keypad.c b/arch/arm/mach-msm/board-halibut-keypad.c new file mode 100644 index 000000000000..49c1075627d3 --- /dev/null +++ b/arch/arm/mach-msm/board-halibut-keypad.c @@ -0,0 +1,177 @@ +/* linux/arch/arm/mach-msm/board-halibut-keypad.c + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <asm/mach-types.h> +#include <linux/platform_device.h> +#include <linux/gpio_event.h> + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_halibut." +static int halibut_ffa; +module_param_named(ffa, halibut_ffa, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define SCAN_FUNCTION_KEYS 0 /* don't turn this on without updating the ffa support */ + +static unsigned int halibut_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int halibut_col_gpios[] = { 36, 37, 38, 39, 40 }; + +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(halibut_col_gpios) + (col)) + +static const unsigned short halibut_keymap[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static const unsigned short halibut_keymap_ffa[ARRAY_SIZE(halibut_col_gpios) * ARRAY_SIZE(halibut_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +static struct gpio_event_matrix_info halibut_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = halibut_keymap, + .output_gpios = halibut_row_gpios, + .input_gpios = halibut_col_gpios, + .noutputs = ARRAY_SIZE(halibut_row_gpios), + .ninputs = ARRAY_SIZE(halibut_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +struct gpio_event_info *halibut_keypad_info[] = { + &halibut_matrix_info.info +}; + +static struct gpio_event_platform_data halibut_keypad_data = { + .name = "halibut_keypad", + .info = halibut_keypad_info, + .info_count = ARRAY_SIZE(halibut_keypad_info) +}; + +static struct platform_device halibut_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &halibut_keypad_data, + }, +}; + +static int __init halibut_init_keypad(void) +{ + if (!machine_is_halibut()) + return 0; + if (halibut_ffa) + halibut_matrix_info.keymap = halibut_keymap_ffa; + return platform_device_register(&halibut_keypad_device); +} + +device_initcall(halibut_init_keypad); diff --git a/arch/arm/mach-msm/board-halibut.c b/arch/arm/mach-msm/board-halibut.c index e61967dde9a1..abb123aab8e2 100644 --- a/arch/arm/mach-msm/board-halibut.c +++ b/arch/arm/mach-msm/board-halibut.c @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/board-halibut.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -13,33 +14,64 @@ * GNU General Public License for more details. * */ - #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/io.h> #include <linux/delay.h> +#include <linux/bootmem.h> +#include <linux/i2c.h> +#include <linux/android_pmem.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> #include <mach/hardware.h> +#include <mach/irqs.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> #include <asm/mach/flash.h> +#include <asm/setup.h> +#include <mach/mmc.h> +#include <mach/vreg.h> +#include <mach/mpp.h> +#include <mach/gpio.h> #include <mach/irqs.h> #include <mach/board.h> #include <mach/msm_iomap.h> - -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> +#include <mach/msm_serial_hs.h> +#include <mach/msm_hsusb.h> +#include <mach/vreg.h> +#include <mach/msm_rpcrouter.h> +#include <mach/memory.h> +#include <mach/camera.h> #include "devices.h" +#include "socinfo.h" +#include "clock.h" +#include "msm-keypad-devices.h" +#include "pm.h" + +#ifdef CONFIG_MSM_STACKED_MEMORY +#define MSM_SMI_BASE 0x100000 +#define MSM_SMI_SIZE 0x800000 + +#define MSM_PMEM_GPU0_BASE MSM_SMI_BASE +#define MSM_PMEM_GPU0_SIZE 0x800000 +#endif + +#define MSM_PMEM_MDP_SIZE 0x800000 +#define MSM_PMEM_CAMERA_SIZE 0xa00000 +#define MSM_PMEM_ADSP_SIZE 0x800000 +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_FB_SIZE 0x200000 static struct resource smc91x_resources[] = { [0] = { .start = 0x9C004300, - .end = 0x9C004400, + .end = 0x9C0043ff, .flags = IORESOURCE_MEM, }, [1] = { @@ -49,6 +81,132 @@ static struct resource smc91x_resources[] = { }, }; +#ifdef CONFIG_USB_FUNCTION +static struct usb_mass_storage_platform_data usb_mass_storage_pdata = { + .nluns = 0x02, + .buf_size = 16384, + .vendor = "GOOGLE", + .product = "Mass storage", + .release = 0xffff, +}; + +static struct platform_device mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &usb_mass_storage_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x05C6, + .product_id = 0xF000, + .adb_product_id = 0x9015, + .version = 0x0100, + .product_name = "Qualcomm HSUSB Device", + .manufacturer_name = "Qualcomm Incorporated", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_FUNCTION +static void hsusb_gpio_init(void) +{ + if (gpio_request(111, "ulpi_data_0")) + pr_err("failed to request gpio ulpi_data_0\n"); + if (gpio_request(112, "ulpi_data_1")) + pr_err("failed to request gpio ulpi_data_1\n"); + if (gpio_request(113, "ulpi_data_2")) + pr_err("failed to request gpio ulpi_data_2\n"); + if (gpio_request(114, "ulpi_data_3")) + pr_err("failed to request gpio ulpi_data_3\n"); + if (gpio_request(115, "ulpi_data_4")) + pr_err("failed to request gpio ulpi_data_4\n"); + if (gpio_request(116, "ulpi_data_5")) + pr_err("failed to request gpio ulpi_data_5\n"); + if (gpio_request(117, "ulpi_data_6")) + pr_err("failed to request gpio ulpi_data_6\n"); + if (gpio_request(118, "ulpi_data_7")) + pr_err("failed to request gpio ulpi_data_7\n"); + if (gpio_request(119, "ulpi_dir")) + pr_err("failed to request gpio ulpi_dir\n"); + if (gpio_request(120, "ulpi_next")) + pr_err("failed to request gpio ulpi_next\n"); + if (gpio_request(121, "ulpi_stop")) + pr_err("failed to request gpio ulpi_stop\n"); +} + +static unsigned usb_gpio_lpm_config[] = { + GPIO_CFG(111, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 0 */ + GPIO_CFG(112, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 1 */ + GPIO_CFG(113, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 2 */ + GPIO_CFG(114, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 3 */ + GPIO_CFG(115, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 4 */ + GPIO_CFG(116, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 5 */ + GPIO_CFG(117, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 6 */ + GPIO_CFG(118, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DATA 7 */ + GPIO_CFG(119, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* DIR */ + GPIO_CFG(120, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* NEXT */ + GPIO_CFG(121, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* STOP */ +}; + +static unsigned usb_gpio_lpm_unconfig[] = { + GPIO_CFG(111, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 0 */ + GPIO_CFG(112, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 1 */ + GPIO_CFG(113, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 2 */ + GPIO_CFG(114, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 3 */ + GPIO_CFG(115, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 4 */ + GPIO_CFG(116, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 5 */ + GPIO_CFG(117, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 6 */ + GPIO_CFG(118, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DATA 7 */ + GPIO_CFG(119, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DIR */ + GPIO_CFG(120, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* NEXT */ + GPIO_CFG(121, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_2MA), /* STOP */ +}; + +static int usb_config_gpio(int config) +{ + int pin, rc; + + if (config) { + for (pin = 0; pin < ARRAY_SIZE(usb_gpio_lpm_config); pin++) { + rc = gpio_tlmm_config(usb_gpio_lpm_config[pin], + GPIO_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, usb_gpio_lpm_config[pin], rc); + return -EIO; + } + } + } else { + for (pin = 0; pin < ARRAY_SIZE(usb_gpio_lpm_unconfig); pin++) { + rc = gpio_tlmm_config(usb_gpio_lpm_unconfig[pin], + GPIO_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, usb_gpio_lpm_config[pin], rc); + return -EIO; + } + } + } + + return 0; +} +#endif + + static struct platform_device smc91x_device = { .name = "smc91x", .id = 0, @@ -57,33 +215,565 @@ static struct platform_device smc91x_device = { }; static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) &msm_device_uart3, +#endif + &msm_device_uart_dm1, &msm_device_smd, + &msm_device_dmov, &msm_device_nand, - &msm_device_hsusb, &msm_device_i2c, &smc91x_device, + &msm_device_tssc, + &android_pmem_camera_device, + &android_pmem_device, + &android_pmem_adsp_device, +#ifdef CONFIG_MSM_STACKED_MEMORY + &android_pmem_gpu0_device, +#endif + &android_pmem_gpu1_device, + &msm_device_hsusb_otg, + &msm_device_hsusb_host, +#if defined(CONFIG_USB_FUNCTION) || defined(CONFIG_USB_ANDROID) + &msm_device_hsusb_peripheral, +#endif +#ifdef CONFIG_USB_FUNCTION + &mass_storage_device, +#endif +#ifdef CONFIG_USB_ANDROID + &android_usb_device, +#endif + +#ifdef CONFIG_BT + &msm_bt_power_device, +#endif + &halibut_snd, + &msm_bluesleep_device, + &msm_fb_device, + &mddi_toshiba_device, + &mddi_sharp_device, }; extern struct sys_timer msm_timer; +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO("mt9d112", 0x78 >> 1), + }, + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +}; + +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCLK */ + GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], GPIO_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} + +static void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +static void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +#define MSM_PROBE_INIT(name) name##_probe_init +static struct msm_camera_sensor_info msm_camera_sensor[] = { + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .sensor_name = "mt9d112", + .flash_type = MSM_CAMERA_FLASH_NONE, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(mt9d112), +#endif + }, + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .sensor_name = "s5k3e2fx", + .flash_type = MSM_CAMERA_FLASH_NONE, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(s5k3e2fx), +#endif + }, + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 88, + .sensor_name = "mt9p012", + .flash_type = MSM_CAMERA_FLASH_LED, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(mt9p012), +#endif + }, + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .sensor_name = "mt9t013", + .flash_type = MSM_CAMERA_FLASH_NONE, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(mt9t013), +#endif + }, +}; +#undef MSM_PROBE_INIT + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .snum = ARRAY_SIZE(msm_camera_sensor), + .sinfo = &msm_camera_sensor[0], + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +static void __init msm_camera_add_device(void) +{ + msm_camera_register_device(NULL, 0, &msm_camera_device_data); + config_camera_off_gpios(); +} + static void __init halibut_init_irq(void) { msm_init_irq(); } +static struct msm_acpu_clock_platform_data halibut_clock_data = { + .acpu_switch_time_us = 50, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, + .max_axi_khz = 128000, +}; + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); +static void sdcc_gpio_init(void) +{ +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + int rc = 0; + if (gpio_request(49, "sdc1_status_irq")) + pr_err("failed to request gpio sdc1_status_irq\n"); + rc = gpio_tlmm_config(GPIO_CFG(49, 0, GPIO_INPUT, GPIO_PULL_UP, + GPIO_2MA), GPIO_ENABLE); + if (rc) + printk(KERN_ERR "%s: Failed to configure GPIO %d\n", + __func__, rc); +#endif + /* SDC1 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + if (gpio_request(51, "sdc1_data_3")) + pr_err("failed to request gpio sdc1_data_3\n"); + if (gpio_request(52, "sdc1_data_2")) + pr_err("failed to request gpio sdc1_data_2\n"); + if (gpio_request(53, "sdc1_data_1")) + pr_err("failed to request gpio sdc1_data_1\n"); + if (gpio_request(54, "sdc1_data_0")) + pr_err("failed to request gpio sdc1_data_0\n"); + if (gpio_request(55, "sdc1_cmd")) + pr_err("failed to request gpio sdc1_cmd\n"); + if (gpio_request(56, "sdc1_clk")) + pr_err("failed to request gpio sdc1_clk\n"); +#endif + + if (machine_is_msm7201a_ffa()) + return; + + /* SDC2 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + if (gpio_request(62, "sdc2_clk")) + pr_err("failed to request gpio sdc2_clk\n"); + if (gpio_request(63, "sdc2_cmd")) + pr_err("failed to request gpio sdc2_cmd\n"); + if (gpio_request(64, "sdc2_data_3")) + pr_err("failed to request gpio sdc2_data_3\n"); + if (gpio_request(65, "sdc2_data_2")) + pr_err("failed to request gpio sdc2_data_2\n"); + if (gpio_request(66, "sdc2_data_1")) + pr_err("failed to request gpio sdc2_data_1\n"); + if (gpio_request(67, "sdc2_data_0")) + pr_err("failed to request gpio sdc2_data_0\n"); +#endif + + /* SDC4 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + if (gpio_request(19, "sdc4_data_3")) + pr_err("failed to request gpio sdc4_data_3\n"); + if (gpio_request(20, "sdc4_data_2")) + pr_err("failed to request gpio sdc4_data_2\n"); + if (gpio_request(21, "sdc4_data_1")) + pr_err("failed to request gpio sdc4_data_1\n"); + if (gpio_request(107, "sdc4_cmd")) + pr_err("failed to request gpio sdc4_cmd\n"); + if (gpio_request(108, "sdc4_data_0")) + pr_err("failed to request gpio sdc4_data_0\n"); + if (gpio_request(109, "sdc4_clk")) + pr_err("failed to request gpio sdc4_clk\n"); +#endif +} + +static unsigned sdcc_cfg_data[][6] = { + /* SDC1 configs */ + { + GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + }, + /* SDC2 configs */ + { + GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, + { + /* SDC3 configs */ + }, + /* SDC4 configs */ + { + GPIO_CFG(19, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(20, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(21, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(107, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(108, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(109, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + } +}; + +static unsigned long vreg_sts, gpio_sts; +static struct mpp *mpp_mmc; +static struct vreg *vreg_mmc; + +static void msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int i, rc; + + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return; + + if (enable) + set_bit(dev_id, &gpio_sts); + else + clear_bit(dev_id, &gpio_sts); + + for (i = 0; i < ARRAY_SIZE(sdcc_cfg_data[dev_id - 1]); i++) { + rc = gpio_tlmm_config(sdcc_cfg_data[dev_id - 1][i], + enable ? GPIO_ENABLE : GPIO_DISABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, sdcc_cfg_data[dev_id - 1][i], rc); + } + } +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + msm_sdcc_setup_gpio(pdev->id, !!vdd); + + if (vdd == 0) { + if (!vreg_sts) + return 0; + + clear_bit(pdev->id, &vreg_sts); + + if (!vreg_sts) { + if (machine_is_msm7201a_ffa()) + rc = mpp_config_digital_out(mpp_mmc, + MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_LOW)); + else + rc = vreg_disable(vreg_mmc); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + return 0; + } + + if (!vreg_sts) { + if (machine_is_msm7201a_ffa()) + rc = mpp_config_digital_out(mpp_mmc, + MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_HIGH)); + else { + rc = vreg_set_level(vreg_mmc, 2850); + if (!rc) + rc = vreg_enable(vreg_mmc); + } + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + set_bit(pdev->id, &vreg_sts); + return 0; +} + +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION +static unsigned int halibut_sdcc_slot_status(struct device *dev) +{ + return (unsinged int) gpio_get_value(49); +} +#endif + +static struct mmc_platform_data halibut_sdcc_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION + .status = halibut_sdcc_slot_status, + .status_irq = MSM_GPIO_TO_INT(49), + .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +#endif +}; + +static void __init halibut_init_mmc(void) +{ + if (machine_is_msm7201a_ffa()) { + mpp_mmc = mpp_get(NULL, "mpp3"); + if (!mpp_mmc) { + printk(KERN_ERR "%s: mpp get failed (%ld)\n", + __func__, PTR_ERR(vreg_mmc)); + return; + } + } else { + vreg_mmc = vreg_get(NULL, "mmc"); + if (IS_ERR(vreg_mmc)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_mmc)); + return; + } + } + + sdcc_gpio_init(); +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + msm_add_sdcc(1, &halibut_sdcc_data); +#endif + + if (machine_is_msm7201a_surf()) { +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + msm_add_sdcc(2, &halibut_sdcc_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + msm_add_sdcc(4, &halibut_sdcc_data); +#endif + } +} + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = 97, +}; + +static void __init msm_fb_add_devices(void) +{ + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("ebi2", 0); + msm_fb_register_device("pmdh", &mddi_pdata); + msm_fb_register_device("emdh", 0); + msm_fb_register_device("tvenc", &tvenc_pdata); +} + +static struct msm_i2c_platform_data msm_i2c_pdata = { + .clk_freq = 100000, +}; + +static void __init msm_device_i2c_init(void) +{ + msm_device_i2c.dev.platform_data = &msm_i2c_pdata; +} + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 16000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 12000, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 2000, +}; + static void __init halibut_init(void) { + if (socinfo_init() < 0) + BUG(); + + if (machine_is_msm7201a_ffa()) { + smc91x_resources[0].start = 0x98000300; + smc91x_resources[0].end = 0x980003ff; + smc91x_resources[1].start = MSM_GPIO_TO_INT(85); + smc91x_resources[1].end = MSM_GPIO_TO_INT(85); + } + + /* All 7x01 2.0 based boards are expected to have RAM chips capable + * of 160 MHz. */ + if (cpu_is_msm7x01() + && SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 2) + halibut_clock_data.max_axi_khz = 160000; + + msm_acpu_clock_init(&halibut_clock_data); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + msm_hsusb_pdata.soc_version = socinfo_get_version(); + msm_acpu_clock_init(&halibut_clock_data); + msm_device_hsusb_peripheral.dev.platform_data = &msm_hsusb_pdata, + msm_device_hsusb_host.dev.platform_data = &msm_hsusb_pdata, platform_add_devices(devices, ARRAY_SIZE(devices)); + halibut_init_mmc(); + msm_pm_set_platform_data(msm_pm_data); +} + +static void __init msm_halibut_allocate_memory_regions(void) +{ + void *addr; + unsigned long size; + + size = MSM_PMEM_MDP_SIZE; + addr = alloc_bootmem(size); + android_pmem_pdata.start = __pa(addr); + android_pmem_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for pmem\n", size, addr, __pa(addr)); + + size = MSM_PMEM_CAMERA_SIZE; + addr = alloc_bootmem(size); + android_pmem_camera_pdata.start = __pa(addr); + android_pmem_camera_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for camera pmem\n", size, addr, __pa(addr)); + + size = MSM_PMEM_ADSP_SIZE; + addr = alloc_bootmem(size); + android_pmem_adsp_pdata.start = __pa(addr); + android_pmem_adsp_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for adsp pmem\n", size, addr, __pa(addr)); + + size = MSM_PMEM_GPU1_SIZE; + addr = alloc_bootmem_aligned(size, 0x100000); + android_pmem_gpu1_pdata.start = __pa(addr); + android_pmem_gpu1_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for gpu1 pmem\n", size, addr, __pa(addr)); + + size = MSM_FB_SIZE; + addr = alloc_bootmem(size); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); + } static void __init halibut_map_io(void) { + msm_shared_ram_phys = 0x01F00000; + msm_map_common_io(); - msm_clock_init(); + msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a); + msm_halibut_allocate_memory_regions(); } MACHINE_START(HALIBUT, "Halibut Board (QCT SURF7200A)") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x10000100, + .map_io = halibut_map_io, + .init_irq = halibut_init_irq, + .init_machine = halibut_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(MSM7201A_FFA, "QCT FFA7201A Board") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x10000100, + .map_io = halibut_map_io, + .init_irq = halibut_init_irq, + .init_machine = halibut_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(MSM7201A_SURF, "QCT SURF7201A Board") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif .boot_params = 0x10000100, .map_io = halibut_map_io, .init_irq = halibut_init_irq, diff --git a/arch/arm/mach-msm/board-msm7x27.c b/arch/arm/mach-msm/board-msm7x27.c new file mode 100644 index 000000000000..32cdf4cabc72 --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x27.c @@ -0,0 +1,1195 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/bootmem.h> +#include <linux/usb/mass_storage_function.h> + +#include <mach/hardware.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> +#include <asm/setup.h> +#ifdef CONFIG_CACHE_L2X0 +#include <asm/hardware/cache-l2x0.h> +#endif + +#include <asm/mach/mmc.h> +#include <mach/vreg.h> +#include <mach/mpp.h> +#include <mach/gpio.h> +#include <mach/board.h> +#include <mach/msm_iomap.h> +#include <mach/msm_rpcrouter.h> +#include <mach/msm_hsusb.h> +#include <mach/msm_serial_hs.h> +#include <mach/memory.h> + +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/i2c.h> +#include <linux/android_pmem.h> +#include <mach/camera.h> + +#include "devices.h" +#include "socinfo.h" +#include "clock.h" +#include "msm-keypad-devices.h" +#include "pm.h" + +#define MSM_PMEM_MDP_SIZE 0x800000 +#define MSM_PMEM_ADSP_SIZE 0x800000 +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_FB_SIZE 0x200000 + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x9C004300, + .end = 0x9C0043ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(132), + .end = MSM_GPIO_TO_INT(132), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct usb_mass_storage_platform_data usb_mass_storage_pdata = { + .nluns = 0x02, + .buf_size = 16384, + .vendor = "GOOGLE", + .product = "Mass storage", + .release = 0xffff, +}; + +static struct platform_device mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &usb_mass_storage_pdata, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct usb_function_map usb_functions_map[] = { + {"diag", 0}, + {"adb", 1}, + {"modem", 2}, + {"nmea", 3}, + {"mass_storage", 4}, + {"ethernet", 5}, +}; + +/* dynamic composition */ +static struct usb_composition usb_func_composition[] = { + { + .product_id = 0x9012, + .functions = 0x5, /* 0101 */ + }, + + { + .product_id = 0x9013, + .functions = 0x15, /* 10101 */ + }, + + { + .product_id = 0x9014, + .functions = 0x30, /* 110000 */ + }, + + { + .product_id = 0x9016, + .functions = 0xD, /* 01101 */ + }, + + { + .product_id = 0x9017, + .functions = 0x1D, /* 11101 */ + }, + + { + .product_id = 0xF000, + .functions = 0x10, /* 10000 */ + }, + + { + .product_id = 0xF009, + .functions = 0x20, /* 100000 */ + }, + + { + .product_id = 0x9018, + .functions = 0x1F, /* 011111 */ + }, + +}; + +static struct msm_hsusb_platform_data msm_hsusb_pdata = { + .version = 0x0100, + .phy_info = (USB_PHY_INTEGRATED | USB_PHY_MODEL_65NM), + .vendor_id = 0x5c6, + .product_name = "Qualcomm HSUSB Device", + .serial_number = "1234567890ABCDEF", + .manufacturer_name = "Qualcomm Incorporated", + .compositions = usb_func_composition, + .num_compositions = ARRAY_SIZE(usb_func_composition), + .function_map = usb_functions_map, + .num_functions = ARRAY_SIZE(usb_functions_map), +}; + +#define SND(desc, num) { .name = #desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(HANDSET, 0), + SND(MONO_HEADSET, 2), + SND(HEADSET, 3), + SND(SPEAKER, 6), + SND(TTY_HEADSET, 8), + SND(TTY_VCO, 9), + SND(TTY_HCO, 10), + SND(BT, 12), + SND(IN_S_SADC_OUT_HANDSET, 16), + SND(IN_S_SADC_OUT_SPEAKER_PHONE, 25), + SND(CURRENT, 27), +}; +#undef SND + +static struct msm_snd_endpoints msm_device_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = sizeof(snd_endpoints_list) / sizeof(struct snd_endpoint) +}; + +static struct platform_device msm_device_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &msm_device_snd_endpoints + }, +}; + +#define DEC0_FORMAT ((1<<MSM_ADSP_CODEC_MP3)| \ + (1<<MSM_ADSP_CODEC_AAC)|(1<<MSM_ADSP_CODEC_WMA)| \ + (1<<MSM_ADSP_CODEC_WMAPRO)|(1<<MSM_ADSP_CODEC_AMRWB)| \ + (1<<MSM_ADSP_CODEC_AMRNB)|(1<<MSM_ADSP_CODEC_WAV)| \ + (1<<MSM_ADSP_CODEC_ADPCM)|(1<<MSM_ADSP_CODEC_YADPCM)| \ + (1<<MSM_ADSP_CODEC_EVRC)|(1<<MSM_ADSP_CODEC_QCELP)) +#define DEC1_FORMAT ((1<<MSM_ADSP_CODEC_WAV)|(1<<MSM_ADSP_CODEC_ADPCM)| \ + (1<<MSM_ADSP_CODEC_YADPCM)|(1<<MSM_ADSP_CODEC_QCELP)| \ + (1<<MSM_ADSP_CODEC_MP3)) +#define DEC2_FORMAT ((1<<MSM_ADSP_CODEC_WAV)|(1<<MSM_ADSP_CODEC_ADPCM)| \ + (1<<MSM_ADSP_CODEC_YADPCM)|(1<<MSM_ADSP_CODEC_QCELP)| \ + (1<<MSM_ADSP_CODEC_MP3)) +#define DEC3_FORMAT ((1<<MSM_ADSP_CODEC_WAV)|(1<<MSM_ADSP_CODEC_ADPCM)| \ + (1<<MSM_ADSP_CODEC_YADPCM)|(1<<MSM_ADSP_CODEC_QCELP)) +#define DEC4_FORMAT (1<<MSM_ADSP_CODEC_MIDI) + +static unsigned int dec_concurrency_table[] = { + /* Audio LP */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DMA)), 0, + 0, 0, 0, + + /* Concurrency 1 */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC1_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC2_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC3_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC4_FORMAT), + + /* Concurrency 2 */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC1_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC2_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC3_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC4_FORMAT), + + /* Concurrency 3 */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC1_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC2_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC3_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC4_FORMAT), + + /* Concurrency 4 */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC1_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC2_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC3_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC4_FORMAT), + + /* Concurrency 5 */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_TUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC1_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC2_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC3_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC4_FORMAT), + + /* Concurrency 6 */ + (DEC0_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC1_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC2_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC3_FORMAT|(1<<MSM_ADSP_MODE_NONTUNNEL)|(1<<MSM_ADSP_OP_DM)), + (DEC4_FORMAT), +}; + +#define DEC_INFO(name, queueid, decid, nr_codec) { .module_name = name, \ + .module_queueid = queueid, .module_decid = decid, \ + .nr_codec_support = nr_codec} + +static struct msm_adspdec_info dec_info_list[] = { + DEC_INFO("AUDPLAY0TASK", 13, 0, 11), /* AudPlay0BitStreamCtrlQueue */ + DEC_INFO("AUDPLAY1TASK", 14, 1, 5), /* AudPlay1BitStreamCtrlQueue */ + DEC_INFO("AUDPLAY2TASK", 15, 2, 5), /* AudPlay2BitStreamCtrlQueue */ + DEC_INFO("AUDPLAY3TASK", 16, 3, 4), /* AudPlay3BitStreamCtrlQueue */ + DEC_INFO("AUDPLAY4TASK", 17, 4, 1), /* AudPlay4BitStreamCtrlQueue */ +}; + +static struct msm_adspdec_database msm_device_adspdec_database = { + .num_dec = ARRAY_SIZE(dec_info_list), + .num_concurrency_support = (ARRAY_SIZE(dec_concurrency_table) / \ + ARRAY_SIZE(dec_info_list)), + .dec_concurrency_table = dec_concurrency_table, + .dec_info_list = dec_info_list, +}; + +static struct platform_device msm_device_adspdec = { + .name = "msm_adspdec", + .id = -1, + .dev = { + .platform_data = &msm_device_adspdec_database + }, +}; + +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .no_allocator = 0, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .no_allocator = 1, + .cached = 0, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &android_pmem_pdata }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct platform_device android_pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &android_pmem_gpu1_pdata }, +}; + +#define LCDC_CONFIG_PROC 21 +#define LCDC_UN_CONFIG_PROC 22 +#define LCDC_API_PROG 0x30000066 +#define LCDC_API_VERS 0x00010001 + +#define GPIO_OUT_132 132 +#define GPIO_OUT_131 131 +#define GPIO_OUT_103 103 +#define GPIO_OUT_102 102 +#define GPIO_OUT_88 88 + +static struct msm_rpc_endpoint *lcdc_ep; + +static int msm_fb_lcdc_config(int on) +{ + int rc = 0; + struct rpc_request_hdr hdr; + + if (on) + printk(KERN_INFO "lcdc config\n"); + else + printk(KERN_INFO "lcdc un-config\n"); + + lcdc_ep = msm_rpc_connect_compatible(LCDC_API_PROG, LCDC_API_VERS, 0); + if (IS_ERR(lcdc_ep)) { + printk(KERN_ERR "%s: msm_rpc_connect failed! rc = %ld\n", + __func__, PTR_ERR(lcdc_ep)); + return -EINVAL; + } + + rc = msm_rpc_call(lcdc_ep, + (on) ? LCDC_CONFIG_PROC : LCDC_UN_CONFIG_PROC, + &hdr, sizeof(hdr), + 5 * HZ); + if (rc) + printk(KERN_ERR + "%s: msm_rpc_call failed! rc = %d\n", __func__, rc); + + msm_rpc_close(lcdc_ep); + return rc; +} + +static int gpio_array_num[] = { + GPIO_OUT_132, /* spi_clk */ + GPIO_OUT_131, /* spi_cs */ + GPIO_OUT_103, /* spi_sdi */ + GPIO_OUT_102, /* spi_sdoi */ + GPIO_OUT_88 + }; + +static void lcdc_gordon_gpio_init(void) +{ + if (gpio_request(GPIO_OUT_132, "spi_clk")) + pr_err("failed to request gpio spi_clk\n"); + if (gpio_request(GPIO_OUT_131, "spi_cs")) + pr_err("failed to request gpio spi_cs\n"); + if (gpio_request(GPIO_OUT_103, "spi_sdi")) + pr_err("failed to request gpio spi_sdi\n"); + if (gpio_request(GPIO_OUT_102, "spi_sdoi")) + pr_err("failed to request gpio spi_sdoi\n"); + if (gpio_request(GPIO_OUT_88, "gpio_dac")) + pr_err("failed to request gpio_dac\n"); +} + +static uint32_t lcdc_gpio_table[] = { + GPIO_CFG(GPIO_OUT_132, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + GPIO_CFG(GPIO_OUT_131, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + GPIO_CFG(GPIO_OUT_103, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + GPIO_CFG(GPIO_OUT_102, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + GPIO_CFG(GPIO_OUT_88, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +static void config_lcdc_gpio_table(uint32_t *table, int len, unsigned enable) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], + enable ? GPIO_ENABLE : GPIO_DISABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} + +static void lcdc_gordon_config_gpios(int enable) +{ + config_lcdc_gpio_table(lcdc_gpio_table, + ARRAY_SIZE(lcdc_gpio_table), enable); +} + +static struct lcdc_platform_data lcdc_pdata = { + .lcdc_gpio_config = msm_fb_lcdc_config +}; + +static struct msm_panel_common_pdata lcdc_gordon_panel_data = { + .panel_config_gpio = lcdc_gordon_config_gpios, + .gpio_num = gpio_array_num, +}; + +static struct platform_device lcdc_gordon_panel_device = { + .name = "lcdc_gordon_vga", + .id = 0, + .dev = { + .platform_data = &lcdc_gordon_panel_data, + } +}; + +static struct resource msm_fb_resources[] = { + { + .flags = IORESOURCE_DMA, + } +}; + +static int msm_fb_detect_panel(const char *name) +{ + int ret = -EPERM; + + if (machine_is_msm7x27_ffa() || machine_is_msm7x27_ffa()) { + if (!strcmp(name, "lcdc_gordon_vga")) + ret = 0; + else + ret = -ENODEV; + } + + return ret; +} + +static struct msm_fb_platform_data msm_fb_pdata = { + .detect_client = msm_fb_detect_panel, +}; + +static struct platform_device msm_fb_device = { + .name = "msm_fb", + .id = 0, + .num_resources = ARRAY_SIZE(msm_fb_resources), + .resource = msm_fb_resources, + .dev = { + .platform_data = &msm_fb_pdata, + } +}; + +#ifdef CONFIG_BT +static struct platform_device msm_bt_power_device = { + .name = "bt_power", +}; + +enum { + BT_WAKE, + BT_RFR, + BT_CTS, + BT_RX, + BT_TX, + BT_PCM_DOUT, + BT_PCM_DIN, + BT_PCM_SYNC, + BT_PCM_CLK, + BT_HOST_WAKE, +}; + +static unsigned bt_config_power_on[] = { + GPIO_CFG(42, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* WAKE */ + GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* RFR */ + GPIO_CFG(44, 2, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* CTS */ + GPIO_CFG(45, 2, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* Rx */ + GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* Tx */ + GPIO_CFG(68, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* PCM_DOUT */ + GPIO_CFG(69, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* PCM_DIN */ + GPIO_CFG(70, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* PCM_SYNC */ + GPIO_CFG(71, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* PCM_CLK */ + GPIO_CFG(83, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), /* HOST_WAKE */ +}; +static unsigned bt_config_power_off[] = { + GPIO_CFG(42, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* WAKE */ + GPIO_CFG(43, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* RFR */ + GPIO_CFG(44, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* CTS */ + GPIO_CFG(45, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* Rx */ + GPIO_CFG(46, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* Tx */ + GPIO_CFG(68, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCM_DOUT */ + GPIO_CFG(69, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCM_DIN */ + GPIO_CFG(70, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCM_SYNC */ + GPIO_CFG(71, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCM_CLK */ + GPIO_CFG(83, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HOST_WAKE */ +}; + +static int bluetooth_power(int on) +{ + struct vreg *vreg_bt; + int pin, rc; + + printk(KERN_DEBUG "%s\n", __func__); + + /* do not have vreg bt defined, gp6 is the same */ + /* vreg_get parameter 1 (struct device *) is ignored */ + vreg_bt = vreg_get(NULL, "gp6"); + + if (IS_ERR(vreg_bt)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_bt)); + return PTR_ERR(vreg_bt); + } + + if (on) { + for (pin = 0; pin < ARRAY_SIZE(bt_config_power_on); pin++) { + rc = gpio_tlmm_config(bt_config_power_on[pin], + GPIO_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, bt_config_power_on[pin], rc); + return -EIO; + } + } + + /* units of mV, steps of 50 mV */ + rc = vreg_set_level(vreg_bt, 2600); + if (rc) { + printk(KERN_ERR "%s: vreg set level failed (%d)\n", + __func__, rc); + return -EIO; + } + rc = vreg_enable(vreg_bt); + if (rc) { + printk(KERN_ERR "%s: vreg enable failed (%d)\n", + __func__, rc); + return -EIO; + } + } else { + rc = vreg_disable(vreg_bt); + if (rc) { + printk(KERN_ERR "%s: vreg disable failed (%d)\n", + __func__, rc); + return -EIO; + } + for (pin = 0; pin < ARRAY_SIZE(bt_config_power_off); pin++) { + rc = gpio_tlmm_config(bt_config_power_off[pin], + GPIO_ENABLE); + if (rc) { + printk(KERN_ERR + "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, bt_config_power_off[pin], rc); + return -EIO; + } + } + } + return 0; +} + +static void __init bt_power_init(void) +{ + msm_bt_power_device.dev.platform_data = &bluetooth_power; +} +#else +#define bt_power_init(x) do {} while (0) +#endif + +static struct resource bluesleep_resources[] = { + { + .name = "gpio_host_wake", + .start = 83, + .end = 83, + .flags = IORESOURCE_IO, + }, + { + .name = "gpio_ext_wake", + .start = 42, + .end = 42, + .flags = IORESOURCE_IO, + }, + { + .name = "host_wake", + .start = MSM_GPIO_TO_INT(83), + .end = MSM_GPIO_TO_INT(83), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device msm_bluesleep_device = { + .name = "bluesleep", + .id = -1, + .num_resources = ARRAY_SIZE(bluesleep_resources), + .resource = bluesleep_resources, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO("mt9d112", 0x78 >> 1), + }, + { + I2C_BOARD_INFO("s5k3e2fx", 0x20 >> 1), + }, + { + I2C_BOARD_INFO("mt9p012", 0x6C >> 1), + }, + { + I2C_BOARD_INFO("mt9t013", 0x6C), + }, +}; + +static uint32_t camera_off_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + GPIO_CFG(1, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + GPIO_CFG(2, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + GPIO_CFG(3, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + GPIO_CFG(4, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + GPIO_CFG(5, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + GPIO_CFG(6, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + GPIO_CFG(7, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + GPIO_CFG(8, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + GPIO_CFG(9, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + GPIO_CFG(10, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + GPIO_CFG(11, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + GPIO_CFG(12, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* PCLK */ + GPIO_CFG(13, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 0, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* parallel CAMERA interfaces */ + GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], GPIO_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} + +static void config_camera_on_gpios(void) +{ + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); +} + +static void config_camera_off_gpios(void) +{ + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); +} + +#define MSM_PROBE_INIT(name) name##_probe_init +static struct msm_camera_sensor_info msm_camera_sensor[] = { + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .sensor_name = "mt9d112", + .flash_type = MSM_CAMERA_FLASH_NONE, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(mt9d112), +#endif + }, + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .sensor_name = "s5k3e2fx", + .flash_type = MSM_CAMERA_FLASH_NONE, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(s5k3e2fx), +#endif + }, + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 88, + .sensor_name = "mt9p012", + .flash_type = MSM_CAMERA_FLASH_LED, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(mt9p012), +#endif + }, + { + .sensor_reset = 89, + .sensor_pwd = 85, + .vcm_pwd = 0, + .sensor_name = "mt9t013", + .flash_type = MSM_CAMERA_FLASH_NONE, +#ifdef CONFIG_MSM_CAMERA + .sensor_probe = MSM_PROBE_INIT(mt9t013), +#endif + }, +}; +#undef MSM_PROBE_INIT + +static struct msm_camera_device_platform_data msm_camera_device_data = { + .camera_gpio_on = config_camera_on_gpios, + .camera_gpio_off = config_camera_off_gpios, + .snum = ARRAY_SIZE(msm_camera_sensor), + .sinfo = &msm_camera_sensor[0], + .ioext.mdcphy = MSM_MDC_PHYS, + .ioext.mdcsz = MSM_MDC_SIZE, + .ioext.appphy = MSM_CLK_CTL_PHYS, + .ioext.appsz = MSM_CLK_CTL_SIZE, +}; + +static void __init msm_camera_add_device(void) +{ + msm_camera_register_device(NULL, 0, &msm_camera_device_data); + config_camera_off_gpios(); +} + +static struct platform_device *devices[] __initdata = { +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart3, +#endif + &msm_device_smd, + &msm_device_dmov, + &msm_device_nand, + &msm_device_hsusb_otg, + &msm_device_hsusb_host, + &msm_device_hsusb_peripheral, + &mass_storage_device, + &msm_device_i2c, + &smc91x_device, + &msm_device_tssc, + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_gpu1_device, + &msm_fb_device, + &lcdc_gordon_panel_device, + &msm_device_uart_dm1, +#ifdef CONFIG_BT + &msm_bt_power_device, +#endif + &msm_device_snd, + &msm_device_adspdec, + &msm_bluesleep_device, +}; + +static struct msm_panel_common_pdata mdp_pdata = { + .gpio = 97, +}; + +static void __init msm_fb_add_devices(void) +{ + msm_fb_register_device("mdp", &mdp_pdata); + msm_fb_register_device("pmdh", 0); + msm_fb_register_device("lcdc", &lcdc_pdata); +} + +extern struct sys_timer msm_timer; + +static void __init msm7x27_init_irq(void) +{ + msm_init_irq(); +} + +static struct msm_acpu_clock_platform_data msm7x27_clock_data = { + .acpu_switch_time_us = 50, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, + .max_axi_khz = 128000, +}; + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static void sdcc_gpio_init(void) +{ + /* SDC1 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + if (gpio_request(51, "sdc1_data_3")) + pr_err("failed to request gpio sdc1_data_3\n"); + if (gpio_request(52, "sdc1_data_2")) + pr_err("failed to request gpio sdc1_data_2\n"); + if (gpio_request(53, "sdc1_data_1")) + pr_err("failed to request gpio sdc1_data_1\n"); + if (gpio_request(54, "sdc1_data_0")) + pr_err("failed to request gpio sdc1_data_0\n"); + if (gpio_request(55, "sdc1_cmd")) + pr_err("failed to request gpio sdc1_cmd\n"); + if (gpio_request(56, "sdc1_clk")) + pr_err("failed to request gpio sdc1_clk\n"); +#endif + + if (machine_is_msm7x27_ffa() || machine_is_msm7x27_ffa()) + return; + + /* SDC2 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + if (gpio_request(62, "sdc2_clk")) + pr_err("failed to request gpio sdc2_clk\n"); + if (gpio_request(63, "sdc2_cmd")) + pr_err("failed to request gpio sdc2_cmd\n"); + if (gpio_request(64, "sdc2_data_3")) + pr_err("failed to request gpio sdc2_data_3\n"); + if (gpio_request(65, "sdc2_data_2")) + pr_err("failed to request gpio sdc2_data_2\n"); + if (gpio_request(66, "sdc2_data_1")) + pr_err("failed to request gpio sdc2_data_1\n"); + if (gpio_request(67, "sdc2_data_0")) + pr_err("failed to request gpio sdc2_data_0\n"); +#endif + + /* SDC3 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + if (gpio_request(88, "sdc3_clk")) + pr_err("failed to request gpio sdc3_clk\n"); + if (gpio_request(89, "sdc3_cmd")) + pr_err("failed to request gpio sdc3_cmd\n"); + if (gpio_request(90, "sdc3_data_3")) + pr_err("failed to request gpio sdc3_data_3\n"); + if (gpio_request(91, "sdc3_data_2")) + pr_err("failed to request gpio sdc3_data_2\n"); + if (gpio_request(92, "sdc3_data_1")) + pr_err("failed to request gpio sdc3_data_1\n"); + if (gpio_request(93, "sdc3_data_0")) + pr_err("failed to request gpio sdc3_data_0\n"); +#endif + + /* SDC4 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + if (gpio_request(19, "sdc4_data_3")) + pr_err("failed to request gpio sdc4_data_3\n"); + if (gpio_request(20, "sdc4_data_2")) + pr_err("failed to request gpio sdc4_data_2\n"); + if (gpio_request(21, "sdc4_data_1")) + pr_err("failed to request gpio sdc4_data_1\n"); + if (gpio_request(107, "sdc4_cmd")) + pr_err("failed to request gpio sdc4_cmd\n"); + if (gpio_request(108, "sdc4_data_0")) + pr_err("failed to request gpio sdc4_data_0\n"); + if (gpio_request(109, "sdc4_clk")) + pr_err("failed to request gpio sdc4_clk\n"); +#endif +} + +static unsigned sdcc_cfg_data[][6] = { + /* SDC1 configs */ + { + GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + }, + /* SDC2 configs */ + { + GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, + /* SDC3 configs */ + { + GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, + /* SDC4 configs */ + { + GPIO_CFG(19, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(20, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(21, 4, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(107, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(108, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(109, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + } +}; + +static unsigned long vreg_sts, gpio_sts; +static struct mpp *mpp_mmc; +static struct vreg *vreg_mmc; + +static void msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int i, rc; + + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return; + + if (enable) + set_bit(dev_id, &gpio_sts); + else + clear_bit(dev_id, &gpio_sts); + + for (i = 0; i < ARRAY_SIZE(sdcc_cfg_data[dev_id - 1]); i++) { + rc = gpio_tlmm_config(sdcc_cfg_data[dev_id - 1][i], + enable ? GPIO_ENABLE : GPIO_DISABLE); + if (rc) + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, sdcc_cfg_data[dev_id - 1][i], rc); + } +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + msm_sdcc_setup_gpio(pdev->id, !!vdd); + + if (vdd == 0) { + if (!vreg_sts) + return 0; + + clear_bit(pdev->id, &vreg_sts); + + if (!vreg_sts) { + if (machine_is_msm7x27_ffa()) { + rc = mpp_config_digital_out(mpp_mmc, + MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_LOW)); + } else + rc = vreg_disable(vreg_mmc); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + return 0; + } + + if (!vreg_sts) { + if (machine_is_msm7x27_ffa()) { + rc = mpp_config_digital_out(mpp_mmc, + MPP_CFG(MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_OUT_CTRL_HIGH)); + } else { + rc = vreg_set_level(vreg_mmc, 2850); + if (!rc) + rc = vreg_enable(vreg_mmc); + } + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + set_bit(pdev->id, &vreg_sts); + return 0; +} + +static struct mmc_platform_data msm7x27_sdcc_data = { + .ocr_mask = MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +}; + +static void __init msm7x27_init_mmc(void) +{ + if (machine_is_msm7x27_ffa()) { + mpp_mmc = mpp_get(NULL, "mpp3"); + if (!mpp_mmc) { + printk(KERN_ERR "%s: mpp get failed (%ld)\n", + __func__, PTR_ERR(vreg_mmc)); + return; + } + } else { + vreg_mmc = vreg_get(NULL, "mmc"); + if (IS_ERR(vreg_mmc)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg_mmc)); + return; + } + } + + sdcc_gpio_init(); +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + msm_add_sdcc(1, &msm7x27_sdcc_data); +#endif + + if (machine_is_msm7x27_surf()) { +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + msm_add_sdcc(2, &msm7x27_sdcc_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + msm_add_sdcc(3, &msm7x27_sdcc_data); +#endif +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + msm_add_sdcc(4, &msm7x27_sdcc_data); +#endif + } +} + +static struct msm_i2c_platform_data msm_i2c_pdata = { + .clk_freq = 100000, +}; + +static void __init msm_device_i2c_init(void) +{ + msm_device_i2c.dev.platform_data = &msm_i2c_pdata; +} + +static struct msm_pm_platform_data msm7x27_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].supported = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 16000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].residency = 20000, + + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].supported = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 12000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].residency = 20000, + + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].supported = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].suspend_enabled + = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 2000, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].residency = 0, +}; + +static void __init msm7x27_init(void) +{ + if (socinfo_init() < 0) + BUG(); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + if (machine_is_msm7x27_ffa()) { + smc91x_resources[0].start = 0x98000300; + smc91x_resources[0].end = 0x980003ff; + smc91x_resources[1].start = MSM_GPIO_TO_INT(85); + smc91x_resources[1].end = MSM_GPIO_TO_INT(85); + if (gpio_tlmm_config(GPIO_CFG(85, 0, + GPIO_INPUT, + GPIO_PULL_DOWN, + GPIO_2MA), + GPIO_ENABLE)) { + printk(KERN_ERR + "%s: Err: Config GPIO-85 INT\n", + __func__); + } + + msm7x27_clock_data.max_axi_khz = 160000; + } + + if (cpu_is_msm7x27()) + msm7x27_clock_data.max_axi_khz = 200000; + + msm_acpu_clock_init(&msm7x27_clock_data); + msm_device_hsusb_peripheral.dev.platform_data = &msm_hsusb_pdata; + msm_device_hsusb_host.dev.platform_data = &msm_hsusb_pdata; + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_camera_add_device(); + msm_device_i2c_init(); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + +#ifdef CONFIG_SURF_FFA_GPIO_KEYPAD + if (machine_is_msm7x27_ffa()) + platform_device_register(&keypad_device_7k_ffa); + else + platform_device_register(&keypad_device_surf); +#endif + lcdc_gordon_gpio_init(); + msm_fb_add_devices(); + msm7x27_init_mmc(); + bt_power_init(); + + if (cpu_is_msm7x27()) + msm_pm_set_platform_data(msm7x27_pm_data); + else + msm_pm_set_platform_data(msm7x27_pm_data); +} + +static void __init msm_msm7x27_allocate_memory_regions(void) +{ + void *addr; + unsigned long size; + + size = MSM_PMEM_MDP_SIZE; + addr = alloc_bootmem(size); + android_pmem_pdata.start = __pa(addr); + android_pmem_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for pmem\n", size, addr, __pa(addr)); + + size = MSM_PMEM_ADSP_SIZE; + addr = alloc_bootmem(size); + android_pmem_adsp_pdata.start = __pa(addr); + android_pmem_adsp_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for adsp pmem\n", size, addr, __pa(addr)); + + size = MSM_PMEM_GPU1_SIZE; + addr = alloc_bootmem_aligned(size, 0x100000); + android_pmem_gpu1_pdata.start = __pa(addr); + android_pmem_gpu1_pdata.size = size; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical)" + "for gpu1 pmem\n", size, addr, __pa(addr)); + + size = MSM_FB_SIZE; + addr = alloc_bootmem(size); + msm_fb_resources[0].start = __pa(addr); + msm_fb_resources[0].end = msm_fb_resources[0].start + size - 1; + printk(KERN_INFO "allocating %lu bytes at %p (%lx physical) for fb\n", + size, addr, __pa(addr)); +} + +static void __init msm7x27_map_io(void) +{ + msm_map_common_io(); + /* Technically dependent on the SoC but using machine_is + * macros since socinfo is not available this early and there + * are plans to restructure the code which will eliminate the + * need for socinfo. + */ + if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) + msm_clock_init(msm_clocks_7x27, msm_num_clocks_7x27); + else + msm_clock_init(msm_clocks_7x27, msm_num_clocks_7x27); + msm_msm7x27_allocate_memory_regions(); + +#ifdef CONFIG_CACHE_L2X0 + if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) { + /* 7x27 has 256KB L2 cache: + 64Kb/Way and 4-Way Associativity; + R/W latency: 3 cycles; + evmon/parity/share disabled. */ + l2x0_init(MSM_L2CC_BASE, 0x00068012, 0xfe000000); + } +#endif +} + +MACHINE_START(MSM7X27_SURF, "QCT MSM7x27 SURF") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x00200100, + .map_io = msm7x27_map_io, + .init_irq = msm7x27_init_irq, + .init_machine = msm7x27_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(MSM7X27_FFA, "QCT MSM7x27 FFA") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x00200100, + .map_io = msm7x27_map_io, + .init_irq = msm7x27_init_irq, + .init_machine = msm7x27_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c new file mode 100644 index 000000000000..3dc0fc54322a --- /dev/null +++ b/arch/arm/mach-msm/board-msm7x30.c @@ -0,0 +1,209 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include <mach/gpio.h> +#include <mach/board.h> +#include <mach/memory.h> +#include <mach/msm_iomap.h> + +#include "devices.h" +#include "timer.h" +#include "socinfo.h" +#include "pm.h" + +static struct resource smc91x_resources[] = { + [0] = { + .start = 0x8A000300, + .end = 0x8A0003ff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = MSM_GPIO_TO_INT(156), + .end = MSM_GPIO_TO_INT(156), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + +static struct platform_device *devices[] __initdata = { + &msm_device_smd, + &msm_device_dmov, + &smc91x_device, + &msm_device_nand, +#ifdef CONFIG_SERIAL_MSM_CONSOLE + &msm_device_uart2, +#endif +}; + +static void __init msm7x30_init_irq(void) +{ + msm_init_irq(); +} + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].supported = 0, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].suspend_enabled = 0, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 16000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].residency = 20000, + + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].supported = 0, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].suspend_enabled = 0, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].idle_enabled = 0, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 12000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].residency = 20000, + + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].supported = 0, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].suspend_enabled + = 0, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].idle_enabled = 0, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 2000, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].residency = 10000, + + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].supported = 0, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].suspend_enabled = 0, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].idle_enabled = 0, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency = 500, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].residency = 0, +}; + +#ifdef CONFIG_SERIAL_MSM_CONSOLE +static struct msm_gpio uart2_config_data[] = { + { GPIO_CFG(49, 2, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_RFR"}, + { GPIO_CFG(50, 2, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_CTS"}, + { GPIO_CFG(51, 2, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_Rx"}, + { GPIO_CFG(52, 2, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_2MA), "UART2_Tx"}, +}; + +static void msm7x30_init_uart2(void) +{ + msm_gpios_request_enable(uart2_config_data, + ARRAY_SIZE(uart2_config_data)); + +} +#endif + +static void __init msm7x30_init(void) +{ + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n", + __func__); + platform_add_devices(devices, ARRAY_SIZE(devices)); + msm_pm_set_platform_data(msm_pm_data); +#ifdef CONFIG_SERIAL_MSM_CONSOLE + msm7x30_init_uart2(); +#endif +} + +static void __init msm7x30_map_io(void) +{ + msm_shared_ram_phys = 0x00100000; + msm_map_msm7x30_io(); + msm_clock_init(msm_clocks_7x30, msm_num_clocks_7x30); +} + +MACHINE_START(MSM7X30_SURF, "QCT MSM7X30 SURF") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x00200100, + .map_io = msm7x30_map_io, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(MSM7X30_FFA, "QCT MSM7X30 FFA") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x00200100, + .map_io = msm7x30_map_io, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(MSM7X30_FLUID, "QCT MSM7X30 FLUID") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x00200100, + .map_io = msm7x30_map_io, + .init_irq = msm7x30_init_irq, + .init_machine = msm7x30_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c new file mode 100644 index 000000000000..f9a038db9310 --- /dev/null +++ b/arch/arm/mach-msm/board-qsd8x50.c @@ -0,0 +1,538 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/bootmem.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> + +#include <asm/mach-types.h> +#include <asm/mach/arch.h> + +#include <mach/mmc.h> +#include <mach/vreg.h> +#include <mach/mpp.h> +#include <mach/gpio.h> +#include <mach/board.h> +#include <mach/sirc.h> +#include <mach/msm_touchpad.h> +#include <mach/msm_i2ckbd.h> +#include <mach/pmic.h> +#include <mach/memory.h> + +#include "devices.h" +#include "timer.h" +#include "socinfo.h" +#include "msm-keypad-devices.h" +#include "pm.h" + +#define TOUCHPAD_SUSPEND 34 +#define TOUCHPAD_IRQ 38 + +#define MSM_PMEM_MDP_SIZE 0x800000 +#define MSM_PMEM_CAMERA_SIZE 0xa00000 +#define MSM_PMEM_ADSP_SIZE 0x1100000 +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_FB_SIZE 0x500000 +#define MSM_AUDIO_SIZE 0x200000 +#define MSM_GPU_PHYS_SIZE SZ_2M + +#define MSM_SMI_BASE 0x2b00000 +#define MSM_SMI_SIZE 0x1500000 + +#define MSM_FB_BASE MSM_SMI_BASE +#define MSM_GPU_PHYS_BASE (MSM_FB_BASE + MSM_FB_SIZE) +#define MSM_PMEM_GPU0_BASE (MSM_GPU_PHYS_BASE + MSM_GPU_PHYS_SIZE) +#define MSM_PMEM_GPU0_SIZE (MSM_SMI_SIZE - MSM_FB_SIZE - MSM_GPU_PHYS_SIZE) + +static struct resource smc91x_resources[] = { + [0] = { + .flags = IORESOURCE_MEM, + }, + [1] = { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smc91x_device = { + .name = "smc91x", + .id = 0, + .num_resources = ARRAY_SIZE(smc91x_resources), + .resource = smc91x_resources, +}; + + +static struct platform_device *devices[] __initdata = { + &smc91x_device, + &msm_device_smd, + &msm_device_dmov, + &msm_device_nand, +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) + &msm_device_uart3, +#endif +}; + +#define KBD_RST 35 +#define KBD_IRQ 36 + +static void kbd_gpio_release(void) +{ + gpio_free(KBD_IRQ); + gpio_free(KBD_RST); +} + +static int kbd_gpio_setup(void) +{ + int rc; + int respin = KBD_RST; + int irqpin = KBD_IRQ; + unsigned rescfg = + GPIO_CFG(respin, 0, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA); + unsigned irqcfg = + GPIO_CFG(irqpin, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA); + + rc = gpio_request(irqpin, "gpio_keybd_irq"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + irqpin, rc); + goto err_gpioconfig; + } + rc = gpio_request(respin, "gpio_keybd_reset"); + if (rc) { + pr_err("gpio_request failed on pin %d (rc=%d)\n", + respin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(rescfg, GPIO_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + respin, rc); + goto err_gpioconfig; + } + rc = gpio_tlmm_config(irqcfg, GPIO_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config failed on pin %d (rc=%d)\n", + irqpin, rc); + goto err_gpioconfig; + } + return rc; + +err_gpioconfig: + kbd_gpio_release(); + return rc; +} + +static struct msm_i2ckbd_platform_data msm_kybd_data = { + .hwrepeat = 0, + .scanset1 = 1, + .gpioreset = KBD_RST, + .gpioirq = KBD_IRQ, + .gpio_setup = kbd_gpio_setup, + .gpio_shutdown = kbd_gpio_release, +}; +static void config_gpio_table(uint32_t *table, int len) +{ + int n, rc; + for (n = 0; n < len; n++) { + rc = gpio_tlmm_config(table[n], GPIO_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, table[n], rc); + break; + } + } +} + +static void __init qsd8x50_init_irq(void) +{ + msm_init_irq(); + msm_init_sirc(); +} + +static void sdcc_gpio_init(void) +{ + /* SDC1 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT + if (gpio_request(51, "sdc1_data_3")) + pr_err("failed to request gpio sdc1_data_3\n"); + if (gpio_request(52, "sdc1_data_2")) + pr_err("failed to request gpio sdc1_data_2\n"); + if (gpio_request(53, "sdc1_data_1")) + pr_err("failed to request gpio sdc1_data_1\n"); + if (gpio_request(54, "sdc1_data_0")) + pr_err("failed to request gpio sdc1_data_0\n"); + if (gpio_request(55, "sdc1_cmd")) + pr_err("failed to request gpio sdc1_cmd\n"); + if (gpio_request(56, "sdc1_clk")) + pr_err("failed to request gpio sdc1_clk\n"); +#endif + + if (machine_is_qsd8x50_ffa()) + return; + + /* SDC2 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT + if (gpio_request(62, "sdc2_clk")) + pr_err("failed to request gpio sdc2_clk\n"); + if (gpio_request(63, "sdc2_cmd")) + pr_err("failed to request gpio sdc2_cmd\n"); + if (gpio_request(64, "sdc2_data_3")) + pr_err("failed to request gpio sdc2_data_3\n"); + if (gpio_request(65, "sdc2_data_2")) + pr_err("failed to request gpio sdc2_data_2\n"); + if (gpio_request(66, "sdc2_data_1")) + pr_err("failed to request gpio sdc2_data_1\n"); + if (gpio_request(67, "sdc2_data_0")) + pr_err("failed to request gpio sdc2_data_0\n"); +#endif + + /* SDC3 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT + if (gpio_request(88, "sdc3_clk")) + pr_err("failed to request gpio sdc3_clk\n"); + if (gpio_request(89, "sdc3_cmd")) + pr_err("failed to request gpio sdc3_cmd\n"); + if (gpio_request(90, "sdc3_data_3")) + pr_err("failed to request gpio sdc3_data_3\n"); + if (gpio_request(91, "sdc3_data_2")) + pr_err("failed to request gpio sdc3_data_2\n"); + if (gpio_request(92, "sdc3_data_1")) + pr_err("failed to request gpio sdc3_data_1\n"); + if (gpio_request(93, "sdc3_data_0")) + pr_err("failed to request gpio sdc3_data_0\n"); +#endif + + /* SDC4 GPIOs */ +#ifdef CONFIG_MMC_MSM_SDC4_SUPPORT + if (gpio_request(142, "sdc4_clk")) + pr_err("failed to request gpio sdc4_clk\n"); + if (gpio_request(143, "sdc4_cmd")) + pr_err("failed to request gpio sdc4_cmd\n"); + if (gpio_request(144, "sdc4_data_0")) + pr_err("failed to request gpio sdc4_data_0\n"); + if (gpio_request(145, "sdc4_data_1")) + pr_err("failed to request gpio sdc4_data_1\n"); + if (gpio_request(146, "sdc4_data_2")) + pr_err("failed to request gpio sdc4_data_2\n"); + if (gpio_request(147, "sdc4_data_3")) + pr_err("failed to request gpio sdc4_data_3\n"); +#endif +} + +static unsigned sdcc_cfg_data[][6] = { + /* SDC1 configs */ + { + GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + }, + /* SDC2 configs */ + { + GPIO_CFG(62, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(63, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(64, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(65, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(66, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(67, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, + /* SDC3 configs */ + { + GPIO_CFG(88, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(89, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(90, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(91, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(92, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(93, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + }, + /* SDC4 configs */ + { + GPIO_CFG(142, 3, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), + GPIO_CFG(143, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(144, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(145, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(146, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + GPIO_CFG(147, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + } +}; + +static unsigned long vreg_sts, gpio_sts; +static struct vreg *vreg_mmc; + +static void msm_sdcc_setup_gpio(int dev_id, unsigned int enable) +{ + int i, rc; + + if (!(test_bit(dev_id, &gpio_sts)^enable)) + return; + + if (enable) + set_bit(dev_id, &gpio_sts); + else + clear_bit(dev_id, &gpio_sts); + + for (i = 0; i < ARRAY_SIZE(sdcc_cfg_data[dev_id - 1]); i++) { + rc = gpio_tlmm_config(sdcc_cfg_data[dev_id - 1][i], + enable ? GPIO_ENABLE : GPIO_DISABLE); + if (rc) + printk(KERN_ERR "%s: gpio_tlmm_config(%#x)=%d\n", + __func__, sdcc_cfg_data[dev_id - 1][i], rc); + } +} + +static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd) +{ + int rc = 0; + struct platform_device *pdev; + + pdev = container_of(dv, struct platform_device, dev); + msm_sdcc_setup_gpio(pdev->id, !!vdd); + + if (vdd == 0) { + if (!vreg_sts) + return 0; + + clear_bit(pdev->id, &vreg_sts); + + if (!vreg_sts && !machine_is_qsd8x50_ffa()) { + rc = vreg_disable(vreg_mmc); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + return 0; + } + + if (!vreg_sts && !machine_is_qsd8x50_ffa()) { + rc = vreg_set_level(vreg_mmc, 2850); + if (!rc) + rc = vreg_enable(vreg_mmc); + if (rc) + printk(KERN_ERR "%s: return val: %d \n", + __func__, rc); + } + set_bit(pdev->id, &vreg_sts); + return 0; +} + +static struct mmc_platform_data qsd8x50_sdcc_data = { + .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29, + .translate_vdd = msm_sdcc_setup_power, +}; + +static void __init qsd8x50_cfg_smc91x(void) +{ + int rc = 0; + + if (machine_is_qsd8x50_surf()) { + smc91x_resources[0].start = 0x70000300; + smc91x_resources[0].end = 0x700003ff; + smc91x_resources[1].start = MSM_GPIO_TO_INT(156); + smc91x_resources[1].end = MSM_GPIO_TO_INT(156); + } else if (machine_is_qsd8x50_ffa()) { + smc91x_resources[0].start = 0x84000300; + smc91x_resources[0].end = 0x840003ff; + smc91x_resources[1].start = MSM_GPIO_TO_INT(87); + smc91x_resources[1].end = MSM_GPIO_TO_INT(87); + + rc = gpio_tlmm_config(GPIO_CFG(87, 0, GPIO_INPUT, + GPIO_PULL_DOWN, GPIO_2MA), + GPIO_ENABLE); + if (rc) { + printk(KERN_ERR "%s: gpio_tlmm_config=%d\n", + __func__, rc); + } + } else + printk(KERN_ERR "%s: invalid machine type\n", __func__); +} + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].supported = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 8594, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].residency = 23740, + + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].supported = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 4594, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].residency = 23740, + + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].supported = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].suspend_enabled + = 1, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].idle_enabled = 0, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 443, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].residency = 1098, + + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].supported = 1, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].suspend_enabled = 1, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].idle_enabled = 1, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].latency = 2, + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT].residency = 0, +}; + +static void __init gp6_init(void) +{ + struct vreg *vreg; + int rc; + + vreg = vreg_get(NULL, "gp6"); + if (IS_ERR(vreg)) { + printk(KERN_ERR "%s: vreg get failed (%ld)\n", + __func__, PTR_ERR(vreg)); + return; + } + + /* units of mV, steps of 50 mV */ + rc = vreg_set_level(vreg, 2850); + if (rc) { + printk(KERN_ERR "%s: vreg set level failed (%d)\n", + __func__, rc); + return; + } + + rc = vreg_enable(vreg); + if (rc) { + printk(KERN_ERR "%s: vreg enable failed (%d)\n", + __func__, rc); + return; + } + + if (machine_is_qsd8x50_ffa()) + vreg_mmc = vreg; +} + +static void __init qsd8x50_init(void) +{ + if (socinfo_init() < 0) + printk(KERN_ERR "%s: socinfo_init() failed!\n", + __func__); + qsd8x50_cfg_smc91x(); + platform_add_devices(devices, ARRAY_SIZE(devices)); + gp6_init(); + msm_pm_set_platform_data(msm_pm_data); + +#ifdef CONFIG_SURF_FFA_GPIO_KEYPAD + if (machine_is_qsd8x50_ffa()) + platform_device_register(&keypad_device_8k_ffa); + else + platform_device_register(&keypad_device_surf); +#endif +} + + +static void __init qsd8x50_map_io(void) +{ + msm_map_qsd8x50_io(); + msm_clock_init(msm_clocks_8x50, msm_num_clocks_8x50); +} + +MACHINE_START(QSD8X50_SURF, "QCT QSD8X50 SURF") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .map_io = qsd8x50_map_io, + .init_irq = qsd8x50_init_irq, + .init_machine = qsd8x50_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(QSD8X50_GRAPEFRUIT, "QCT QSD8X50 GRAPEFRUIT") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .map_io = qsd8x50_map_io, + .init_irq = qsd8x50_init_irq, + .init_machine = qsd8x50_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(QSD8X50_FFA, "QCT QSD8X50 FFA") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .map_io = qsd8x50_map_io, + .init_irq = qsd8x50_init_irq, + .init_machine = qsd8x50_init, + .timer = &msm_timer, +MACHINE_END + +MACHINE_START(QSD8X50_ST1, "QCT QSD8X50 ST1") +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x20000100, + .map_io = qsd8x50_map_io, + .init_irq = qsd8x50_init_irq, + .init_machine = qsd8x50_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-sapphire-gpio.c b/arch/arm/mach-msm/board-sapphire-gpio.c new file mode 100644 index 000000000000..2f2df97956cf --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-gpio.c @@ -0,0 +1,326 @@ +/* arch/arm/mach-msm/board-sapphire-gpio.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/irq.h> +#include <linux/pm.h> +#include <linux/sysdev.h> + +#include <linux/io.h> +#include <linux/gpio.h> +#include <asm/mach-types.h> + +#include "gpio_chip.h" +#include "board-sapphire.h" + +#ifdef DEBUG_SAPPHIRE_GPIO +#define DBG(fmt, arg...) printk(KERN_INFO "%s: " fmt "\n", __func__, ## arg) +#else +#define DBG(fmt, arg...) do {} while (0) +#endif + +#define SAPPHIRE_CPLD_INT_STATUS (SAPPHIRE_CPLD_BASE + 0x0E) +#define SAPPHIRE_CPLD_INT_LEVEL (SAPPHIRE_CPLD_BASE + 0x08) +#define SAPPHIRE_CPLD_INT_MASK (SAPPHIRE_CPLD_BASE + 0x0C) + +/*CPLD misc reg offset*/ +static const int _g_CPLD_MISCn_Offset[] = { 0x0A, /*misc1 reg*/ + 0x00, /*misc2 reg*/ + 0x02, /*misc3 reg*/ + 0x04, /*misc4 reg*/ + 0x06}; /*misc5 reg*/ +/*CPLD INT Bank*/ +/*BANK0: int1 status, int2 level, int3 mask*/ +static const int _g_INT_BANK_Offset[][3] = {{0x0E, 0x08, 0x0C} }; + +static uint8_t sapphire_cpld_initdata[4] = { + [0] = 0x80, /* for serial debug UART3, low current misc2*/ + [1] = 0x34, /* jog & tp enable, I2C pull misc3*/ + [3] = 0x04, /* mmdi 32k en misc5*/ +}; + +/*save current working int mask, so the value can be restored after resume. +Sapphire has only bank0.*/ +static uint8_t sapphire_int_mask[] = { + [0] = 0xfb, /* enable all interrupts, bit 2 is not used */ +}; + +/*Sleep have to prepare the wake up source in advance. +default to disable all wakeup sources when suspend.*/ +static uint8_t sapphire_sleep_int_mask[] = { + [0] = 0x00, /* bit2 is not used */ +}; + +static int sapphire_suspended; + +static int sapphire_gpio_read(struct gpio_chip *chip, unsigned n) +{ + if (n < SAPPHIRE_GPIO_INT_B0_BASE) /*MISCn*/ + return !!(readb(CPLD_GPIO_REG(n)) & CPLD_GPIO_BIT_POS_MASK(n)); + else if (n <= SAPPHIRE_GPIO_END) /*gpio n is INT pin*/ + return !!(readb(CPLD_INT_LEVEL_REG_G(n)) & + CPLD_GPIO_BIT_POS_MASK(n)); + return 0; +} + +/*CPLD Write only register :MISC2, MISC3, MISC4, MISC5 => reg=0,2,4,6 +Reading from write-only registers is undefined, so the writing value +should be kept in shadow for later usage.*/ +int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +{ + unsigned long flags; + uint8_t reg_val; + if (n > SAPPHIRE_GPIO_END) + return -1; + + local_irq_save(flags); + reg_val = readb(CPLD_GPIO_REG(n)); + if (on) + reg_val |= CPLD_GPIO_BIT_POS_MASK(n); + else + reg_val &= ~CPLD_GPIO_BIT_POS_MASK(n); + writeb(reg_val, CPLD_GPIO_REG(n)); + + DBG("gpio=%d, l=0x%x\r\n", n, readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); + + return 0; +} + +static int sapphire_gpio_configure(struct gpio_chip *chip, unsigned int gpio, + unsigned long flags) +{ + if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) + sapphire_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); + + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); + + return 0; +} + +static int sapphire_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, + unsigned int *irqp, unsigned long *irqnumflagsp) +{ + DBG("gpio=%d, l=0x%x\r\n", gpio, readb(SAPPHIRE_CPLD_INT_LEVEL)); + DBG("SAPPHIRE_GPIO_INT_B0_BASE=%d, SAPPHIRE_GPIO_LAST_INT=%d\r\n", + SAPPHIRE_GPIO_INT_B0_BASE, SAPPHIRE_GPIO_LAST_INT); + if ((gpio < SAPPHIRE_GPIO_INT_B0_BASE) || + (gpio > SAPPHIRE_GPIO_LAST_INT)) + return -ENOENT; + *irqp = SAPPHIRE_GPIO_TO_INT(gpio); + DBG("*irqp=%d\r\n", *irqp); + if (irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + +/*write 1 to clear INT status bit.*/ +static void sapphire_gpio_irq_ack(unsigned int irq) +{ + /*write 1 to clear*/ + writeb(SAPPHIRE_INT_BIT_MASK(irq), CPLD_INT_STATUS_REG(irq)); +} + +/*unmask/enable the INT +static void sapphire_gpio_irq_unmask(unsigned int irq)*/ +static void sapphire_gpio_irq_enable(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + + local_irq_save(flags); /*disabling all interrupts*/ + + reg_val = readb(CPLD_INT_MASK_REG(irq)) | SAPPHIRE_INT_BIT_MASK(irq); + DBG("(irq=%d,0x%x, 0x%x)\r\n", irq, CPLD_INT_MASK_REG(irq), + SAPPHIRE_INT_BIT_MASK(irq)); + DBG("sapphire_suspended=%d\r\n", sapphire_suspended); + /*printk(KERN_INFO "sapphire_gpio_irq_mask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + if (!sapphire_suspended) + writeb(reg_val, CPLD_INT_MASK_REG(irq)); + + reg_val = readb(CPLD_INT_MASK_REG(irq)); + DBG("reg_val= 0x%x\r\n", reg_val); + DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); /*restore the interrupts*/ +} + +/*mask/disable INT +static void sapphire_gpio_irq_mask(unsigned int irq)*/ +static void sapphire_gpio_irq_disable(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + + local_irq_save(flags); + reg_val = readb(CPLD_INT_MASK_REG(irq)) & ~SAPPHIRE_INT_BIT_MASK(irq); + /*CPLD INT MASK is r/w now.*/ + + /*printk(KERN_INFO "sapphire_gpio_irq_unmask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + DBG("(%d,0x%x, 0x%x, 0x%x)\r\n", irq, reg_val, CPLD_INT_MASK_REG(irq), + SAPPHIRE_INT_BIT_MASK(irq)); + DBG("sapphire_suspended=%d\r\n", sapphire_suspended); + if (!sapphire_suspended) + writeb(reg_val, CPLD_INT_MASK_REG(irq)); + + reg_val = readb(CPLD_INT_MASK_REG(irq)); + DBG("reg_val= 0x%x\r\n", reg_val); + DBG("l=0x%x\r\n", readb(SAPPHIRE_CPLD_INT_LEVEL)); + + local_irq_restore(flags); +} + +/*preparing enable/disable wake source before sleep*/ +int sapphire_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + unsigned long flags; + uint8_t mask = SAPPHIRE_INT_BIT_MASK(irq); + + local_irq_save(flags); + + if (on) /*wake on -> mask the bit*/ + sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] |= mask; + else /*no wake -> unmask the bit*/ + sapphire_sleep_int_mask[CPLD_INT_TO_BANK(irq)] &= ~mask; + local_irq_restore(flags); + return 0; +} + +/*Sapphire has only one INT Bank.*/ +static void sapphire_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + int j; + unsigned v; + int int_base = SAPPHIRE_INT_START; + + v = readb(SAPPHIRE_CPLD_INT_STATUS); /*INT1 status reg, BANK0*/ + + for (j = 0; j < 8 ; j++) { /*8 bit per bank*/ + if (v & (1U << j)) { /*got the INT Bit*/ + DBG("generic_handle_irq j=0x%x\r\n", j); + generic_handle_irq(int_base + j); + } + } + + desc->chip->ack(irq); /*clear CPLD INT in SOC side.*/ + DBG("irq=%d, l=0x%x\r\n", irq, readb(SAPPHIRE_CPLD_INT_LEVEL)); +} + +/*Save current working sources before sleep, so we can restore it after + * resume.*/ +static int sapphire_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + sapphire_suspended = 1; + /*save current masking*/ + sapphire_int_mask[0] = readb(SAPPHIRE_CPLD_BASE + + SAPPHIRE_GPIO_INT_B0_MASK_REG); + + /*set waking source before sleep.*/ + writeb(sapphire_sleep_int_mask[0], + SAPPHIRE_CPLD_BASE + SAPPHIRE_GPIO_INT_B0_MASK_REG); + + return 0; +} + +/*All the registers will be kept till a power loss...*/ +int sapphire_sysdev_resume(struct sys_device *dev) +{ + /*restore the working mask saved before sleep*/ + writeb(sapphire_int_mask[0], SAPPHIRE_CPLD_BASE + + SAPPHIRE_GPIO_INT_B0_MASK_REG); + sapphire_suspended = 0; + return 0; +} + +/** + * linux/irq.h :: struct irq_chip + * @enable: enable the interrupt (defaults to chip->unmask if NULL) + * @disable: disable the interrupt (defaults to chip->mask if NULL) + * @ack: start of a new interrupt + * @mask: mask an interrupt source + * @mask_ack: ack and mask an interrupt source + * @unmask: unmask an interrupt source + */ +static struct irq_chip sapphire_gpio_irq_chip = { + .name = "sapphiregpio", + .ack = sapphire_gpio_irq_ack, + .mask = sapphire_gpio_irq_disable, /*sapphire_gpio_irq_mask,*/ + .unmask = sapphire_gpio_irq_enable, /*sapphire_gpio_irq_unmask,*/ + .set_wake = sapphire_gpio_irq_set_wake, + /*.set_type = sapphire_gpio_irq_set_type,*/ +}; + +/*Thomas:For CPLD*/ +static struct gpio_chip sapphire_gpio_chip = { + .start = SAPPHIRE_GPIO_START, + .end = SAPPHIRE_GPIO_END, + .configure = sapphire_gpio_configure, + .get_irq_num = sapphire_gpio_get_irq_num, + .read = sapphire_gpio_read, + .write = sapphire_gpio_write, +/* .read_detect_status = sapphire_gpio_read_detect_status, + .clear_detect_status = sapphire_gpio_clear_detect_status */ +}; + +struct sysdev_class sapphire_sysdev_class = { + .name = "sapphiregpio_irq", + .suspend = sapphire_sysdev_suspend, + .resume = sapphire_sysdev_resume, +}; + +static struct sys_device sapphire_irq_device = { + .cls = &sapphire_sysdev_class, +}; + +int sapphire_init_gpio(void) +{ + int i; + if (!machine_is_sapphire()) + return 0; + + DBG("%d,%d\r\n", SAPPHIRE_INT_START, SAPPHIRE_INT_END); + DBG("NR_MSM_IRQS=%d, NR_GPIO_IRQS=%d\r\n", NR_MSM_IRQS, NR_GPIO_IRQS); + for (i = SAPPHIRE_INT_START; i <= SAPPHIRE_INT_END; i++) { + set_irq_chip(i, &sapphire_gpio_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + register_gpio_chip(&sapphire_gpio_chip); + + /*setup CPLD INT connecting to SOC's gpio 17 */ + set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); + set_irq_chained_handler(MSM_GPIO_TO_INT(17), sapphire_gpio_irq_handler); + set_irq_wake(MSM_GPIO_TO_INT(17), 1); + + if (sysdev_class_register(&sapphire_sysdev_class) == 0) + sysdev_register(&sapphire_irq_device); + + return 0; +} + +int sapphire_init_cpld(unsigned int sys_rev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sapphire_cpld_initdata); i++) + writeb(sapphire_cpld_initdata[i], SAPPHIRE_CPLD_BASE + i * 2); + return 0; +} + +arch_initcall(sapphire_init_gpio); diff --git a/arch/arm/mach-msm/board-sapphire-h2w.c b/arch/arm/mach-msm/board-sapphire-h2w.c new file mode 100644 index 000000000000..aa83e216974d --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-h2w.c @@ -0,0 +1,545 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * Authors: + * Laurence Chen <Laurence_Chen@htc.com> + * Nick Pelly <npelly@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on sapphire. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + + +#include <linux/module.h> +#include <linux/sysdev.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/hrtimer.h> +#include <linux/switch.h> +#include <linux/input.h> +#include <linux/debugfs.h> +#include <linux/gpio.h> +#include <asm/atomic.h> +#include <mach/board.h> +#include <mach/vreg.h> +#include <asm/mach-types.h> +#include "board-sapphire.h" + +#ifdef CONFIG_DEBUG_SAPPHIRE_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\n", __FUNCTION__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); +enum { + NO_DEVICE = 0, + HTC_HEADSET = 1, +}; + +enum { + UART3 = 0, + GPIO = 1, +}; + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; +}; +static struct h2w_info *hi; + +static ssize_t sapphire_h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case NO_DEVICE: + return sprintf(buf, "No Device\n"); + case HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void configure_cpld(int route) +{ + H2W_DBG(" route = %s", route == UART3 ? "UART3" : "GPIO"); + switch (route) { + case UART3: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + break; + case GPIO: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + break; + } +} + +static void button_pressed(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG(""); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, HTC_HEADSET); + configure_cpld(GPIO); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + + + /* On some non-standard headset adapters (usually those without a + * button) the btn line is pulled down at the same time as the detect + * line. We can check here by sampling the button line, if it is + * low then it is probably a bad adapter so ignore the button. + * If the button is released then we stop ignoring the button, so that + * the user can recover from the situation where a headset is plugged + * in with button held down. + */ + hi->ignore_btn = !gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + hi->debounce_time = ktime_set(0, 20000000); /* 20 ms */ +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, NO_DEVICE); + configure_cpld(UART3); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int clk, cable_in1; + + H2W_DBG(""); + + if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + configure_cpld(GPIO); + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Set GPIO_CABLE_IN1 as output high */ + gpio_direction_output(SAPPHIRE_GPIO_CABLE_IN1, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Save H2W_CLK */ + clk = gpio_get_value(SAPPHIRE_GPIO_H2W_CLK_GPI); + /* Set GPIO_CABLE_IN1 as input */ + gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + cable_in1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + + if (cable_in1 == 0 && clk == 0) { + if (switch_get_state(&hi->sdev) == NO_DEVICE) + insert_headset(); + } else { + configure_cpld(UART3); + H2W_DBG("CABLE_IN1 was low, but not a headset " + "(recent cable_in1 = %d, clk = %d)", cable_in1, clk); + } +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == HTC_HEADSET) { + if (gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && !atomic_read(&hi->btn_state)) + button_pressed(); + } + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + if ((switch_get_state(&hi->sdev) == NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(SAPPHIRE_GPIO_CABLE_IN2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static void h2w_debug_set(void *data, u64 val) +{ + switch_set_state(&hi->sdev, (int)val); +} + +static u64 h2w_debug_get(void *data) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int sapphire_h2w_probe(struct platform_device *pdev) +{ + int ret; + unsigned long irq_flags; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + hi->sdev.name = "h2w"; + hi->sdev.print_name = sapphire_h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(SAPPHIRE_GPIO_CABLE_IN2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(SAPPHIRE_GPIO_CABLE_IN2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(SAPPHIRE_GPIO_CABLE_IN2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + configure_cpld(UART3); + /* Set the CPLD connected H2W GPIO's to input */ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + hi->input->evbit[0] = BIT_MASK(EV_KEY); + hi->input->keybit[BIT_WORD(KEY_MEDIA)] = BIT_MASK(KEY_MEDIA); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(SAPPHIRE_GPIO_CABLE_IN2); +err_request_button_gpio: + gpio_free(SAPPHIRE_GPIO_CABLE_IN1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int sapphire_h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(SAPPHIRE_GPIO_CABLE_IN2); + gpio_free(SAPPHIRE_GPIO_CABLE_IN1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + +static struct platform_device sapphire_h2w_device = { + .name = "sapphire-h2w", +}; + +static struct platform_driver sapphire_h2w_driver = { + .probe = sapphire_h2w_probe, + .remove = sapphire_h2w_remove, + .driver = { + .name = "sapphire-h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init sapphire_h2w_init(void) +{ + if (!machine_is_sapphire()) + return 0; + int ret; + H2W_DBG(""); + ret = platform_driver_register(&sapphire_h2w_driver); + if (ret) + return ret; + return platform_device_register(&sapphire_h2w_device); +} + +static void __exit sapphire_h2w_exit(void) +{ + platform_device_unregister(&sapphire_h2w_device); + platform_driver_unregister(&sapphire_h2w_driver); +} + +module_init(sapphire_h2w_init); +module_exit(sapphire_h2w_exit); + +MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>"); +MODULE_DESCRIPTION("HTC 2 Wire detection driver for sapphire"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-sapphire-keypad.c b/arch/arm/mach-msm/board-sapphire-keypad.c new file mode 100644 index 000000000000..14f12e5e865c --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-keypad.c @@ -0,0 +1,122 @@ +/* arch/arm/mach-msm/board-sapphire-keypad.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/gpio_event.h> +#include <asm/mach-types.h> +#include "gpio_chip.h" +#include "board-sapphire.h" +static char *keycaps = "--qwerty"; +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_sapphire." +module_param_named(keycaps, keycaps, charp, 0); + + +static unsigned int sapphire_col_gpios[] = { 35, 34 }; + +/* KP_MKIN2 (GPIO40) is not used? */ +static unsigned int sapphire_row_gpios[] = { 42, 41 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(sapphire_row_gpios) + (row)) + +/*scan matrix key*/ +/* HOME(up) + MENU (down)*/ +static const unsigned short sapphire_keymap1[ARRAY_SIZE(sapphire_col_gpios) * + ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_MENU, + + [KEYMAP_INDEX(1, 0)] = KEY_HOME, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +/* MENU(up) + HOME (down)*/ +static const unsigned short sapphire_keymap0[ARRAY_SIZE(sapphire_col_gpios) * + ARRAY_SIZE(sapphire_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +}; + +static struct gpio_event_matrix_info sapphire_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = sapphire_keymap1, + .output_gpios = sapphire_col_gpios, + .input_gpios = sapphire_row_gpios, + .noutputs = ARRAY_SIZE(sapphire_col_gpios), + .ninputs = ARRAY_SIZE(sapphire_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .debounce_delay.tv.nsec = 50 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | + GPIOKPF_REMOVE_PHANTOM_KEYS | + GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_direct_entry sapphire_keypad_nav_map[] = { + { SAPPHIRE_POWER_KEY, KEY_END }, + { SAPPHIRE_VOLUME_UP, KEY_VOLUMEUP }, + { SAPPHIRE_VOLUME_DOWN, KEY_VOLUMEDOWN }, +}; + +static struct gpio_event_input_info sapphire_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = sapphire_keypad_nav_map, + .debounce_time.tv.nsec = 20 * NSEC_PER_MSEC, + .keymap_size = ARRAY_SIZE(sapphire_keypad_nav_map) +}; + +static struct gpio_event_info *sapphire_keypad_info[] = { + &sapphire_keypad_matrix_info.info, + &sapphire_keypad_nav_info.info, +}; + +static struct gpio_event_platform_data sapphire_keypad_data = { + .name = "sapphire-keypad", + .info = sapphire_keypad_info, + .info_count = ARRAY_SIZE(sapphire_keypad_info) +}; + +static struct platform_device sapphire_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &sapphire_keypad_data, + }, +}; + +static int __init sapphire_init_keypad(void) +{ + if (!machine_is_sapphire()) + return 0; + + switch (sapphire_get_hwid()) { + case 0: + sapphire_keypad_matrix_info.keymap = sapphire_keymap0; + break; + default: + sapphire_keypad_matrix_info.keymap = sapphire_keymap1; + break; + } + return platform_device_register(&sapphire_keypad_device); +} + +device_initcall(sapphire_init_keypad); + diff --git a/arch/arm/mach-msm/board-sapphire-mmc.c b/arch/arm/mach-msm/board-sapphire-mmc.c new file mode 100644 index 000000000000..ff2a5fdc6c14 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-mmc.c @@ -0,0 +1,479 @@ +/* linux/arch/arm/mach-msm/board-sapphire-mmc.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/err.h> +#include <linux/debugfs.h> + +#include <linux/gpio.h> +#include <linux/io.h> +#include <asm/mach-types.h> + +#include <mach/vreg.h> +#include <mach/htc_pwrsink.h> + +#include <asm/mach/mmc.h> + +#include "devices.h" +#include "gpio_chip.h" +#include "board-sapphire.h" +#include "proc_comm.h" + +#define DEBUG_SDSLOT_VDD 1 + +extern int msm_add_sdcc(unsigned int controller, + struct mmc_platform_data *plat); + +/* ---- COMMON ---- */ +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +/* ---- SDCARD ---- */ + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint opt_disable_sdcard; + +static int __init sapphire_disablesdcard_setup(char *str) +{ + int cal = simple_strtol(str, NULL, 0); + + opt_disable_sdcard = cal; + return 1; +} + +__setup("board_sapphire.disable_sdcard=", sapphire_disablesdcard_setup); + +static struct vreg *vreg_sdslot; /* SD slot power */ + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static unsigned int sdslot_vdd = 0xffffffff; +static unsigned int sdslot_vreg_enabled; + +static uint32_t sapphire_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i, rc; + + BUG_ON(!vreg_sdslot); + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk(KERN_DEBUG "%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(vreg_sdslot); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + rc = vreg_enable(vreg_sdslot); + if (rc) { + printk(KERN_ERR "%s: Error enabling vreg (%d)\n", + __func__, rc); + } + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask == (1 << vdd)) { +#if DEBUG_SDSLOT_VDD + printk(KERN_DEBUG "%s: Setting level to %u\n", + __func__, mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdslot, + mmc_vdd_table[i].level); + if (rc) { + printk(KERN_ERR + "%s: Error setting vreg level (%d)\n", + __func__, rc); + } + return 0; + } + } + + printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int sapphire_sdslot_status(struct device *dev) +{ + unsigned int status; + + status = (unsigned int) gpio_get_value(SAPPHIRE_GPIO_SDMC_CD_N); + return !status; +} + +#define SAPPHIRE_MMC_VDD (MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30) + +static struct mmc_platform_data sapphire_sdslot_data = { + .ocr_mask = SAPPHIRE_MMC_VDD, + .status_irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), + .status = sapphire_sdslot_status, + .translate_vdd = sapphire_sdslot_switchvdd, +}; + +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */ +static int sapphire_wifi_cd = 0; /* WIFI virtual 'card detect' status */ + +static struct sdio_embedded_func wifi_func = { + .f_class = SDIO_CLASS_WLAN, + .f_maxblksize = 512, +}; + +static struct embedded_sdio_data sapphire_wifi_emb_data = { + .cis = { + .vendor = 0x104c, + .device = 0x9066, + .blksize = 512, + .max_dtr = 20000000, + }, + .cccr = { + .multi_block = 0, + .low_speed = 0, + .wide_bus = 1, + .high_power = 0, + .high_speed = 0, + }, + .funcs = &wifi_func, + .num_funcs = 1, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int sapphire_wifi_status_register(void (*callback)(int card_present, + void *dev_id), + void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int sapphire_wifi_status(struct device *dev) +{ + return sapphire_wifi_cd; +} + +int sapphire_wifi_set_carddetect(int val) +{ + printk(KERN_DEBUG "%s: %d\n", __func__, val); + sapphire_wifi_cd = val; + if (wifi_status_cb) + wifi_status_cb(val, wifi_status_cb_devid); + else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_set_carddetect); +#endif + +static int sapphire_wifi_power_state; +static int sapphire_bt_power_state; + +int sapphire_wifi_power(int on) +{ + int rc; + + printk(KERN_DEBUG "%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + rc = vreg_enable(vreg_wifi_osc); + if (rc) + return rc; + htc_pwrsink_set(PWRSINK_WIFI, 70); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + htc_pwrsink_set(PWRSINK_WIFI, 0); + } + gpio_set_value(SAPPHIRE_GPIO_MAC_32K_EN, on); + mdelay(100); + gpio_set_value(SAPPHIRE_GPIO_WIFI_EN, on); + mdelay(100); + if (!on) + vreg_disable(vreg_wifi_osc); + sapphire_wifi_power_state = on; + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_power); +#endif + +/* Eenable VREG_MMC pin to turn on fastclock oscillator : colin */ +int sapphire_bt_fastclock_power(int on) +{ + int rc; + + printk(KERN_DEBUG "sapphire_bt_fastclock_power on = %d\n", on); + if (vreg_wifi_osc) { + if (on) { + rc = vreg_enable(vreg_wifi_osc); + printk(KERN_DEBUG "BT vreg_enable vreg_mmc, rc=%d\n", + rc); + if (rc) { + printk("Error turn sapphire_bt_fastclock_power rc=%d\n", rc); + return rc; + } + } else { + if (!sapphire_wifi_power_state) { + vreg_disable(vreg_wifi_osc); + printk(KERN_DEBUG "BT disable vreg_wifi_osc.\n"); + } else + printk(KERN_DEBUG "BT shouldn't disable vreg_wifi_osc. WiFi is using it!!\n"); + } + } + sapphire_bt_power_state = on; + return 0; +} +EXPORT_SYMBOL(sapphire_bt_fastclock_power); + +static int sapphire_wifi_reset_state; +void sapphire_wifi_reset(int on) +{ + printk(KERN_DEBUG "%s: %d\n", __func__, on); + gpio_set_value(SAPPHIRE_GPIO_WIFI_PA_RESETX, !on); + sapphire_wifi_reset_state = on; + mdelay(50); +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(sapphire_wifi_reset); +#endif + +static struct mmc_platform_data sapphire_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = sapphire_wifi_status, + .register_status_notify = sapphire_wifi_status_register, + .embedded_sdio = &sapphire_wifi_emb_data, +}; + +int __init sapphire_init_mmc(unsigned int sys_rev) +{ + wifi_status_cb = NULL; + + sdslot_vreg_enabled = 0; + + vreg_sdslot = vreg_get(0, "gp6"); + if (IS_ERR(vreg_sdslot)) + return PTR_ERR(vreg_sdslot); + vreg_wifi_osc = vreg_get(0, "mmc"); + if (IS_ERR(vreg_wifi_osc)) + return PTR_ERR(vreg_wifi_osc); + + set_irq_wake(SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_SDMC_CD_N), 1); + + msm_add_sdcc(1, &sapphire_wifi_data); + + if (!opt_disable_sdcard) + msm_add_sdcc(2, &sapphire_sdslot_data); + else + printk(KERN_INFO "sapphire: SD-Card interface disabled\n"); + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int sapphiremmc_dbg_wifi_reset_set(void *data, u64 val) +{ + sapphire_wifi_reset((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = sapphire_wifi_reset_state; + return 0; +} + +static int sapphiremmc_dbg_wifi_cd_set(void *data, u64 val) +{ + sapphire_wifi_set_carddetect((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = sapphire_wifi_cd; + return 0; +} + +static int sapphiremmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + sapphire_wifi_power((int) val); + return 0; +} + +static int sapphiremmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + + *val = sapphire_wifi_power_state; + return 0; +} + +static int sapphiremmc_dbg_sd_pwr_set(void *data, u64 val) +{ + sapphire_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int sapphiremmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int sapphiremmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int sapphiremmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = sapphire_sdslot_status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_reset_fops, + sapphiremmc_dbg_wifi_reset_get, + sapphiremmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_cd_fops, + sapphiremmc_dbg_wifi_cd_get, + sapphiremmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_wifi_pwr_fops, + sapphiremmc_dbg_wifi_pwr_get, + sapphiremmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_pwr_fops, + sapphiremmc_dbg_sd_pwr_get, + sapphiremmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(sapphiremmc_dbg_sd_cd_fops, + sapphiremmc_dbg_sd_cd_get, + sapphiremmc_dbg_sd_cd_set, "%llu\n"); + +static int __init sapphiremmc_dbg_init(void) +{ + struct dentry *dent; + + if (!machine_is_sapphire()) + return 0; + + dent = debugfs_create_dir("sapphiremmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &sapphiremmc_dbg_wifi_pwr_fops); + + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &sapphiremmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &sapphiremmc_dbg_sd_cd_fops); + + return 0; +} + +device_initcall(sapphiremmc_dbg_init); + +#endif diff --git a/arch/arm/mach-msm/board-sapphire-panel.c b/arch/arm/mach-msm/board-sapphire-panel.c new file mode 100644 index 000000000000..9129f4cdcb5f --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-panel.c @@ -0,0 +1,656 @@ +/* linux/arch/arm/mach-msm/board-sapphire-panel.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/leds.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <linux/io.h> +#include <linux/gpio.h> +#include <asm/mach-types.h> + +#include <mach/msm_fb.h> +#include <mach/vreg.h> +#include <mach/htc_pwrsink.h> + +#include "gpio_chip.h" +#include "board-sapphire.h" +#include "proc_comm.h" +#include "devices.h" + +enum sapphire_panel_type { + SAPPHIRE_PANEL_SHARP = 0, + SAPPHIRE_PANEL_TOPPOLY, + NUM_OF_SAPPHIRE_PANELS, +}; +static int g_panel_id = -1 ; + +#define SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS 132 + +static int sapphire_backlight_off; +static int sapphire_backlight_brightness = + SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS; + +static uint8_t sapphire_backlight_last_level = 33; +static DEFINE_MUTEX(sapphire_backlight_lock); + +/* Divide dimming level into 12 sections, and restrict maximum level to 27 */ +#define DIMMING_STEPS 12 +static unsigned dimming_levels[NUM_OF_SAPPHIRE_PANELS][DIMMING_STEPS] = { + {0, 1, 2, 3, 6, 9, 11, 13, 16, 19, 22, 25}, /* Sharp */ + {0, 1, 2, 4, 7, 10, 13, 15, 18, 21, 24, 27}, /* Toppolly */ +}; +static unsigned pwrsink_percents[] = {0, 6, 8, 15, 26, 34, 46, 54, 65, 77, 87, + 100}; + +static void sapphire_set_backlight_level(uint8_t level) +{ + unsigned dimming_factor = 255/DIMMING_STEPS + 1; + int index = (level + dimming_factor - 1) / dimming_factor; + unsigned percent; + unsigned long flags; + int i = 0; + + printk(KERN_INFO "level=%d, new level=dimming_levels[%d]=%d\n", + level, index, dimming_levels[g_panel_id][index]); + percent = pwrsink_percents[index]; + level = dimming_levels[g_panel_id][index]; + + if (sapphire_backlight_last_level == level) + return; + + if (level == 0) { + gpio_set_value(27, 0); + msleep(2); + } else { + local_irq_save(flags); + if (sapphire_backlight_last_level == 0) { + gpio_set_value(27, 1); + udelay(40); + sapphire_backlight_last_level = 33; + } + i = (sapphire_backlight_last_level - level + 33) % 33; + while (i-- > 0) { + gpio_set_value(27, 0); + udelay(1); + gpio_set_value(27, 1); + udelay(1); + } + local_irq_restore(flags); + } + sapphire_backlight_last_level = level; + htc_pwrsink_set(PWRSINK_BACKLIGHT, percent); +} + +#define MDDI_CLIENT_CORE_BASE 0x108000 +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define SPI_BLOCK_BASE 0x120000 +#define I2C_BLOCK_BASE 0x130000 +#define PWM_BLOCK_BASE 0x140000 +#define GPIO_BLOCK_BASE 0x150000 +#define SYSTEM_BLOCK1_BASE 0x160000 +#define SYSTEM_BLOCK2_BASE 0x170000 + + +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define SYSCLKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define PWM0OFF (PWM_BLOCK_BASE|0x1C) + +#define V_VDDE2E_VDD2_GPIO 0 +#define V_VDDE2E_VDD2_GPIO_5M 89 +#define MDDI_RST_N 82 + +#define MDDICAP0 (MDDI_CLIENT_CORE_BASE|0x00) +#define MDDICAP1 (MDDI_CLIENT_CORE_BASE|0x04) +#define MDDICAP2 (MDDI_CLIENT_CORE_BASE|0x08) +#define MDDICAP3 (MDDI_CLIENT_CORE_BASE|0x0C) +#define MDCAPCHG (MDDI_CLIENT_CORE_BASE|0x10) +#define MDCRCERC (MDDI_CLIENT_CORE_BASE|0x14) +#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18) +#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C) +#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20) +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28) +#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define TESTMODE (MDDI_CLIENT_CORE_BASE|0x30) +#define FIFOMONI (MDDI_CLIENT_CORE_BASE|0x34) +#define INTMONI (MDDI_CLIENT_CORE_BASE|0x38) +#define MDIOBIST (MDDI_CLIENT_CORE_BASE|0x3C) +#define MDIOPSET (MDDI_CLIENT_CORE_BASE|0x40) +#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44) +#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48) +#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C) +#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50) +#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54) + +#define SRST (LCD_CONTROL_BLOCK_BASE|0x00) +#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04) +#define START (LCD_CONTROL_BLOCK_BASE|0x08) +#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C) +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define GAMMA (LCD_CONTROL_BLOCK_BASE|0x14) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define HDE_LEFT (LCD_CONTROL_BLOCK_BASE|0x24) +#define VDE_TOP (LCD_CONTROL_BLOCK_BASE|0x28) +#define PXL (LCD_CONTROL_BLOCK_BASE|0x30) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HSW (LCD_CONTROL_BLOCK_BASE|0x38) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40) +#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44) +#define VSW (LCD_CONTROL_BLOCK_BASE|0x48) +#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C) +#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) +#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60) +#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64) +#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68) +#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C) +#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70) +#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74) +#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78) +#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C) +#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80) +#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84) +#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88) +#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C) + +#define SSICTL (SPI_BLOCK_BASE|0x00) +#define SSITIME (SPI_BLOCK_BASE|0x04) +#define SSITX (SPI_BLOCK_BASE|0x08) +#define SSIRX (SPI_BLOCK_BASE|0x0C) +#define SSIINTC (SPI_BLOCK_BASE|0x10) +#define SSIINTS (SPI_BLOCK_BASE|0x14) +#define SSIDBG1 (SPI_BLOCK_BASE|0x18) +#define SSIDBG2 (SPI_BLOCK_BASE|0x1C) +#define SSIID (SPI_BLOCK_BASE|0x20) + +#define WKREQ (SYSTEM_BLOCK1_BASE|0x00) +#define CLKENB (SYSTEM_BLOCK1_BASE|0x04) +#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08) +#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C) +#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00) + +#define GPIODATA (GPIO_BLOCK_BASE|0x00) +#define GPIODIR (GPIO_BLOCK_BASE|0x04) +#define GPIOIS (GPIO_BLOCK_BASE|0x08) +#define GPIOIBE (GPIO_BLOCK_BASE|0x0C) +#define GPIOIEV (GPIO_BLOCK_BASE|0x10) +#define GPIOIE (GPIO_BLOCK_BASE|0x14) +#define GPIORIS (GPIO_BLOCK_BASE|0x18) +#define GPIOMIS (GPIO_BLOCK_BASE|0x1C) +#define GPIOIC (GPIO_BLOCK_BASE|0x20) +#define GPIOOMS (GPIO_BLOCK_BASE|0x24) +#define GPIOPC (GPIO_BLOCK_BASE|0x28) +#define GPIOID (GPIO_BLOCK_BASE|0x30) + +#define SPI_WRITE(reg, val) \ + { SSITX, 0x00010000 | (((reg) & 0xff) << 8) | ((val) & 0xff) }, \ + { 0, 5 }, + +#define SPI_WRITE1(reg) \ + { SSITX, (reg) & 0xff }, \ + { 0, 5 }, + +struct mddi_table { + uint32_t reg; + uint32_t value; +}; +static struct mddi_table mddi_toshiba_init_table[] = { + { DPSET0, 0x09e90046 }, + { DPSET1, 0x00000118 }, + { DPSUS, 0x00000000 }, + { DPRUN, 0x00000001 }, + { 1, 14 }, /* msleep 14 */ + { SYSCKENA, 0x00000001 }, + /*{ CLKENB, 0x000000EF } */ + { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */ + /*{ CLKENB, 0x000025CB }, Clock enable register */ + + { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */ + { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */ + { GPIOSEL, 0/*0x00000173*/}, /* # SYS.GPIOSEL # GPIO port multiplexing control */ + { GPIOPC, 0x03C300C0 }, /* # GPI .GPIOPC # GPIO2,3 PD cut */ + { WKREQ, 0x00000000 }, /* # SYS.WKREQ # Wake-up request event is VSYNC alignment */ + + { GPIOIBE, 0x000003FF }, + { GPIOIS, 0x00000000 }, + { GPIOIC, 0x000003FF }, + { GPIOIE, 0x00000000 }, + + { GPIODATA, 0x00040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x02040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { DRAMPWR, 0x00000001 }, /* eDRAM power */ +}; + +static struct mddi_table mddi_toshiba_panel_init_table[] = { + { SRST, 0x00000003 }, /* FIFO/LCDC not reset */ + { PORT_ENB, 0x00000001 }, /* Enable sync. Port */ + { START, 0x00000000 }, /* To stop operation */ + /*{ START, 0x00000001 }, To start operation */ + { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */ + { CMN, 0x00000000 }, + { GAMMA, 0x00000000 }, /* No Gamma correction */ + { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */ + { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */ + { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */ + { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */ + { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */ + { PXL, 0x00000001 }, /* 1. RGB666 */ + /* 2. Data is valid from 1st frame of beginning. */ + { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */ + { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */ + { HSW, 0x00000004 }, /* HSW= 10 PCLK */ + { VSW, 0x00000001 }, /* VSW=2 HCYCLE */ + { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */ + { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */ + { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */ + { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */ + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { CLKENB, 0x000025CB }, /* Clock enable register */ + + { SSICTL, 0x00000170 }, /* SSI control register */ + { SSITIME, 0x00000250 }, /* SSI timing control register */ + { SSICTL, 0x00000172 }, /* SSI control register */ +}; + + +static struct mddi_table mddi_sharp_init_table[] = { + { VCYCLE, 0x000001eb }, + { HCYCLE, 0x000000ae }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { 1, 10 }, /* msleep 10 */ + SPI_WRITE(0x5f, 0x01) + SPI_WRITE1(0x11) + { 1, 200 }, /* msleep 200 */ + SPI_WRITE1(0x29) + SPI_WRITE1(0xde) + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_sharp_deinit_table[] = { + { 1, 200 }, /* msleep 200 */ + SPI_WRITE(0x10, 0x1) + { 1, 100 }, /* msleep 100 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 1, 10 }, /* msleep 10 */ +}; + +static struct mddi_table mddi_tpo_init_table[] = { + { VCYCLE, 0x000001e5 }, + { HCYCLE, 0x000000ac }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { 0, 20 }, /* udelay 20 */ + { GPIODATA, 0x00000004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 0, 20 }, /* udelay 20 */ + + SPI_WRITE(0x08, 0x01) + { 0, 500 }, /* udelay 500 */ + SPI_WRITE(0x08, 0x00) + SPI_WRITE(0x02, 0x00) + SPI_WRITE(0x03, 0x04) + SPI_WRITE(0x04, 0x0e) + SPI_WRITE(0x09, 0x02) + SPI_WRITE(0x0b, 0x08) + SPI_WRITE(0x0c, 0x53) + SPI_WRITE(0x0d, 0x01) + SPI_WRITE(0x0e, 0xe0) + SPI_WRITE(0x0f, 0x01) + SPI_WRITE(0x10, 0x58) + SPI_WRITE(0x20, 0x1e) + SPI_WRITE(0x21, 0x0a) + SPI_WRITE(0x22, 0x0a) + SPI_WRITE(0x23, 0x1e) + SPI_WRITE(0x25, 0x32) + SPI_WRITE(0x26, 0x00) + SPI_WRITE(0x27, 0xac) + SPI_WRITE(0x29, 0x06) + SPI_WRITE(0x2a, 0xa4) + SPI_WRITE(0x2b, 0x45) + SPI_WRITE(0x2c, 0x45) + SPI_WRITE(0x2d, 0x15) + SPI_WRITE(0x2e, 0x5a) + SPI_WRITE(0x2f, 0xff) + SPI_WRITE(0x30, 0x6b) + SPI_WRITE(0x31, 0x0d) + SPI_WRITE(0x32, 0x48) + SPI_WRITE(0x33, 0x82) + SPI_WRITE(0x34, 0xbd) + SPI_WRITE(0x35, 0xe7) + SPI_WRITE(0x36, 0x18) + SPI_WRITE(0x37, 0x94) + SPI_WRITE(0x38, 0x01) + SPI_WRITE(0x39, 0x5d) + SPI_WRITE(0x3a, 0xae) + SPI_WRITE(0x3b, 0xff) + SPI_WRITE(0x07, 0x09) + { 0, 10 }, /* udelay 10 */ + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_tpo_deinit_table[] = { + SPI_WRITE(0x07, 0x19) + { START, 0x00000000 }, /* To stop operation */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 0, 5 }, /* usleep 5 */ +}; + + +#define GPIOSEL_VWAKEINT (1U << 0) +#define INTMASK_VWAKEOUT (1U << 0) + +static void sapphire_process_mddi_table( + struct msm_mddi_client_data *client_data, + struct mddi_table *table, size_t count) +{ + int i; + for (i = 0; i < count; i++) { + uint32_t reg = table[i].reg; + uint32_t value = table[i].value; + + if (reg == 0) + udelay(value); + else if (reg == 1) + msleep(value); + else + client_data->remote_write(client_data, value, reg); + } +} + +static struct vreg *vreg_lcm_2v85; + +static void sapphire_mddi_power_client(struct msm_mddi_client_data *client_data, + int on) +{ + unsigned id, on_off; + printk(KERN_INFO "sapphire_mddi_client_power:%d\r\n", on); + if (on) { + on_off = 0; + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + + gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 1); + mdelay(5); /* delay time >5ms and <10ms */ + + if (is_12pin_camera()) + gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 1); + else + gpio_set_value(V_VDDE2E_VDD2_GPIO, 1); + + gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 1); + msleep(3); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcm_2v85); + msleep(3); + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + } else { + gpio_set_value(SAPPHIRE_GPIO_MDDI_32K_EN, 0); + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + vreg_disable(vreg_lcm_2v85); + on_off = 1; + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + msleep(5); + if (is_12pin_camera()) + gpio_set_value(V_VDDE2E_VDD2_GPIO_5M, 0); + else + gpio_set_value(V_VDDE2E_VDD2_GPIO, 0); + + msleep(200); + gpio_set_value(SAPPHIRE_MDDI_1V5_EN, 0); + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + } +} + +static int sapphire_mddi_toshiba_client_init( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id; + + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, mddi_toshiba_init_table, + ARRAY_SIZE(mddi_toshiba_init_table)); + client_data->auto_hibernate(client_data, 1); + g_panel_id = panel_id = + (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + if (panel_id > 1) { + printk(KERN_ERR "unknown panel id at mddi_enable\n"); + return -1; + } + return 0; +} + +static int sapphire_mddi_toshiba_client_uninit( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + return 0; +} + +static int sapphire_mddi_panel_unblank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id, ret = 0; + + sapphire_set_backlight_level(0); + client_data->auto_hibernate(client_data, 0); + sapphire_process_mddi_table(client_data, mddi_toshiba_panel_init_table, + ARRAY_SIZE(mddi_toshiba_panel_init_table)); + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + switch (panel_id) { + case 0: + printk(KERN_DEBUG "init sharp panel\n"); + sapphire_process_mddi_table(client_data, + mddi_sharp_init_table, + ARRAY_SIZE(mddi_sharp_init_table)); + break; + case 1: + printk(KERN_DEBUG "init tpo panel\n"); + sapphire_process_mddi_table(client_data, + mddi_tpo_init_table, + ARRAY_SIZE(mddi_tpo_init_table)); + break; + default: + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(sapphire_backlight_brightness); + sapphire_backlight_off = 0; + mutex_unlock(&sapphire_backlight_lock); + client_data->auto_hibernate(client_data, 1); + /* reenable vsync */ + client_data->remote_write(client_data, GPIOSEL_VWAKEINT, + GPIOSEL); + client_data->remote_write(client_data, INTMASK_VWAKEOUT, + INTMASK); + return ret; + +} + +static int sapphire_mddi_panel_blank( + struct msm_mddi_bridge_platform_data *bridge_data, + struct msm_mddi_client_data *client_data) +{ + int panel_id, ret = 0; + + panel_id = (client_data->remote_read(client_data, GPIODATA) >> 4) & 3; + client_data->auto_hibernate(client_data, 0); + switch (panel_id) { + case 0: + printk(KERN_DEBUG "deinit sharp panel\n"); + sapphire_process_mddi_table(client_data, + mddi_sharp_deinit_table, + ARRAY_SIZE(mddi_sharp_deinit_table)); + break; + case 1: + printk(KERN_DEBUG "deinit tpo panel\n"); + sapphire_process_mddi_table(client_data, + mddi_tpo_deinit_table, + ARRAY_SIZE(mddi_tpo_deinit_table)); + break; + default: + printk(KERN_DEBUG "unknown panel_id: %d\n", panel_id); + ret = -1; + }; + client_data->auto_hibernate(client_data, 1); + mutex_lock(&sapphire_backlight_lock); + sapphire_set_backlight_level(0); + sapphire_backlight_off = 1; + mutex_unlock(&sapphire_backlight_lock); + client_data->remote_write(client_data, 0, SYSCLKENA); + client_data->remote_write(client_data, 1, DPSUS); + + return ret; +} + +static void sapphire_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + mutex_lock(&sapphire_backlight_lock); + sapphire_backlight_brightness = value; + if (!sapphire_backlight_off) + sapphire_set_backlight_level(sapphire_backlight_brightness); + mutex_unlock(&sapphire_backlight_lock); +} + +static struct led_classdev sapphire_backlight_led = { + .name = "lcd-backlight", + .brightness = SAPPHIRE_DEFAULT_BACKLIGHT_BRIGHTNESS, + .brightness_set = sapphire_brightness_set, +}; + +static int sapphire_backlight_probe(struct platform_device *pdev) +{ + led_classdev_register(&pdev->dev, &sapphire_backlight_led); + return 0; +} + +static int sapphire_backlight_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&sapphire_backlight_led); + return 0; +} + +static struct platform_driver sapphire_backlight_driver = { + .probe = sapphire_backlight_probe, + .remove = sapphire_backlight_remove, + .driver = { + .name = "sapphire-backlight", + .owner = THIS_MODULE, + }, +}; + +static struct resource resources_msm_fb[] = { + { + .start = SMI64_MSM_FB_BASE, + .end = SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct msm_mddi_bridge_platform_data toshiba_client_data = { + .init = sapphire_mddi_toshiba_client_init, + .uninit = sapphire_mddi_toshiba_client_uninit, + .blank = sapphire_mddi_panel_blank, + .unblank = sapphire_mddi_panel_unblank, + .fb_data = { + .xres = 320, + .yres = 480, + .width = 45, + .height = 67, + .output_format = 0, + }, +}; + +static struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 122880000, + .power_client = sapphire_mddi_power_client, + .fb_resource = resources_msm_fb, + .num_clients = 1, + .client_platform_data = { + { + .product_id = (0xd263 << 16 | 0), + .name = "mddi_c_d263_0000", + .id = 0, + .client_data = &toshiba_client_data, + .clk_rate = 0, + }, + }, +}; + +static struct platform_device sapphire_backlight = { + .name = "sapphire-backlight", +}; + +int __init sapphire_init_panel(void) +{ + int rc = -1; + uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA); /* GPIO27 */ + + if (!machine_is_sapphire()) + return 0; + + /* checking board as soon as possible */ + printk("sapphire_init_panel:machine_is_sapphire=%d, machine_arch_type=%d, MACH_TYPE_SAPPHIRE=%d\r\n", machine_is_sapphire(), machine_arch_type, MACH_TYPE_SAPPHIRE); + if (!machine_is_sapphire()) + return 0; + + vreg_lcm_2v85 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcm_2v85)) + return PTR_ERR(vreg_lcm_2v85); + + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + + /* setup FB by SMI size */ + if (sapphire_get_smi_size() == 32) { + resources_msm_fb[0].start = SMI32_MSM_FB_BASE; + resources_msm_fb[0].end = SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE - 1; + } + + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + platform_device_register(&sapphire_backlight); + return platform_driver_register(&sapphire_backlight_driver); +} + +device_initcall(sapphire_init_panel); diff --git a/arch/arm/mach-msm/board-sapphire-rfkill.c b/arch/arm/mach-msm/board-sapphire-rfkill.c new file mode 100644 index 000000000000..135ffe82c328 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-rfkill.c @@ -0,0 +1,99 @@ +/* linux/arch/arm/mach-msm/board-sapphire-rfkill.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +/* Control bluetooth power for sapphire platform */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/rfkill.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <asm/mach-types.h> +#include "gpio_chip.h" +#include "board-sapphire.h" + +void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); + +static struct rfkill *bt_rfk; +static const char bt_name[] = "brf6300"; + +extern int sapphire_bt_fastclock_power(int on); + +static int bluetooth_set_power(void *data, enum rfkill_state state) +{ + switch (state) { + case RFKILL_STATE_UNBLOCKED: + sapphire_bt_fastclock_power(1); + gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); + break; + case RFKILL_STATE_SOFT_BLOCKED: + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_set_value(SAPPHIRE_GPIO_BT_32K_EN, 0); + sapphire_bt_fastclock_power(0); + break; + default: + printk(KERN_ERR "bad bluetooth rfkill state %d\n", state); + } + return 0; +} + +static int __init sapphire_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + + /* default to bluetooth off */ + rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); + bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); + + bt_rfk = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + if (!bt_rfk) + return -ENOMEM; + + bt_rfk->name = bt_name; + bt_rfk->state = RFKILL_STATE_SOFT_BLOCKED; + /* userspace cannot take exclusive control */ + bt_rfk->user_claim_unsupported = 1; + bt_rfk->user_claim = 0; + bt_rfk->data = NULL; /* user data */ + bt_rfk->toggle_radio = bluetooth_set_power; + + rc = rfkill_register(bt_rfk); + + if (rc) + rfkill_free(bt_rfk); + return rc; +} + +static struct platform_driver sapphire_rfkill_driver = { + .probe = sapphire_rfkill_probe, + .driver = { + .name = "sapphire_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init sapphire_rfkill_init(void) +{ + if (!machine_is_sapphire()) + return 0; + return platform_driver_register(&sapphire_rfkill_driver); +} + +module_init(sapphire_rfkill_init); +MODULE_DESCRIPTION("sapphire rfkill"); +MODULE_AUTHOR("Nick Pelly <npelly@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-sapphire-wifi.c b/arch/arm/mach-msm/board-sapphire-wifi.c new file mode 100644 index 000000000000..43f827c60f13 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire-wifi.c @@ -0,0 +1,74 @@ +/* arch/arm/mach-msm/board-sapphire-wifi.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt <dimitrysh@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/vmalloc.h> +#include <linux/err.h> +#include <linux/wifi_tiwlan.h> + +extern int sapphire_wifi_set_carddetect(int val); +extern int sapphire_wifi_power(int on); +extern int sapphire_wifi_reset(int on); + +#ifdef CONFIG_WIFI_MEM_PREALLOC +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = { + { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) } +}; + +static void *sapphire_wifi_mem_prealloc(int section, unsigned long size) +{ + if ((section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS)) + return NULL; + if (wifi_mem_array[section].size < size) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init sapphire_init_wifi_mem (void) +{ + int i; + + for (i = 0; (i < WMPA_NUMBER_OF_SECTIONS); i++) { + wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size); + if (wifi_mem_array[i].mem_ptr == NULL) + return -ENOMEM; + } + return 0; +} +#endif + +struct wifi_platform_data sapphire_wifi_control = { + .set_power = sapphire_wifi_power, + .set_reset = sapphire_wifi_reset, + .set_carddetect = sapphire_wifi_set_carddetect, +#ifdef CONFIG_WIFI_MEM_PREALLOC + .mem_prealloc = sapphire_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif +}; + +#endif diff --git a/arch/arm/mach-msm/board-sapphire.c b/arch/arm/mach-msm/board-sapphire.c new file mode 100644 index 000000000000..8de7a45ea649 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire.c @@ -0,0 +1,1175 @@ +/* linux/arch/arm/mach-msm/board-sapphire.c + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/keyreset.h> +#include <linux/leds.h> +#include <linux/switch.h> +#include <linux/synaptics_i2c_rmi.h> +#include <linux/elan_i2c.h> +#include <linux/akm8976.h> +#include <mach/htc_headset.h> +#include <linux/sysdev.h> +#include <linux/android_pmem.h> + +#include <linux/delay.h> + +#include <asm/gpio.h> +#include <mach/hardware.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> +#include <asm/system.h> +#include <mach/system.h> +#include <mach/vreg.h> + +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/setup.h> + +#include <linux/gpio_event.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <asm/mach/mmc.h> +#include <linux/mmc/sdio_ids.h> + + +#include "gpio_chip.h" +#include "board-sapphire.h" +#include "pm.h" + +#include <mach/board.h> +#include <mach/board_htc.h> +#include <mach/msm_serial_hs.h> +#include <mach/htc_pwrsink.h> + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#ifdef CONFIG_WIFI_MEM_PREALLOC +extern int sapphire_init_wifi_mem(void); +#endif +extern struct wifi_platform_data sapphire_wifi_control; +#endif + +#include "proc_comm.h" +#include "devices.h" + +void msm_init_irq(void); +void msm_init_gpio(void); +void msm_init_pmic_vibrator(void); + +extern int sapphire_init_mmc(unsigned int); + +struct sapphire_axis_info { + struct gpio_event_axis_info info; + uint16_t in_state; + uint16_t out_state; + uint16_t temp_state; + uint16_t threshold; +}; +static bool nav_just_on; +static int nav_on_jiffies; +static int smi_sz = 64; +static unsigned int hwid = 0; +static unsigned int skuid = 0; +static unsigned engineerid = (0x01 << 1); /* default is 3M sensor */ + +uint16_t sapphire_axis_map(struct gpio_event_axis_info *info, uint16_t in) +{ + struct sapphire_axis_info *ai = container_of(info, struct sapphire_axis_info, info); + uint16_t out = ai->out_state; + + if (nav_just_on) { + if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1) + goto ignore; + nav_just_on = 0; + } + if ((ai->in_state ^ in) & 1) + out--; + if ((ai->in_state ^ in) & 2) + out++; + ai->out_state = out; +ignore: + ai->in_state = in; + if (ai->out_state - ai->temp_state == ai->threshold) { + ai->temp_state++; + ai->out_state = ai->temp_state; + } else if (ai->temp_state - ai->out_state == ai->threshold) { + ai->temp_state--; + ai->out_state = ai->temp_state; + } else if (abs(ai->out_state - ai->temp_state) > ai->threshold) + ai->temp_state = ai->out_state; + + return ai->temp_state; +} + +int sapphire_nav_power(const struct gpio_event_platform_data *pdata, bool on) +{ + gpio_set_value(SAPPHIRE_GPIO_JOG_EN, on); + if (on) { + nav_just_on = 1; + nav_on_jiffies = jiffies; + } + return 0; +} + +static uint32_t sapphire_x_axis_gpios[] = { + SAPPHIRE_BALL_LEFT_0, SAPPHIRE_BALL_RIGHT_0 +}; + +static struct sapphire_axis_info sapphire_x_axis = { + .threshold = 2, + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(sapphire_x_axis_gpios), + .type = EV_REL, + .code = REL_X, + .decoded_size = 1U << ARRAY_SIZE(sapphire_x_axis_gpios), + .map = sapphire_axis_map, + .gpio = sapphire_x_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static uint32_t sapphire_y_axis_gpios[] = { + SAPPHIRE_BALL_UP_0, SAPPHIRE_BALL_DOWN_0 +}; + +static struct sapphire_axis_info sapphire_y_axis = { + .threshold = 2, + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(sapphire_y_axis_gpios), + .type = EV_REL, + .code = REL_Y, + .decoded_size = 1U << ARRAY_SIZE(sapphire_y_axis_gpios), + .map = sapphire_axis_map, + .gpio = sapphire_y_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static struct gpio_event_direct_entry sapphire_nav_buttons[] = { + { SAPPHIRE_GPIO_NAVI_ACT_N, BTN_MOUSE }, + { SAPPHIRE_GPIO_SEARCH_ACT_N, KEY_COMPOSE }, /* CPLD Key Search */ +}; + +static struct gpio_event_input_info sapphire_nav_button_info = { + .info.func = gpio_event_input_func, + .flags = GPIOEDF_PRINT_KEYS | GPIOEDF_PRINT_KEY_DEBOUNCE, + .poll_time.tv.nsec = 40 * NSEC_PER_MSEC, + .type = EV_KEY, + .keymap = sapphire_nav_buttons, + .keymap_size = ARRAY_SIZE(sapphire_nav_buttons) +}; + +static struct gpio_event_info *sapphire_nav_info[] = { + &sapphire_x_axis.info.info, + &sapphire_y_axis.info.info, + &sapphire_nav_button_info.info +}; + +static struct gpio_event_platform_data sapphire_nav_data = { + .name = "sapphire-nav", + .info = sapphire_nav_info, + .info_count = ARRAY_SIZE(sapphire_nav_info), + .power = sapphire_nav_power, +}; + +static struct platform_device sapphire_nav_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 2, + .dev = { + .platform_data = &sapphire_nav_data, + }, +}; + +static int sapphire_reset_keys_up[] = { + BTN_MOUSE, + 0 +}; + +static struct keyreset_platform_data sapphire_reset_keys_pdata = { + .keys_up = sapphire_reset_keys_up, + .keys_down = { + KEY_SEND, + KEY_MENU, + KEY_END, + 0 + }, +}; + +struct platform_device sapphire_reset_keys_device = { + .name = KEYRESET_NAME, + .dev.platform_data = &sapphire_reset_keys_pdata, +}; + +static int sapphire_ts_power(int on) +{ + int gpio_tp_ls_en = SAPPHIRE_TP_LS_EN; + + if (is_12pin_camera()) + gpio_tp_ls_en = SAPPHIRE20_TP_LS_EN; + + if (on) { + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 1); + /* touchscreen must be powered before we enable i2c pullup */ + msleep(2); + /* enable touch panel level shift */ + gpio_direction_output(gpio_tp_ls_en, 1); + msleep(2); + } else { + gpio_direction_output(gpio_tp_ls_en, 0); + udelay(50); + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_TP_EN, 0); + } + + return 0; +} + +static struct synaptics_i2c_rmi_platform_data sapphire_ts_data[] = { +{ + .version = 0x0101, + .power = sapphire_ts_power, + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = -50 * 0x10000 / 4334, + .inactive_right = -50 * 0x10000 / 4334, + .inactive_top = -40 * 0x10000 / 6696, + .inactive_bottom = -40 * 0x10000 / 6696, + .snap_left_on = 50 * 0x10000 / 4334, + .snap_left_off = 60 * 0x10000 / 4334, + .snap_right_on = 50 * 0x10000 / 4334, + .snap_right_off = 60 * 0x10000 / 4334, + .snap_top_on = 100 * 0x10000 / 6696, + .snap_top_off = 110 * 0x10000 / 6696, + .snap_bottom_on = 100 * 0x10000 / 6696, + .snap_bottom_off = 110 * 0x10000 / 6696, + }, + { + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_right = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_top = ((6946 - 6696) / 2) * 0x10000 / 6696, + .inactive_bottom = ((6946 - 6696) / 2) * 0x10000 / 6696, + } +}; + +static struct akm8976_platform_data compass_platform_data = { + .reset = SAPPHIRE_GPIO_COMPASS_RST_N, + .clk_on = SAPPHIRE_GPIO_COMPASS_32K_EN, + .intr = SAPPHIRE_GPIO_COMPASS_IRQ, +}; + +static struct elan_i2c_platform_data elan_i2c_data[] = { + { + .version = 0x104, + .abs_x_min = 0, + .abs_y_min = 0, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + }, + { + .version = 0x103, + .abs_x_min = 0, + .abs_x_max = 512 * 2, + .abs_y_min = 0, + .abs_y_max = 896 * 2, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + }, + { + .version = 0x102, + .abs_x_min = 0, + .abs_x_max = 384, + .abs_y_min = 0, + .abs_y_max = 576, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + }, + { + .version = 0x101, + .abs_x_min = 32 + 1, + .abs_x_max = 352 - 1, + .abs_y_min = 32 + 1, + .abs_y_max = 544 - 1, + .intr_gpio = SAPPHIRE_GPIO_TP_ATT_N, + .power = sapphire_ts_power, + } +}; + +static struct msm_camera_device_platform_data msm_camera_device_mt9t013 = { + .sensor_reset = 108, + .sensor_pwd = 85, + .vcm_pwd = SAPPHIRE_GPIO_VCM_PWDN, + .config_gpio_on = config_sapphire_camera_on_gpios, + .config_gpio_off = config_sapphire_camera_off_gpios, +}; + +static struct platform_device sapphire_camera = { + .name = "camera", + .dev = { + .platform_data = &msm_camera_device_mt9t013, + }, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20), + .platform_data = sapphire_ts_data, + .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_TP_ATT_N) + }, + { + I2C_BOARD_INFO(ELAN_8232_I2C_NAME, 0x10), + .platform_data = &elan_i2c_data, + .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_TP_ATT_N), + }, + { + I2C_BOARD_INFO("akm8976", 0x1C), + .platform_data = &compass_platform_data, + .irq = SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_COMPASS_IRQ), + }, + { + I2C_BOARD_INFO("mt9t013", 0x6C >> 1), + .platform_data = &msm_camera_device_mt9t013, + }, +}; + +#ifdef CONFIG_LEDS_CPLD +static struct resource cpldled_resources[] = { + { + .start = SAPPHIRE_CPLD_LED_BASE, + .end = SAPPHIRE_CPLD_LED_BASE + SAPPHIRE_CPLD_LED_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device android_CPLD_leds = { + .name = "leds-cpld", + .id = -1, + .num_resources = ARRAY_SIZE(cpldled_resources), + .resource = cpldled_resources, +}; +#endif + +static struct gpio_led android_led_list[] = { + { + .name = "button-backlight", + .gpio = SAPPHIRE_GPIO_APKEY_LED_EN, + }, +}; + +static struct gpio_led_platform_data android_leds_data = { + .num_leds = ARRAY_SIZE(android_led_list), + .leds = android_led_list, +}; + +static struct platform_device android_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &android_leds_data, + }, +}; + +#ifdef CONFIG_HTC_HEADSET +/* RTS/CTS to GPO/GPI. */ +static uint32_t uart1_on_gpio_table[] = { + /* allenou, uart hs test, 2008/11/18 */ + #ifdef CONFIG_SERIAL_MSM_HS + /* RTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 2, + GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), + /* CTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 2, + GPIO_INPUT, GPIO_PULL_UP, GPIO_8MA), + #else + /* RTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 1, + GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), + /* CTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 1, + GPIO_INPUT, GPIO_PULL_DOWN, GPIO_4MA), + #endif +}; + +/* RTS,CTS to BT. */ +static uint32_t uart1_off_gpio_table[] = { + /* RTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_RTS, 0, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), + /* CTS */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART1_CTS, 0, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +/* Sapphire: Switch between UART3 and GPIO */ +static uint32_t uart3_on_gpio_table[] = { + /* RX */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART3_RX, 1, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), + /* TX */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_UART3_TX, 1, + GPIO_OUTPUT, GPIO_NO_PULL, GPIO_2MA), +}; + +/* set TX,RX to GPI */ +static uint32_t uart3_off_gpi_table[] = { + /* RX, H2W DATA */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_H2W_DATA, 0, + GPIO_INPUT, GPIO_NO_PULL, GPIO_2MA), + /* TX, H2W CLK */ + PCOM_GPIO_CFG(SAPPHIRE_GPIO_H2W_CLK, 0, + GPIO_INPUT, GPIO_KEEPER, GPIO_2MA), +}; + +static int sapphire_h2w_path = H2W_GPIO; + +static void h2w_config_cpld(int route) +{ + switch (route) { + case H2W_UART1: + /* Make sure uart1 funtion pin opened. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+1, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 1); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + sapphire_h2w_path = H2W_UART1; + printk(KERN_INFO "H2W route = H2W-UART1, BT-X, UART3-X \n"); + break; + case H2W_BT: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 1); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + /* UART1 RTS/CTS to GPO/GPI. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_off_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_off_gpio_table+1, 0); + sapphire_h2w_path = H2W_BT; + printk(KERN_INFO "H2W route = H2W-BT, UART1-X, UART3-X \n"); + break; + case H2W_UART3: + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_on_gpio_table+1, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + /* Make sure uart1 funtion pin opened. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+1, 0); + sapphire_h2w_path = H2W_UART3; + printk(KERN_INFO "H2W route = H2W-UART3, BT-UART1 \n"); + break; + case H2W_GPIO: /*H2W_UART3 TX,RX are changed to H2W_GPIO */ + default: + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 0); + /* Set the CPLD connected H2W GPIO's to input */ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, 0); + /* TX,RX GPI first. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart3_off_gpi_table+1, 0); + /* Make sure uart1 funtion pin opened. */ + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+0, 0); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, + uart1_on_gpio_table+1, 0); + sapphire_h2w_path = H2W_GPIO; + printk(KERN_INFO "H2W route = H2W-GPIO, BT-UART1 \n"); + break; + } +} + +static void h2w_init_cpld(void) +{ + h2w_config_cpld(H2W_GPIO); +} + +static void set_h2w_dat(int n) +{ + gpio_set_value(SAPPHIRE_GPIO_H2W_DATA, n); +} + +static void set_h2w_clk(int n) +{ + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK, n); +} + +static void set_h2w_dat_dir(int n) +{ + if (n == 0) /* input */ + gpio_direction_input(SAPPHIRE_GPIO_H2W_DATA); + else + gpio_configure(SAPPHIRE_GPIO_H2W_DATA, GPIOF_DRIVE_OUTPUT); + + gpio_set_value(SAPPHIRE_GPIO_H2W_DAT_DIR, n); + +} + +static void set_h2w_clk_dir(int n) +{ + if (n == 0) /* input */ + gpio_direction_input(SAPPHIRE_GPIO_H2W_CLK); + else + gpio_configure(SAPPHIRE_GPIO_H2W_CLK, GPIOF_DRIVE_OUTPUT); + + gpio_set_value(SAPPHIRE_GPIO_H2W_CLK_DIR, n); +} + +static int get_h2w_dat(void) +{ + return gpio_get_value(SAPPHIRE_GPIO_H2W_DATA); +} + +static int get_h2w_clk(void) +{ + return gpio_get_value(SAPPHIRE_GPIO_H2W_CLK); +} + +static int set_h2w_path(const char *val, struct kernel_param *kp) +{ + int ret = -EINVAL; + + ret = param_set_int(val, kp); + if (ret) + return ret; + + switch (sapphire_h2w_path) { + case H2W_GPIO: + case H2W_UART1: + case H2W_UART3: + case H2W_BT: + break; + default: + sapphire_h2w_path = -1; + return -EINVAL; + } + + h2w_config_cpld(sapphire_h2w_path); + return ret; +} +module_param_call(h2w_path, set_h2w_path, param_get_int, + &sapphire_h2w_path, S_IWUSR | S_IRUGO); + + +static struct h2w_platform_data sapphire_h2w_data = { + .power_name = "wlan", + .cable_in1 = SAPPHIRE_GPIO_CABLE_IN1, + .cable_in2 = SAPPHIRE_GPIO_CABLE_IN2, + .h2w_clk = SAPPHIRE_GPIO_H2W_CLK, + .h2w_data = SAPPHIRE_GPIO_H2W_DATA, + .debug_uart = H2W_UART3, + .config_cpld = h2w_config_cpld, + .init_cpld = h2w_init_cpld, + .set_dat = set_h2w_dat, + .set_clk = set_h2w_clk, + .set_dat_dir = set_h2w_dat_dir, + .set_clk_dir = set_h2w_clk_dir, + .get_dat = get_h2w_dat, + .get_clk = get_h2w_clk, +}; + +static struct platform_device sapphire_h2w = { + .name = "h2w", + .id = -1, + .dev = { + .platform_data = &sapphire_h2w_data, + }, +}; +#endif + +static void sapphire_phy_reset(void) +{ + gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 0); + mdelay(10); + gpio_set_value(SAPPHIRE_GPIO_USB_PHY_RST_N, 1); + mdelay(10); +} + +static struct pwr_sink sapphire_pwrsink_table[] = { + { + .id = PWRSINK_AUDIO, + .ua_max = 100000, + }, + { + .id = PWRSINK_BACKLIGHT, + .ua_max = 125000, + }, + { + .id = PWRSINK_LED_BUTTON, + .ua_max = 0, + }, + { + .id = PWRSINK_LED_KEYBOARD, + .ua_max = 0, + }, + { + .id = PWRSINK_GP_CLK, + .ua_max = 0, + }, + { + .id = PWRSINK_BLUETOOTH, + .ua_max = 15000, + }, + { + .id = PWRSINK_CAMERA, + .ua_max = 0, + }, + { + .id = PWRSINK_SDCARD, + .ua_max = 0, + }, + { + .id = PWRSINK_VIDEO, + .ua_max = 0, + }, + { + .id = PWRSINK_WIFI, + .ua_max = 200000, + }, + { + .id = PWRSINK_SYSTEM_LOAD, + .ua_max = 100000, + .percent_util = 38, + }, +}; + +static struct pwr_sink_platform_data sapphire_pwrsink_data = { + .num_sinks = ARRAY_SIZE(sapphire_pwrsink_table), + .sinks = sapphire_pwrsink_table, + .suspend_late = NULL, + .resume_early = NULL, + .suspend_early = NULL, + .resume_late = NULL, +}; + +static struct platform_device sapphire_pwr_sink = { + .name = "htc_pwrsink", + .id = -1, + .dev = { + .platform_data = &sapphire_pwrsink_data, + }, +}; + +static struct platform_device sapphire_rfkill = { + .name = "sapphire_rfkill", + .id = -1, +}; + +static struct msm_pmem_setting pmem_setting_32 = { + .pmem_start = SMI32_MSM_PMEM_MDP_BASE, + .pmem_size = SMI32_MSM_PMEM_MDP_SIZE, + .pmem_adsp_start = SMI32_MSM_PMEM_ADSP_BASE, + .pmem_adsp_size = SMI32_MSM_PMEM_ADSP_SIZE, + .pmem_gpu0_start = MSM_PMEM_GPU0_BASE, + .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE, + .pmem_gpu1_start = MSM_PMEM_GPU1_BASE, + .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE, + .pmem_camera_start = 0, + .pmem_camera_size = 0, + .ram_console_start = MSM_RAM_CONSOLE_BASE, + .ram_console_size = MSM_RAM_CONSOLE_SIZE, +}; + +static struct msm_pmem_setting pmem_setting_64 = { + .pmem_start = SMI64_MSM_PMEM_MDP_BASE, + .pmem_size = SMI64_MSM_PMEM_MDP_SIZE, + .pmem_adsp_start = SMI64_MSM_PMEM_ADSP_BASE, + .pmem_adsp_size = SMI64_MSM_PMEM_ADSP_SIZE, + .pmem_gpu0_start = MSM_PMEM_GPU0_BASE, + .pmem_gpu0_size = MSM_PMEM_GPU0_SIZE, + .pmem_gpu1_start = MSM_PMEM_GPU1_BASE, + .pmem_gpu1_size = MSM_PMEM_GPU1_SIZE, + .pmem_camera_start = SMI64_MSM_PMEM_CAMERA_BASE, + .pmem_camera_size = SMI64_MSM_PMEM_CAMERA_SIZE, + .ram_console_start = MSM_RAM_CONSOLE_BASE, + .ram_console_size = MSM_RAM_CONSOLE_SIZE, +}; + +#ifdef CONFIG_WIFI_CONTROL_FUNC +static struct platform_device sapphire_wifi = { + .name = "msm_wifi", + .id = 1, + .num_resources = 0, + .resource = NULL, + .dev = { + .platform_data = &sapphire_wifi_control, + }, +}; +#endif + +#define SND(num, desc) { .name = desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(0, "HANDSET"), + SND(1, "SPEAKER"), + SND(2, "HEADSET"), + SND(3, "BT"), + SND(44, "BT_EC_OFF"), + SND(10, "HEADSET_AND_SPEAKER"), + SND(256, "CURRENT"), + + /* Bluetooth accessories. */ + + SND(12, "HTC BH S100"), + SND(13, "HTC BH M100"), + SND(14, "Motorola H500"), + SND(15, "Nokia HS-36W"), + SND(16, "PLT 510v.D"), + SND(17, "M2500 by Plantronics"), + SND(18, "Nokia HDW-3"), + SND(19, "HBH-608"), + SND(20, "HBH-DS970"), + SND(21, "i.Tech BlueBAND"), + SND(22, "Nokia BH-800"), + SND(23, "Motorola H700"), + SND(24, "HTC BH M200"), + SND(25, "Jabra JX10"), + SND(26, "320Plantronics"), + SND(27, "640Plantronics"), + SND(28, "Jabra BT500"), + SND(29, "Motorola HT820"), + SND(30, "HBH-IV840"), + SND(31, "6XXPlantronics"), + SND(32, "3XXPlantronics"), + SND(33, "HBH-PV710"), + SND(34, "Motorola H670"), + SND(35, "HBM-300"), + SND(36, "Nokia BH-208"), + SND(37, "Samsung WEP410"), + SND(38, "Jabra BT8010"), + SND(39, "Motorola S9"), + SND(40, "Jabra BT620s"), + SND(41, "Nokia BH-902"), + SND(42, "HBH-DS220"), + SND(43, "HBH-DS980"), +}; +#undef SND + +static struct msm_snd_endpoints sapphire_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = ARRAY_SIZE(snd_endpoints_list), +}; + +static struct platform_device sapphire_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &sapphire_snd_endpoints, + }, +}; + +static struct platform_device *devices[] __initdata = { + &msm_device_smd, + &msm_device_dmov, + &msm_device_nand, + &msm_device_i2c, + &msm_device_uart1, +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W) + &msm_device_uart3, +#endif +#ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, +#endif + &sapphire_nav_device, + &sapphire_reset_keys_device, + &android_leds, +#ifdef CONFIG_LEDS_CPLD + &android_CPLD_leds, +#endif +#ifdef CONFIG_HTC_HEADSET + &sapphire_h2w, +#endif + &sapphire_rfkill, +#ifdef CONFIG_WIFI_CONTROL_FUNC + &sapphire_wifi, +#endif + +#ifdef CONFIG_HTC_PWRSINK + &sapphire_pwr_sink, +#endif + &sapphire_snd, + &sapphire_camera, +}; + +extern struct sys_timer msm_timer; + +static void __init sapphire_init_irq(void) +{ + printk(KERN_DEBUG "sapphire_init_irq()\n"); + msm_init_irq(); +} + +static uint cpld_iset; +static uint cpld_charger_en; +static uint cpld_usb_h2w_sw; +static uint opt_disable_uart3; + +module_param_named(iset, cpld_iset, uint, 0); +module_param_named(charger_en, cpld_charger_en, uint, 0); +module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); +module_param_named(disable_uart3, opt_disable_uart3, uint, 0); + +static void sapphire_reset(void) +{ + gpio_set_value(SAPPHIRE_GPIO_PS_HOLD, 0); +} + +static uint32_t gpio_table[] = { + /* BLUETOOTH */ +#ifdef CONFIG_SERIAL_MSM_HS + PCOM_GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#else + PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#endif +}; + + +static uint32_t camera_off_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static uint32_t camera_off_gpio_12pins_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* PCLK */ + PCOM_GPIO_CFG(13, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* MCLK */ +}; + +static uint32_t camera_on_gpio_12pins_table[] = { + /* CAMERA */ + PCOM_GPIO_CFG(0, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT0 */ + PCOM_GPIO_CFG(1, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT1 */ + PCOM_GPIO_CFG(2, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT2 */ + PCOM_GPIO_CFG(3, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT3 */ + PCOM_GPIO_CFG(4, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT4 */ + PCOM_GPIO_CFG(5, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT5 */ + PCOM_GPIO_CFG(6, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT6 */ + PCOM_GPIO_CFG(7, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT7 */ + PCOM_GPIO_CFG(8, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT8 */ + PCOM_GPIO_CFG(9, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT9 */ + PCOM_GPIO_CFG(10, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT10 */ + PCOM_GPIO_CFG(11, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* DAT11 */ + PCOM_GPIO_CFG(12, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_16MA), /* PCLK */ + PCOM_GPIO_CFG(13, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* HSYNC_IN */ + PCOM_GPIO_CFG(14, 1, GPIO_INPUT, GPIO_PULL_DOWN, GPIO_2MA), /* VSYNC_IN */ + PCOM_GPIO_CFG(15, 1, GPIO_OUTPUT, GPIO_PULL_DOWN, GPIO_16MA), /* MCLK */ +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for (n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +void config_sapphire_camera_on_gpios(void) +{ + /*Add for judage it's 10 pins or 12 pins platform ----->*/ + if (is_12pin_camera()) { + config_gpio_table(camera_on_gpio_12pins_table, + ARRAY_SIZE(camera_on_gpio_12pins_table)); + } else { + config_gpio_table(camera_on_gpio_table, + ARRAY_SIZE(camera_on_gpio_table)); + } + /*End Of Add for judage it's 10 pins or 12 pins platform*/ +} + +void config_sapphire_camera_off_gpios(void) +{ + /*Add for judage it's 10 pins or 12 pins platform ----->*/ + if (is_12pin_camera()) { + config_gpio_table(camera_off_gpio_12pins_table, + ARRAY_SIZE(camera_off_gpio_12pins_table)); + } else { + config_gpio_table(camera_off_gpio_table, + ARRAY_SIZE(camera_off_gpio_table)); + } + /*End Of Add for judage it's 10 pins or 12 pins platform*/ +} + +static void __init config_gpios(void) +{ + config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table)); + config_sapphire_camera_off_gpios(); +} + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static struct msm_acpu_clock_platform_data sapphire_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .wakeup_irq = MSM_GPIO_TO_INT(45), + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0x32, +}; +#endif + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 16000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 12000, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 2000, +}; + +static void __init sapphire_init(void) +{ + int rc; + int i; + printk("sapphire_init() revision=%d\n", system_rev); + + /* + * Setup common MSM GPIOS + */ + config_gpios(); + + msm_hw_reset_hook = sapphire_reset; + + msm_acpu_clock_init(&sapphire_clock_data); + + /* adjust GPIOs based on bootloader request */ + printk("sapphire_init: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw); + gpio_set_value(SAPPHIRE_GPIO_USB_H2W_SW, cpld_usb_h2w_sw); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + if (!opt_disable_uart3) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + + /* gpio_configure(108, IRQF_TRIGGER_LOW); */ + + /* H2W pins <-> UART3, Bluetooth <-> UART1 */ + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL0, 0); + gpio_set_value(SAPPHIRE_GPIO_H2W_SEL1, 1); + /* put the AF VCM in powerdown mode to avoid noise */ + if (sapphire_is_5M_camera()) + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 0); + else + sapphire_gpio_write(NULL, SAPPHIRE_GPIO_VCM_PWDN, 1); + mdelay(100); + +#ifdef CONFIG_SERIAL_MSM_HS + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +#endif + msm_add_usb_devices(sapphire_phy_reset); + + if (32 == smi_sz) + msm_add_mem_devices(&pmem_setting_32); + else + msm_add_mem_devices(&pmem_setting_64); + + rc = sapphire_init_mmc(system_rev); + if (rc) + printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); + +#ifdef CONFIG_WIFI_MEM_PREALLOC + rc = sapphire_init_wifi_mem(); + if (rc) { + printk(KERN_CRIT "%s: WiFi memory init failure (%d)\n", + __func__, rc); + } +#endif + msm_init_pmic_vibrator(); + + platform_add_devices(devices, ARRAY_SIZE(devices)); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + msm_pm_set_platform_data(msm_pm_data); +} + +static struct map_desc sapphire_io_desc[] __initdata = { + { + .virtual = SAPPHIRE_CPLD_BASE, + .pfn = __phys_to_pfn(SAPPHIRE_CPLD_START), + .length = SAPPHIRE_CPLD_SIZE, + .type = MT_DEVICE_NONSHARED + } +}; + + +unsigned int sapphire_get_hwid(void) +{ + printk(KERN_DEBUG "sapphire_get_hwid=0x%x\r\n", hwid); + + return hwid; +} + +unsigned int sapphire_get_skuid(void) +{ + printk(KERN_DEBUG "sapphire_get_skuid=0x%x\r\n", skuid); + + return skuid; +} + +unsigned sapphire_engineerid(void) +{ + printk(KERN_DEBUG "sapphire_engineerid=0x%x\r\n", engineerid); + + return engineerid; +} + +int sapphire_is_5M_camera(void) +{ + int ret = 0; + if (sapphire_get_skuid() == 0x1FF00 && !(sapphire_engineerid() & 0x02)) + ret = 1; + else if (sapphire_get_skuid() == 0x20100 && !(sapphire_engineerid() & 0x02)) + ret = 1; + printk(KERN_DEBUG "sapphire_is_5M_camera=%d\n", ret); + return ret; +} + +/* it can support 3M and 5M sensor */ +unsigned int is_12pin_camera(void) +{ + unsigned int ret = 0; + + if (sapphire_get_skuid() == 0x1FF00 || sapphire_get_skuid() == 0x20100) + ret = 1; + else + ret = 0; + printk(KERN_DEBUG "is_12pin_camera=%d\r\n", ret); + return ret; +} + +int sapphire_get_smi_size(void) +{ + printk(KERN_DEBUG "get_smi_size=%d\r\n", smi_sz); + return smi_sz; +} + +static void __init sapphire_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + smi_sz = parse_tag_smi((const struct tag *)tags); + printk("sapphire_fixup:smisize=%d\n", smi_sz); + hwid = parse_tag_hwid((const struct tag *)tags); + printk("sapphire_fixup:hwid=0x%x\n", hwid); + skuid = parse_tag_skuid((const struct tag *)tags); + printk("sapphire_fixup:skuid=0x%x\n", skuid); + engineerid = parse_tag_engineerid((const struct tag *)tags); + printk("sapphire_fixup:engineerid=0x%x\n", engineerid); + + mi->nr_banks = 1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + if (smi_sz == 32) { + mi->bank[0].size = (84*1024*1024); + } else if (smi_sz == 64) { + mi->bank[0].size = (101*1024*1024); + } else { + printk(KERN_ERR "can not get smi size\n"); + + /*Give a default value when not get smi size*/ + smi_sz = 64; + mi->bank[0].size = (101*1024*1024); + printk(KERN_ERR "use default : smisize=%d\n", smi_sz); + } +} + +static void __init sapphire_map_io(void) +{ + msm_map_common_io(); + iotable_init(sapphire_io_desc, ARRAY_SIZE(sapphire_io_desc)); + msm_clock_init(); +} + +MACHINE_START(SAPPHIRE, "sapphire") +/* Maintainer: Brian Swetland <swetland@google.com> */ +#ifdef CONFIG_MSM_DEBUG_UART + .phys_io = MSM_DEBUG_UART_PHYS, + .io_pg_offst = ((MSM_DEBUG_UART_BASE) >> 18) & 0xfffc, +#endif + .boot_params = 0x10000100, + .fixup = sapphire_fixup, + .map_io = sapphire_map_io, + .init_irq = sapphire_init_irq, + .init_machine = sapphire_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-sapphire.h b/arch/arm/mach-msm/board-sapphire.h new file mode 100644 index 000000000000..3327dfeafde0 --- /dev/null +++ b/arch/arm/mach-msm/board-sapphire.h @@ -0,0 +1,219 @@ +/* linux/arch/arm/mach-msm/board-sapphire.h + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. +*/ +#ifndef __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H +#define __ARCH_ARM_MACH_MSM_BOARD_SAPPHIRE_H + +#include <mach/board.h> + +#define MSM_SMI_BASE 0x00000000 +#define MSM_SMI_SIZE 0x00800000 + +#define MSM_EBI_BASE 0x10000000 +#define MSM_EBI_SIZE 0x06e00000 + +#define MSM_PMEM_GPU0_BASE 0x00000000 +#define MSM_PMEM_GPU0_SIZE 0x00700000 + +#define SMI64_MSM_PMEM_MDP_BASE 0x02000000 +#define SMI64_MSM_PMEM_MDP_SIZE 0x00800000 + +#define SMI64_MSM_PMEM_ADSP_BASE 0x02800000 +#define SMI64_MSM_PMEM_ADSP_SIZE 0x00800000 + +#define SMI64_MSM_PMEM_CAMERA_BASE 0x03000000 +#define SMI64_MSM_PMEM_CAMERA_SIZE 0x01000000 + +#define SMI64_MSM_FB_BASE 0x00700000 +#define SMI64_MSM_FB_SIZE 0x00100000 + +#define SMI64_MSM_LINUX_BASE MSM_EBI_BASE +#define SMI64_MSM_LINUX_SIZE 0x06500000 + + +#define SMI32_MSM_LINUX_BASE MSM_EBI_BASE +#define SMI32_MSM_LINUX_SIZE 0x5400000 + +#define SMI32_MSM_PMEM_MDP_BASE SMI32_MSM_LINUX_BASE + SMI32_MSM_LINUX_SIZE +#define SMI32_MSM_PMEM_MDP_SIZE 0x800000 + +#define SMI32_MSM_PMEM_ADSP_BASE SMI32_MSM_PMEM_MDP_BASE + SMI32_MSM_PMEM_MDP_SIZE +#define SMI32_MSM_PMEM_ADSP_SIZE 0x800000 + +#define SMI32_MSM_FB_BASE SMI32_MSM_PMEM_ADSP_BASE + SMI32_MSM_PMEM_ADSP_SIZE +#define SMI32_MSM_FB_SIZE 0x9b000 + + +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_PMEM_GPU1_BASE MSM_RAM_CONSOLE_BASE - MSM_PMEM_GPU1_SIZE + +#define MSM_RAM_CONSOLE_BASE MSM_EBI_BASE + 0x6d00000 +#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K + +#if (SMI32_MSM_FB_BASE + SMI32_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#if (SMI64_MSM_FB_BASE + SMI64_MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#define DECLARE_MSM_IOMAP +#include <mach/msm_iomap.h> + +/* +** SOC GPIO +*/ +#define SAPPHIRE_BALL_UP_0 94 +#define SAPPHIRE_BALL_LEFT_0 18 +#define SAPPHIRE_BALL_DOWN_0 49 +#define SAPPHIRE_BALL_RIGHT_0 19 + +#define SAPPHIRE_POWER_KEY 20 +#define SAPPHIRE_VOLUME_UP 36 +#define SAPPHIRE_VOLUME_DOWN 39 + +#define SAPPHIRE_GPIO_PS_HOLD (25) +#define SAPPHIRE_MDDI_1V5_EN (28) +#define SAPPHIRE_BL_PWM (27) +#define SAPPHIRE_TP_LS_EN (1) +#define SAPPHIRE20_TP_LS_EN (88) + +/* H2W */ +#define SAPPHIRE_GPIO_CABLE_IN1 (83) +#define SAPPHIRE_GPIO_CABLE_IN2 (37) +#define SAPPHIRE_GPIO_UART3_RX (86) +#define SAPPHIRE_GPIO_UART3_TX (87) +#define SAPPHIRE_GPIO_H2W_DATA (86) +#define SAPPHIRE_GPIO_H2W_CLK (87) + +#define SAPPHIRE_GPIO_UART1_RTS (43) +#define SAPPHIRE_GPIO_UART1_CTS (44) + +/* +** CPLD GPIO +** +** Sapphire Altera CPLD can keep the registers value and +** doesn't need a shadow to backup. +**/ +#define SAPPHIRE_CPLD_BASE 0xE8100000 /* VA */ +#define SAPPHIRE_CPLD_START 0x98000000 /* PA */ +#define SAPPHIRE_CPLD_SIZE SZ_4K + +#define SAPPHIRE_GPIO_START (128) /* Pseudo GPIO number */ + +/* Sapphire has one INT BANK only. */ +#define SAPPHIRE_GPIO_INT_B0_MASK_REG (0x0c) /*INT3 MASK*/ +#define SAPPHIRE_GPIO_INT_B0_STAT_REG (0x0e) /*INT1 STATUS*/ + +/* LED control register */ +#define SAPPHIRE_CPLD_LED_BASE (SAPPHIRE_CPLD_BASE + 0x10) /* VA */ +#define SAPPHIRE_CPLD_LED_START (SAPPHIRE_CPLD_START + 0x10) /* PA */ +#define SAPPHIRE_CPLD_LED_SIZE 0x08 + +/* MISCn: GPO pin to Enable/Disable some functions. */ +#define SAPPHIRE_GPIO_MISC1_BASE (SAPPHIRE_GPIO_START + 0x00) +#define SAPPHIRE_GPIO_MISC2_BASE (SAPPHIRE_GPIO_START + 0x08) +#define SAPPHIRE_GPIO_MISC3_BASE (SAPPHIRE_GPIO_START + 0x10) +#define SAPPHIRE_GPIO_MISC4_BASE (SAPPHIRE_GPIO_START + 0x18) +#define SAPPHIRE_GPIO_MISC5_BASE (SAPPHIRE_GPIO_START + 0x20) + +/* INT BANK0: INT1: int status, INT2: int level, INT3: int Mask */ +#define SAPPHIRE_GPIO_INT_B0_BASE (SAPPHIRE_GPIO_START + 0x28) + +/* MISCn GPIO: */ +#define SAPPHIRE_GPIO_CPLD128_VER_0 (SAPPHIRE_GPIO_MISC1_BASE + 4) +#define SAPPHIRE_GPIO_CPLD128_VER_1 (SAPPHIRE_GPIO_MISC1_BASE + 5) +#define SAPPHIRE_GPIO_CPLD128_VER_2 (SAPPHIRE_GPIO_MISC1_BASE + 6) +#define SAPPHIRE_GPIO_CPLD128_VER_3 (SAPPHIRE_GPIO_MISC1_BASE + 7) + +#define SAPPHIRE_GPIO_H2W_DAT_DIR (SAPPHIRE_GPIO_MISC2_BASE + 2) +#define SAPPHIRE_GPIO_H2W_CLK_DIR (SAPPHIRE_GPIO_MISC2_BASE + 3) +#define SAPPHIRE_GPIO_H2W_SEL0 (SAPPHIRE_GPIO_MISC2_BASE + 6) +#define SAPPHIRE_GPIO_H2W_SEL1 (SAPPHIRE_GPIO_MISC2_BASE + 7) + +#define SAPPHIRE_GPIO_I2C_PULL (SAPPHIRE_GPIO_MISC3_BASE + 2) +#define SAPPHIRE_GPIO_TP_EN (SAPPHIRE_GPIO_MISC3_BASE + 4) +#define SAPPHIRE_GPIO_JOG_EN (SAPPHIRE_GPIO_MISC3_BASE + 5) +#define SAPPHIRE_GPIO_JOG_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 6) +#define SAPPHIRE_GPIO_APKEY_LED_EN (SAPPHIRE_GPIO_MISC3_BASE + 7) + +#define SAPPHIRE_GPIO_VCM_PWDN (SAPPHIRE_GPIO_MISC4_BASE + 0) +#define SAPPHIRE_GPIO_USB_H2W_SW (SAPPHIRE_GPIO_MISC4_BASE + 1) +#define SAPPHIRE_GPIO_COMPASS_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 2) +#define SAPPHIRE_GPIO_USB_PHY_RST_N (SAPPHIRE_GPIO_MISC4_BASE + 5) +#define SAPPHIRE_GPIO_WIFI_PA_RESETX (SAPPHIRE_GPIO_MISC4_BASE + 6) +#define SAPPHIRE_GPIO_WIFI_EN (SAPPHIRE_GPIO_MISC4_BASE + 7) + +#define SAPPHIRE_GPIO_BT_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 0) +#define SAPPHIRE_GPIO_MAC_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 1) +#define SAPPHIRE_GPIO_MDDI_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 2) +#define SAPPHIRE_GPIO_COMPASS_32K_EN (SAPPHIRE_GPIO_MISC5_BASE + 3) + +/* INT STATUS/LEVEL/MASK : INT GPIO should be the last. */ +#define SAPPHIRE_GPIO_NAVI_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 0) +#define SAPPHIRE_GPIO_COMPASS_IRQ (SAPPHIRE_GPIO_INT_B0_BASE + 1) +#define SAPPHIRE_GPIO_SEARCH_ACT_N (SAPPHIRE_GPIO_INT_B0_BASE + 2) +#define SAPPHIRE_GPIO_AUD_HSMIC_DET_N (SAPPHIRE_GPIO_INT_B0_BASE + 3) +#define SAPPHIRE_GPIO_SDMC_CD_N (SAPPHIRE_GPIO_INT_B0_BASE + 4) +#define SAPPHIRE_GPIO_CAM_BTN_STEP1_N (SAPPHIRE_GPIO_INT_B0_BASE + 5) +#define SAPPHIRE_GPIO_CAM_BTN_STEP2_N (SAPPHIRE_GPIO_INT_B0_BASE + 6) +#define SAPPHIRE_GPIO_TP_ATT_N (SAPPHIRE_GPIO_INT_B0_BASE + 7) + +#define SAPPHIRE_GPIO_END SAPPHIRE_GPIO_TP_ATT_N +#define SAPPHIRE_GPIO_LAST_INT (SAPPHIRE_GPIO_TP_ATT_N) + +/* Bit position in the CPLD MISCn by the CPLD GPIOn: only bit0-7 is used. */ +#define CPLD_GPIO_BIT_POS_MASK(n) (1U << ((n) & 7)) +#define CPLD_GPIO_REG_OFFSET(n) _g_CPLD_MISCn_Offset[((n)-SAPPHIRE_GPIO_START) >> 3] +#define CPLD_GPIO_REG(n) (CPLD_GPIO_REG_OFFSET(n) + SAPPHIRE_CPLD_BASE) + +/* +** CPLD INT Start +*/ +#define SAPPHIRE_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS) /* pseudo number for CPLD INT */ +/* Using INT status/Bank0 for GPIO to INT */ +#define SAPPHIRE_GPIO_TO_INT(n) ((n-SAPPHIRE_GPIO_INT_B0_BASE) + SAPPHIRE_INT_START) +#define SAPPHIRE_INT_END (SAPPHIRE_GPIO_TO_INT(SAPPHIRE_GPIO_END)) + +/* get the INT reg by GPIO number */ +#define CPLD_INT_GPIO_TO_BANK(n) (((n)-SAPPHIRE_GPIO_INT_B0_BASE) >> 3) +#define CPLD_INT_STATUS_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][0] +#define CPLD_INT_LEVEL_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][1] +#define CPLD_INT_MASK_REG_OFFSET_G(n) _g_INT_BANK_Offset[CPLD_INT_GPIO_TO_BANK(n)][2] +#define CPLD_INT_STATUS_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET_G(n)) +#define CPLD_INT_LEVEL_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET_G(n)) +#define CPLD_INT_MASK_REG_G(n) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET_G(n)) + +/* get the INT reg by INT number */ +#define CPLD_INT_TO_BANK(i) ((i-SAPPHIRE_INT_START) >> 3) +#define CPLD_INT_STATUS_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][0] +#define CPLD_INT_LEVEL_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][1] +#define CPLD_INT_MASK_REG_OFFSET(i) _g_INT_BANK_Offset[CPLD_INT_TO_BANK(i)][2] +#define CPLD_INT_STATUS_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_STATUS_REG_OFFSET(i)) +#define CPLD_INT_LEVEL_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_LEVEL_REG_OFFSET(i)) +#define CPLD_INT_MASK_REG(i) (SAPPHIRE_CPLD_BASE + CPLD_INT_MASK_REG_OFFSET(i) ) + +/* return the bit mask by INT number */ +#define SAPPHIRE_INT_BIT_MASK(i) (1U << ((i - SAPPHIRE_INT_START) & 7)) + +void config_sapphire_camera_on_gpios(void); +void config_sapphire_camera_off_gpios(void); +int sapphire_get_smi_size(void); +unsigned int sapphire_get_hwid(void); +unsigned int sapphire_get_skuid(void); +unsigned int is_12pin_camera(void); +int sapphire_is_5M_camera(void); +int sapphire_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on); + +#endif /* GUARD */ diff --git a/arch/arm/mach-msm/board-trout-gpio.c b/arch/arm/mach-msm/board-trout-gpio.c new file mode 100644 index 000000000000..527379ec3597 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-gpio.c @@ -0,0 +1,305 @@ +/* arch/arm/mach-msm/board-trout-gpio.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/irq.h> +#include <linux/pm.h> +#include <linux/sysdev.h> + +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/mach-types.h> + +#include <mach/htc_pwrsink.h> + +#include "board-trout.h" +#include "gpio_chip.h" + +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_trout." + +static uint cpld_usb_h2w_sw; +module_param_named(usb_h2w_sw, cpld_usb_h2w_sw, uint, 0); + +static uint8_t trout_cpld_shadow[4] = { +#if defined(CONFIG_MSM_DEBUG_UART1) + /* H2W pins <-> UART1 */ + [0] = 0x40, // for serial debug, low current +#else + /* H2W pins <-> UART3, Bluetooth <-> UART1 */ + [0] = 0x80, // for serial debug, low current +#endif + [1] = 0x04, // I2C_PULL + [3] = 0x04, // mmdi 32k en +}; +static uint8_t trout_int_mask[2] = { + [0] = 0xff, /* mask all interrupts */ + [1] = 0xff, +}; +static uint8_t trout_sleep_int_mask[] = { + [0] = 0xff, + [1] = 0xff, +}; +static int trout_suspended; + +static int trout_gpio_read(struct gpio_chip *chip, unsigned n) +{ + uint8_t b; + int reg; + if (n >= TROUT_GPIO_VIRTUAL_BASE) + n += TROUT_GPIO_VIRTUAL_TO_REAL_OFFSET; + b = 1U << (n & 7); + reg = (n & 0x78) >> 2; // assumes base is 128 + return !!(readb(TROUT_CPLD_BASE + reg) & b); +} + +static void update_pwrsink(unsigned gpio, unsigned on) +{ + switch(gpio) { + case TROUT_GPIO_UI_LED_EN: + htc_pwrsink_set(PWRSINK_LED_BUTTON, on ? 100 : 0); + break; + case TROUT_GPIO_QTKEY_LED_EN: + htc_pwrsink_set(PWRSINK_LED_KEYBOARD, on ? 100 : 0); + break; + } +} + +static uint8_t trout_gpio_write_shadow(unsigned n, unsigned on) +{ + uint8_t b = 1U << (n & 7); + int reg = (n & 0x78) >> 2; // assumes base is 128 + + if(on) + return trout_cpld_shadow[reg >> 1] |= b; + else + return trout_cpld_shadow[reg >> 1] &= ~b; +} + +static int trout_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +{ + int reg = (n & 0x78) >> 2; // assumes base is 128 + unsigned long flags; + uint8_t reg_val; + + if ((reg >> 1) >= ARRAY_SIZE(trout_cpld_shadow)) { + printk(KERN_ERR "trout_gpio_write called on input %d\n", n); + return -ENOTSUPP; + } + + local_irq_save(flags); + update_pwrsink(n, on); + reg_val = trout_gpio_write_shadow(n, on); + writeb(reg_val, TROUT_CPLD_BASE + reg); + local_irq_restore(flags); + return 0; +} + +static int trout_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags) +{ + if(flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) + trout_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); + return 0; +} + +static int trout_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) +{ + if ((gpio < TROUT_GPIO_BANK0_FIRST_INT_SOURCE || + gpio > TROUT_GPIO_BANK0_LAST_INT_SOURCE) && + (gpio < TROUT_GPIO_BANK1_FIRST_INT_SOURCE || + gpio > TROUT_GPIO_BANK1_LAST_INT_SOURCE)) + return -ENOENT; + *irqp = TROUT_GPIO_TO_INT(gpio); + if(irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + +static void trout_gpio_irq_ack(unsigned int irq) +{ + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + int reg = TROUT_BANK_TO_STAT_REG(bank); + /*printk(KERN_INFO "trout_gpio_irq_ack irq %d\n", irq);*/ + writeb(mask, TROUT_CPLD_BASE + reg); +} + +static void trout_gpio_irq_mask(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + int reg = TROUT_BANK_TO_MASK_REG(bank); + + local_irq_save(flags); + reg_val = trout_int_mask[bank] |= mask; + /*printk(KERN_INFO "trout_gpio_irq_mask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + if (!trout_suspended) + writeb(reg_val, TROUT_CPLD_BASE + reg); + local_irq_restore(flags); +} + +static void trout_gpio_irq_unmask(unsigned int irq) +{ + unsigned long flags; + uint8_t reg_val; + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + int reg = TROUT_BANK_TO_MASK_REG(bank); + + local_irq_save(flags); + reg_val = trout_int_mask[bank] &= ~mask; + /*printk(KERN_INFO "trout_gpio_irq_unmask irq %d => %d:%02x\n", + irq, bank, reg_val);*/ + if (!trout_suspended) + writeb(reg_val, TROUT_CPLD_BASE + reg); + local_irq_restore(flags); +} + +int trout_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + unsigned long flags; + int bank = TROUT_INT_TO_BANK(irq); + uint8_t mask = TROUT_INT_TO_MASK(irq); + + local_irq_save(flags); + if(on) + trout_sleep_int_mask[bank] &= ~mask; + else + trout_sleep_int_mask[bank] |= mask; + local_irq_restore(flags); + return 0; +} + +static void trout_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + int j, m; + unsigned v; + int bank; + int stat_reg; + int int_base = TROUT_INT_START; + uint8_t int_mask; + + for (bank = 0; bank < 2; bank++) { + stat_reg = TROUT_BANK_TO_STAT_REG(bank); + v = readb(TROUT_CPLD_BASE + stat_reg); + int_mask = trout_int_mask[bank]; + if (v & int_mask) { + writeb(v & int_mask, TROUT_CPLD_BASE + stat_reg); + printk(KERN_ERR "trout_gpio_irq_handler: got masked " + "interrupt: %d:%02x\n", bank, v & int_mask); + } + v &= ~int_mask; + while (v) { + m = v & -v; + j = fls(m) - 1; + /*printk(KERN_INFO "msm_gpio_irq_handler %d:%02x %02x b" + "it %d irq %d\n", bank, v, m, j, int_base + j);*/ + v &= ~m; + generic_handle_irq(int_base + j); + } + int_base += TROUT_INT_BANK0_COUNT; + } + desc->chip->ack(irq); +} + +static int trout_sysdev_suspend(struct sys_device *dev, pm_message_t state) +{ + trout_suspended = 1; + writeb(trout_sleep_int_mask[0], + TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK0_REG); + writeb(trout_sleep_int_mask[1], + TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK1_REG); + writeb(trout_sleep_int_mask[0], + TROUT_CPLD_BASE + TROUT_GPIO_INT_STAT0_REG); + writeb(trout_sleep_int_mask[1], + TROUT_CPLD_BASE + TROUT_GPIO_INT_STAT1_REG); + return 0; +} + +int trout_sysdev_resume(struct sys_device *dev) +{ + writeb(trout_int_mask[0], TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK0_REG); + writeb(trout_int_mask[1], TROUT_CPLD_BASE + TROUT_GPIO_INT_MASK1_REG); + trout_suspended = 0; + return 0; +} + +static struct irq_chip trout_gpio_irq_chip = { + .name = "troutgpio", + .ack = trout_gpio_irq_ack, + .mask = trout_gpio_irq_mask, + .unmask = trout_gpio_irq_unmask, + .set_wake = trout_gpio_irq_set_wake, + //.set_type = trout_gpio_irq_set_type, +}; + +static struct gpio_chip trout_gpio_chip = { + .start = TROUT_GPIO_START, + .end = TROUT_GPIO_END, + .configure = trout_gpio_configure, + .get_irq_num = trout_gpio_get_irq_num, + .read = trout_gpio_read, + .write = trout_gpio_write, +// .read_detect_status = trout_gpio_read_detect_status, +// .clear_detect_status = trout_gpio_clear_detect_status +}; + +struct sysdev_class trout_sysdev_class = { + .name = "troutgpio_irq", + .suspend = trout_sysdev_suspend, + .resume = trout_sysdev_resume, +}; + +static struct sys_device trout_irq_device = { + .cls = &trout_sysdev_class, +}; + +static int __init trout_init_gpio(void) +{ + int i; + + if (!machine_is_trout()) + return 0; + + /* adjust GPIOs based on bootloader request */ + pr_info("trout_init_gpio: cpld_usb_hw2_sw = %d\n", cpld_usb_h2w_sw); + trout_gpio_write_shadow(TROUT_GPIO_USB_H2W_SW, cpld_usb_h2w_sw); + + for(i = 0; i < ARRAY_SIZE(trout_cpld_shadow); i++) + writeb(trout_cpld_shadow[i], TROUT_CPLD_BASE + i * 2); + + for(i = TROUT_INT_START; i <= TROUT_INT_END; i++) { + set_irq_chip(i, &trout_gpio_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + register_gpio_chip(&trout_gpio_chip); + + set_irq_type(MSM_GPIO_TO_INT(17), IRQF_TRIGGER_HIGH); + set_irq_chained_handler(MSM_GPIO_TO_INT(17), trout_gpio_irq_handler); + set_irq_wake(MSM_GPIO_TO_INT(17), 1); + + if(sysdev_class_register(&trout_sysdev_class) == 0) + sysdev_register(&trout_irq_device); + + return 0; +} + +postcore_initcall(trout_init_gpio); diff --git a/arch/arm/mach-msm/board-trout-keypad.c b/arch/arm/mach-msm/board-trout-keypad.c new file mode 100644 index 000000000000..0299d0686de9 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-keypad.c @@ -0,0 +1,345 @@ +/* arch/arm/mach-msm/board-trout-keypad.c + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/gpio_event.h> +#include <asm/mach-types.h> + +#include "board-trout.h" + +static char *keycaps = "--qwerty"; +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "board_trout." +module_param_named(keycaps, keycaps, charp, 0); + + +static unsigned int trout_col_gpios[] = { 35, 34, 33, 32, 31, 23, 30, 78 }; +static unsigned int trout_row_gpios[] = { 42, 41, 40, 39, 38, 37, 36 }; + +#define KEYMAP_INDEX(col, row) ((col)*ARRAY_SIZE(trout_row_gpios) + (row)) + +static const unsigned short trout_keymap[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTALT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, +// [KEYMAP_INDEX(1, 0)] = 229, // SOFT1 + [KEYMAP_INDEX(1, 1)] = KEY_SEND, + [KEYMAP_INDEX(1, 2)] = KEY_END, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTALT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_SLASH, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, + //[KEYMAP_INDEX(4, 5)] = KEY_, + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_COMPOSE, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_DOT, + [KEYMAP_INDEX(6, 5)] = KEY_COMMA, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static unsigned int trout_col_gpios_evt2[] = { 35, 34, 33, 32, 31, 23, 30, 109 }; +static unsigned int trout_row_gpios_evt2[] = { 42, 41, 40, 39, 38, 37, 36 }; + +static const unsigned short trout_keymap_evt2_1[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +// [KEYMAP_INDEX(1, 2)] = KEY_, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_COMPOSE, + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_SLASH, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, +// [KEYMAP_INDEX(4, 5)] = KEY_, + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_COMMA, + [KEYMAP_INDEX(6, 5)] = KEY_DOT, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static const unsigned short trout_keymap_evt2_2[ARRAY_SIZE(trout_col_gpios) * ARRAY_SIZE(trout_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_BACK, + [KEYMAP_INDEX(0, 1)] = KEY_HOME, +// [KEYMAP_INDEX(0, 2)] = KEY_, + [KEYMAP_INDEX(0, 3)] = KEY_BACKSPACE, + [KEYMAP_INDEX(0, 4)] = KEY_ENTER, + [KEYMAP_INDEX(0, 5)] = KEY_RIGHTSHIFT, + [KEYMAP_INDEX(0, 6)] = KEY_P, + + [KEYMAP_INDEX(1, 0)] = KEY_MENU, /* external menu key */ + [KEYMAP_INDEX(1, 1)] = KEY_SEND, +// [KEYMAP_INDEX(1, 2)] = KEY_, + [KEYMAP_INDEX(1, 3)] = KEY_LEFTSHIFT, + [KEYMAP_INDEX(1, 4)] = KEY_A, + [KEYMAP_INDEX(1, 5)] = KEY_F1, /* qwerty menu key */ + [KEYMAP_INDEX(1, 6)] = KEY_Q, + + [KEYMAP_INDEX(2, 0)] = KEY_U, + [KEYMAP_INDEX(2, 1)] = KEY_7, + [KEYMAP_INDEX(2, 2)] = KEY_K, + [KEYMAP_INDEX(2, 3)] = KEY_J, + [KEYMAP_INDEX(2, 4)] = KEY_M, + [KEYMAP_INDEX(2, 5)] = KEY_DOT, + [KEYMAP_INDEX(2, 6)] = KEY_8, + + [KEYMAP_INDEX(3, 0)] = KEY_5, + [KEYMAP_INDEX(3, 1)] = KEY_6, + [KEYMAP_INDEX(3, 2)] = KEY_B, + [KEYMAP_INDEX(3, 3)] = KEY_H, + [KEYMAP_INDEX(3, 4)] = KEY_N, + [KEYMAP_INDEX(3, 5)] = KEY_SPACE, + [KEYMAP_INDEX(3, 6)] = KEY_Y, + + [KEYMAP_INDEX(4, 0)] = KEY_4, + [KEYMAP_INDEX(4, 1)] = KEY_R, + [KEYMAP_INDEX(4, 2)] = KEY_V, + [KEYMAP_INDEX(4, 3)] = KEY_G, + [KEYMAP_INDEX(4, 4)] = KEY_C, + [KEYMAP_INDEX(4, 5)] = KEY_EMAIL, // @ + [KEYMAP_INDEX(4, 6)] = KEY_T, + + [KEYMAP_INDEX(5, 0)] = KEY_2, + [KEYMAP_INDEX(5, 1)] = KEY_W, + [KEYMAP_INDEX(5, 2)] = KEY_LEFTALT, + [KEYMAP_INDEX(5, 3)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(5, 4)] = KEY_S, + [KEYMAP_INDEX(5, 5)] = KEY_Z, + [KEYMAP_INDEX(5, 6)] = KEY_1, + + [KEYMAP_INDEX(6, 0)] = KEY_I, + [KEYMAP_INDEX(6, 1)] = KEY_0, + [KEYMAP_INDEX(6, 2)] = KEY_O, + [KEYMAP_INDEX(6, 3)] = KEY_L, + [KEYMAP_INDEX(6, 4)] = KEY_COMMA, + [KEYMAP_INDEX(6, 5)] = KEY_RIGHTALT, + [KEYMAP_INDEX(6, 6)] = KEY_9, + + [KEYMAP_INDEX(7, 0)] = KEY_3, + [KEYMAP_INDEX(7, 1)] = KEY_E, + [KEYMAP_INDEX(7, 2)] = KEY_COMPOSE, + [KEYMAP_INDEX(7, 3)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(7, 4)] = KEY_X, + [KEYMAP_INDEX(7, 5)] = KEY_F, + [KEYMAP_INDEX(7, 6)] = KEY_D +}; + +static struct gpio_event_matrix_info trout_keypad_matrix_info = { + .info.func = gpio_event_matrix_func, + .keymap = trout_keymap, + .output_gpios = trout_col_gpios, + .input_gpios = trout_row_gpios, + .noutputs = ARRAY_SIZE(trout_col_gpios), + .ninputs = ARRAY_SIZE(trout_row_gpios), + .settle_time.tv.nsec = 40 * NSEC_PER_USEC, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_REMOVE_PHANTOM_KEYS |GPIOKPF_PRINT_UNMAPPED_KEYS /*| GPIOKPF_PRINT_MAPPED_KEYS*/ +}; + +static struct gpio_event_direct_entry trout_keypad_nav_map[] = { + { TROUT_POWER_KEY, KEY_POWER }, + { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP + { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA }, +}; + +static struct gpio_event_direct_entry trout_keypad_nav_map_evt2[] = { + { TROUT_POWER_KEY, KEY_END }, + { TROUT_GPIO_CAM_BTN_STEP1_N, KEY_CAMERA-1 }, //steal KEY_HP + { TROUT_GPIO_CAM_BTN_STEP2_N, KEY_CAMERA }, +}; + +static struct gpio_event_input_info trout_keypad_nav_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = trout_keypad_nav_map, + .keymap_size = ARRAY_SIZE(trout_keypad_nav_map) +}; + +static struct gpio_event_direct_entry trout_keypad_switch_map[] = { + { TROUT_GPIO_SLIDING_DET, SW_LID } +}; + +static struct gpio_event_input_info trout_keypad_switch_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_SW, + .keymap = trout_keypad_switch_map, + .keymap_size = ARRAY_SIZE(trout_keypad_switch_map) +}; + +static struct gpio_event_info *trout_keypad_info[] = { + &trout_keypad_matrix_info.info, + &trout_keypad_nav_info.info, + &trout_keypad_switch_info.info, +}; + +static struct gpio_event_platform_data trout_keypad_data = { + .name = "trout-keypad", + .info = trout_keypad_info, + .info_count = ARRAY_SIZE(trout_keypad_info) +}; + +static struct platform_device trout_keypad_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 0, + .dev = { + .platform_data = &trout_keypad_data, + }, +}; + +static int __init trout_init_keypad(void) +{ + if (!machine_is_trout()) + return 0; + + switch (system_rev) { + case 0: + /* legacy default keylayout */ + break; + case 1: + /* v1 has a new keyboard layout */ + trout_keypad_matrix_info.keymap = trout_keymap_evt2_1; + trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2; + trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2; + + /* v1 has new direct keys */ + trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2; + trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2); + + /* userspace needs to know about these changes as well */ + trout_keypad_data.name = "trout-keypad-v2"; + break; + default: /* 2, 3, 4 currently */ + /* v2 has a new keyboard layout */ + trout_keypad_matrix_info.keymap = trout_keymap_evt2_2; + trout_keypad_matrix_info.output_gpios = trout_col_gpios_evt2; + trout_keypad_matrix_info.input_gpios = trout_row_gpios_evt2; + + /* v2 has new direct keys */ + trout_keypad_nav_info.keymap = trout_keypad_nav_map_evt2; + trout_keypad_nav_info.keymap_size = ARRAY_SIZE(trout_keypad_nav_map_evt2); + + /* userspace needs to know about these changes as well */ + if (!strcmp(keycaps, "qwertz")) { + trout_keypad_data.name = "trout-keypad-qwertz"; + } else { + trout_keypad_data.name = "trout-keypad-v3"; + } + break; + } + return platform_device_register(&trout_keypad_device); +} + +device_initcall(trout_init_keypad); + diff --git a/arch/arm/mach-msm/board-trout-mmc.c b/arch/arm/mach-msm/board-trout-mmc.c new file mode 100644 index 000000000000..aee7378b6728 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-mmc.c @@ -0,0 +1,437 @@ +/* linux/arch/arm/mach-msm/board-trout-mmc.c +** Author: Brian Swetland <swetland@google.com> +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/err.h> +#include <linux/debugfs.h> + +#include <asm/gpio.h> +#include <asm/io.h> + +#include <mach/vreg.h> +#include <mach/htc_pwrsink.h> + +#include <mach/mmc.h> + +#include "devices.h" + +#include "board-trout.h" + +#include "proc_comm.h" + +#define DEBUG_SDSLOT_VDD 1 + +extern int msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat); + +/* ---- COMMON ---- */ +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +/* ---- SDCARD ---- */ + +static uint32_t sdcard_on_gpio_table[] = { + PCOM_GPIO_CFG(62, 2, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(63, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(64, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ +}; + +static uint32_t sdcard_off_gpio_table[] = { + PCOM_GPIO_CFG(62, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(63, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(64, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(65, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(66, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(67, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ +}; + +static uint opt_disable_sdcard; + +static int __init trout_disablesdcard_setup(char *str) +{ + int cal = simple_strtol(str, NULL, 0); + + opt_disable_sdcard = cal; + return 1; +} + +__setup("board_trout.disable_sdcard=", trout_disablesdcard_setup); + +static struct vreg *vreg_sdslot; /* SD slot power */ + +struct mmc_vdd_xlat { + int mask; + int level; +}; + +static struct mmc_vdd_xlat mmc_vdd_table[] = { + { MMC_VDD_165_195, 1800 }, + { MMC_VDD_20_21, 2050 }, + { MMC_VDD_21_22, 2150 }, + { MMC_VDD_22_23, 2250 }, + { MMC_VDD_23_24, 2350 }, + { MMC_VDD_24_25, 2450 }, + { MMC_VDD_25_26, 2550 }, + { MMC_VDD_26_27, 2650 }, + { MMC_VDD_27_28, 2750 }, + { MMC_VDD_28_29, 2850 }, + { MMC_VDD_29_30, 2950 }, +}; + +static unsigned int sdslot_vdd = 0xffffffff; +static unsigned int sdslot_vreg_enabled; + +static uint32_t trout_sdslot_switchvdd(struct device *dev, unsigned int vdd) +{ + int i, rc; + + BUG_ON(!vreg_sdslot); + + if (vdd == sdslot_vdd) + return 0; + + sdslot_vdd = vdd; + + if (vdd == 0) { +#if DEBUG_SDSLOT_VDD + printk("%s: Disabling SD slot power\n", __func__); +#endif + config_gpio_table(sdcard_off_gpio_table, + ARRAY_SIZE(sdcard_off_gpio_table)); + vreg_disable(vreg_sdslot); + sdslot_vreg_enabled = 0; + return 0; + } + + if (!sdslot_vreg_enabled) { + rc = vreg_enable(vreg_sdslot); + if (rc) { + printk(KERN_ERR "%s: Error enabling vreg (%d)\n", + __func__, rc); + } + config_gpio_table(sdcard_on_gpio_table, + ARRAY_SIZE(sdcard_on_gpio_table)); + sdslot_vreg_enabled = 1; + } + + for (i = 0; i < ARRAY_SIZE(mmc_vdd_table); i++) { + if (mmc_vdd_table[i].mask == (1 << vdd)) { +#if DEBUG_SDSLOT_VDD + printk("%s: Setting level to %u\n", + __func__, mmc_vdd_table[i].level); +#endif + rc = vreg_set_level(vreg_sdslot, + mmc_vdd_table[i].level); + if (rc) { + printk(KERN_ERR + "%s: Error setting vreg level (%d)\n", + __func__, rc); + } + return 0; + } + } + + printk(KERN_ERR "%s: Invalid VDD %d specified\n", __func__, vdd); + return 0; +} + +static unsigned int trout_sdslot_status(struct device *dev) +{ + unsigned int status; + + status = (unsigned int) gpio_get_value(TROUT_GPIO_SDMC_CD_N); + return (!status); +} + +#define TROUT_MMC_VDD MMC_VDD_165_195 | MMC_VDD_20_21 | MMC_VDD_21_22 \ + | MMC_VDD_22_23 | MMC_VDD_23_24 | MMC_VDD_24_25 \ + | MMC_VDD_25_26 | MMC_VDD_26_27 | MMC_VDD_27_28 \ + | MMC_VDD_28_29 | MMC_VDD_29_30 + +static struct mmc_platform_data trout_sdslot_data = { + .ocr_mask = TROUT_MMC_VDD, + .status_irq = TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), + .status = trout_sdslot_status, + .translate_vdd = trout_sdslot_switchvdd, +}; + +/* ---- WIFI ---- */ + +static uint32_t wifi_on_gpio_table[] = { + PCOM_GPIO_CFG(51, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_8MA), /* CMD */ + PCOM_GPIO_CFG(56, 1, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_8MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_INPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static uint32_t wifi_off_gpio_table[] = { + PCOM_GPIO_CFG(51, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT3 */ + PCOM_GPIO_CFG(52, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT2 */ + PCOM_GPIO_CFG(53, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT1 */ + PCOM_GPIO_CFG(54, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* DAT0 */ + PCOM_GPIO_CFG(55, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CMD */ + PCOM_GPIO_CFG(56, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* CLK */ + PCOM_GPIO_CFG(29, 0, GPIO_OUTPUT, GPIO_NO_PULL, GPIO_4MA), /* WLAN IRQ */ +}; + +static struct vreg *vreg_wifi_osc; /* WIFI 32khz oscilator */ +static int trout_wifi_cd = 0; /* WIFI virtual 'card detect' status */ + +static struct sdio_embedded_func wifi_func = { + .f_class = SDIO_CLASS_WLAN, + .f_maxblksize = 512, +}; + +static struct embedded_sdio_data trout_wifi_emb_data = { + .cis = { + .vendor = 0x104c, + .device = 0x9066, + .blksize = 512, + /*.max_dtr = 24000000, Max of chip - no worky on Trout */ + .max_dtr = 20000000, + }, + .cccr = { + .multi_block = 0, + .low_speed = 0, + .wide_bus = 1, + .high_power = 0, + .high_speed = 0, + }, + .funcs = &wifi_func, + .num_funcs = 1, +}; + +static void (*wifi_status_cb)(int card_present, void *dev_id); +static void *wifi_status_cb_devid; + +static int trout_wifi_status_register(void (*callback)(int card_present, void *dev_id), void *dev_id) +{ + if (wifi_status_cb) + return -EAGAIN; + wifi_status_cb = callback; + wifi_status_cb_devid = dev_id; + return 0; +} + +static unsigned int trout_wifi_status(struct device *dev) +{ + return trout_wifi_cd; +} + +int trout_wifi_set_carddetect(int val) +{ + printk("%s: %d\n", __func__, val); + trout_wifi_cd = val; + if (wifi_status_cb) { + wifi_status_cb(val, wifi_status_cb_devid); + } else + printk(KERN_WARNING "%s: Nobody to notify\n", __func__); + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(trout_wifi_set_carddetect); +#endif + +static int trout_wifi_power_state; + +int trout_wifi_power(int on) +{ + int rc; + + printk("%s: %d\n", __func__, on); + + if (on) { + config_gpio_table(wifi_on_gpio_table, + ARRAY_SIZE(wifi_on_gpio_table)); + rc = vreg_enable(vreg_wifi_osc); + if (rc) + return rc; + htc_pwrsink_set(PWRSINK_WIFI, 70); + } else { + config_gpio_table(wifi_off_gpio_table, + ARRAY_SIZE(wifi_off_gpio_table)); + htc_pwrsink_set(PWRSINK_WIFI, 0); + } + gpio_set_value( TROUT_GPIO_MAC_32K_EN, on); + mdelay(100); + gpio_set_value( TROUT_GPIO_WIFI_EN, on); + mdelay(100); + if (!on) { + vreg_disable(vreg_wifi_osc); + } + trout_wifi_power_state = on; + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(trout_wifi_power); +#endif + +static int trout_wifi_reset_state; +int trout_wifi_reset(int on) +{ + printk("%s: %d\n", __func__, on); + gpio_set_value( TROUT_GPIO_WIFI_PA_RESETX, !on ); + trout_wifi_reset_state = on; + mdelay(50); + return 0; +} +#ifndef CONFIG_WIFI_CONTROL_FUNC +EXPORT_SYMBOL(trout_wifi_reset); +#endif + +static struct mmc_platform_data trout_wifi_data = { + .ocr_mask = MMC_VDD_28_29, + .status = trout_wifi_status, + .register_status_notify = trout_wifi_status_register, + .embedded_sdio = &trout_wifi_emb_data, +}; + +int __init trout_init_mmc(unsigned int sys_rev) +{ + wifi_status_cb = NULL; + + sdslot_vreg_enabled = 0; + + vreg_sdslot = vreg_get(0, "gp6"); + if (IS_ERR(vreg_sdslot)) + return PTR_ERR(vreg_sdslot); + vreg_wifi_osc = vreg_get(0, "mmc"); + if (IS_ERR(vreg_wifi_osc)) + return PTR_ERR(vreg_wifi_osc); + + set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SDMC_CD_N), 1); + + msm_add_sdcc(1, &trout_wifi_data); + + if (!opt_disable_sdcard) + msm_add_sdcc(2, &trout_sdslot_data); + else + printk(KERN_INFO "trout: SD-Card interface disabled\n"); + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int troutmmc_dbg_wifi_reset_set(void *data, u64 val) +{ + trout_wifi_reset((int) val); + return 0; +} + +static int troutmmc_dbg_wifi_reset_get(void *data, u64 *val) +{ + *val = trout_wifi_reset_state; + return 0; +} + +static int troutmmc_dbg_wifi_cd_set(void *data, u64 val) +{ + trout_wifi_set_carddetect((int) val); + return 0; +} + +static int troutmmc_dbg_wifi_cd_get(void *data, u64 *val) +{ + *val = trout_wifi_cd; + return 0; +} + +static int troutmmc_dbg_wifi_pwr_set(void *data, u64 val) +{ + trout_wifi_power((int) val); + return 0; +} + +static int troutmmc_dbg_wifi_pwr_get(void *data, u64 *val) +{ + + *val = trout_wifi_power_state; + return 0; +} + +static int troutmmc_dbg_sd_pwr_set(void *data, u64 val) +{ + trout_sdslot_switchvdd(NULL, (unsigned int) val); + return 0; +} + +static int troutmmc_dbg_sd_pwr_get(void *data, u64 *val) +{ + *val = sdslot_vdd; + return 0; +} + +static int troutmmc_dbg_sd_cd_set(void *data, u64 val) +{ + return -ENOSYS; +} + +static int troutmmc_dbg_sd_cd_get(void *data, u64 *val) +{ + *val = trout_sdslot_status(NULL); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_reset_fops, + troutmmc_dbg_wifi_reset_get, + troutmmc_dbg_wifi_reset_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_cd_fops, + troutmmc_dbg_wifi_cd_get, + troutmmc_dbg_wifi_cd_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_wifi_pwr_fops, + troutmmc_dbg_wifi_pwr_get, + troutmmc_dbg_wifi_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_sd_pwr_fops, + troutmmc_dbg_sd_pwr_get, + troutmmc_dbg_sd_pwr_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(troutmmc_dbg_sd_cd_fops, + troutmmc_dbg_sd_cd_get, + troutmmc_dbg_sd_cd_set, "%llu\n"); + +static int __init troutmmc_dbg_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("troutmmc_dbg", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("wifi_reset", 0644, dent, NULL, + &troutmmc_dbg_wifi_reset_fops); + debugfs_create_file("wifi_cd", 0644, dent, NULL, + &troutmmc_dbg_wifi_cd_fops); + debugfs_create_file("wifi_pwr", 0644, dent, NULL, + &troutmmc_dbg_wifi_pwr_fops); + + debugfs_create_file("sd_pwr", 0644, dent, NULL, + &troutmmc_dbg_sd_pwr_fops); + debugfs_create_file("sd_cd", 0644, dent, NULL, + &troutmmc_dbg_sd_cd_fops); + + return 0; +} + +device_initcall(troutmmc_dbg_init); + +#endif diff --git a/arch/arm/mach-msm/board-trout-panel.c b/arch/arm/mach-msm/board-trout-panel.c new file mode 100644 index 000000000000..900b8b1a6f76 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-panel.c @@ -0,0 +1,642 @@ +/* linux/arch/arm/mach-msm/board-trout-mddi.c +** Author: Brian Swetland <swetland@google.com> +*/ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/leds.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/mach-types.h> + +#include <mach/msm_fb.h> +#include <mach/vreg.h> +#include <mach/htc_pwrsink.h> + +#include "board-trout.h" +#include "proc_comm.h" +#include "devices.h" + +#define TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS 255 + +static struct clk *gp_clk; +static int trout_backlight_off; +static int trout_backlight_brightness = TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS; +static int trout_new_backlight = 1; +static uint8_t trout_backlight_last_level = 33; +static DEFINE_MUTEX(trout_backlight_lock); + +static void trout_set_backlight_level(uint8_t level) +{ + unsigned percent = ((int)level * 100) / 255; + + if (trout_new_backlight) { + unsigned long flags; + int i = 0; + level = (int)level * 34 / 256; + + if (trout_backlight_last_level == level) + return; + + if (level == 0) { + gpio_set_value(27, 0); + msleep(2); + } else { + local_irq_save(flags); + if (trout_backlight_last_level == 0) { + gpio_set_value(27, 1); + udelay(40); + trout_backlight_last_level = 33; + } + i = (trout_backlight_last_level - level + 33) % 33; + while (i-- > 0) { + gpio_set_value(27, 0); + udelay(1); + gpio_set_value(27, 1); + udelay(1); + } + local_irq_restore(flags); + } + trout_backlight_last_level = level; + } + else { + if(level) { + clk_enable(gp_clk); + writel((1U << 16) | (~level & 0xffff), + MSM_CLK_CTL_BASE + 0x58); + /* Going directly to a 100% duty cycle does not + * seem to work */ + if(level == 255) { + writel((~127 << 16) | 0xb20, + MSM_CLK_CTL_BASE + 0x5c); + udelay(1); + } + writel((~127 << 16) | 0xb58, MSM_CLK_CTL_BASE + 0x5c); + } + else { + writel(0x0, MSM_CLK_CTL_BASE + 0x5c); + clk_disable(gp_clk); + } + } + htc_pwrsink_set(PWRSINK_BACKLIGHT, percent); +} + +#define MDDI_CLIENT_CORE_BASE 0x108000 +#define LCD_CONTROL_BLOCK_BASE 0x110000 +#define SPI_BLOCK_BASE 0x120000 +#define I2C_BLOCK_BASE 0x130000 +#define PWM_BLOCK_BASE 0x140000 +#define GPIO_BLOCK_BASE 0x150000 +#define SYSTEM_BLOCK1_BASE 0x160000 +#define SYSTEM_BLOCK2_BASE 0x170000 + + +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define SYSCLKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define PWM0OFF (PWM_BLOCK_BASE|0x1C) + +#define V_VDDE2E_VDD2_GPIO 0 +#define MDDI_RST_N 82 + +#define MDDICAP0 (MDDI_CLIENT_CORE_BASE|0x00) +#define MDDICAP1 (MDDI_CLIENT_CORE_BASE|0x04) +#define MDDICAP2 (MDDI_CLIENT_CORE_BASE|0x08) +#define MDDICAP3 (MDDI_CLIENT_CORE_BASE|0x0C) +#define MDCAPCHG (MDDI_CLIENT_CORE_BASE|0x10) +#define MDCRCERC (MDDI_CLIENT_CORE_BASE|0x14) +#define TTBUSSEL (MDDI_CLIENT_CORE_BASE|0x18) +#define DPSET0 (MDDI_CLIENT_CORE_BASE|0x1C) +#define DPSET1 (MDDI_CLIENT_CORE_BASE|0x20) +#define DPSUS (MDDI_CLIENT_CORE_BASE|0x24) +#define DPRUN (MDDI_CLIENT_CORE_BASE|0x28) +#define SYSCKENA (MDDI_CLIENT_CORE_BASE|0x2C) +#define TESTMODE (MDDI_CLIENT_CORE_BASE|0x30) +#define FIFOMONI (MDDI_CLIENT_CORE_BASE|0x34) +#define INTMONI (MDDI_CLIENT_CORE_BASE|0x38) +#define MDIOBIST (MDDI_CLIENT_CORE_BASE|0x3C) +#define MDIOPSET (MDDI_CLIENT_CORE_BASE|0x40) +#define BITMAP0 (MDDI_CLIENT_CORE_BASE|0x44) +#define BITMAP1 (MDDI_CLIENT_CORE_BASE|0x48) +#define BITMAP2 (MDDI_CLIENT_CORE_BASE|0x4C) +#define BITMAP3 (MDDI_CLIENT_CORE_BASE|0x50) +#define BITMAP4 (MDDI_CLIENT_CORE_BASE|0x54) + +#define SRST (LCD_CONTROL_BLOCK_BASE|0x00) +#define PORT_ENB (LCD_CONTROL_BLOCK_BASE|0x04) +#define START (LCD_CONTROL_BLOCK_BASE|0x08) +#define PORT (LCD_CONTROL_BLOCK_BASE|0x0C) +#define CMN (LCD_CONTROL_BLOCK_BASE|0x10) +#define GAMMA (LCD_CONTROL_BLOCK_BASE|0x14) +#define INTFLG (LCD_CONTROL_BLOCK_BASE|0x18) +#define INTMSK (LCD_CONTROL_BLOCK_BASE|0x1C) +#define MPLFBUF (LCD_CONTROL_BLOCK_BASE|0x20) +#define HDE_LEFT (LCD_CONTROL_BLOCK_BASE|0x24) +#define VDE_TOP (LCD_CONTROL_BLOCK_BASE|0x28) +#define PXL (LCD_CONTROL_BLOCK_BASE|0x30) +#define HCYCLE (LCD_CONTROL_BLOCK_BASE|0x34) +#define HSW (LCD_CONTROL_BLOCK_BASE|0x38) +#define HDE_START (LCD_CONTROL_BLOCK_BASE|0x3C) +#define HDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x40) +#define VCYCLE (LCD_CONTROL_BLOCK_BASE|0x44) +#define VSW (LCD_CONTROL_BLOCK_BASE|0x48) +#define VDE_START (LCD_CONTROL_BLOCK_BASE|0x4C) +#define VDE_SIZE (LCD_CONTROL_BLOCK_BASE|0x50) +#define WAKEUP (LCD_CONTROL_BLOCK_BASE|0x54) +#define WSYN_DLY (LCD_CONTROL_BLOCK_BASE|0x58) +#define REGENB (LCD_CONTROL_BLOCK_BASE|0x5C) +#define VSYNIF (LCD_CONTROL_BLOCK_BASE|0x60) +#define WRSTB (LCD_CONTROL_BLOCK_BASE|0x64) +#define RDSTB (LCD_CONTROL_BLOCK_BASE|0x68) +#define ASY_DATA (LCD_CONTROL_BLOCK_BASE|0x6C) +#define ASY_DATB (LCD_CONTROL_BLOCK_BASE|0x70) +#define ASY_DATC (LCD_CONTROL_BLOCK_BASE|0x74) +#define ASY_DATD (LCD_CONTROL_BLOCK_BASE|0x78) +#define ASY_DATE (LCD_CONTROL_BLOCK_BASE|0x7C) +#define ASY_DATF (LCD_CONTROL_BLOCK_BASE|0x80) +#define ASY_DATG (LCD_CONTROL_BLOCK_BASE|0x84) +#define ASY_DATH (LCD_CONTROL_BLOCK_BASE|0x88) +#define ASY_CMDSET (LCD_CONTROL_BLOCK_BASE|0x8C) + +#define SSICTL (SPI_BLOCK_BASE|0x00) +#define SSITIME (SPI_BLOCK_BASE|0x04) +#define SSITX (SPI_BLOCK_BASE|0x08) +#define SSIRX (SPI_BLOCK_BASE|0x0C) +#define SSIINTC (SPI_BLOCK_BASE|0x10) +#define SSIINTS (SPI_BLOCK_BASE|0x14) +#define SSIDBG1 (SPI_BLOCK_BASE|0x18) +#define SSIDBG2 (SPI_BLOCK_BASE|0x1C) +#define SSIID (SPI_BLOCK_BASE|0x20) + +#define WKREQ (SYSTEM_BLOCK1_BASE|0x00) +#define CLKENB (SYSTEM_BLOCK1_BASE|0x04) +#define DRAMPWR (SYSTEM_BLOCK1_BASE|0x08) +#define INTMASK (SYSTEM_BLOCK1_BASE|0x0C) +#define GPIOSEL (SYSTEM_BLOCK2_BASE|0x00) + +#define GPIODATA (GPIO_BLOCK_BASE|0x00) +#define GPIODIR (GPIO_BLOCK_BASE|0x04) +#define GPIOIS (GPIO_BLOCK_BASE|0x08) +#define GPIOIBE (GPIO_BLOCK_BASE|0x0C) +#define GPIOIEV (GPIO_BLOCK_BASE|0x10) +#define GPIOIE (GPIO_BLOCK_BASE|0x14) +#define GPIORIS (GPIO_BLOCK_BASE|0x18) +#define GPIOMIS (GPIO_BLOCK_BASE|0x1C) +#define GPIOIC (GPIO_BLOCK_BASE|0x20) +#define GPIOOMS (GPIO_BLOCK_BASE|0x24) +#define GPIOPC (GPIO_BLOCK_BASE|0x28) +#define GPIOID (GPIO_BLOCK_BASE|0x30) + +#define SPI_WRITE(reg, val) \ + { SSITX, 0x00010000 | (((reg) & 0xff) << 8) | ((val) & 0xff) }, \ + { 0, 5 }, + +#define SPI_WRITE1(reg) \ + { SSITX, (reg) & 0xff }, \ + { 0, 5 }, + +struct mddi_table { + uint32_t reg; + uint32_t value; +}; +static struct mddi_table mddi_toshiba_init_table[] = { + { DPSET0, 0x09e90046 }, + { DPSET1, 0x00000118 }, + { DPSUS, 0x00000000 }, + { DPRUN, 0x00000001 }, + { 1, 14 }, /* msleep 14 */ + { SYSCKENA, 0x00000001 }, + //{ CLKENB, 0x000000EF }, + { CLKENB, 0x0000A1EF }, /* # SYS.CLKENB # Enable clocks for each module (without DCLK , i2cCLK) */ + //{ CLKENB, 0x000025CB }, /* Clock enable register */ + + { GPIODATA, 0x02000200 }, /* # GPI .GPIODATA # GPIO2(RESET_LCD_N) set to 0 , GPIO3(eDRAM_Power) set to 0 */ + { GPIODIR, 0x000030D }, /* 24D # GPI .GPIODIR # Select direction of GPIO port (0,2,3,6,9 output) */ + { GPIOSEL, 0/*0x00000173*/}, /* # SYS.GPIOSEL # GPIO port multiplexing control */ + { GPIOPC, 0x03C300C0 }, /* # GPI .GPIOPC # GPIO2,3 PD cut */ + { WKREQ, 0x00000000 }, /* # SYS.WKREQ # Wake-up request event is VSYNC alignment */ + + { GPIOIBE, 0x000003FF }, + { GPIOIS, 0x00000000 }, + { GPIOIC, 0x000003FF }, + { GPIOIE, 0x00000000 }, + + { GPIODATA, 0x00040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x02040004 }, /* # GPI .GPIODATA # eDRAM VD supply */ + { DRAMPWR, 0x00000001 }, /* eDRAM power */ +}; + +static struct mddi_table mddi_toshiba_panel_init_table[] = { + { SRST, 0x00000003 }, /* FIFO/LCDC not reset */ + { PORT_ENB, 0x00000001 }, /* Enable sync. Port */ + { START, 0x00000000 }, /* To stop operation */ + //{ START, 0x00000001 }, /* To start operation */ + { PORT, 0x00000004 }, /* Polarity of VS/HS/DE. */ + { CMN, 0x00000000 }, + { GAMMA, 0x00000000 }, /* No Gamma correction */ + { INTFLG, 0x00000000 }, /* VSYNC interrupt flag clear/status */ + { INTMSK, 0x00000000 }, /* VSYNC interrupt mask is off. */ + { MPLFBUF, 0x00000000 }, /* Select frame buffer's base address. */ + { HDE_LEFT, 0x00000000 }, /* The value of HDE_LEFT. */ + { VDE_TOP, 0x00000000 }, /* The value of VDE_TPO. */ + { PXL, 0x00000001 }, /* 1. RGB666 */ + /* 2. Data is valid from 1st frame of beginning. */ + { HDE_START, 0x00000006 }, /* HDE_START= 14 PCLK */ + { HDE_SIZE, 0x0000009F }, /* HDE_SIZE=320 PCLK */ + { HSW, 0x00000004 }, /* HSW= 10 PCLK */ + { VSW, 0x00000001 }, /* VSW=2 HCYCLE */ + { VDE_START, 0x00000003 }, /* VDE_START=4 HCYCLE */ + { VDE_SIZE, 0x000001DF }, /* VDE_SIZE=480 HCYCLE */ + { WAKEUP, 0x000001e2 }, /* Wakeup position in VSYNC mode. */ + { WSYN_DLY, 0x00000000 }, /* Wakeup position in VSIN mode. */ + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { CLKENB, 0x000025CB }, /* Clock enable register */ + + { SSICTL, 0x00000170 }, /* SSI control register */ + { SSITIME, 0x00000250 }, /* SSI timing control register */ + { SSICTL, 0x00000172 }, /* SSI control register */ +}; + + +static struct mddi_table mddi_sharp_init_table[] = { + { VCYCLE, 0x000001eb }, + { HCYCLE, 0x000000ae }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 1, 1 }, /* msleep 1 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { 1, 10 }, /* msleep 10 */ + SPI_WRITE(0x5f, 0x01) + SPI_WRITE1(0x11) + { 1, 200 }, /* msleep 200 */ + SPI_WRITE1(0x29) + SPI_WRITE1(0xde) + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_sharp_deinit_table[] = { + { 1, 200 }, /* msleep 200 */ + SPI_WRITE(0x10, 0x1) + { 1, 100 }, /* msleep 100 */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 1, 10 }, /* msleep 10 */ +}; + +static struct mddi_table mddi_tpo_init_table[] = { + { VCYCLE, 0x000001e5 }, + { HCYCLE, 0x000000ac }, + { REGENB, 0x00000001 }, /* Set 1 to enable to change the value of registers. */ + { 0, 20 }, /* udelay 20 */ + { GPIODATA, 0x00000004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { 0, 20 }, /* udelay 20 */ + + SPI_WRITE(0x08, 0x01) + { 0, 500 }, /* udelay 500 */ + SPI_WRITE(0x08, 0x00) + SPI_WRITE(0x02, 0x00) + SPI_WRITE(0x03, 0x04) + SPI_WRITE(0x04, 0x0e) + SPI_WRITE(0x09, 0x02) + SPI_WRITE(0x0b, 0x08) + SPI_WRITE(0x0c, 0x53) + SPI_WRITE(0x0d, 0x01) + SPI_WRITE(0x0e, 0xe0) + SPI_WRITE(0x0f, 0x01) + SPI_WRITE(0x10, 0x58) + SPI_WRITE(0x20, 0x1e) + SPI_WRITE(0x21, 0x0a) + SPI_WRITE(0x22, 0x0a) + SPI_WRITE(0x23, 0x1e) + SPI_WRITE(0x25, 0x32) + SPI_WRITE(0x26, 0x00) + SPI_WRITE(0x27, 0xac) + SPI_WRITE(0x29, 0x06) + SPI_WRITE(0x2a, 0xa4) + SPI_WRITE(0x2b, 0x45) + SPI_WRITE(0x2c, 0x45) + SPI_WRITE(0x2d, 0x15) + SPI_WRITE(0x2e, 0x5a) + SPI_WRITE(0x2f, 0xff) + SPI_WRITE(0x30, 0x6b) + SPI_WRITE(0x31, 0x0d) + SPI_WRITE(0x32, 0x48) + SPI_WRITE(0x33, 0x82) + SPI_WRITE(0x34, 0xbd) + SPI_WRITE(0x35, 0xe7) + SPI_WRITE(0x36, 0x18) + SPI_WRITE(0x37, 0x94) + SPI_WRITE(0x38, 0x01) + SPI_WRITE(0x39, 0x5d) + SPI_WRITE(0x3a, 0xae) + SPI_WRITE(0x3b, 0xff) + SPI_WRITE(0x07, 0x09) + { 0, 10 }, /* udelay 10 */ + { START, 0x00000001 }, /* To start operation */ +}; + +static struct mddi_table mddi_tpo_deinit_table[] = { + SPI_WRITE(0x07, 0x19) + { START, 0x00000000 }, /* To stop operation */ + { GPIODATA, 0x00040004 }, /* GPIO2 high */ + { GPIODIR, 0x00000004 }, /* GPIO2 out */ + { GPIODATA, 0x00040000 }, /* GPIO2 low */ + { 0, 5 }, /* usleep 5 */ +}; + + +#define GPIOSEL_VWAKEINT (1U << 0) +#define INTMASK_VWAKEOUT (1U << 0) + +static void trout_process_mddi_table(struct msm_mddi_client_data *cdata, + struct mddi_table *table, size_t count) +{ + int i; + for(i = 0; i < count; i++) { + uint32_t reg = table[i].reg; + uint32_t value = table[i].value; + + if (reg == 0) + udelay(value); + else if (reg == 1) + msleep(value); + else + cdata->remote_write(cdata, value, reg); + } +} + +static struct vreg *vreg_mddi_1v5; +static struct vreg *vreg_lcm_2v85; + +static void trout_mddi_power_client(struct msm_mddi_client_data *cdata, + int on) +{ + unsigned id, on_off; + if(on) { + on_off = 0; + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_mddi_1v5); + mdelay(5); // delay time >5ms and <10ms + gpio_set_value(V_VDDE2E_VDD2_GPIO, 1); + gpio_set_value(TROUT_GPIO_MDDI_32K_EN, 1); + msleep(3); + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + vreg_enable(vreg_lcm_2v85); + msleep(3); + gpio_set_value(MDDI_RST_N, 1); + msleep(10); + } else { + gpio_set_value(TROUT_GPIO_MDDI_32K_EN, 0); + gpio_set_value(MDDI_RST_N, 0); + msleep(10); + vreg_disable(vreg_lcm_2v85); + on_off = 1; + id = PM_VREG_PDOWN_AUX_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + msleep(5); + gpio_set_value(V_VDDE2E_VDD2_GPIO, 0); + msleep(200); + vreg_disable(vreg_mddi_1v5); + id = PM_VREG_PDOWN_MDDI_ID; + msm_proc_comm(PCOM_VREG_PULLDOWN, &on_off, &id); + } +} + +static int trout_mddi_toshiba_client_init(struct msm_mddi_client_data *cdata) +{ + int panel_id; + + cdata->auto_hibernate(cdata, 0); + trout_process_mddi_table(cdata, mddi_toshiba_init_table, + ARRAY_SIZE(mddi_toshiba_init_table)); + cdata->auto_hibernate(cdata, 1); + panel_id = (cdata->remote_read(cdata, GPIODATA) >> 4) & 3; + if (panel_id > 1) { + printk("unknown panel id at mddi_enable\n"); + return -1; + } + return 0; +} + +static int trout_mddi_toshiba_client_uninit(struct msm_mddi_client_data *cdata) +{ + return 0; +} + +static int trout_mddi_panel_unblank(struct msm_panel_data *panel_data) +{ + struct msm_mddi_panel_info *panel = container_of(panel_data, + struct msm_mddi_panel_info, panel_data); + struct msm_mddi_client_data *mddi_client = panel->client_data; + + int panel_id, ret = 0; + + trout_set_backlight_level(0); + mddi_client->auto_hibernate(mddi_client, 0); + trout_process_mddi_table(mddi_client, mddi_toshiba_panel_init_table, + ARRAY_SIZE(mddi_toshiba_panel_init_table)); + panel_id = (mddi_client->remote_read(mddi_client, GPIODATA) >> 4) & 3; + switch(panel_id) { + case 0: + printk("init sharp panel\n"); + trout_process_mddi_table(mddi_client, + mddi_sharp_init_table, + ARRAY_SIZE(mddi_sharp_init_table)); + break; + case 1: + printk("init tpo panel\n"); + trout_process_mddi_table(mddi_client, + mddi_tpo_init_table, + ARRAY_SIZE(mddi_tpo_init_table)); + break; + default: + printk("unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mutex_lock(&trout_backlight_lock); + trout_set_backlight_level(trout_backlight_brightness); + trout_backlight_off = 0; + mutex_unlock(&trout_backlight_lock); + mddi_client->auto_hibernate(mddi_client, 1); + // reenable vsync + mddi_client->remote_write(mddi_client, GPIOSEL_VWAKEINT, + GPIOSEL); + mddi_client->remote_write(mddi_client, INTMASK_VWAKEOUT, + INTMASK); + return ret; + +} + +static int trout_mddi_panel_blank(struct msm_panel_data *panel_data) +{ + struct msm_mddi_panel_info *panel = container_of(panel_data, + struct msm_mddi_panel_info, panel_data); + struct msm_mddi_client_data *mddi_client = panel->client_data; + int panel_id, ret = 0; + + panel_id = (mddi_client->remote_read(mddi_client, GPIODATA) >> 4) & 3; + mddi_client->auto_hibernate(mddi_client, 0); + switch(panel_id) { + case 0: + printk("deinit sharp panel\n"); + trout_process_mddi_table(mddi_client, + mddi_sharp_deinit_table, + ARRAY_SIZE(mddi_sharp_deinit_table)); + break; + case 1: + printk("deinit tpo panel\n"); + trout_process_mddi_table(mddi_client, + mddi_tpo_deinit_table, + ARRAY_SIZE(mddi_tpo_deinit_table)); + break; + default: + printk("unknown panel_id: %d\n", panel_id); + ret = -1; + }; + mddi_client->auto_hibernate(mddi_client,1); + mutex_lock(&trout_backlight_lock); + trout_set_backlight_level(0); + trout_backlight_off = 1; + mutex_unlock(&trout_backlight_lock); + mddi_client->remote_write(mddi_client, 0, SYSCLKENA); + mddi_client->remote_write(mddi_client, 1, DPSUS); + + return ret; +} + +static void trout_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + mutex_lock(&trout_backlight_lock); + trout_backlight_brightness = value; + if(!trout_backlight_off) + trout_set_backlight_level(trout_backlight_brightness); + mutex_unlock(&trout_backlight_lock); +} + +static struct led_classdev trout_backlight_led = { + .name = "lcd-backlight", + .brightness = TROUT_DEFAULT_BACKLIGHT_BRIGHTNESS, + .brightness_set = trout_brightness_set, +}; + +static int trout_backlight_probe(struct platform_device *pdev) +{ + led_classdev_register(&pdev->dev, &trout_backlight_led); + return 0; +} + +static int trout_backlight_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&trout_backlight_led); + return 0; +} + +static struct platform_driver trout_backlight_driver = { + .probe = trout_backlight_probe, + .remove = trout_backlight_remove, + .driver = { + .name = "trout-backlight", + .owner = THIS_MODULE, + }, +}; + +static struct resource resources_msm_fb[] = { + { + .start = MSM_FB_BASE, + .end = MSM_FB_BASE + MSM_FB_SIZE, + .flags = IORESOURCE_MEM, + }, +}; + +struct msm_mddi_toshiba_client_data toshiba_client_data = { + .init = trout_mddi_toshiba_client_init, + .uninit = trout_mddi_toshiba_client_uninit, + .blank = trout_mddi_panel_blank, + .unblank = trout_mddi_panel_unblank, + .fb_data = { + .xres = 320, + .yres = 480, + .width = 45, + .height = 67, + .output_format = 0, + }, +}; + +struct msm_mddi_platform_data mddi_pdata = { + .clk_rate = 122880000, + .power_client = trout_mddi_power_client, + .fb_resource = resources_msm_fb, + .num_clients = 1, + .client_platform_data = { + { + .product_id = (0xd263 << 16 | 0), + .name = "mddi_c_d263_0000", + .id = 0, + .client_data = &toshiba_client_data, + .clk_rate = 0, + }, + }, +}; + +static struct platform_device trout_backlight = { + .name = "trout-backlight", +}; + +int __init trout_init_panel(void) +{ + int rc; + + if (!machine_is_trout()) + return 0; + vreg_mddi_1v5 = vreg_get(0, "gp2"); + if (IS_ERR(vreg_mddi_1v5)) + return PTR_ERR(vreg_mddi_1v5); + vreg_lcm_2v85 = vreg_get(0, "gp4"); + if (IS_ERR(vreg_lcm_2v85)) + return PTR_ERR(vreg_lcm_2v85); + + trout_new_backlight = system_rev >= 5; + if (trout_new_backlight) { + uint32_t config = PCOM_GPIO_CFG(27, 0, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + } + else { + uint32_t config = PCOM_GPIO_CFG(27, 1, GPIO_OUTPUT, + GPIO_NO_PULL, GPIO_8MA); + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, 0); + + gp_clk = clk_get(NULL, "gp_clk"); + if (IS_ERR(gp_clk)) { + printk(KERN_ERR "trout_init_panel: could not get gp" + "clock\n"); + gp_clk = NULL; + } + rc = clk_set_rate(gp_clk, 19200000); + if (rc) + printk(KERN_ERR "trout_init_panel: set clock rate " + "failed\n"); + } + + rc = platform_device_register(&msm_device_mdp); + if (rc) + return rc; + msm_device_mddi0.dev.platform_data = &mddi_pdata; + rc = platform_device_register(&msm_device_mddi0); + if (rc) + return rc; + platform_device_register(&trout_backlight); + return platform_driver_register(&trout_backlight_driver); +} + +device_initcall(trout_init_panel); diff --git a/arch/arm/mach-msm/board-trout-rfkill.c b/arch/arm/mach-msm/board-trout-rfkill.c new file mode 100644 index 000000000000..5212431dda82 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-rfkill.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2008 Google, Inc. + * Author: Nick Pelly <npelly@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* Control bluetooth power for trout platform */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/rfkill.h> +#include <linux/delay.h> +#include <asm/gpio.h> + +#include "board-trout.h" + +void rfkill_switch_all(enum rfkill_type type, enum rfkill_state state); + +static struct rfkill *bt_rfk; +static const char bt_name[] = "brf6300"; + +static int bluetooth_set_power(void *data, enum rfkill_state state) +{ + switch (state) { + case RFKILL_STATE_UNBLOCKED: + gpio_set_value(TROUT_GPIO_BT_32K_EN, 1); + udelay(10); + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_HIGH); + break; + case RFKILL_STATE_SOFT_BLOCKED: + gpio_configure(101, GPIOF_DRIVE_OUTPUT | GPIOF_OUTPUT_LOW); + gpio_set_value(TROUT_GPIO_BT_32K_EN, 0); + break; + default: + printk(KERN_ERR "bad bluetooth rfkill state %d\n", state); + } + return 0; +} + +static int __init trout_rfkill_probe(struct platform_device *pdev) +{ + int rc = 0; + + /* default to bluetooth off */ + rfkill_switch_all(RFKILL_TYPE_BLUETOOTH, RFKILL_STATE_SOFT_BLOCKED); + bluetooth_set_power(NULL, RFKILL_STATE_SOFT_BLOCKED); + + bt_rfk = rfkill_allocate(&pdev->dev, RFKILL_TYPE_BLUETOOTH); + if (!bt_rfk) + return -ENOMEM; + + bt_rfk->name = bt_name; + bt_rfk->state = RFKILL_STATE_SOFT_BLOCKED; + /* userspace cannot take exclusive control */ + bt_rfk->user_claim_unsupported = 1; + bt_rfk->user_claim = 0; + bt_rfk->data = NULL; // user data + bt_rfk->toggle_radio = bluetooth_set_power; + + rc = rfkill_register(bt_rfk); + + if (rc) + rfkill_free(bt_rfk); + return rc; +} + +static struct platform_driver trout_rfkill_driver = { + .probe = trout_rfkill_probe, + .driver = { + .name = "trout_rfkill", + .owner = THIS_MODULE, + }, +}; + +static int __init trout_rfkill_init(void) +{ + return platform_driver_register(&trout_rfkill_driver); +} + +module_init(trout_rfkill_init); +MODULE_DESCRIPTION("trout rfkill"); +MODULE_AUTHOR("Nick Pelly <npelly@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/board-trout-wifi.c b/arch/arm/mach-msm/board-trout-wifi.c new file mode 100644 index 000000000000..51b26a405369 --- /dev/null +++ b/arch/arm/mach-msm/board-trout-wifi.c @@ -0,0 +1,74 @@ +/* arch/arm/mach-msm/board-trout-wifi.c + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt <dimitrysh@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/vmalloc.h> +#include <linux/err.h> +#include <linux/wifi_tiwlan.h> + +extern int trout_wifi_set_carddetect(int val); +extern int trout_wifi_power(int on); +extern int trout_wifi_reset(int on); + +#ifdef CONFIG_WIFI_MEM_PREALLOC +typedef struct wifi_mem_prealloc_struct { + void *mem_ptr; + unsigned long size; +} wifi_mem_prealloc_t; + +static wifi_mem_prealloc_t wifi_mem_array[WMPA_NUMBER_OF_SECTIONS] = { + { NULL, (WMPA_SECTION_SIZE_0 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_1 + WMPA_SECTION_HEADER) }, + { NULL, (WMPA_SECTION_SIZE_2 + WMPA_SECTION_HEADER) } +}; + +static void *trout_wifi_mem_prealloc(int section, unsigned long size) +{ + if( (section < 0) || (section >= WMPA_NUMBER_OF_SECTIONS) ) + return NULL; + if( wifi_mem_array[section].size < size ) + return NULL; + return wifi_mem_array[section].mem_ptr; +} + +int __init trout_init_wifi_mem( void ) +{ + int i; + + for(i=0;( i < WMPA_NUMBER_OF_SECTIONS );i++) { + wifi_mem_array[i].mem_ptr = vmalloc(wifi_mem_array[i].size); + if( wifi_mem_array[i].mem_ptr == NULL ) + return -ENOMEM; + } + return 0; +} +#endif + +struct wifi_platform_data trout_wifi_control = { + .set_power = trout_wifi_power, + .set_reset = trout_wifi_reset, + .set_carddetect = trout_wifi_set_carddetect, +#ifdef CONFIG_WIFI_MEM_PREALLOC + .mem_prealloc = trout_wifi_mem_prealloc, +#else + .mem_prealloc = NULL, +#endif +}; + +#endif diff --git a/arch/arm/mach-msm/board-trout.c b/arch/arm/mach-msm/board-trout.c new file mode 100644 index 000000000000..97ee39a5db23 --- /dev/null +++ b/arch/arm/mach-msm/board-trout.c @@ -0,0 +1,841 @@ +/* arch/arm/mach-msm/board-trout.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/keyreset.h> +#include <linux/leds.h> +#include <linux/switch.h> +#include <linux/../../../drivers/staging/android/timed_gpio.h> +#include <linux/synaptics_i2c_rmi.h> +#include <linux/akm8976.h> +#include <linux/sysdev.h> +#include <linux/android_pmem.h> + +#include <linux/delay.h> + +#include <asm/gpio.h> +#include <mach/hardware.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/flash.h> +#include <asm/system.h> +#include <mach/system.h> +#include <mach/vreg.h> + +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/setup.h> + +#include <linux/gpio_event.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <mach/mmc.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/msm_audio.h> + +#include "board-trout.h" + +#include "gpio_chip.h" +#include "pm.h" + +#include <mach/board.h> +#include <mach/board_htc.h> +#include <mach/msm_serial_hs.h> +#include <mach/htc_pwrsink.h> +#ifdef CONFIG_HTC_HEADSET +#include <mach/htc_headset.h> +#endif +#ifdef CONFIG_WIFI_CONTROL_FUNC +#include <linux/wifi_tiwlan.h> +#endif + +#include "proc_comm.h" +#include "devices.h" + +void msm_init_irq(void); +void msm_init_gpio(void); + +extern int trout_init_mmc(unsigned int); +#ifdef CONFIG_WIFI_CONTROL_FUNC +#ifdef CONFIG_WIFI_MEM_PREALLOC +extern int trout_init_wifi_mem(void); +#endif +extern struct wifi_platform_data trout_wifi_control; +#endif + +struct trout_axis_info { + struct gpio_event_axis_info info; + uint16_t in_state; + uint16_t out_state; +}; +static bool nav_just_on; +static int nav_on_jiffies; + +uint16_t trout_axis_map(struct gpio_event_axis_info *info, uint16_t in) +{ + struct trout_axis_info *ai = container_of(info, struct trout_axis_info, info); + uint16_t out = ai->out_state; + + if (nav_just_on) { + if (jiffies == nav_on_jiffies || jiffies == nav_on_jiffies + 1) + goto ignore; + nav_just_on = 0; + } + if((ai->in_state ^ in) & 1) + out--; + if((ai->in_state ^ in) & 2) + out++; + ai->out_state = out; +ignore: + ai->in_state = in; + return out; +} + +int trout_nav_power(const struct gpio_event_platform_data *pdata, bool on) +{ + gpio_set_value(TROUT_GPIO_JOG_EN, on); + if (on) { + nav_just_on = 1; + nav_on_jiffies = jiffies; + } + return 0; +} + +static uint32_t trout_4_x_axis_gpios[] = { + TROUT_4_BALL_LEFT_0, TROUT_4_BALL_RIGHT_0 +}; +static uint32_t trout_5_x_axis_gpios[] = { + TROUT_5_BALL_LEFT_0, TROUT_5_BALL_RIGHT_0 +}; + +static struct trout_axis_info trout_x_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(trout_5_x_axis_gpios), + .type = EV_REL, + .code = REL_X, + .decoded_size = 1U << ARRAY_SIZE(trout_5_x_axis_gpios), + .map = trout_axis_map, + .gpio = trout_5_x_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static uint32_t trout_4_y_axis_gpios[] = { + TROUT_4_BALL_UP_0, TROUT_4_BALL_DOWN_0 +}; +static uint32_t trout_5_y_axis_gpios[] = { + TROUT_5_BALL_UP_0, TROUT_5_BALL_DOWN_0 +}; + +static struct trout_axis_info trout_y_axis = { + .info = { + .info.func = gpio_event_axis_func, + .count = ARRAY_SIZE(trout_5_y_axis_gpios), + .type = EV_REL, + .code = REL_Y, + .decoded_size = 1U << ARRAY_SIZE(trout_5_y_axis_gpios), + .map = trout_axis_map, + .gpio = trout_5_y_axis_gpios, + .flags = GPIOEAF_PRINT_UNKNOWN_DIRECTION /*| GPIOEAF_PRINT_RAW | GPIOEAF_PRINT_EVENT */ + } +}; + +static struct gpio_event_direct_entry trout_nav_buttons[] = { + { TROUT_GPIO_NAVI_ACT_N, BTN_MOUSE } +}; + +static struct gpio_event_input_info trout_nav_button_info = { + .info.func = gpio_event_input_func, + .flags = 0, + .type = EV_KEY, + .keymap = trout_nav_buttons, + .keymap_size = ARRAY_SIZE(trout_nav_buttons) +}; + +static struct gpio_event_info *trout_nav_info[] = { + &trout_x_axis.info.info, + &trout_y_axis.info.info, + &trout_nav_button_info.info +}; + +static struct gpio_event_platform_data trout_nav_data = { + .name = "trout-nav", + .info = trout_nav_info, + .info_count = ARRAY_SIZE(trout_nav_info), + .power = trout_nav_power, +}; + +static struct platform_device trout_nav_device = { + .name = GPIO_EVENT_DEV_NAME, + .id = 2, + .dev = { + .platform_data = &trout_nav_data, + }, +}; + +static int trout_reset_keys_up[] = { + BTN_MOUSE, + 0 +}; + +static struct keyreset_platform_data trout_reset_keys_pdata = { + .keys_up = trout_reset_keys_up, + .keys_down = { + KEY_SEND, + KEY_MENU, + KEY_END, + 0 + }, +}; + +struct platform_device trout_reset_keys_device = { + .name = KEYRESET_NAME, + .dev.platform_data = &trout_reset_keys_pdata, +}; + +static int trout_ts_power(int on) +{ + int tp_ls_gpio = system_rev < 5 ? TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN; + if (on) { + gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 1); + gpio_set_value(TROUT_GPIO_TP_EN, 1); + /* touchscreen must be powered before we enable i2c pullup */ + msleep(2); + /* enable touch panel level shift */ + gpio_set_value(tp_ls_gpio, 1); + msleep(2); + } + else { + gpio_set_value(tp_ls_gpio, 0); + udelay(50); + gpio_set_value(TROUT_GPIO_TP_EN, 0); + gpio_set_value(TROUT_GPIO_TP_I2C_PULL, 0); + } + return 0; +} + +static struct synaptics_i2c_rmi_platform_data trout_ts_data[] = { + { + .version = 0x010c, + .power = trout_ts_power, + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = -100 * 0x10000 / 4334, + .inactive_right = -100 * 0x10000 / 4334, + .inactive_top = -40 * 0x10000 / 6696, + .inactive_bottom = -40 * 0x10000 / 6696, + .snap_left_on = 300 * 0x10000 / 4334, + .snap_left_off = 310 * 0x10000 / 4334, + .snap_right_on = 300 * 0x10000 / 4334, + .snap_right_off = 310 * 0x10000 / 4334, + .snap_top_on = 100 * 0x10000 / 6696, + .snap_top_off = 110 * 0x10000 / 6696, + .snap_bottom_on = 100 * 0x10000 / 6696, + .snap_bottom_off = 110 * 0x10000 / 6696, + }, + { + .flags = SYNAPTICS_FLIP_Y | SYNAPTICS_SNAP_TO_INACTIVE_EDGE, + .inactive_left = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_right = ((4674 - 4334) / 2 + 200) * 0x10000 / 4334, + .inactive_top = ((6946 - 6696) / 2) * 0x10000 / 6696, + .inactive_bottom = ((6946 - 6696) / 2) * 0x10000 / 6696, + } +}; + +static struct akm8976_platform_data compass_platform_data = { + .reset = TROUT_GPIO_COMPASS_RST_N, + .clk_on = TROUT_GPIO_COMPASS_32K_EN, + .intr = TROUT_GPIO_COMPASS_IRQ, +}; + +static struct i2c_board_info i2c_devices[] = { + { + I2C_BOARD_INFO(SYNAPTICS_I2C_RMI_NAME, 0x20), + .platform_data = trout_ts_data, + .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_TP_ATT_N) + }, + { + I2C_BOARD_INFO("akm8976", 0x1C), + .platform_data = &compass_platform_data, + .irq = TROUT_GPIO_TO_INT(TROUT_GPIO_COMPASS_IRQ), + }, + { + I2C_BOARD_INFO("pca963x", 0x62), + }, +}; + +static struct android_pmem_platform_data android_pmem_pdata = { + .name = "pmem", + .start = MSM_PMEM_MDP_BASE, + .size = MSM_PMEM_MDP_SIZE, + .no_allocator = 1, + .cached = 1, +}; + +static struct android_pmem_platform_data android_pmem_adsp_pdata = { + .name = "pmem_adsp", + .start = MSM_PMEM_ADSP_BASE, + .size = MSM_PMEM_ADSP_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_camera_pdata = { + .name = "pmem_camera", + .start = MSM_PMEM_CAMERA_BASE, + .size = MSM_PMEM_CAMERA_SIZE, + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data android_pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .start = MSM_PMEM_GPU0_BASE, + .size = MSM_PMEM_GPU0_SIZE, + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct android_pmem_platform_data android_pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .start = MSM_PMEM_GPU1_BASE, + .size = MSM_PMEM_GPU1_SIZE, + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct platform_device android_pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &android_pmem_pdata }, +}; + +static struct platform_device android_pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &android_pmem_adsp_pdata }, +}; + +static struct platform_device android_pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &android_pmem_gpu0_pdata }, +}; + +static struct platform_device android_pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &android_pmem_gpu1_pdata }, +}; + +static struct platform_device android_pmem_camera_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &android_pmem_camera_pdata }, +}; + +static struct timed_gpio timed_gpios[] = { + { + .name = "vibrator", + .gpio = TROUT_GPIO_HAPTIC_PWM, + .max_timeout = 15000, + }, + { + .name = "flash", + .gpio = TROUT_GPIO_FLASH_EN, + .max_timeout = 400, + }, +}; + +static struct timed_gpio_platform_data timed_gpio_data = { + .num_gpios = ARRAY_SIZE(timed_gpios), + .gpios = timed_gpios, +}; + +static struct platform_device android_timed_gpios = { + .name = "timed-gpio", + .id = -1, + .dev = { + .platform_data = &timed_gpio_data, + }, +}; + +static struct gpio_led android_led_list[] = { + { + .name = "spotlight", + .gpio = TROUT_GPIO_SPOTLIGHT_EN, + }, + { + .name = "keyboard-backlight", + .gpio = TROUT_GPIO_QTKEY_LED_EN, + }, + { + .name = "button-backlight", + .gpio = TROUT_GPIO_UI_LED_EN, + }, +}; + +static struct gpio_led_platform_data android_leds_data = { + .num_leds = ARRAY_SIZE(android_led_list), + .leds = android_led_list, +}; + +static struct platform_device android_leds = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &android_leds_data, + }, +}; + +static struct gpio_switch_platform_data sd_door_switch_data = { + .name = "sd-door", + .gpio = TROUT_GPIO_SD_DOOR_N, + .state_on = "open", + .state_off = "closed", +}; + +static struct platform_device sd_door_switch = { + .name = "switch-gpio", + .id = -1, + .dev = { + .platform_data = &sd_door_switch_data, + }, +}; + +#ifdef CONFIG_HTC_HEADSET +static void h2w_config_cpld(int route) +{ + switch (route) { + case H2W_UART3: + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 1); + break; + case H2W_GPIO: + gpio_set_value(TROUT_GPIO_H2W_SEL0, 0); + gpio_set_value(TROUT_GPIO_H2W_SEL1, 0); + break; + } +} + +static void h2w_init_cpld(void) +{ + h2w_config_cpld(H2W_UART3); + gpio_set_value(TROUT_GPIO_H2W_CLK_DIR, 0); + gpio_set_value(TROUT_GPIO_H2W_DAT_DIR, 0); +} + +static struct h2w_platform_data trout_h2w_data = { + .cable_in1 = TROUT_GPIO_CABLE_IN1, + .cable_in2 = TROUT_GPIO_CABLE_IN2, + .h2w_clk = TROUT_GPIO_H2W_CLK_GPI, + .h2w_data = TROUT_GPIO_H2W_DAT_GPI, + .debug_uart = H2W_UART3, + .config_cpld = h2w_config_cpld, + .init_cpld = h2w_init_cpld, +}; + +static struct platform_device trout_h2w = { + .name = "h2w", + .id = -1, + .dev = { + .platform_data = &trout_h2w_data, + }, +}; +#endif + +static void trout_phy_reset(void) +{ + gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 0); + mdelay(10); + gpio_set_value(TROUT_GPIO_USB_PHY_RST_N, 1); + mdelay(10); +} + +static struct resource trout_ram_console_resource[] = { + { + .start = MSM_RAM_CONSOLE_BASE, + .end = MSM_RAM_CONSOLE_BASE + MSM_RAM_CONSOLE_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device trout_ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(trout_ram_console_resource), + .resource = trout_ram_console_resource, +}; + +static struct pwr_sink trout_pwrsink_table[] = { + { + .id = PWRSINK_AUDIO, + .ua_max = 90000, + }, + { + .id = PWRSINK_BACKLIGHT, + .ua_max = 128000, + }, + { + .id = PWRSINK_LED_BUTTON, + .ua_max = 17000, + }, + { + .id = PWRSINK_LED_KEYBOARD, + .ua_max = 22000, + }, + { + .id = PWRSINK_GP_CLK, + .ua_max = 30000, + }, + { + .id = PWRSINK_BLUETOOTH, + .ua_max = 15000, + }, + { + .id = PWRSINK_CAMERA, + .ua_max = 0, + }, + { + .id = PWRSINK_SDCARD, + .ua_max = 0, + }, + { + .id = PWRSINK_VIDEO, + .ua_max = 0, + }, + { + .id = PWRSINK_WIFI, + .ua_max = 200000, + }, + { + .id = PWRSINK_SYSTEM_LOAD, + .ua_max = 63000, + .percent_util = 100, + }, +}; + +static struct pwr_sink_platform_data trout_pwrsink_data = { + .num_sinks = ARRAY_SIZE(trout_pwrsink_table), + .sinks = trout_pwrsink_table, + .suspend_late = NULL, + .resume_early = NULL, + .suspend_early = NULL, + .resume_late = NULL, +}; + +static struct platform_device trout_pwr_sink = { + .name = "htc_pwrsink", + .id = -1, + .dev = { + .platform_data = &trout_pwrsink_data, + }, +}; + +static struct platform_device trout_rfkill = { + .name = "trout_rfkill", + .id = -1, +}; + +#ifdef CONFIG_WIFI_CONTROL_FUNC +static struct platform_device trout_wifi = { + .name = "msm_wifi", + .id = 1, + .num_resources = 0, + .resource = NULL, + .dev = { + .platform_data = &trout_wifi_control, + }, +}; +#endif + +#define SND(num, desc) { .name = desc, .id = num } +static struct snd_endpoint snd_endpoints_list[] = { + SND(0, "HANDSET"), + SND(1, "SPEAKER"), + SND(2, "HEADSET"), + SND(3, "BT"), + SND(44, "BT_EC_OFF"), + SND(10, "HEADSET_AND_SPEAKER"), + SND(256, "CURRENT"), + + /* Bluetooth accessories. */ + + SND(12, "HTC BH S100"), + SND(13, "HTC BH M100"), + SND(14, "Motorola H500"), + SND(15, "Nokia HS-36W"), + SND(16, "PLT 510v.D"), + SND(17, "M2500 by Plantronics"), + SND(18, "Nokia HDW-3"), + SND(19, "HBH-608"), + SND(20, "HBH-DS970"), + SND(21, "i.Tech BlueBAND"), + SND(22, "Nokia BH-800"), + SND(23, "Motorola H700"), + SND(24, "HTC BH M200"), + SND(25, "Jabra JX10"), + SND(26, "320Plantronics"), + SND(27, "640Plantronics"), + SND(28, "Jabra BT500"), + SND(29, "Motorola HT820"), + SND(30, "HBH-IV840"), + SND(31, "6XXPlantronics"), + SND(32, "3XXPlantronics"), + SND(33, "HBH-PV710"), + SND(34, "Motorola H670"), + SND(35, "HBM-300"), + SND(36, "Nokia BH-208"), + SND(37, "Samsung WEP410"), + SND(38, "Jabra BT8010"), + SND(39, "Motorola S9"), + SND(40, "Jabra BT620s"), + SND(41, "Nokia BH-902"), + SND(42, "HBH-DS220"), + SND(43, "HBH-DS980"), +}; +#undef SND + +static struct msm_snd_endpoints trout_snd_endpoints = { + .endpoints = snd_endpoints_list, + .num = ARRAY_SIZE(snd_endpoints_list), +}; + +static struct platform_device trout_snd = { + .name = "msm_snd", + .id = -1, + .dev = { + .platform_data = &trout_snd_endpoints, + }, +}; + +static struct platform_device *devices[] __initdata = { + &msm_device_smd, + &msm_device_dmov, + &msm_device_nand, + &msm_device_i2c, + &msm_device_uart1, +#if !defined(CONFIG_MSM_SERIAL_DEBUGGER) && !defined(CONFIG_TROUT_H2W) + &msm_device_uart3, +#endif +#ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, +#endif + &trout_nav_device, + &trout_reset_keys_device, + &android_leds, + &sd_door_switch, + &android_timed_gpios, + &android_pmem_device, + &android_pmem_adsp_device, + &android_pmem_gpu0_device, + &android_pmem_gpu1_device, + &android_pmem_camera_device, + &trout_ram_console_device, + &trout_rfkill, +#ifdef CONFIG_WIFI_CONTROL_FUNC + &trout_wifi, +#endif +#ifdef CONFIG_HTC_HEADSET + &trout_h2w, +#endif +#ifdef CONFIG_HTC_PWRSINK + &trout_pwr_sink, +#endif + &trout_snd, +}; + +extern struct sys_timer msm_timer; + +static void __init trout_init_irq(void) +{ + printk("trout_init_irq()\n"); + msm_init_irq(); +} + +static uint opt_disable_uart3; + +module_param_named(disable_uart3, opt_disable_uart3, uint, 0); + +static void trout_reset(void) +{ + gpio_set_value(TROUT_GPIO_PS_HOLD, 0); +} + +static uint32_t gpio_table[] = { + /* BLUETOOTH */ +#ifdef CONFIG_SERIAL_MSM_HS + PCOM_GPIO_CFG(43, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 2, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 3, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#else + PCOM_GPIO_CFG(43, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RTS */ + PCOM_GPIO_CFG(44, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* CTS */ + PCOM_GPIO_CFG(45, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* RX */ + PCOM_GPIO_CFG(46, 1, GPIO_OUTPUT, GPIO_PULL_UP, GPIO_4MA), /* TX */ +#endif +}; + +static void config_gpio_table(uint32_t *table, int len) +{ + int n; + unsigned id; + for(n = 0; n < len; n++) { + id = table[n]; + msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &id, 0); + } +} + +static void __init config_gpios(void) +{ + config_gpio_table(gpio_table, ARRAY_SIZE(gpio_table)); +} + +void msm_serial_debug_init(unsigned int base, int irq, + struct device *clk_device, int signal_irq); + +static struct msm_acpu_clock_platform_data trout_clock_data = { + .acpu_switch_time_us = 20, + .max_speed_delta_khz = 256000, + .vdd_switch_time_us = 62, + .power_collapse_khz = 19200000, + .wait_for_irq_khz = 128000000, +}; + +#ifdef CONFIG_SERIAL_MSM_HS +static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = { + .wakeup_irq = MSM_GPIO_TO_INT(45), + .inject_rx_on_wakeup = 1, + .rx_to_inject = 0x32, +}; +#endif + +static struct msm_pm_platform_data msm_pm_data[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE].latency = 16000, + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN].latency = 12000, + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT].latency = 2000, +}; + +static void __init trout_init(void) +{ + int rc; + + printk("trout_init() revision=%d\n", system_rev); + + /* + * Setup common MSM GPIOS + */ + config_gpios(); + + msm_hw_reset_hook = trout_reset; + + gpio_direction_output(system_rev < 5 ? + TROUT_4_TP_LS_EN : TROUT_5_TP_LS_EN, 0); + + msm_acpu_clock_init(&trout_clock_data); + +#if defined(CONFIG_MSM_SERIAL_DEBUGGER) + if (!opt_disable_uart3) + msm_serial_debug_init(MSM_UART3_PHYS, INT_UART3, + &msm_device_uart3.dev, 1); +#endif + + /* gpio_configure(108, IRQF_TRIGGER_LOW); */ + + /* put the AF VCM in powerdown mode to avoid noise */ + gpio_set_value(TROUT_GPIO_VCM_PWDN, 1); + mdelay(100); + + if (system_rev < 5) { + trout_x_axis.info.gpio = trout_4_x_axis_gpios; + trout_y_axis.info.gpio = trout_4_y_axis_gpios; + } + +#ifdef CONFIG_SERIAL_MSM_HS + msm_device_uart_dm1.dev.platform_data = &msm_uart_dm1_pdata; +#endif + msm_add_usb_devices(trout_phy_reset); + + rc = trout_init_mmc(system_rev); + if (rc) + printk(KERN_CRIT "%s: MMC init failure (%d)\n", __func__, rc); + +#ifdef CONFIG_WIFI_MEM_PREALLOC + rc = trout_init_wifi_mem(); + if (rc) + printk(KERN_CRIT "%s: WiFi Memory init failure (%d)\n", __func__, rc); +#endif + + platform_add_devices(devices, ARRAY_SIZE(devices)); + i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices)); + msm_pm_set_platform_data(msm_pm_data); + + /* SD card door should wake the device */ + set_irq_wake(TROUT_GPIO_TO_INT(TROUT_GPIO_SD_DOOR_N), 1); +} + +static struct map_desc trout_io_desc[] __initdata = { + { + .virtual = TROUT_CPLD_BASE, + .pfn = __phys_to_pfn(TROUT_CPLD_START), + .length = TROUT_CPLD_SIZE, + .type = MT_DEVICE_NONSHARED + } +}; + +static void __init trout_fixup(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + mi->nr_banks=1; + mi->bank[0].start = PHYS_OFFSET; + mi->bank[0].node = PHYS_TO_NID(PHYS_OFFSET); + mi->bank[0].size = (101*1024*1024); +} + +static void __init trout_map_io(void) +{ + msm_shared_ram_phys = 0x01F00000; + + msm_map_common_io(); + iotable_init(trout_io_desc, ARRAY_SIZE(trout_io_desc)); + msm_clock_init(msm_clocks_7x01a, msm_num_clocks_7x01a); +} + +MACHINE_START(TROUT, "trout") +/* Maintainer: Brian Swetland <swetland@google.com> */ + +/* this is broken... can we just opt out of specifying something here? */ + .phys_io = 0x80000000, + .io_pg_offst = ((0x80000000) >> 18) & 0xfffc, + + .boot_params = 0x10000100, + .fixup = trout_fixup, + .map_io = trout_map_io, + .init_irq = trout_init_irq, + .init_machine = trout_init, + .timer = &msm_timer, +MACHINE_END diff --git a/arch/arm/mach-msm/board-trout.h b/arch/arm/mach-msm/board-trout.h new file mode 100644 index 000000000000..308c4df90189 --- /dev/null +++ b/arch/arm/mach-msm/board-trout.h @@ -0,0 +1,162 @@ +/* linux/arch/arm/mach-msm/board-trout.h +** Author: Brian Swetland <swetland@google.com> +*/ +#ifndef __ARCH_ARM_MACH_MSM_BOARD_TROUT_H +#define __ARCH_ARM_MACH_MSM_BOARD_TROUT_H + +#include <mach/board.h> + +#define MSM_SMI_BASE 0x00000000 +#define MSM_SMI_SIZE 0x00800000 + +#define MSM_EBI_BASE 0x10000000 +#define MSM_EBI_SIZE 0x06e00000 + +#define MSM_PMEM_GPU0_BASE 0x00000000 +#define MSM_PMEM_GPU0_SIZE 0x00700000 + +#define MSM_PMEM_MDP_BASE 0x02000000 +#define MSM_PMEM_MDP_SIZE 0x00800000 + +#define MSM_PMEM_ADSP_BASE 0x02800000 +#define MSM_PMEM_ADSP_SIZE 0x00800000 + +#define MSM_PMEM_CAMERA_BASE 0x03000000 +#define MSM_PMEM_CAMERA_SIZE 0x00800000 + +#define MSM_FB_BASE 0x03800000 +#define MSM_FB_SIZE 0x00100000 + +#define MSM_LINUX_BASE MSM_EBI_BASE +#define MSM_LINUX_SIZE 0x06500000 + +#define MSM_PMEM_GPU1_SIZE 0x800000 +#define MSM_PMEM_GPU1_BASE MSM_RAM_CONSOLE_BASE - MSM_PMEM_GPU1_SIZE + +#define MSM_RAM_CONSOLE_BASE MSM_EBI_BASE + 0x6d00000 +#define MSM_RAM_CONSOLE_SIZE 128 * SZ_1K + +#if (MSM_FB_BASE + MSM_FB_SIZE) >= (MSM_PMEM_GPU1_BASE) +#error invalid memory map +#endif + +#define DECLARE_MSM_IOMAP +#include <mach/msm_iomap.h> + +#define TROUT_4_BALL_UP_0 1 +#define TROUT_4_BALL_LEFT_0 18 +#define TROUT_4_BALL_DOWN_0 57 +#define TROUT_4_BALL_RIGHT_0 91 + +#define TROUT_5_BALL_UP_0 94 +#define TROUT_5_BALL_LEFT_0 18 +#define TROUT_5_BALL_DOWN_0 90 +#define TROUT_5_BALL_RIGHT_0 19 + +#define TROUT_POWER_KEY 20 + +#define TROUT_4_TP_LS_EN 19 +#define TROUT_5_TP_LS_EN 1 + +#define TROUT_CPLD_BASE 0xE8100000 +#define TROUT_CPLD_START 0x98000000 +#define TROUT_CPLD_SIZE SZ_4K + +#define TROUT_GPIO_CABLE_IN1 (83) +#define TROUT_GPIO_CABLE_IN2 (49) + +#define TROUT_GPIO_START (128) + +#define TROUT_GPIO_INT_MASK0_REG (0x0c) +#define TROUT_GPIO_INT_STAT0_REG (0x0e) +#define TROUT_GPIO_INT_MASK1_REG (0x14) +#define TROUT_GPIO_INT_STAT1_REG (0x10) + +#define TROUT_GPIO_HAPTIC_PWM (28) +#define TROUT_GPIO_PS_HOLD (25) + +#define TROUT_GPIO_MISC2_BASE (TROUT_GPIO_START + 0x00) +#define TROUT_GPIO_MISC3_BASE (TROUT_GPIO_START + 0x08) +#define TROUT_GPIO_MISC4_BASE (TROUT_GPIO_START + 0x10) +#define TROUT_GPIO_MISC5_BASE (TROUT_GPIO_START + 0x18) +#define TROUT_GPIO_INT2_BASE (TROUT_GPIO_START + 0x20) +#define TROUT_GPIO_MISC1_BASE (TROUT_GPIO_START + 0x28) +#define TROUT_GPIO_VIRTUAL_BASE (TROUT_GPIO_START + 0x30) +#define TROUT_GPIO_INT5_BASE (TROUT_GPIO_START + 0x48) + +#define TROUT_GPIO_CHARGER_EN (TROUT_GPIO_MISC2_BASE + 0) +#define TROUT_GPIO_ISET (TROUT_GPIO_MISC2_BASE + 1) +#define TROUT_GPIO_H2W_DAT_DIR (TROUT_GPIO_MISC2_BASE + 2) +#define TROUT_GPIO_H2W_CLK_DIR (TROUT_GPIO_MISC2_BASE + 3) +#define TROUT_GPIO_H2W_DAT_GPO (TROUT_GPIO_MISC2_BASE + 4) +#define TROUT_GPIO_H2W_CLK_GPO (TROUT_GPIO_MISC2_BASE + 5) +#define TROUT_GPIO_H2W_SEL0 (TROUT_GPIO_MISC2_BASE + 6) +#define TROUT_GPIO_H2W_SEL1 (TROUT_GPIO_MISC2_BASE + 7) + +#define TROUT_GPIO_SPOTLIGHT_EN (TROUT_GPIO_MISC3_BASE + 0) +#define TROUT_GPIO_FLASH_EN (TROUT_GPIO_MISC3_BASE + 1) +#define TROUT_GPIO_I2C_PULL (TROUT_GPIO_MISC3_BASE + 2) +#define TROUT_GPIO_TP_I2C_PULL (TROUT_GPIO_MISC3_BASE + 3) +#define TROUT_GPIO_TP_EN (TROUT_GPIO_MISC3_BASE + 4) +#define TROUT_GPIO_JOG_EN (TROUT_GPIO_MISC3_BASE + 5) +#define TROUT_GPIO_UI_LED_EN (TROUT_GPIO_MISC3_BASE + 6) +#define TROUT_GPIO_QTKEY_LED_EN (TROUT_GPIO_MISC3_BASE + 7) + +#define TROUT_GPIO_VCM_PWDN (TROUT_GPIO_MISC4_BASE + 0) +#define TROUT_GPIO_USB_H2W_SW (TROUT_GPIO_MISC4_BASE + 1) +#define TROUT_GPIO_COMPASS_RST_N (TROUT_GPIO_MISC4_BASE + 2) +#define TROUT_GPIO_HAPTIC_EN_UP (TROUT_GPIO_MISC4_BASE + 3) +#define TROUT_GPIO_HAPTIC_EN_MAIN (TROUT_GPIO_MISC4_BASE + 4) +#define TROUT_GPIO_USB_PHY_RST_N (TROUT_GPIO_MISC4_BASE + 5) +#define TROUT_GPIO_WIFI_PA_RESETX (TROUT_GPIO_MISC4_BASE + 6) +#define TROUT_GPIO_WIFI_EN (TROUT_GPIO_MISC4_BASE + 7) + +#define TROUT_GPIO_BT_32K_EN (TROUT_GPIO_MISC5_BASE + 0) +#define TROUT_GPIO_MAC_32K_EN (TROUT_GPIO_MISC5_BASE + 1) +#define TROUT_GPIO_MDDI_32K_EN (TROUT_GPIO_MISC5_BASE + 2) +#define TROUT_GPIO_COMPASS_32K_EN (TROUT_GPIO_MISC5_BASE + 3) + +#define TROUT_GPIO_NAVI_ACT_N (TROUT_GPIO_INT2_BASE + 0) +#define TROUT_GPIO_COMPASS_IRQ (TROUT_GPIO_INT2_BASE + 1) +#define TROUT_GPIO_SLIDING_DET (TROUT_GPIO_INT2_BASE + 2) +#define TROUT_GPIO_AUD_HSMIC_DET_N (TROUT_GPIO_INT2_BASE + 3) +#define TROUT_GPIO_SD_DOOR_N (TROUT_GPIO_INT2_BASE + 4) +#define TROUT_GPIO_CAM_BTN_STEP1_N (TROUT_GPIO_INT2_BASE + 5) +#define TROUT_GPIO_CAM_BTN_STEP2_N (TROUT_GPIO_INT2_BASE + 6) +#define TROUT_GPIO_TP_ATT_N (TROUT_GPIO_INT2_BASE + 7) +#define TROUT_GPIO_BANK0_FIRST_INT_SOURCE (TROUT_GPIO_NAVI_ACT_N) +#define TROUT_GPIO_BANK0_LAST_INT_SOURCE (TROUT_GPIO_TP_ATT_N) + +#define TROUT_GPIO_H2W_DAT_GPI (TROUT_GPIO_MISC1_BASE + 0) +#define TROUT_GPIO_H2W_CLK_GPI (TROUT_GPIO_MISC1_BASE + 1) +#define TROUT_GPIO_CPLD128_VER_0 (TROUT_GPIO_MISC1_BASE + 4) +#define TROUT_GPIO_CPLD128_VER_1 (TROUT_GPIO_MISC1_BASE + 5) +#define TROUT_GPIO_CPLD128_VER_2 (TROUT_GPIO_MISC1_BASE + 6) +#define TROUT_GPIO_CPLD128_VER_3 (TROUT_GPIO_MISC1_BASE + 7) + +#define TROUT_GPIO_SDMC_CD_N (TROUT_GPIO_VIRTUAL_BASE + 0) +#define TROUT_GPIO_END (TROUT_GPIO_SDMC_CD_N) +#define TROUT_GPIO_BANK1_FIRST_INT_SOURCE (TROUT_GPIO_SDMC_CD_N) +#define TROUT_GPIO_BANK1_LAST_INT_SOURCE (TROUT_GPIO_SDMC_CD_N) + +#define TROUT_GPIO_VIRTUAL_TO_REAL_OFFSET \ + (TROUT_GPIO_INT5_BASE - TROUT_GPIO_VIRTUAL_BASE) + +#define TROUT_INT_START (NR_MSM_IRQS + NR_GPIO_IRQS) +#define TROUT_INT_BANK0_COUNT (8) +#define TROUT_INT_BANK1_START (TROUT_INT_START + TROUT_INT_BANK0_COUNT) +#define TROUT_INT_BANK1_COUNT (1) +#define TROUT_INT_END (TROUT_INT_START + TROUT_INT_BANK0_COUNT + \ + TROUT_INT_BANK1_COUNT - 1) +#define TROUT_GPIO_TO_INT(n) (((n) <= TROUT_GPIO_BANK0_LAST_INT_SOURCE) ? \ + (TROUT_INT_START - TROUT_GPIO_BANK0_FIRST_INT_SOURCE + (n)) : \ + (TROUT_INT_BANK1_START - TROUT_GPIO_BANK1_FIRST_INT_SOURCE + (n))) + +#define TROUT_INT_TO_BANK(n) ((n - TROUT_INT_START) / TROUT_INT_BANK0_COUNT) +#define TROUT_INT_TO_MASK(n) (1U << ((n - TROUT_INT_START) & 7)) +#define TROUT_BANK_TO_MASK_REG(bank) \ + (bank ? TROUT_GPIO_INT_MASK1_REG : TROUT_GPIO_INT_MASK0_REG) +#define TROUT_BANK_TO_STAT_REG(bank) \ + (bank ? TROUT_GPIO_INT_STAT1_REG : TROUT_GPIO_INT_STAT0_REG) + +#endif /* GUARD */ diff --git a/arch/arm/mach-msm/clock-7x01a.c b/arch/arm/mach-msm/clock-7x01a.c deleted file mode 100644 index 62230a3428ee..000000000000 --- a/arch/arm/mach-msm/clock-7x01a.c +++ /dev/null @@ -1,126 +0,0 @@ -/* arch/arm/mach-msm/clock-7x01a.c - * - * Clock tables for MSM7X01A - * - * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/kernel.h> -#include <linux/platform_device.h> - -#include "clock.h" -#include "devices.h" - -/* clock IDs used by the modem processor */ - -#define ACPU_CLK 0 /* Applications processor clock */ -#define ADM_CLK 1 /* Applications data mover clock */ -#define ADSP_CLK 2 /* ADSP clock */ -#define EBI1_CLK 3 /* External bus interface 1 clock */ -#define EBI2_CLK 4 /* External bus interface 2 clock */ -#define ECODEC_CLK 5 /* External CODEC clock */ -#define EMDH_CLK 6 /* External MDDI host clock */ -#define GP_CLK 7 /* General purpose clock */ -#define GRP_CLK 8 /* Graphics clock */ -#define I2C_CLK 9 /* I2C clock */ -#define ICODEC_RX_CLK 10 /* Internal CODEX RX clock */ -#define ICODEC_TX_CLK 11 /* Internal CODEX TX clock */ -#define IMEM_CLK 12 /* Internal graphics memory clock */ -#define MDC_CLK 13 /* MDDI client clock */ -#define MDP_CLK 14 /* Mobile display processor clock */ -#define PBUS_CLK 15 /* Peripheral bus clock */ -#define PCM_CLK 16 /* PCM clock */ -#define PMDH_CLK 17 /* Primary MDDI host clock */ -#define SDAC_CLK 18 /* Stereo DAC clock */ -#define SDC1_CLK 19 /* Secure Digital Card clocks */ -#define SDC1_PCLK 20 -#define SDC2_CLK 21 -#define SDC2_PCLK 22 -#define SDC3_CLK 23 -#define SDC3_PCLK 24 -#define SDC4_CLK 25 -#define SDC4_PCLK 26 -#define TSIF_CLK 27 /* Transport Stream Interface clocks */ -#define TSIF_REF_CLK 28 -#define TV_DAC_CLK 29 /* TV clocks */ -#define TV_ENC_CLK 30 -#define UART1_CLK 31 /* UART clocks */ -#define UART2_CLK 32 -#define UART3_CLK 33 -#define UART1DM_CLK 34 -#define UART2DM_CLK 35 -#define USB_HS_CLK 36 /* High speed USB core clock */ -#define USB_HS_PCLK 37 /* High speed USB pbus clock */ -#define USB_OTG_CLK 38 /* Full speed USB clock */ -#define VDC_CLK 39 /* Video controller clock */ -#define VFE_CLK 40 /* Camera / Video Front End clock */ -#define VFE_MDC_CLK 41 /* VFE MDDI client clock */ - -#define NR_CLKS 42 - -#define CLOCK(clk_name, clk_id, clk_dev, clk_flags) { \ - .name = clk_name, \ - .id = clk_id, \ - .flags = clk_flags, \ - .dev = clk_dev, \ - } - -#define OFF CLKFLAG_AUTO_OFF -#define MINMAX CLKFLAG_USE_MIN_MAX_TO_SET - -struct clk msm_clocks[] = { - CLOCK("adm_clk", ADM_CLK, NULL, 0), - CLOCK("adsp_clk", ADSP_CLK, NULL, 0), - CLOCK("ebi1_clk", EBI1_CLK, NULL, 0), - CLOCK("ebi2_clk", EBI2_CLK, NULL, 0), - CLOCK("ecodec_clk", ECODEC_CLK, NULL, 0), - CLOCK("emdh_clk", EMDH_CLK, NULL, OFF), - CLOCK("gp_clk", GP_CLK, NULL, 0), - CLOCK("grp_clk", GRP_CLK, NULL, OFF), - CLOCK("i2c_clk", I2C_CLK, &msm_device_i2c.dev, 0), - CLOCK("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), - CLOCK("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), - CLOCK("imem_clk", IMEM_CLK, NULL, OFF), - CLOCK("mdc_clk", MDC_CLK, NULL, 0), - CLOCK("mdp_clk", MDP_CLK, NULL, OFF), - CLOCK("pbus_clk", PBUS_CLK, NULL, 0), - CLOCK("pcm_clk", PCM_CLK, NULL, 0), - CLOCK("pmdh_clk", PMDH_CLK, NULL, OFF | MINMAX), - CLOCK("sdac_clk", SDAC_CLK, NULL, OFF), - CLOCK("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), - CLOCK("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), - CLOCK("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), - CLOCK("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), - CLOCK("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), - CLOCK("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), - CLOCK("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), - CLOCK("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), - CLOCK("tsif_clk", TSIF_CLK, NULL, 0), - CLOCK("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), - CLOCK("tv_dac_clk", TV_DAC_CLK, NULL, 0), - CLOCK("tv_enc_clk", TV_ENC_CLK, NULL, 0), - CLOCK("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), - CLOCK("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), - CLOCK("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), - CLOCK("uart1dm_clk", UART1DM_CLK, NULL, OFF), - CLOCK("uart2dm_clk", UART2DM_CLK, NULL, 0), - CLOCK("usb_hs_clk", USB_HS_CLK, &msm_device_hsusb.dev, OFF), - CLOCK("usb_hs_pclk", USB_HS_PCLK, &msm_device_hsusb.dev, OFF), - CLOCK("usb_otg_clk", USB_OTG_CLK, NULL, 0), - CLOCK("vdc_clk", VDC_CLK, NULL, OFF | MINMAX), - CLOCK("vfe_clk", VFE_CLK, NULL, OFF), - CLOCK("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), -}; - -unsigned msm_num_clocks = ARRAY_SIZE(msm_clocks); diff --git a/arch/arm/mach-msm/clock-7x30.c b/arch/arm/mach-msm/clock-7x30.c new file mode 100644 index 000000000000..d1bab48c85fa --- /dev/null +++ b/arch/arm/mach-msm/clock-7x30.c @@ -0,0 +1,957 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/ctype.h> +#include <linux/bitops.h> +#include <linux/io.h> +#include <linux/spinlock.h> +#include <mach/msm_iomap.h> +#include <mach/clk.h> + +#include "clock.h" +#include "clock-7x30.h" + +struct clk_freq_tbl { + uint32_t freq_hz; + uint32_t src; + uint32_t md_val; + uint32_t ns_val; + uint32_t mode; +}; + +struct clk_local { + uint32_t count; + uint32_t type; + uint32_t md_reg; + uint32_t ns_reg; + uint32_t freq_mask; + uint32_t br_en_mask; + uint32_t root_en_mask; + int parent; + uint32_t *children; + struct clk_freq_tbl *freq_tbl; + struct clk_freq_tbl *current_freq; +}; + + +enum { + SRC_PLL0 = 4, /* Modem PLL */ + SRC_PLL1 = 1, /* Global PLL */ + SRC_PLL3 = 3, /* Multimedia/Peripheral PLL or Backup PLL1 */ + SRC_PLL4 = 2, /* Display PLL */ + SRC_LPXO = 6, /* Low power XO. */ + SRC_MAX /* Used for sources that can't be turned on/off. */ +}; + +static uint32_t src_pll_tbl[] = { + [SRC_PLL0] = PLL_0, + [SRC_PLL1] = PLL_1, + [SRC_PLL3] = PLL_3, + [SRC_PLL4] = PLL_4, +}; + +#define B(x) BIT(x) +#define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) +#define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) + +#define MD8(m, n) (BVAL(15, 8, m) | BVAL(7, 0, ~(n))) +#define N8(msb, lsb, m, n) (BVAL(msb, lsb, ~(n-m))) +#define MD16(m, n) (BVAL(31, 16, m) | BVAL(15, 0, ~(n))) +#define N16(m, n) (BVAL(31, 16, ~(n-m))) +#define SPDIV(s, d) (BVAL(4, 3, d-1) | BVAL(2, 0, s)) +#define SDIV(s, d) (BVAL(6, 3, d-1) | BVAL(2, 0, s)) +#define F_MASK_BASIC (BM(6, 3)|BM(2, 0)) +#define F_MASK_MND16 (BM(31, 16)|BM(4, 3)|BM(2, 0)) +#define F_MASK_MND8(m, l) (BM(m, l)|BM(4, 3)|BM(2, 0)) + +#define F_RAW(f, s, m_v, n_v, mde) { \ + .freq_hz = f, \ + .src = s, \ + .md_val = m_v, \ + .ns_val = n_v, \ + .mode = mde, \ + } + +#define FREQ_END 0 +#define F_BASIC(f, s, div) F_RAW(f, s, 0, SDIV(s, div), 0) +#define F_MND16(f, s, div, m, n) \ + F_RAW(f, s, MD16(m, n), N16(m, n)|SPDIV(s, div), !!(n)) +#define F_MND8(f, nmsb, nlsb, s, div, m, n) \ + F_RAW(f, s, MD8(m, n), N8(nmsb, nlsb, m, n)|SPDIV(s, div), !!(n)) +#define F_END F_RAW(FREQ_END, SRC_MAX, 0, 0, 0) + +static struct clk_freq_tbl clk_tbl_tcxo[] = { + F_RAW(19200000, SRC_MAX, 0, 0, 0), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_uartdm[] = { + F_MND16( 3686400, SRC_PLL3, 3, 3, 200), + F_MND16( 7372800, SRC_PLL3, 3, 3, 100), + F_MND16(14745600, SRC_PLL3, 3, 3, 50), + F_MND16(46400000, SRC_PLL3, 3, 145, 768), + F_MND16(51200000, SRC_PLL3, 3, 5, 24), + F_MND16(58982400, SRC_PLL3, 3, 6, 25), + F_MND16(64000000, SRC_PLL1, 4, 1, 3), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mdh[] = { + F_BASIC( 73728000, SRC_PLL3, 10), + F_BASIC( 92160000, SRC_PLL3, 8), + F_BASIC(122880000, SRC_PLL3, 6), + F_BASIC(184320000, SRC_PLL3, 4), + F_BASIC(245760000, SRC_PLL3, 3), + F_BASIC(368640000, SRC_PLL3, 2), + F_BASIC(384000000, SRC_PLL1, 2), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_grp[] = { + F_BASIC( 24576000, SRC_LPXO, 1), + F_BASIC( 46000000, SRC_PLL3, 16), + F_BASIC( 49000000, SRC_PLL3, 15), + F_BASIC( 52000000, SRC_PLL3, 14), + F_BASIC( 56000000, SRC_PLL3, 13), + F_BASIC( 61440000, SRC_PLL3, 12), + F_BASIC( 67000000, SRC_PLL3, 11), + F_BASIC( 73000000, SRC_PLL3, 10), + F_BASIC( 81000000, SRC_PLL3, 9), + F_BASIC( 92000000, SRC_PLL3, 8), + F_BASIC(105000000, SRC_PLL3, 7), + F_BASIC(120000000, SRC_PLL3, 6), + F_BASIC(150000000, SRC_PLL3, 5), + F_BASIC(183000000, SRC_PLL3, 4), + F_BASIC(192000000, SRC_PLL1, 4), + F_BASIC(245760000, SRC_PLL3, 3), + /* Sync to AXI. Hence this "rate" is not fixed. */ + F_RAW(1, SRC_MAX, 0, B(14), 0), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_sdc1_3[] = { + F_MND8( 144000, 19, 12, SRC_LPXO, 1, 1, 171), + F_MND8( 400000, 19, 12, SRC_LPXO, 1, 2, 123), + F_MND8(16000000, 19, 12, SRC_PLL3, 3, 14, 215), + F_MND8(17000000, 19, 12, SRC_PLL3, 4, 19, 206), + F_MND8(20000000, 19, 12, SRC_PLL3, 4, 23, 212), + F_MND8(25000000, 19, 12, SRC_LPXO, 1, 0, 0), + F_MND8(50000000, 19, 12, SRC_PLL3, 3, 1, 5), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_sdc2_4[] = { + F_MND8( 144000, 20, 13, SRC_LPXO, 1, 1, 171), + F_MND8( 400000, 20, 13, SRC_LPXO, 1, 2, 123), + F_MND8(16000000, 20, 13, SRC_PLL3, 3, 14, 215), + F_MND8(17000000, 20, 13, SRC_PLL3, 4, 19, 206), + F_MND8(20000000, 20, 13, SRC_PLL3, 4, 23, 212), + F_MND8(25000000, 20, 13, SRC_LPXO, 1, 0, 0), + F_MND8(50000000, 20, 13, SRC_PLL3, 3, 1, 5), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mdp_core[] = { + F_BASIC( 46000000, SRC_PLL3, 16), + F_BASIC( 49000000, SRC_PLL3, 15), + F_BASIC( 52000000, SRC_PLL3, 14), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mdp_lcdc[] = { + F_MND16(25000000, SRC_LPXO, 1, 0, 0), + F_MND16(30000000, SRC_PLL3, 4, 1, 6), + F_MND16(40000000, SRC_PLL3, 2, 1, 9), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mdp_vsync[] = { + F_RAW(24576000, SRC_MAX, 0, 0, 0), /* Initialized to LPXO. */ + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mi2s_codec[] = { + F_MND16( 2048000, SRC_LPXO, 4, 1, 3), + F_MND16(12288000, SRC_LPXO, 2, 0, 0), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mi2s[] = { + F_MND16(12288000, SRC_LPXO, 2, 0, 0), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_midi[] = { + F_MND8(98304000, 19, 12, SRC_PLL3, 3, 2, 5), + F_END, +}; +static struct clk_freq_tbl clk_tbl_sdac[] = { + F_MND16( 256000, SRC_LPXO, 4, 1, 24), + F_MND16( 352800, SRC_LPXO, 1, 147, 10240), + F_MND16( 384000, SRC_LPXO, 4, 1, 16), + F_MND16( 512000, SRC_LPXO, 4, 1, 12), + F_MND16( 705600, SRC_LPXO, 1, 147, 5120), + F_MND16( 768000, SRC_LPXO, 4, 1, 8), + F_MND16(1024000, SRC_LPXO, 4, 1, 6), + F_MND16(1411200, SRC_LPXO, 1, 147, 2560), + F_MND16(1536000, SRC_LPXO, 4, 1, 4), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_tv[] = { + F_MND8(27000000, 23, 16, SRC_PLL4, 2, 2, 33), + F_MND8(74250000, 23, 16, SRC_PLL4, 2, 1, 6), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_usb[] = { + F_MND8(60000000, 23, 16, SRC_PLL1, 2, 5, 32), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_vfe_jpeg[] = { + F_MND16( 36000000, SRC_PLL3, 4, 1, 5), + F_MND16( 46000000, SRC_PLL3, 4, 1, 4), + F_MND16( 61440000, SRC_PLL3, 4, 1, 3), + F_MND16( 74000000, SRC_PLL3, 2, 1, 5), + F_MND16( 82000000, SRC_PLL3, 3, 1, 3), + F_MND16( 92000000, SRC_PLL3, 4, 1, 2), + F_MND16( 98000000, SRC_PLL3, 3, 2, 5), + F_MND16(105000000, SRC_PLL3, 2, 2, 7), + F_MND16(122880000, SRC_PLL3, 2, 1, 3), + F_MND16(148000000, SRC_PLL3, 2, 2, 5), + F_MND16(154000000, SRC_PLL1, 2, 2, 5), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_cam[] = { + F_MND16( 6000000, SRC_PLL1, 4, 1, 32), + F_MND16( 8000000, SRC_PLL1, 4, 1, 24), + F_MND16(12000000, SRC_PLL1, 4, 1, 16), + F_MND16(16000000, SRC_PLL1, 4, 1, 12), + F_MND16(19000000, SRC_PLL1, 4, 1, 10), + F_MND16(24000000, SRC_PLL1, 4, 1, 8), + F_MND16(32000000, SRC_PLL1, 4, 1, 6), + F_MND16(48000000, SRC_PLL1, 4, 1, 4), + F_MND16(64000000, SRC_PLL1, 4, 1, 3), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_vpe[] = { + F_MND8( 24576000, 22, 15, SRC_LPXO, 1, 0, 0), + F_MND8( 30720000, 22, 15, SRC_PLL3, 4, 1, 6), + F_MND8( 61440000, 22, 15, SRC_PLL3, 4, 1, 3), + F_MND8( 81920000, 22, 15, SRC_PLL3, 3, 1, 3), + F_MND8(122880000, 22, 15, SRC_PLL3, 3, 1, 2), + F_MND8(147000000, 22, 15, SRC_PLL3, 1, 1, 5), + F_MND8(153600000, 22, 15, SRC_PLL1, 1, 1, 5), + F_MND8(170667000, 22, 15, SRC_PLL1, 1, 2, 9), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_mfc[] = { + F_MND8( 24576000, 24, 17, SRC_LPXO, 1, 0, 0), + F_MND8( 30720000, 24, 17, SRC_PLL3, 4, 1, 6), + F_MND8( 61440000, 24, 17, SRC_PLL3, 4, 1, 3), + F_MND8( 81920000, 24, 17, SRC_PLL3, 3, 1, 3), + F_MND8(122880000, 24, 17, SRC_PLL3, 3, 1, 2), + F_MND8(147000000, 24, 17, SRC_PLL3, 1, 1, 5), + F_MND8(153600000, 24, 17, SRC_PLL1, 1, 1, 5), + F_MND8(170667000, 24, 17, SRC_PLL1, 1, 2, 9), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_spi[] = { + F_MND8(10000000, 19, 12, SRC_PLL3, 4, 7, 129), + F_MND8(26000000, 19, 12, SRC_PLL3, 4, 34, 241), + F_END, +}; + +static struct clk_freq_tbl clk_tbl_lpa_codec[] = { + F_RAW(1, SRC_MAX, 0, 0, 0), /* src = MI2S_CODEC_RX */ + F_RAW(2, SRC_MAX, 0, 1, 0), /* src = ECODEC_CIF */ + F_RAW(3, SRC_MAX, 0, 2, 0), /* src = MI2S */ + F_RAW(4, SRC_MAX, 0, 3, 0), /* src = SDAC */ + F_END, +}; + +static struct clk_freq_tbl dummy_freq = F_END; + +#define MND 1 /* Integer predivider and fractional MN:D divider. */ +#define BASIC 2 /* Integer divider. */ +#define NORATE 3 /* Just on/off. */ + +#define C(x) L_7X30_##x##_CLK + +#define CLK_LOCAL(id, t, md, ns, f_msk, br, root, tbl, par, chld_lst) \ + [C(id)] = { \ + .type = t, \ + .md_reg = md, \ + .ns_reg = ns, \ + .freq_mask = f_msk, \ + .br_en_mask = br, \ + .root_en_mask = root, \ + .parent = C(par), \ + .children = chld_lst, \ + .freq_tbl = tbl, \ + .current_freq = &dummy_freq, \ + } + +#define CLK_BASIC(id, ns, br, root, tbl, par) \ + CLK_LOCAL(id, BASIC, 0, ns, F_MASK_BASIC, br, root, tbl, \ + par, NULL) +#define CLK_MND8_P(id, ns, m, l, br, root, tbl, par, chld_lst) \ + CLK_LOCAL(id, MND, (ns-4), ns, F_MASK_MND8(m, l), br, root, \ + tbl, par, chld_lst) +#define CLK_MND8(id, ns, m, l, br, root, tbl, chld_lst) \ + CLK_MND8_P(id, ns, m, l, br, root, tbl, NONE, chld_lst) +#define CLK_MND16(id, ns, br, root, tbl, par, chld_lst) \ + CLK_LOCAL(id, MND, (ns-4), ns, F_MASK_MND16, br, root, tbl, \ + par, chld_lst) +#define CLK_1RATE(id, ns, br, root, tbl) \ + CLK_LOCAL(id, BASIC, 0, ns, 0, br, root, tbl, NONE, NULL) +#define CLK_SLAVE(id, ns, br, par) \ + CLK_LOCAL(id, NORATE, 0, ns, 0, br, 0, NULL, par, NULL) +#define CLK_NORATE(id, ns, br, root) \ + CLK_LOCAL(id, NORATE, 0, ns, 0, br, root, NULL, NONE, NULL) +#define CLK_GLBL(id, glbl, root) \ + CLK_LOCAL(id, NORATE, 0, glbl, 0, 0, root, NULL, NONE, NULL) +#define CLK_BRIDGE(id, glbl, root, par) \ + CLK_LOCAL(id, NORATE, 0, glbl, 0, 0, root, NULL, par, NULL) + +#define REG(off) (MSM_CLK_CTL_BASE + off) +#define MNCNTR_EN_MASK B(8) +#define MNCNTR_RST_MASK B(7) +#define MNCNTR_MODE_MASK BM(6, 5) +#define MNCNTR_MODE BVAL(6, 5, 0x2) /* Dual-edge mode. */ + +/* Register offsets used more than once. */ +#define USBH_MD 0x02BC +#define USBH_NS 0x02C0 +#define USBH2_NS 0x046C +#define USBH3_NS 0x0470 +#define CAM_VFE_NS 0x0044 +#define GLBL_CLK_ENA_SC 0x03BC +#define GLBL_CLK_ENA_2_SC 0x03C0 +#define SDAC_NS 0x009C +#define TV_NS 0x00CC +#define MI2S_RX_NS 0x0070 +#define MI2S_TX_NS 0x0078 +#define MI2S_NS 0x02E0 +#define LPA_NS 0x02E8 +#define MDC_NS 0x007C +#define MDP_VSYNC_REG 0x0460 +#define PLL_ENA_REG 0x0260 + +static uint32_t pll_count[NUM_PLL]; + +static uint32_t chld_grp_3d_src[] = {C(IMEM), C(GRP_3D), C(NONE)}; +static uint32_t chld_mdp_lcdc_p[] = {C(MDP_LCDC_PAD_P), C(NONE)}; +static uint32_t chld_mi2s_codec_rx[] = {C(MI2S_CODEC_RX_S), C(NONE)}; +static uint32_t chld_mi2s_codec_tx[] = {C(MI2S_CODEC_TX_S), C(NONE)}; +static uint32_t chld_mi2s[] = {C(MI2S_S), C(NONE)}; +static uint32_t chld_sdac_m[] = {C(SDAC_S), C(NONE)}; +static uint32_t chld_tv[] = {C(TV_DAC), C(TV_ENC), C(TSIF_REF), C(NONE)}; +static uint32_t chld_usb_src[] = { + C(USB_HS), C(USB_HS_CORE), + C(USB_HS2), C(USB_HS2_CORE), + C(USB_HS3), C(USB_HS3_CORE), + C(NONE), +}; +uint32_t chld_vfe[] = {C(VFE_MDC), C(VFE_CAMIF), C(NONE)}; + +static struct clk_local clk_local_tbl[] = { + CLK_NORATE(MDC, MDC_NS, B(9), B(11)), + CLK_NORATE(LPA_CORE, LPA_NS, B(5), 0), + + CLK_1RATE(I2C, 0x0068, B(9), B(11), clk_tbl_tcxo), + CLK_1RATE(I2C_2, 0x02D8, B(0), B(2), clk_tbl_tcxo), + CLK_1RATE(QUP_I2C, 0x04F0, B(0), B(2), clk_tbl_tcxo), + CLK_1RATE(UART1, 0x00E0, B(5), B(4), clk_tbl_tcxo), + CLK_1RATE(UART3, 0x0468, B(5), B(4), clk_tbl_tcxo), + + CLK_BASIC(EMDH, 0x0050, 0, B(11), clk_tbl_mdh, AXI_LI_ADSP_A), + CLK_BASIC(PMDH, 0x008C, 0, B(11), clk_tbl_mdh, AXI_LI_ADSP_A), + CLK_BASIC(MDP, 0x014C, B(9), B(11), clk_tbl_mdp_core, AXI_MDP), + + CLK_MND8_P(VPE, 0x015C, 22, 15, B(9), B(11), clk_tbl_vpe, + AXI_VPE, NULL), + /* Combining MFC and MFC_DIV2 clocks. */ + CLK_MND8_P(MFC, 0x0154, 24, 17, B(9)|B(15), B(11), clk_tbl_mfc, + AXI_MFC, NULL), + + CLK_MND8(SDC1, 0x00A4, 19, 12, B(9), B(11), clk_tbl_sdc1_3, NULL), + CLK_MND8(SDC2, 0x00AC, 20, 13, B(9), B(11), clk_tbl_sdc2_4, NULL), + CLK_MND8(SDC3, 0x00B4, 19, 12, B(9), B(11), clk_tbl_sdc1_3, NULL), + CLK_MND8(SDC4, 0x00BC, 20, 13, B(9), B(11), clk_tbl_sdc2_4, NULL), + CLK_MND8(SPI, 0x02C8, 19, 12, B(9), B(11), clk_tbl_spi, NULL), + CLK_MND8(MIDI, 0x02D0, 19, 12, B(9), B(11), clk_tbl_midi, NULL), + CLK_MND8_P(USB_HS_SRC, USBH_NS, 23, 16, 0, B(11), clk_tbl_usb, + AXI_LI_ADSP_A, chld_usb_src), + CLK_SLAVE(USB_HS, USBH_NS, B(9), USB_HS_SRC), + CLK_SLAVE(USB_HS_CORE, USBH_NS, B(13), USB_HS_SRC), + CLK_SLAVE(USB_HS2, USBH2_NS, B(9), USB_HS_SRC), + CLK_SLAVE(USB_HS2_CORE, USBH2_NS, B(4), USB_HS_SRC), + CLK_SLAVE(USB_HS3, USBH3_NS, B(9), USB_HS_SRC), + CLK_SLAVE(USB_HS3_CORE, USBH3_NS, B(4), USB_HS_SRC), + CLK_MND8(TV, TV_NS, 23, 16, 0, B(11), clk_tbl_tv, chld_tv), + CLK_SLAVE(TV_DAC, TV_NS, B(12), TV), + CLK_SLAVE(TV_ENC, TV_NS, B(9), TV), + /* Hacking root & branch into one param. */ + CLK_SLAVE(TSIF_REF, 0x00C4, B(9)|B(11), TV), + + CLK_MND16(UART1DM, 0x00D4, B(9), B(11), clk_tbl_uartdm, NONE, NULL), + CLK_MND16(UART2DM, 0x00DC, B(9), B(11), clk_tbl_uartdm, NONE, NULL), + CLK_MND16(JPEG, 0x0164, B(9), B(11), clk_tbl_vfe_jpeg, + AXI_LI_JPEG, NULL), + CLK_MND16(CAM, 0x0374, 0, B(9), clk_tbl_cam, NONE, NULL), + CLK_MND16(VFE, CAM_VFE_NS, B(9), B(13), clk_tbl_vfe_jpeg, + AXI_LI_VFE, chld_vfe), + CLK_SLAVE(VFE_MDC, CAM_VFE_NS, B(11), VFE), + CLK_SLAVE(VFE_CAMIF, CAM_VFE_NS, B(15), VFE), + + CLK_MND16(SDAC_M, SDAC_NS, B(12), B(11), clk_tbl_sdac, + NONE, chld_sdac_m), + CLK_SLAVE(SDAC_S, SDAC_NS, B(9), SDAC_M), + + CLK_MND16(MDP_LCDC_P, 0x0390, B(9), B(11), clk_tbl_mdp_lcdc, + NONE, chld_mdp_lcdc_p), + CLK_SLAVE(MDP_LCDC_PAD_P, 0x0390, B(12), MDP_LCDC_P), + CLK_1RATE(MDP_VSYNC, MDP_VSYNC_REG, B(0), 0, clk_tbl_mdp_vsync), + + CLK_MND16(MI2S_CODEC_RX_M, MI2S_RX_NS, B(12), B(11), + clk_tbl_mi2s_codec, NONE, chld_mi2s_codec_rx), + CLK_SLAVE(MI2S_CODEC_RX_S, MI2S_RX_NS, B(9), MI2S_CODEC_RX_M), + + CLK_MND16(MI2S_CODEC_TX_M, MI2S_TX_NS, B(12), B(11), + clk_tbl_mi2s_codec, NONE, chld_mi2s_codec_tx), + CLK_SLAVE(MI2S_CODEC_TX_S, MI2S_TX_NS, B(9), MI2S_CODEC_TX_M), + + CLK_MND16(MI2S_M, MI2S_NS, B(12), B(11), + clk_tbl_mi2s, NONE, chld_mi2s), + CLK_SLAVE(MI2S_S, MI2S_NS, B(9), MI2S_M), + + CLK_LOCAL(GRP_2D, BASIC, 0, 0x0034, F_MASK_BASIC | (7 << 12), + B(7), B(11), clk_tbl_grp, AXI_GRP_2D, NULL), + CLK_LOCAL(GRP_3D_SRC, BASIC, 0, 0x0084, F_MASK_BASIC | (7 << 12), + 0, B(11), clk_tbl_grp, AXI_LI_GRP, chld_grp_3d_src), + CLK_SLAVE(GRP_3D, 0x0084, B(7), GRP_3D_SRC), + CLK_SLAVE(IMEM, 0x0084, B(9), GRP_3D_SRC), + CLK_LOCAL(LPA_CODEC, BASIC, 0, LPA_NS, BM(1, 0), B(9), 0, + clk_tbl_lpa_codec, NONE, NULL), + + /* Peripheral bus clocks. */ + CLK_GLBL(ADM, GLBL_CLK_ENA_SC, B(5)), + CLK_GLBL(CAMIF_PAD_P, GLBL_CLK_ENA_SC, B(9)), + CLK_GLBL(EMDH_P, GLBL_CLK_ENA_2_SC, B(3)), + CLK_GLBL(GRP_2D_P, GLBL_CLK_ENA_SC, B(24)), + CLK_GLBL(GRP_3D_P, GLBL_CLK_ENA_2_SC, B(17)), + CLK_GLBL(JPEG_P, GLBL_CLK_ENA_2_SC, B(24)), + CLK_GLBL(LPA_P, GLBL_CLK_ENA_2_SC, B(7)), + CLK_GLBL(MDP_P, GLBL_CLK_ENA_2_SC, B(6)), + CLK_GLBL(MFC_P, GLBL_CLK_ENA_2_SC, B(26)), + CLK_GLBL(PMDH_P, GLBL_CLK_ENA_2_SC, B(4)), + CLK_GLBL(ROTATOR_IMEM, GLBL_CLK_ENA_2_SC, B(23)), + CLK_GLBL(ROTATOR_P, GLBL_CLK_ENA_2_SC, B(25)), + CLK_GLBL(SDC1_H, GLBL_CLK_ENA_SC, B(7)), + CLK_GLBL(SDC2_H, GLBL_CLK_ENA_SC, B(8)), + CLK_GLBL(SDC3_H, GLBL_CLK_ENA_SC, B(27)), + CLK_GLBL(SDC4_H, GLBL_CLK_ENA_SC, B(28)), + CLK_GLBL(SPI_P, GLBL_CLK_ENA_2_SC, B(10)), + CLK_GLBL(TSIF_P, GLBL_CLK_ENA_SC, B(18)), + CLK_GLBL(UART1DM_P, GLBL_CLK_ENA_SC, B(17)), + CLK_GLBL(UART2DM_P, GLBL_CLK_ENA_SC, B(26)), + CLK_GLBL(USB_HS2_P, GLBL_CLK_ENA_2_SC, B(8)), + CLK_GLBL(USB_HS3_P, GLBL_CLK_ENA_2_SC, B(9)), + CLK_GLBL(USB_HS_P, GLBL_CLK_ENA_SC, B(25)), + CLK_GLBL(VFE_P, GLBL_CLK_ENA_2_SC, B(27)), + + /* AXI bridge clocks. */ + CLK_BRIDGE(AXI_LI_APPS, GLBL_CLK_ENA_SC, B(2), NONE), + CLK_BRIDGE(AXI_LI_ADSP_A, GLBL_CLK_ENA_2_SC, B(14), AXI_LI_APPS), + CLK_BRIDGE(AXI_LI_JPEG, GLBL_CLK_ENA_2_SC, B(19), AXI_LI_APPS), + CLK_BRIDGE(AXI_LI_VFE, GLBL_CLK_ENA_SC, B(23), AXI_LI_APPS), + CLK_BRIDGE(AXI_MDP, GLBL_CLK_ENA_2_SC, B(29), AXI_LI_APPS), + + CLK_BRIDGE(AXI_IMEM, GLBL_CLK_ENA_2_SC, B(18), NONE), + + CLK_BRIDGE(AXI_LI_VG, GLBL_CLK_ENA_SC, B(3), NONE), + CLK_BRIDGE(AXI_GRP_2D, GLBL_CLK_ENA_SC, B(21), AXI_LI_VG), + CLK_BRIDGE(AXI_LI_GRP, GLBL_CLK_ENA_SC, B(22), AXI_LI_VG), + CLK_BRIDGE(AXI_MFC, GLBL_CLK_ENA_2_SC, B(20), AXI_LI_VG), + CLK_BRIDGE(AXI_ROTATOR, GLBL_CLK_ENA_2_SC, B(22), AXI_LI_VG), + CLK_BRIDGE(AXI_VPE, GLBL_CLK_ENA_2_SC, B(21), AXI_LI_VG), +}; + +static DEFINE_SPINLOCK(clock_reg_lock); +static DEFINE_SPINLOCK(pll_vote_lock); + +void pll_enable(uint32_t pll) +{ + uint32_t reg_val; + unsigned long flags; + + spin_lock_irqsave(&pll_vote_lock, flags); + if (!pll_count[pll]) { + reg_val = readl(REG(PLL_ENA_REG)); + reg_val |= (1 << pll); + writel(reg_val, REG(PLL_ENA_REG)); + } + pll_count[pll]++; + spin_unlock_irqrestore(&pll_vote_lock, flags); +} + +static void src_enable(uint32_t src) +{ + /* SRC_MAX is used as a placeholder for some freqencies that don't + * have any direct PLL dependency. */ + if (src == SRC_MAX || src == SRC_LPXO) + return; + + pll_enable(src_pll_tbl[src]); +} + +void pll_disable(uint32_t pll) +{ + uint32_t reg_val; + unsigned long flags; + + spin_lock_irqsave(&pll_vote_lock, flags); + if (pll_count[pll]) + pll_count[pll]--; + else + pr_warning("Reference count mismatch in PLL disable!\n"); + + if (pll_count[pll] == 0) { + reg_val = readl(REG(PLL_ENA_REG)); + reg_val &= ~(1 << pll); + writel(reg_val, REG(PLL_ENA_REG)); + } + spin_unlock_irqrestore(&pll_vote_lock, flags); +} + +static void src_disable(uint32_t src) +{ + /* SRC_MAX is used as a placeholder for some freqencies that don't + * have any direct PLL dependency. */ + if (src == SRC_MAX || src == SRC_LPXO) + return; + + pll_disable(src_pll_tbl[src]); + +} + +/* + * SoC specific register-based control of clocks. + */ +static int _soc_clk_enable(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + void *ns_reg = REG(t->ns_reg); + uint32_t reg_val = 0; + + reg_val = readl(ns_reg); + if (t->type == MND) { + /* mode can be either 0 or 1. So the R-value of the + * expression will evaluate to MNCNTR_EN_MASK or 0. This + * avoids the need for a "if(mode == 1)". A "&" will not work + * here. */ + reg_val |= (MNCNTR_EN_MASK * t->current_freq->mode); + writel(reg_val, ns_reg); + } + if (t->root_en_mask) { + reg_val |= t->root_en_mask; + writel(reg_val, ns_reg); + } + if (t->br_en_mask) { + reg_val |= t->br_en_mask; + writel(reg_val, ns_reg); + } + return 0; +} + +static void _soc_clk_disable(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + void *ns_reg = REG(t->ns_reg); + uint32_t reg_val = 0; + + reg_val = readl(ns_reg); + + if (t->br_en_mask) { + reg_val &= ~(t->br_en_mask); + writel(reg_val, ns_reg); + } + if (t->root_en_mask) { + reg_val &= ~(t->root_en_mask); + writel(reg_val, ns_reg); + } + if (t->type == MND) { + reg_val &= ~MNCNTR_EN_MASK; + writel(reg_val, ns_reg); + } +} + +static int soc_clk_enable_nolock(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + int ret = 0; + + if (!t->count) { + if (t->parent != C(NONE)) + soc_clk_enable_nolock(t->parent); + src_enable(t->current_freq->src); + ret = _soc_clk_enable(id); + } + t->count++; + + return ret; +} + +static void soc_clk_disable_nolock(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + + if (!t->count) { + pr_warning("Reference count mismatch in clock disable!\n"); + return; + } + if (t->count) + t->count--; + if (t->count == 0) { + _soc_clk_disable(id); + src_disable(t->current_freq->src); + if (t->parent != C(NONE)) + soc_clk_disable_nolock(t->parent); + } + + return; +} + +static int soc_clk_enable(unsigned id) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&clock_reg_lock, flags); + ret = soc_clk_enable_nolock(id); + spin_unlock_irqrestore(&clock_reg_lock, flags); + + return ret; +} + +static void soc_clk_disable(unsigned id) +{ + unsigned long flags; + + spin_lock_irqsave(&clock_reg_lock, flags); + soc_clk_disable_nolock(id); + spin_unlock_irqrestore(&clock_reg_lock, flags); + + return; +} + +static int soc_clk_reset(unsigned id, enum clk_reset_action action) +{ + return -EPERM; +} + +static int soc_clk_set_rate(unsigned id, unsigned rate) +{ + struct clk_local *t = &clk_local_tbl[id]; + struct clk_freq_tbl *cf = t->current_freq; + struct clk_freq_tbl *nf; + uint32_t *chld = t->children; + void *ns_reg = REG(t->ns_reg); + void *md_reg = REG(t->md_reg); + uint32_t reg_val = 0; + int i, ret = 0; + unsigned long flags; + + if (t->type != MND && t->type != BASIC) + return -EPERM; + + spin_lock_irqsave(&clock_reg_lock, flags); + + if (rate == cf->freq_hz) + goto release_lock; + + for (nf = t->freq_tbl; nf->freq_hz != FREQ_END; nf++) + if (nf->freq_hz == rate) + break; + + if (nf->freq_hz == FREQ_END) { + ret = -EINVAL; + goto release_lock; + } + + if (t->freq_mask == 0) { + t->current_freq = nf; + goto release_lock; + } + + /* Disable all branches before changing rate to prevent jitter. */ + for (i = 0; chld && chld[i] != C(NONE); i++) { + struct clk_local *ch = &clk_local_tbl[chld[i]]; + /* Don't bother turning off if it is already off. + * Checking ch->count is cheaper (cache) than reading and + * writing to a register (uncached/unbuffered). */ + if (ch->count) { + reg_val = readl(REG(ch->ns_reg)); + reg_val &= ~(ch->br_en_mask); + writel(reg_val, REG(ch->ns_reg)); + } + } + if (t->count) + _soc_clk_disable(id); + + /* Turn on PLL of the new freq. */ + src_enable(nf->src); + + /* Some clocks share the same register, so must be careful when + * assuming a register doesn't need to be re-read. */ + reg_val = readl(ns_reg); + if (t->type == MND) { + reg_val |= MNCNTR_RST_MASK; + writel(reg_val, ns_reg); + /* TODO: Currently writing 0's into reserved bits for 8-bit + * MND. Can be avoided by adding md_mask. */ + if (nf->mode) + writel(nf->md_val, md_reg); + reg_val &= ~MNCNTR_MODE_MASK; + reg_val |= (MNCNTR_MODE * nf->mode); + } + reg_val &= ~(t->freq_mask); + reg_val |= nf->ns_val; + writel(reg_val, ns_reg); + + if (t->type == MND) { + reg_val &= ~MNCNTR_RST_MASK; + writel(reg_val, ns_reg); + } + + /* Turn off PLL of the old freq. */ + src_disable(cf->src); + + /* Current freq must be updated before _soc_clk_enable() is called to + * make sure the MNCNTR_E bit is set correctly. */ + t->current_freq = nf; + + if (t->count) + _soc_clk_enable(id); + /* Enable only branches that were ON before. */ + for (i = 0; chld && chld[i] != C(NONE); i++) { + struct clk_local *ch = &clk_local_tbl[chld[i]]; + if (ch->count) { + reg_val = readl(REG(ch->ns_reg)); + reg_val |= ch->br_en_mask; + writel(reg_val, REG(ch->ns_reg)); + } + } + +release_lock: + spin_unlock_irqrestore(&clock_reg_lock, flags); + return ret; +} + +static int soc_clk_set_min_rate(unsigned id, unsigned rate) +{ + return -EPERM; +} + +static int soc_clk_set_max_rate(unsigned id, unsigned rate) +{ + return -EPERM; +} + +static int soc_clk_set_flags(unsigned id, unsigned flags) +{ + return -EPERM; +} + +static unsigned soc_clk_get_rate(unsigned id) +{ + struct clk_local *t = &clk_local_tbl[id]; + unsigned long flags; + unsigned ret = 0; + + spin_lock_irqsave(&clock_reg_lock, flags); + if (t->type == MND && t->type == BASIC) + ret = t->current_freq->freq_hz; + else { + /* Walk up the tree to see if any parent has a rate. */ + while (t->type == NORATE && t->parent != C(NONE)) + t = &clk_local_tbl[t->parent]; + if (t->type == MND || t->type == BASIC) + ret = t->current_freq->freq_hz; + } + spin_unlock_irqrestore(&clock_reg_lock, flags); + + /* Return 0 if the rate has never been set. Might not be correct, + * but it's good enough. */ + if (ret == FREQ_END) + ret = 0; + + return ret; +} + +static unsigned soc_clk_is_enabled(unsigned id) +{ + return !!(clk_local_tbl[id].count); +} + +static long soc_clk_round_rate(unsigned id, unsigned rate) +{ + struct clk_local *t = &clk_local_tbl[id]; + struct clk_freq_tbl *f; + + if (t->type != MND && t->type != BASIC) + return -EINVAL; + + for (f = t->freq_tbl; f->freq_hz != FREQ_END; f++) + if (f->freq_hz >= rate) + return f->freq_hz; + + return -EPERM; +} + +struct clk_ops clk_ops_7x30 = { + .enable = soc_clk_enable, + .disable = soc_clk_disable, + .reset = soc_clk_reset, + .set_rate = soc_clk_set_rate, + .set_min_rate = soc_clk_set_min_rate, + .set_max_rate = soc_clk_set_max_rate, + .set_flags = soc_clk_set_flags, + .get_rate = soc_clk_get_rate, + .is_enabled = soc_clk_is_enabled, + .round_rate = soc_clk_round_rate, +}; + +#if 0 +static struct reg_init { + void *reg; + uint32_t mask; + uint32_t val; +} ri_list[] __initdata = { + /* TODO: Remove next line from commercial code. */ + {REG(PLL_ENA_REG), 0x7F, 0x7F}, /* Turn on all PLLs. */ + + /* Enable UMDX_P clock. Known to causes issues, so never turn off. */ + {REG(GLBL_CLK_ENA_2_SC), B(2), B(2)}, + {REG(0x0050), 0x3 << 17, 0x3}, /* EMDH RX div = div-4. */ + {REG(0x008C), 0x3 << 17, 0x3}, /* PMDH RX div = div-4. */ + /* MI2S_CODEC_RX_S src = MI2S_CODEC_RX_M. */ + {REG(MI2S_RX_NS), B(14), 0x0}, + /* MI2S_CODEC_TX_S src = MI2S_CODEC_TX_M. */ + {REG(MI2S_TX_NS), B(14), 0x0}, + {REG(MI2S_NS), B(14), 0x0}, /* MI2S_S src = MI2S_M. */ + {REG(LPA_NS), B(4), B(4)}, /* LPA CORE src = LPA_CODEC. */ + {REG(0x02EC), 0xF, 0xD}, /* MI2S_CODEC_RX_S div = div-8. */ + {REG(0x02F0), 0xF, 0xD}, /* MI2S_CODEC_TX_S div = div-8. */ + {REG(0x02E4), 0xF, 0x3}, /* MI2S_S div = div-4. */ + {REG(MDC_NS), 0x3, 0x3}, /* MDC src = external MDH src. */ + {REG(SDAC_NS), 0x3 << 14, 0x0}, /* SDAC div = div-1. */ + /* Disable sources TCXO/5 & TCXO/6. UART1 src = TCXO*/ + {REG(0x00E0), 0x3 << 25 | 0x7, 0x0}, + {REG(0x0468), 0x7, 0x0}, /* UART3 src = TCXO. */ + {REG(MDP_VSYNC_REG), 0xC, 0x4}, /* MDP VSYNC src = LPXO. */ + + /* USBH core clocks src = USB_HS_SRC. */ + {REG(USBH_NS), B(15), B(15)}, + {REG(USBH2_NS), B(6), B(6)}, + {REG(USBH3_NS), B(6), B(6)}, +}; + +#define set_1rate(clk) \ + soc_clk_set_rate(C(clk), clk_local_tbl[C(clk)].freq_tbl->freq_hz) +static __init int soc_clk_init(void) +{ + int i; + uint32_t val; + + /* Disable all the child clocks of USB_HS_SRC. This needs to be done + * before the register init loop since it changes the source of the + * USB HS core clocks. */ + for (i = 0; chld_usb_src[i] != C(NONE); i++) + _soc_clk_disable(chld_usb_src[i]); + + soc_clk_set_rate(C(USB_HS_SRC), clk_tbl_usb[0].freq_hz); + + for (i = 0; i < ARRAY_SIZE(ri_list); i++) { + val = readl(ri_list[i].reg); + val &= ~ri_list[i].mask; + val |= ri_list[i].val; + writel(val, ri_list[i].reg); + } + + /* This is just to update the driver data structures. The actual + * register set up is taken care of in the register init loop. */ + set_1rate(I2C); + set_1rate(I2C_2); + set_1rate(QUP_I2C); + set_1rate(UART1); + set_1rate(UART3); + + return 0; +} + +arch_initcall(soc_clk_init); +#endif diff --git a/arch/arm/mach-msm/clock-7x30.h b/arch/arm/mach-msm/clock-7x30.h new file mode 100644 index 000000000000..7480ace1a986 --- /dev/null +++ b/arch/arm/mach-msm/clock-7x30.h @@ -0,0 +1,143 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_7X30_H +#define __ARCH_ARM_MACH_MSM_CLOCK_7X30_H + +enum { + L_7X30_NONE_CLK = -1, + L_7X30_ADM_CLK, + L_7X30_I2C_CLK, + L_7X30_I2C_2_CLK, + L_7X30_QUP_I2C_CLK, + L_7X30_UART1DM_CLK, + L_7X30_UART1DM_P_CLK, + L_7X30_UART2DM_CLK, + L_7X30_UART2DM_P_CLK, + L_7X30_EMDH_CLK, + L_7X30_EMDH_P_CLK, + L_7X30_PMDH_CLK, + L_7X30_PMDH_P_CLK, + L_7X30_GRP_2D_CLK, + L_7X30_GRP_2D_P_CLK, + L_7X30_GRP_3D_SRC_CLK, + L_7X30_GRP_3D_CLK, + L_7X30_GRP_3D_P_CLK, + L_7X30_IMEM_CLK, + L_7X30_SDC1_CLK, + L_7X30_SDC1_H_CLK, + L_7X30_SDC2_CLK, + L_7X30_SDC2_H_CLK, + L_7X30_SDC3_CLK, + L_7X30_SDC3_H_CLK, + L_7X30_SDC4_CLK, + L_7X30_SDC4_H_CLK, + L_7X30_MDP_CLK, + L_7X30_MDP_P_CLK, + L_7X30_MDP_LCDC_P_CLK, + L_7X30_MDP_LCDC_PAD_P_CLK, + L_7X30_MDP_VSYNC_CLK, + L_7X30_MI2S_CODEC_RX_M_CLK, + L_7X30_MI2S_CODEC_RX_S_CLK, + L_7X30_MI2S_CODEC_TX_M_CLK, + L_7X30_MI2S_CODEC_TX_S_CLK, + L_7X30_MI2S_M_CLK, + L_7X30_MI2S_S_CLK, + L_7X30_LPA_CODEC_CLK, + L_7X30_LPA_CORE_CLK, + L_7X30_LPA_P_CLK, + L_7X30_MIDI_CLK, + L_7X30_MDC_CLK, + L_7X30_ROTATOR_IMEM_CLK, + L_7X30_ROTATOR_P_CLK, + L_7X30_SDAC_M_CLK, + L_7X30_SDAC_S_CLK, + L_7X30_UART1_CLK, + L_7X30_UART3_CLK, + L_7X30_TV_CLK, + L_7X30_TV_DAC_CLK, + L_7X30_TV_ENC_CLK, + L_7X30_TSIF_REF_CLK, + L_7X30_TSIF_P_CLK, + L_7X30_USB_HS_SRC_CLK, + L_7X30_USB_HS_CLK, + L_7X30_USB_HS_CORE_CLK, + L_7X30_USB_HS_P_CLK, + L_7X30_USB_HS2_CLK, + L_7X30_USB_HS2_CORE_CLK, + L_7X30_USB_HS2_P_CLK, + L_7X30_USB_HS3_CLK, + L_7X30_USB_HS3_CORE_CLK, + L_7X30_USB_HS3_P_CLK, + L_7X30_VFE_CLK, + L_7X30_VFE_P_CLK, + L_7X30_VFE_MDC_CLK, + L_7X30_VFE_CAMIF_CLK, + L_7X30_CAMIF_PAD_P_CLK, + L_7X30_CAM_CLK, + L_7X30_JPEG_CLK, + L_7X30_JPEG_P_CLK, + L_7X30_VPE_CLK, + L_7X30_MFC_CLK, + L_7X30_MFC_P_CLK, + L_7X30_SPI_CLK, + L_7X30_SPI_P_CLK, + + L_7X30_AXI_LI_VG_CLK, + L_7X30_AXI_LI_GRP_CLK, + L_7X30_AXI_LI_JPEG_CLK, + L_7X30_AXI_GRP_2D_CLK, + L_7X30_AXI_MFC_CLK, + L_7X30_AXI_VPE_CLK, + L_7X30_AXI_LI_VFE_CLK, + L_7X30_AXI_LI_APPS_CLK, + L_7X30_AXI_MDP_CLK, + L_7X30_AXI_IMEM_CLK, + L_7X30_AXI_LI_ADSP_A_CLK, + L_7X30_AXI_ROTATOR_CLK, + + L_7X30_NR_CLKS +}; + +struct clk_ops; +extern struct clk_ops clk_ops_7x30; + +void pll_enable(uint32_t pll); +void pll_disable(uint32_t pll); + +#define CLK_7X30(clk_name, clk_id, clk_dev, clk_flags) { \ + .name = clk_name, \ + .id = L_7X30_##clk_id, \ + .ops = &clk_ops_7x30, \ + .flags = clk_flags, \ + .dev = clk_dev, \ + .dbg_name = #clk_id, \ + } + +#endif + diff --git a/arch/arm/mach-msm/clock-pcom.c b/arch/arm/mach-msm/clock-pcom.c new file mode 100644 index 000000000000..e4997254a279 --- /dev/null +++ b/arch/arm/mach-msm/clock-pcom.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/err.h> +#include <linux/ctype.h> +#include <linux/stddef.h> +#include <mach/clk.h> + +#include "proc_comm.h" +#include "clock.h" + +/* + * glue for the proc_comm interface + */ +int pc_clk_enable(unsigned id) +{ + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +void pc_clk_disable(unsigned id) +{ + msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); +} + +int pc_clk_reset(unsigned id, enum clk_reset_action action) +{ + int rc; + + if (action == CLK_RESET_ASSERT) + rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_ASSERT, &id, NULL); + else + rc = msm_proc_comm(PCOM_CLKCTL_RPC_RESET_DEASSERT, &id, NULL); + + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +int pc_clk_set_rate(unsigned id, unsigned rate) +{ + /* The rate _might_ be rounded off to the nearest KHz value by the + * remote function. So a return value of 0 doesn't necessarily mean + * that the exact rate was set successfully. + */ + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +int pc_clk_set_min_rate(unsigned id, unsigned rate) +{ + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +int pc_clk_set_max_rate(unsigned id, unsigned rate) +{ + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate); + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +int pc_clk_set_flags(unsigned id, unsigned flags) +{ + int rc = msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags); + if (rc < 0) + return rc; + else + return (int)id < 0 ? -EINVAL : 0; +} + +unsigned pc_clk_get_rate(unsigned id) +{ + if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) + return 0; + else + return id; +} + +unsigned pc_clk_is_enabled(unsigned id) +{ + if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) + return 0; + else + return id; +} + +long pc_clk_round_rate(unsigned id, unsigned rate) +{ + + /* Not supported. */ + return -EPERM; +} + +struct clk_ops clk_ops_pcom = { + .enable = pc_clk_enable, + .disable = pc_clk_disable, + .reset = pc_clk_reset, + .set_rate = pc_clk_set_rate, + .set_min_rate = pc_clk_set_min_rate, + .set_max_rate = pc_clk_set_max_rate, + .set_flags = pc_clk_set_flags, + .get_rate = pc_clk_get_rate, + .is_enabled = pc_clk_is_enabled, + .round_rate = pc_clk_round_rate, +}; diff --git a/arch/arm/mach-msm/clock-pcom.h b/arch/arm/mach-msm/clock-pcom.h new file mode 100644 index 000000000000..39a2976ae56b --- /dev/null +++ b/arch/arm/mach-msm/clock-pcom.h @@ -0,0 +1,147 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_CLOCK_PCOM_H +#define __ARCH_ARM_MACH_MSM_CLOCK_PCOM_H + +/* clock IDs used by the modem processor */ + +#define P_ACPU_CLK 0 /* Applications processor clock */ +#define P_ADM_CLK 1 /* Applications data mover clock */ +#define P_ADSP_CLK 2 /* ADSP clock */ +#define P_EBI1_CLK 3 /* External bus interface 1 clock */ +#define P_EBI2_CLK 4 /* External bus interface 2 clock */ +#define P_ECODEC_CLK 5 /* External CODEC clock */ +#define P_EMDH_CLK 6 /* External MDDI host clock */ +#define P_GP_CLK 7 /* General purpose clock */ +#define P_GRP_CLK 8 /* Graphics clock */ +#define P_I2C_CLK 9 /* I2C clock */ +#define P_ICODEC_RX_CLK 10 /* Internal CODEX RX clock */ +#define P_ICODEC_TX_CLK 11 /* Internal CODEX TX clock */ +#define P_IMEM_CLK 12 /* Internal graphics memory clock */ +#define P_MDC_CLK 13 /* MDDI client clock */ +#define P_MDP_CLK 14 /* Mobile display processor clock */ +#define P_PBUS_CLK 15 /* Peripheral bus clock */ +#define P_PCM_CLK 16 /* PCM clock */ +#define P_PMDH_CLK 17 /* Primary MDDI host clock */ +#define P_SDAC_CLK 18 /* Stereo DAC clock */ +#define P_SDC1_CLK 19 /* Secure Digital Card clocks */ +#define P_SDC1_PCLK 20 +#define P_SDC2_CLK 21 +#define P_SDC2_PCLK 22 +#define P_SDC3_CLK 23 +#define P_SDC3_PCLK 24 +#define P_SDC4_CLK 25 +#define P_SDC4_PCLK 26 +#define P_TSIF_CLK 27 /* Transport Stream Interface clocks */ +#define P_TSIF_REF_CLK 28 +#define P_TV_DAC_CLK 29 /* TV clocks */ +#define P_TV_ENC_CLK 30 +#define P_UART1_CLK 31 /* UART clocks */ +#define P_UART2_CLK 32 +#define P_UART3_CLK 33 +#define P_UART1DM_CLK 34 +#define P_UART2DM_CLK 35 +#define P_USB_HS_CLK 36 /* High speed USB core clock */ +#define P_USB_HS_PCLK 37 /* High speed USB pbus clock */ +#define P_USB_OTG_CLK 38 /* Full speed USB clock */ +#define P_VDC_CLK 39 /* Video controller clock */ +#if CONFIG_MSM_AMSS_VERSION >= 6350 +#define P_VFE_MDC_CLK 40 /* Camera / Video Front End clock */ +#define P_VFE_CLK 41 /* VFE MDDI client clock */ +#else/* For radio code base others */ +#define P_VFE_MDC_CLK 41 /* VFE MDDI client clock */ +#define P_VFE_CLK 40 /* Camera / Video Front End clock */ +#endif + +#define P_MDP_LCDC_PCLK_CLK 42 +#define P_MDP_LCDC_PAD_PCLK_CLK 43 +#define P_MDP_VSYNC_CLK 44 +#define P_SPI_CLK 45 +#define P_VFE_AXI_CLK 46 +#define P_USB_HS2_CLK 47 /* High speed USB 2 core clock */ +#define P_USB_HS2_PCLK 48 /* High speed USB 2 pbus clock */ +#define P_USB_HS3_CLK 49 /* High speed USB 3 core clock */ +#define P_USB_HS3_PCLK 50 /* High speed USB 3 pbus clock */ +#define P_GRP_PCLK 51 /* Graphics pbus clock */ +#define P_USB_PHY_CLK 52 /* USB PHY clock */ +#define P_USB_HS_CORE_CLK 53 /* High speed USB 1 core clock */ +#define P_USB_HS2_CORE_CLK 54 /* High speed USB 2 core clock */ +#define P_USB_HS3_CORE_CLK 55 /* High speed USB 3 core clock */ +#define P_CAM_MCLK_CLK 56 +#define P_CAMIF_PAD_PCLK 57 +#define P_GRP_2D_CLK 58 +#define P_GRP_2D_PCLK 59 +#define P_I2S_CLK 60 +#define P_JPEG_CLK 61 +#define P_JPEG_PCLK 62 +#define P_LPA_CODEC_CLK 63 +#define P_LPA_CORE_CLK 64 +#define P_LPA_PCLK 65 +#define P_MDC_IO_CLK 66 +#define P_MDC_PCLK 67 +#define P_MFC_CLK 68 +#define P_MFC_DIV2_CLK 69 +#define P_MFC_PCLK 70 +#define P_QUP_I2C_CLK 71 +#define P_ROTATOR_IMEM_CLK 72 +#define P_ROTATOR_PCLK 73 +#define P_VFE_CAMIF_CLK 74 +#define P_VFE_PCLK 75 +#define P_VPE_CLK 76 +#define P_I2C_2_CLK 77 +#define P_MI2S_CODEC_RX_SCLK 78 +#define P_MI2S_CODEC_RX_MCLK 79 +#define P_MI2S_CODEC_TX_SCLK 80 +#define P_MI2S_CODEC_TX_MCLK 81 +#define P_PMDH_PCLK 82 +#define P_EMDH_PCLK 83 +#define P_SPI_PCLK 84 +#define P_TSIF_PCLK 85 +#define P_MDP_PCLK 86 +#define P_SDAC_MCLK 87 +#define P_MI2S_HDMI_CLK 88 +#define P_MI2S_HDMI_MCLK 89 +#define P_AXI_ROTATOR_CLK 90 +#define P_HDMI_CLK 91 + +#define P_NR_CLKS 92 + +struct clk_ops; +extern struct clk_ops clk_ops_pcom; + +#define CLK_PCOM(clk_name, clk_id, clk_dev, clk_flags) { \ + .name = clk_name, \ + .id = P_##clk_id, \ + .ops = &clk_ops_pcom, \ + .flags = clk_flags, \ + .dev = clk_dev, \ + .dbg_name = #clk_id, \ + } + +#endif diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c index 3b1ce36f1032..c7622f9c2169 100644 --- a/arch/arm/mach-msm/clock.c +++ b/arch/arm/mach-msm/clock.c @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/clock.c * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,68 +22,28 @@ #include <linux/err.h> #include <linux/clk.h> #include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/ctype.h> +#include <linux/pm_qos_params.h> +#include <mach/clk.h> #include "clock.h" #include "proc_comm.h" static DEFINE_MUTEX(clocks_mutex); static DEFINE_SPINLOCK(clocks_lock); +static DEFINE_SPINLOCK(ebi1_vote_lock); static LIST_HEAD(clocks); +struct clk *msm_clocks; +unsigned msm_num_clocks; /* - * glue for the proc_comm interface + * Bitmap of enabled clocks, excluding ACPU which is always + * enabled */ -static inline int pc_clk_enable(unsigned id) -{ - return msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL); -} - -static inline void pc_clk_disable(unsigned id) -{ - msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); -} - -static inline int pc_clk_set_rate(unsigned id, unsigned rate) -{ - return msm_proc_comm(PCOM_CLKCTL_RPC_SET_RATE, &id, &rate); -} - -static inline int pc_clk_set_min_rate(unsigned id, unsigned rate) -{ - return msm_proc_comm(PCOM_CLKCTL_RPC_MIN_RATE, &id, &rate); -} - -static inline int pc_clk_set_max_rate(unsigned id, unsigned rate) -{ - return msm_proc_comm(PCOM_CLKCTL_RPC_MAX_RATE, &id, &rate); -} - -static inline int pc_clk_set_flags(unsigned id, unsigned flags) -{ - return msm_proc_comm(PCOM_CLKCTL_RPC_SET_FLAGS, &id, &flags); -} - -static inline unsigned pc_clk_get_rate(unsigned id) -{ - if (msm_proc_comm(PCOM_CLKCTL_RPC_RATE, &id, NULL)) - return 0; - else - return id; -} - -static inline unsigned pc_clk_is_enabled(unsigned id) -{ - if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLED, &id, NULL)) - return 0; - else - return id; -} - -static inline int pc_pll_request(unsigned id, unsigned on) -{ - on = !!on; - return msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on); -} +static DECLARE_BITMAP(clock_map_enabled, NR_CLKS); +static DEFINE_SPINLOCK(clock_map_lock); +static struct notifier_block axi_freq_notifier_block; /* * Standard clock functions defined in include/linux/clk.h @@ -119,8 +79,12 @@ int clk_enable(struct clk *clk) unsigned long flags; spin_lock_irqsave(&clocks_lock, flags); clk->count++; - if (clk->count == 1) - pc_clk_enable(clk->id); + if (clk->count == 1) { + clk->ops->enable(clk->id); + spin_lock(&clock_map_lock); + clock_map_enabled[BIT_WORD(clk->id)] |= BIT_MASK(clk->id); + spin_unlock(&clock_map_lock); + } spin_unlock_irqrestore(&clocks_lock, flags); return 0; } @@ -132,31 +96,52 @@ void clk_disable(struct clk *clk) spin_lock_irqsave(&clocks_lock, flags); BUG_ON(clk->count == 0); clk->count--; - if (clk->count == 0) - pc_clk_disable(clk->id); + if (clk->count == 0) { + clk->ops->disable(clk->id); + spin_lock(&clock_map_lock); + clock_map_enabled[BIT_WORD(clk->id)] &= ~BIT_MASK(clk->id); + spin_unlock(&clock_map_lock); + } spin_unlock_irqrestore(&clocks_lock, flags); } EXPORT_SYMBOL(clk_disable); +int clk_reset(struct clk *clk, enum clk_reset_action action) +{ + return clk->ops->reset(clk->id, action); +} +EXPORT_SYMBOL(clk_reset); + unsigned long clk_get_rate(struct clk *clk) { - return pc_clk_get_rate(clk->id); + return clk->ops->get_rate(clk->id); } EXPORT_SYMBOL(clk_get_rate); int clk_set_rate(struct clk *clk, unsigned long rate) { - int ret; - if (clk->flags & CLKFLAG_USE_MIN_MAX_TO_SET) { - ret = pc_clk_set_max_rate(clk->id, rate); - if (ret) - return ret; - return pc_clk_set_min_rate(clk->id, rate); - } - return pc_clk_set_rate(clk->id, rate); + return clk->ops->set_rate(clk->id, rate); } EXPORT_SYMBOL(clk_set_rate); +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + return clk->ops->round_rate(clk->id, rate); +} +EXPORT_SYMBOL(clk_round_rate); + +int clk_set_min_rate(struct clk *clk, unsigned long rate) +{ + return clk->ops->set_min_rate(clk->id, rate); +} +EXPORT_SYMBOL(clk_set_min_rate); + +int clk_set_max_rate(struct clk *clk, unsigned long rate) +{ + return clk->ops->set_max_rate(clk->id, rate); +} +EXPORT_SYMBOL(clk_set_max_rate); + int clk_set_parent(struct clk *clk, struct clk *parent) { return -ENOSYS; @@ -173,22 +158,231 @@ int clk_set_flags(struct clk *clk, unsigned long flags) { if (clk == NULL || IS_ERR(clk)) return -EINVAL; - return pc_clk_set_flags(clk->id, flags); + return clk->ops->set_flags(clk->id, flags); } EXPORT_SYMBOL(clk_set_flags); +/* EBI1 is the only shared clock that several clients want to vote on as of + * this commit. If this changes in the future, then it might be better to + * make clk_min_rate handle the voting or make ebi1_clk_set_min_rate more + * generic to support different clocks. + */ +static unsigned long ebi1_min_rate[CLKVOTE_MAX]; +static struct clk *ebi1_clk; + +/* Rate is in Hz to be consistent with the other clk APIs. */ +int ebi1_clk_set_min_rate(enum clkvote_client client, unsigned long rate) +{ + static unsigned long last_set_val = -1; + unsigned long new_val; + unsigned long flags; + int ret = 0, i; + + spin_lock_irqsave(&ebi1_vote_lock, flags); + + ebi1_min_rate[client] = (rate == MSM_AXI_MAX_FREQ) ? + (clk_get_max_axi_khz() * 1000) : rate; + + new_val = ebi1_min_rate[0]; + for (i = 1; i < CLKVOTE_MAX; i++) + if (ebi1_min_rate[i] > new_val) + new_val = ebi1_min_rate[i]; + + /* This check is to save a proc_comm call. */ + if (last_set_val != new_val) { + ret = clk_set_min_rate(ebi1_clk, new_val); + if (ret < 0) { + pr_err("Setting EBI1 min rate to %lu Hz failed!\n", + new_val); + pr_err("Last successful value was %lu Hz.\n", + last_set_val); + } else { + last_set_val = new_val; + } + } -void __init msm_clock_init(void) + spin_unlock_irqrestore(&ebi1_vote_lock, flags); + + return ret; +} + +static int axi_freq_notifier_handler(struct notifier_block *block, + unsigned long min_freq, void *v) +{ + /* convert min_freq from KHz to Hz, unless it's a magic value */ + if (min_freq != MSM_AXI_MAX_FREQ) + min_freq *= 1000; + + return ebi1_clk_set_min_rate(CLKVOTE_PMQOS, min_freq); +} + +/* + * Find out whether any clock is enabled that needs the TCXO clock. + * + * On exit, the buffer 'reason' holds a bitmap of ids of all enabled + * clocks found that require TCXO. + * + * reason: buffer to hold the bitmap; must be compatible with + * linux/bitmap.h + * nbits: number of bits that the buffer can hold; 0 is ok + * + * Return value: + * 0: does not require the TCXO clock + * 1: requires the TCXO clock + */ +int msm_clock_require_tcxo(unsigned long *reason, int nbits) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&clock_map_lock, flags); + ret = !bitmap_empty(clock_map_enabled, NR_CLKS); + if (nbits > 0) + bitmap_copy(reason, clock_map_enabled, min(nbits, NR_CLKS)); + spin_unlock_irqrestore(&clock_map_lock, flags); + + return ret; +} + +/* + * Find the clock matching the given id and copy its name to the + * provided buffer. + * + * Return value: + * -ENODEV: there is no clock matching the given id + * 0: success + */ +int msm_clock_get_name(uint32_t id, char *name, uint32_t size) +{ + struct clk *c_clk; + int ret = -ENODEV; + + mutex_lock(&clocks_mutex); + list_for_each_entry(c_clk, &clocks, list) { + if (id == c_clk->id) { + strlcpy(name, c_clk->name, size); + ret = 0; + break; + } + } + mutex_unlock(&clocks_mutex); + + return ret; +} + +void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks) { unsigned n; spin_lock_init(&clocks_lock); mutex_lock(&clocks_mutex); + msm_clocks = clock_tbl; + msm_num_clocks = num_clocks; for (n = 0; n < msm_num_clocks; n++) list_add_tail(&msm_clocks[n].list, &clocks); mutex_unlock(&clocks_mutex); + + ebi1_clk = clk_get(NULL, "ebi1_clk"); + BUG_ON(ebi1_clk == NULL); + + axi_freq_notifier_block.notifier_call = axi_freq_notifier_handler; + pm_qos_add_notifier(PM_QOS_SYSTEM_BUS_FREQ, &axi_freq_notifier_block); + +} + +#if defined(CONFIG_DEBUG_FS) +static struct clk *msm_clock_get_nth(unsigned index) +{ + if (index < msm_num_clocks) + return msm_clocks + index; + else + return 0; } +static int clock_debug_rate_set(void *data, u64 val) +{ + struct clk *clock = data; + int ret; + + /* Only increases to max rate will succeed, but that's actually good + * for debugging purposes. So we don't check for error. */ + if (clock->flags & CLK_MAX) + clk_set_max_rate(clock, val); + if (clock->flags & CLK_MIN) + ret = clk_set_min_rate(clock, val); + else + ret = clk_set_rate(clock, val); + if (ret != 0) + printk(KERN_ERR "clk_set%s_rate failed (%d)\n", + (clock->flags & CLK_MIN) ? "_min" : "", ret); + return ret; +} + +static int clock_debug_rate_get(void *data, u64 *val) +{ + struct clk *clock = data; + *val = clk_get_rate(clock); + return 0; +} + +static int clock_debug_enable_set(void *data, u64 val) +{ + struct clk *clock = data; + int rc = 0; + + if (val) + rc = clock->ops->enable(clock->id); + else + clock->ops->disable(clock->id); + + return rc; +} + +static int clock_debug_enable_get(void *data, u64 *val) +{ + struct clk *clock = data; + + *val = clock->ops->is_enabled(clock->id); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get, + clock_debug_rate_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get, + clock_debug_enable_set, "%llu\n"); + +static int __init clock_debug_init(void) +{ + struct dentry *dent_rate; + struct dentry *dent_enable; + struct clk *clock; + unsigned n = 0; + char temp[50], *ptr; + + dent_rate = debugfs_create_dir("clk_rate", 0); + if (IS_ERR(dent_rate)) + return PTR_ERR(dent_rate); + + dent_enable = debugfs_create_dir("clk_enable", 0); + if (IS_ERR(dent_enable)) + return PTR_ERR(dent_enable); + + while ((clock = msm_clock_get_nth(n++)) != 0) { + strncpy(temp, clock->dbg_name, ARRAY_SIZE(temp)-1); + for (ptr = temp; *ptr; ptr++) + *ptr = tolower(*ptr); + debugfs_create_file(temp, 0644, dent_rate, + clock, &clock_rate_fops); + debugfs_create_file(temp, 0644, dent_enable, + clock, &clock_enable_fops); + } + return 0; +} + +device_initcall(clock_debug_init); +#endif + /* The bootloader and/or AMSS may have left various clocks enabled. * Disable any clocks that belong to us (CLKFLAG_AUTO_OFF) but have * not been explicitly enabled by a clk_enable() call. @@ -205,7 +399,7 @@ static int __init clock_late_init(void) spin_lock_irqsave(&clocks_lock, flags); if (!clk->count) { count++; - pc_clk_disable(clk->id); + clk->ops->disable(clk->id); } spin_unlock_irqrestore(&clocks_lock, flags); } diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h index f875e1544e5f..9608a6c6bbcb 100644 --- a/arch/arm/mach-msm/clock.h +++ b/arch/arm/mach-msm/clock.h @@ -1,7 +1,7 @@ /* arch/arm/mach-msm/clock.h * * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -18,6 +18,10 @@ #define __ARCH_ARM_MACH_MSM_CLOCK_H #include <linux/list.h> +#include <mach/clk.h> + +#include "clock-pcom.h" +#include "clock-7x30.h" #define CLKFLAG_INVERT 0x00000001 #define CLKFLAG_NOINVERT 0x00000002 @@ -25,14 +29,30 @@ #define CLKFLAG_NORESET 0x00000008 #define CLK_FIRST_AVAILABLE_FLAG 0x00000100 -#define CLKFLAG_USE_MIN_MAX_TO_SET 0x00000200 -#define CLKFLAG_AUTO_OFF 0x00000400 +#define CLKFLAG_AUTO_OFF 0x00000200 +#define CLKFLAG_MIN 0x00000400 +#define CLKFLAG_MAX 0x00000800 + +struct clk_ops { + int (*enable)(unsigned id); + void (*disable)(unsigned id); + int (*reset)(unsigned id, enum clk_reset_action action); + int (*set_rate)(unsigned id, unsigned rate); + int (*set_min_rate)(unsigned id, unsigned rate); + int (*set_max_rate)(unsigned id, unsigned rate); + int (*set_flags)(unsigned id, unsigned flags); + unsigned (*get_rate)(unsigned id); + unsigned (*is_enabled)(unsigned id); + long (*round_rate)(unsigned id, unsigned rate); +}; struct clk { uint32_t id; uint32_t count; uint32_t flags; const char *name; + struct clk_ops *ops; + const char *dbg_name; struct list_head list; struct device *dev; }; @@ -41,8 +61,47 @@ struct clk { #define A11S_CLK_SEL_ADDR (MSM_CSR_BASE + 0x104) #define A11S_VDD_SVS_PLEVEL_ADDR (MSM_CSR_BASE + 0x124) -extern struct clk msm_clocks[]; -extern unsigned msm_num_clocks; +#ifdef CONFIG_DEBUG_FS +#define CLOCK_DBG_NAME(x) .dbg_name = x, +#else +#define CLOCK_DBG_NAME(x) +#endif + +#define CLOCK(clk_name, clk_id, clk_dev, clk_flags) { \ + .name = clk_name, \ + .id = clk_id, \ + .flags = clk_flags, \ + .dev = clk_dev, \ + CLOCK_DBG_NAME(#clk_id) \ + } + +#define OFF CLKFLAG_AUTO_OFF +#define CLK_MIN CLKFLAG_MIN +#define CLK_MAX CLKFLAG_MAX +#define CLK_MINMAX (CLK_MIN | CLK_MAX) +#define NR_CLKS P_NR_CLKS + +enum { + PLL_0 = 0, + PLL_1, + PLL_2, + PLL_3, + PLL_4, + PLL_5, + PLL_6, + NUM_PLL +}; + +enum clkvote_client { + CLKVOTE_ACPUCLK = 0, + CLKVOTE_PMQOS, + CLKVOTE_MAX, +}; + +int msm_clock_require_tcxo(unsigned long *reason, int nbits); +int msm_clock_get_name(uint32_t id, char *name, uint32_t size); +int ebi1_clk_set_min_rate(enum clkvote_client client, unsigned long rate); +unsigned long clk_get_max_axi_khz(void); #endif diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c new file mode 100644 index 000000000000..8c20add08267 --- /dev/null +++ b/arch/arm/mach-msm/cpufreq.c @@ -0,0 +1,126 @@ +/* arch/arm/mach-msm/cpufreq.c + * + * MSM architecture cpufreq driver + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: Mike A. Chan <mikechan@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/init.h> +#include <linux/cpufreq.h> +#include "acpuclock.h" + +#define dprintk(msg...) \ + cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-msm", msg) + +#ifdef CONFIG_MSM_CPU_FREQ_SCREEN +static void msm_early_suspend(struct early_suspend *handler) +{ + acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_OFF * 1000, SETRATE_CPUFREQ); +} + +static void msm_late_resume(struct early_suspend *handler) +{ + acpuclk_set_rate(CONFIG_MSM_CPU_FREQ_SCREEN_ON * 1000, SETRATE_CPUFREQ); +} + +static struct early_suspend msm_power_suspend = { + .suspend = msm_early_suspend, + .resume = msm_late_resume, +}; + +static int __init clock_late_init(void) +{ + register_early_suspend(&msm_power_suspend); + return 0; +} + +late_initcall(clock_late_init); +#elif defined(CONFIG_CPU_FREQ_MSM) + +static int msm_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int index; + int ret = 0; + struct cpufreq_freqs freqs; + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(smp_processor_id()); + + if (cpufreq_frequency_table_target(policy, table, target_freq, relation, + &index)) { + pr_err("cpufreq: invalid target_freq: %d\n", target_freq); + return -EINVAL; + } + +#ifdef CONFIG_CPU_FREQ_DEBUG + dprintk("target %d r %d (%d-%d) selected %d\n", target_freq, + relation, policy->min, policy->max, table[index].frequency); +#endif + freqs.old = policy->cur; + freqs.new = table[index].frequency; + freqs.cpu = smp_processor_id(); + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + ret = acpuclk_set_rate(table[index].frequency * 1000, SETRATE_CPUFREQ); + if (!ret) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + return ret; +} + +static int msm_cpufreq_verify(struct cpufreq_policy *policy) +{ + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + return 0; +} + +static int __init msm_cpufreq_init(struct cpufreq_policy *policy) +{ + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(smp_processor_id()); + + policy->cur = acpuclk_get_rate(); + if (cpufreq_frequency_table_cpuinfo(policy, table)) { +#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX + policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_MIN; + policy->cpuinfo.max_freq = CONFIG_MSM_CPU_FREQ_MAX; +#endif + } +#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX + policy->min = CONFIG_MSM_CPU_FREQ_MIN; + policy->max = CONFIG_MSM_CPU_FREQ_MAX; +#endif + + policy->cpuinfo.transition_latency = + acpuclk_get_switch_time() * NSEC_PER_USEC; + return 0; +} + +static struct cpufreq_driver msm_cpufreq_driver = { + /* lps calculations are handled here. */ + .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS, + .init = msm_cpufreq_init, + .verify = msm_cpufreq_verify, + .target = msm_cpufreq_target, + .name = "msm", +}; + +static int __init msm_cpufreq_register(void) +{ + return cpufreq_register_driver(&msm_cpufreq_driver); +} + +late_initcall(msm_cpufreq_register); +#endif diff --git a/arch/arm/mach-msm/dal.c b/arch/arm/mach-msm/dal.c new file mode 100644 index 000000000000..cefe03c5eb24 --- /dev/null +++ b/arch/arm/mach-msm/dal.c @@ -0,0 +1,1363 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Device access library (DAL) implementation. + */ + +#include <linux/kernel.h> +#include <linux/completion.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/semaphore.h> + +#include <mach/dal.h> +#include <mach/msm_smd.h> + +#define DALRPC_PROTOCOL_VERSION 0x11 +#define DALRPC_SUCCESS 0 +#define DALRPC_MAX_PORTNAME_LEN 64 +#define DALRPC_MAX_ATTACH_PARAM_LEN 64 +#define DALRPC_MAX_SERVICE_NAME_LEN 32 +#define DALRPC_MAX_PARAMS 128 +#define DALRPC_MAX_PARAMS_SIZE (DALRPC_MAX_PARAMS * 4) +#define DALRPC_MAX_MSG_SIZE (sizeof(struct dalrpc_msg_hdr) + \ + DALRPC_MAX_PARAMS_SIZE) +#define DALRPC_MSGID_DDI 0x0 +#define DALRPC_MSGID_DDI_REPLY 0x80 +#define DALRPC_MSGID_ATTACH_REPLY 0x81 +#define DALRPC_MSGID_DETACH_REPLY 0x82 +#define DALRPC_MSGID_ASYNCH 0xC0 +#define ROUND_BUFLEN(x) (((x + 3) & ~0x3)) + +struct dalrpc_msg_hdr { + uint32_t len:16; + uint32_t proto_ver:8; + uint32_t prio:7; + uint32_t async:1; + uint32_t ddi_idx:16; + uint32_t proto_id:8; + uint32_t msgid:8; + void *from; + void *to; +}; + +struct dalrpc_msg { + struct dalrpc_msg_hdr hdr; + uint32_t param[DALRPC_MAX_PARAMS]; +}; + +struct dalrpc_event_handle { + struct list_head list; + + int flag; + spinlock_t lock; +}; + +struct dalrpc_cb_handle { + struct list_head list; + + void (*fn)(void *, uint32_t, void *, uint32_t); + void *context; +}; + +struct daldevice_handle {; + struct list_head list; + + void *remote_handle; + struct completion read_completion; + struct dalrpc_port *port; + struct dalrpc_msg msg; + struct mutex client_lock; +}; + +struct dalrpc_port { + struct list_head list; + + char port[DALRPC_MAX_PORTNAME_LEN+1]; + int refcount; + + struct workqueue_struct *wq; + struct work_struct port_work; + struct mutex write_lock; + + smd_channel_t *ch; + + struct dalrpc_msg msg_in; + struct daldevice_handle *msg_owner; + unsigned msg_bytes_read; + + struct list_head event_list; + struct mutex event_list_lock; + + struct list_head cb_list; + struct mutex cb_list_lock; +}; + +static LIST_HEAD(port_list); +static LIST_HEAD(client_list); +static DEFINE_MUTEX(pc_lists_lock); + +static DECLARE_WAIT_QUEUE_HEAD(event_wq); + +static int client_exists(void *handle) +{ + struct daldevice_handle *h; + + if (!handle) + return 0; + + mutex_lock(&pc_lists_lock); + + list_for_each_entry(h, &client_list, list) + if (h == handle) { + mutex_unlock(&pc_lists_lock); + return 1; + } + + mutex_unlock(&pc_lists_lock); + + return 0; +} + +static int client_exists_locked(void *handle) +{ + struct daldevice_handle *h; + + /* this function must be called with pc_lists_lock acquired */ + + if (!handle) + return 0; + + list_for_each_entry(h, &client_list, list) + if (h == handle) + return 1; + + return 0; +} + +static int port_exists(struct dalrpc_port *p) +{ + struct dalrpc_port *p_iter; + + /* this function must be called with pc_lists_lock acquired */ + + if (!p) + return 0; + + list_for_each_entry(p_iter, &port_list, list) + if (p_iter == p) + return 1; + + return 0; +} + +static struct dalrpc_port *port_name_exists(char *port) +{ + struct dalrpc_port *p; + + /* this function must be called with pc_lists_lock acquired */ + + list_for_each_entry(p, &port_list, list) + if (!strcmp(p->port, port)) + return p; + + return NULL; +} + +static void port_close(struct dalrpc_port *p) +{ + mutex_lock(&pc_lists_lock); + + p->refcount--; + if (p->refcount == 0) + list_del(&p->list); + + mutex_unlock(&pc_lists_lock); + + if (p->refcount == 0) { + destroy_workqueue(p->wq); + smd_close(p->ch); + kfree(p); + } +} + +static int event_exists(struct dalrpc_port *p, + struct dalrpc_event_handle *ev) +{ + struct dalrpc_event_handle *ev_iter; + + /* this function must be called with event_list_lock acquired */ + + list_for_each_entry(ev_iter, &p->event_list, list) + if (ev_iter == ev) + return 1; + + return 0; +} + +static int cb_exists(struct dalrpc_port *p, + struct dalrpc_cb_handle *cb) +{ + struct dalrpc_cb_handle *cb_iter; + + /* this function must be called with the cb_list_lock acquired */ + + list_for_each_entry(cb_iter, &p->cb_list, list) + if (cb_iter == cb) + return 1; + + return 0; +} + +static int check_version(struct dalrpc_msg_hdr *msg_hdr) +{ + static int version_msg = 1; + + /* disabled because asynch events currently have no version */ + return 0; + + if (msg_hdr->proto_ver != DALRPC_PROTOCOL_VERSION) { + if (version_msg) { + printk(KERN_ERR "dalrpc: incompatible verison\n"); + version_msg = 0; + } + return -1; + } + return 0; +} + +static void process_asynch(struct dalrpc_port *p) +{ + struct dalrpc_event_handle *ev; + struct dalrpc_cb_handle *cb; + + ev = (struct dalrpc_event_handle *)p->msg_in.param[0]; + cb = (struct dalrpc_cb_handle *)p->msg_in.param[0]; + + mutex_lock(&p->event_list_lock); + if (event_exists(p, ev)) { + spin_lock(&ev->lock); + ev->flag = 1; + spin_unlock(&ev->lock); + smp_mb(); + wake_up_all(&event_wq); + mutex_unlock(&p->event_list_lock); + return; + } + mutex_unlock(&p->event_list_lock); + + mutex_lock(&p->cb_list_lock); + if (cb_exists(p, cb)) { + cb->fn(cb->context, p->msg_in.param[1], + &p->msg_in.param[3], p->msg_in.param[2]); + mutex_unlock(&p->cb_list_lock); + return; + } + mutex_unlock(&p->cb_list_lock); +} + +static void process_msg(struct dalrpc_port *p) +{ + switch (p->msg_in.hdr.msgid) { + + case DALRPC_MSGID_DDI_REPLY: + case DALRPC_MSGID_ATTACH_REPLY: + case DALRPC_MSGID_DETACH_REPLY: + complete(&p->msg_owner->read_completion); + break; + + case DALRPC_MSGID_ASYNCH: + process_asynch(p); + break; + + default: + printk(KERN_ERR "process_msg: bad msgid %#x\n", + p->msg_in.hdr.msgid); + } +} + +static void flush_msg(struct dalrpc_port *p) +{ + int bytes_read, len; + + len = p->msg_in.hdr.len - sizeof(struct dalrpc_msg_hdr); + while (len > 0) { + bytes_read = smd_read(p->ch, NULL, len); + if (bytes_read <= 0) + break; + len -= bytes_read; + } + p->msg_bytes_read = 0; +} + +static int check_header(struct dalrpc_port *p) +{ + if (check_version(&p->msg_in.hdr) || + p->msg_in.hdr.len > DALRPC_MAX_MSG_SIZE || + (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH && + !client_exists_locked(p->msg_in.hdr.to))) { + printk(KERN_ERR "dalrpc_read_msg: bad msg\n"); + flush_msg(p); + return 1; + } + p->msg_owner = (struct daldevice_handle *)p->msg_in.hdr.to; + + if (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH) + memcpy(&p->msg_owner->msg.hdr, &p->msg_in.hdr, + sizeof(p->msg_in.hdr)); + + return 0; +} + +static int dalrpc_read_msg(struct dalrpc_port *p) +{ + uint8_t *read_ptr; + int bytes_read; + + /* read msg header */ + while (p->msg_bytes_read < sizeof(p->msg_in.hdr)) { + read_ptr = (uint8_t *)&p->msg_in.hdr + p->msg_bytes_read; + + bytes_read = smd_read(p->ch, read_ptr, + sizeof(p->msg_in.hdr) - + p->msg_bytes_read); + if (bytes_read <= 0) + return 0; + p->msg_bytes_read += bytes_read; + + if (p->msg_bytes_read == sizeof(p->msg_in.hdr) && + check_header(p)) + return 1; + } + + /* read remainder of msg */ + if (p->msg_in.hdr.msgid != DALRPC_MSGID_ASYNCH) + read_ptr = (uint8_t *)&p->msg_owner->msg; + else + read_ptr = (uint8_t *)&p->msg_in; + read_ptr += p->msg_bytes_read; + + while (p->msg_bytes_read < p->msg_in.hdr.len) { + bytes_read = smd_read(p->ch, read_ptr, + p->msg_in.hdr.len - p->msg_bytes_read); + if (bytes_read <= 0) + return 0; + p->msg_bytes_read += bytes_read; + read_ptr += bytes_read; + } + + process_msg(p); + p->msg_bytes_read = 0; + p->msg_owner = NULL; + + return 1; +} + +static void dalrpc_work(struct work_struct *work) +{ + struct dalrpc_port *p = container_of(work, + struct dalrpc_port, + port_work); + + /* must lock port/client lists to ensure port doesn't disappear + under an asynch event */ + mutex_lock(&pc_lists_lock); + if (port_exists(p)) + while (dalrpc_read_msg(p)) + ; + mutex_unlock(&pc_lists_lock); +} + +static void dalrpc_smd_cb(void *priv, unsigned smd_flags) +{ + struct dalrpc_port *p = priv; + + if (smd_flags != SMD_EVENT_DATA) + return; + + queue_work(p->wq, &p->port_work); +} + +static struct dalrpc_port *dalrpc_port_open(char *port, int cpu) +{ + struct dalrpc_port *p; + char wq_name[32]; + + p = port_name_exists(port); + if (p) { + p->refcount++; + return p; + } + + p = kzalloc(sizeof(struct dalrpc_port), GFP_KERNEL); + if (!p) + return NULL; + + strncpy(p->port, port, sizeof(p->port) - 1); + p->refcount = 1; + + snprintf(wq_name, sizeof(wq_name), "dalrpc_rcv_%s", port); + p->wq = create_singlethread_workqueue(wq_name); + if (!p->wq) { + printk(KERN_ERR "dalrpc_init: unable to create workqueue\n"); + goto no_wq; + } + INIT_WORK(&p->port_work, dalrpc_work); + + mutex_init(&p->write_lock); + mutex_init(&p->event_list_lock); + mutex_init(&p->cb_list_lock); + + INIT_LIST_HEAD(&p->event_list); + INIT_LIST_HEAD(&p->cb_list); + + p->msg_owner = NULL; + p->msg_bytes_read = 0; + + if (smd_named_open_on_edge(port, cpu, &p->ch, p, + dalrpc_smd_cb)) { + printk(KERN_ERR "dalrpc_port_init() failed to open port\n"); + goto no_smd; + } + + list_add(&p->list, &port_list); + + return p; + +no_smd: + destroy_workqueue(p->wq); +no_wq: + kfree(p); + return NULL; +} + +static void dalrpc_sendwait(struct daldevice_handle *h) +{ + u8 *buf = (u8 *)&h->msg; + int len = h->msg.hdr.len; + int written; + + mutex_lock(&h->port->write_lock); + do { + written = smd_write(h->port->ch, buf + (h->msg.hdr.len - len), + len); + if (written < 0) + break; + len -= written; + } while (len); + mutex_unlock(&h->port->write_lock); + + wait_for_completion(&h->read_completion); +} + +int daldevice_attach(uint32_t device_id, char *port, int cpu, + void **handle_ptr) +{ + struct daldevice_handle *h; + char dyn_port[DALRPC_MAX_PORTNAME_LEN + 1] = "DAL00"; + int ret; + int tries = 0; + + if (!port) + port = dyn_port; + + if (strlen(port) > DALRPC_MAX_PORTNAME_LEN) + return -EINVAL; + + h = kzalloc(sizeof(struct daldevice_handle), GFP_KERNEL); + if (!h) { + *handle_ptr = NULL; + return -ENOMEM; + } + + init_completion(&h->read_completion); + mutex_init(&h->client_lock); + + mutex_lock(&pc_lists_lock); + list_add(&h->list, &client_list); + mutex_unlock(&pc_lists_lock); + + /* 3 attempts, enough for one each on the user specified port, the + * dynamic discovery port, and the port recommended by the dynamic + * discovery port */ + while (tries < 3) { + tries++; + + mutex_lock(&pc_lists_lock); + h->port = dalrpc_port_open(port, cpu); + if (!h->port) { + list_del(&h->list); + mutex_unlock(&pc_lists_lock); + printk(KERN_ERR "daldevice_attach: could not " + "open port\n"); + kfree(h); + *handle_ptr = NULL; + return -EIO; + } + mutex_unlock(&pc_lists_lock); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4 + + DALRPC_MAX_ATTACH_PARAM_LEN + + DALRPC_MAX_SERVICE_NAME_LEN; + h->msg.hdr.proto_ver = DALRPC_PROTOCOL_VERSION; + h->msg.hdr.ddi_idx = 0; + h->msg.hdr.msgid = 0x1; + h->msg.hdr.prio = 0; + h->msg.hdr.async = 0; + h->msg.hdr.from = h; + h->msg.hdr.to = 0; + h->msg.param[0] = device_id; + + memset(&h->msg.param[1], 0, + DALRPC_MAX_ATTACH_PARAM_LEN + + DALRPC_MAX_SERVICE_NAME_LEN); + + dalrpc_sendwait(h); + ret = h->msg.param[0]; + + if (ret == DALRPC_SUCCESS) { + h->remote_handle = h->msg.hdr.from; + *handle_ptr = h; + break; + } else if (strnlen((char *)&h->msg.param[1], + DALRPC_MAX_PORTNAME_LEN)) { + /* another port was recommended in the response. */ + strncpy(dyn_port, (char *)&h->msg.param[1], + DALRPC_MAX_PORTNAME_LEN); + dyn_port[DALRPC_MAX_PORTNAME_LEN] = 0; + port = dyn_port; + } else if (port == dyn_port) { + /* the dynamic discovery port (or port that + * was recommended by it) did not recognize + * the device id, give up */ + daldevice_detach(h); + break; + } else + /* the user specified port did not work, try + * the dynamic discovery port */ + port = dyn_port; + + port_close(h->port); + } + + return ret; +} +EXPORT_SYMBOL(daldevice_attach); + +static void dalrpc_ddi_prologue(uint32_t ddi_idx, struct daldevice_handle *h) +{ + h->msg.hdr.proto_ver = DALRPC_PROTOCOL_VERSION; + h->msg.hdr.prio = 0; + h->msg.hdr.async = 0; + h->msg.hdr.msgid = DALRPC_MSGID_DDI; + h->msg.hdr.from = h; + h->msg.hdr.to = h->remote_handle; + h->msg.hdr.ddi_idx = ddi_idx; +} + +int daldevice_detach(void *handle) +{ + struct daldevice_handle *h = handle; + + if (!client_exists(h)) + return -EINVAL; + + dalrpc_ddi_prologue(0, h); + + if (!h->remote_handle) + goto norpc; + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.msgid = 0x2; + h->msg.param[0] = 0; + + dalrpc_sendwait(h); + +norpc: + mutex_lock(&pc_lists_lock); + list_del(&h->list); + mutex_unlock(&pc_lists_lock); + + port_close(h->port); + + kfree(h); + + return 0; +} +EXPORT_SYMBOL(daldevice_detach); + +uint32_t dalrpc_fcn_0(uint32_t ddi_idx, void *handle, uint32_t s1) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.proto_id = 0; + h->msg.param[0] = s1; + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_0); + +uint32_t dalrpc_fcn_1(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 1; + h->msg.param[0] = s1; + h->msg.param[1] = s2; + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_1); + +uint32_t dalrpc_fcn_2(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t *p_s2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.proto_id = 2; + h->msg.param[0] = s1; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) + *p_s2 = h->msg.param[1]; + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_2); + +uint32_t dalrpc_fcn_3(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t s3) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12; + h->msg.hdr.proto_id = 3; + h->msg.param[0] = s1; + h->msg.param[1] = s2; + h->msg.param[2] = s3; + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_3); + +uint32_t dalrpc_fcn_4(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t *p_s3) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 4; + h->msg.param[0] = s1; + h->msg.param[1] = s2; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) + *p_s3 = h->msg.param[1]; + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_4); + +uint32_t dalrpc_fcn_5(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((ilen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 5; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_5); + +uint32_t dalrpc_fcn_6(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 6; + h->msg.param[0] = s1; + h->msg.param[1] = ilen; + memcpy(&h->msg.param[2], ibuf, ilen); + + dalrpc_sendwait(h); + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_6); + +uint32_t dalrpc_fcn_7(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + uint32_t *oalen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 7; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + *oalen = h->msg.param[1]; + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_7); + +uint32_t dalrpc_fcn_8(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 8) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 8; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_8); + +uint32_t dalrpc_fcn_9(uint32_t ddi_idx, void *handle, void *obuf, + uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 4; + h->msg.hdr.proto_id = 9; + h->msg.param[0] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_9); + +uint32_t dalrpc_fcn_10(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen, void *obuf, + uint32_t olen, uint32_t *oalen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 12) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 10; + h->msg.param[0] = s1; + h->msg.param[1] = ilen; + memcpy(&h->msg.param[2], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 2; + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + *oalen = h->msg.param[1]; + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_10); + +uint32_t dalrpc_fcn_11(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 11; + h->msg.param[0] = s1; + h->msg.param[1] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_11); + +uint32_t dalrpc_fcn_12(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen, uint32_t *oalen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + + if ((olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 8; + h->msg.hdr.proto_id = 12; + h->msg.param[0] = s1; + h->msg.param[1] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + *oalen = h->msg.param[1]; + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_12); + +uint32_t dalrpc_fcn_13(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + ilen2 + 12) > DALRPC_MAX_PARAMS_SIZE || + (olen + 4) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + + ROUND_BUFLEN(ilen) + ROUND_BUFLEN(ilen2); + h->msg.hdr.proto_id = 13; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx++] = ilen2; + memcpy(&h->msg.param[param_idx], ibuf2, ilen2); + param_idx += (ROUND_BUFLEN(ilen2) / 4); + h->msg.param[param_idx] = olen; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_13); + +uint32_t dalrpc_fcn_14(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + void *obuf2, uint32_t olen2, uint32_t *oalen2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + 12) > DALRPC_MAX_PARAMS_SIZE || + (olen + olen2 + 8) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 12 + + ROUND_BUFLEN(ilen); + h->msg.hdr.proto_id = 14; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx++] = olen; + h->msg.param[param_idx] = olen2; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + param_idx = (ROUND_BUFLEN(h->msg.param[1]) / 4) + 2; + if (h->msg.param[param_idx] > olen2) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + memcpy(obuf2, &h->msg.param[param_idx + 1], + h->msg.param[param_idx]); + *oalen2 = h->msg.param[param_idx]; + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_14); + +uint32_t dalrpc_fcn_15(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen, uint32_t *oalen, + void *obuf2, uint32_t olen2) +{ + struct daldevice_handle *h = handle; + uint32_t ret; + int param_idx; + + if ((ilen + ilen2 + 16) > DALRPC_MAX_PARAMS_SIZE || + (olen + olen2 + 8) > DALRPC_MAX_PARAMS_SIZE) + return -EINVAL; + + if (!client_exists(h)) + return -EINVAL; + + mutex_lock(&h->client_lock); + + dalrpc_ddi_prologue(ddi_idx, h); + + h->msg.hdr.len = sizeof(struct dalrpc_msg_hdr) + 16 + + ROUND_BUFLEN(ilen) + ROUND_BUFLEN(ilen2); + h->msg.hdr.proto_id = 15; + h->msg.param[0] = ilen; + memcpy(&h->msg.param[1], ibuf, ilen); + param_idx = (ROUND_BUFLEN(ilen) / 4) + 1; + h->msg.param[param_idx++] = ilen2; + memcpy(&h->msg.param[param_idx], ibuf2, ilen2); + param_idx += (ROUND_BUFLEN(ilen2) / 4); + h->msg.param[param_idx++] = olen; + h->msg.param[param_idx] = olen2; + + dalrpc_sendwait(h); + + if (h->msg.param[0] == DALRPC_SUCCESS) { + if (h->msg.param[1] > olen) { + mutex_unlock(&h->client_lock); + return -EIO; + } + param_idx = (ROUND_BUFLEN(h->msg.param[1]) / 4) + 2; + if (h->msg.param[param_idx] > olen2) { + mutex_unlock(&h->client_lock); + return -EIO; + } + memcpy(obuf, &h->msg.param[2], h->msg.param[1]); + memcpy(obuf2, &h->msg.param[param_idx + 1], + h->msg.param[param_idx]); + *oalen = h->msg.param[1]; + } + + ret = h->msg.param[0]; + mutex_unlock(&h->client_lock); + return ret; +} +EXPORT_SYMBOL(dalrpc_fcn_15); + +void *dalrpc_alloc_event(void *handle) +{ + struct daldevice_handle *h; + struct dalrpc_event_handle *ev; + + h = (struct daldevice_handle *)handle; + + if (!client_exists(h)) + return NULL; + + ev = kmalloc(sizeof(struct dalrpc_event_handle), GFP_KERNEL); + if (!ev) + return NULL; + + ev->flag = 0; + spin_lock_init(&ev->lock); + + mutex_lock(&h->port->event_list_lock); + list_add(&ev->list, &h->port->event_list); + mutex_unlock(&h->port->event_list_lock); + + return ev; +} +EXPORT_SYMBOL(dalrpc_alloc_event); + +void *dalrpc_alloc_cb(void *handle, + void (*fn)(void *, uint32_t, void *, uint32_t), + void *context) +{ + struct daldevice_handle *h; + struct dalrpc_cb_handle *cb; + + h = (struct daldevice_handle *)handle; + + if (!client_exists(h)) + return NULL; + + cb = kmalloc(sizeof(struct dalrpc_cb_handle), GFP_KERNEL); + if (!cb) + return NULL; + + cb->fn = fn; + cb->context = context; + + mutex_lock(&h->port->cb_list_lock); + list_add(&cb->list, &h->port->cb_list); + mutex_unlock(&h->port->cb_list_lock); + + return cb; +} +EXPORT_SYMBOL(dalrpc_alloc_cb); + +void dalrpc_dealloc_event(void *handle, + void *ev_h) +{ + struct daldevice_handle *h; + struct dalrpc_event_handle *ev; + + h = (struct daldevice_handle *)handle; + ev = (struct dalrpc_event_handle *)ev_h; + + mutex_lock(&h->port->event_list_lock); + list_del(&ev->list); + mutex_unlock(&h->port->event_list_lock); + kfree(ev); +} +EXPORT_SYMBOL(dalrpc_dealloc_event); + +void dalrpc_dealloc_cb(void *handle, + void *cb_h) +{ + struct daldevice_handle *h; + struct dalrpc_cb_handle *cb; + + h = (struct daldevice_handle *)handle; + cb = (struct dalrpc_cb_handle *)cb_h; + + mutex_lock(&h->port->cb_list_lock); + list_del(&cb->list); + mutex_unlock(&h->port->cb_list_lock); + kfree(cb); +} +EXPORT_SYMBOL(dalrpc_dealloc_cb); + +static int event_occurred(int num_events, struct dalrpc_event_handle **events, + int *occurred) +{ + int i; + + for (i = 0; i < num_events; i++) { + spin_lock(&events[i]->lock); + if (events[i]->flag) { + events[i]->flag = 0; + spin_unlock(&events[i]->lock); + *occurred = i; + return 1; + } + spin_unlock(&events[i]->lock); + } + + return 0; +} + +int dalrpc_event_wait_multiple(int num, void **ev_h, int timeout) +{ + struct dalrpc_event_handle **events; + int ret, occurred; + + events = (struct dalrpc_event_handle **)ev_h; + + if (timeout == DALRPC_TIMEOUT_INFINITE) { + wait_event(event_wq, + event_occurred(num, events, &occurred)); + return occurred; + } + + ret = wait_event_timeout(event_wq, + event_occurred(num, events, &occurred), + timeout); + if (ret > 0) + return occurred; + else + return -ETIMEDOUT; +} +EXPORT_SYMBOL(dalrpc_event_wait_multiple); diff --git a/arch/arm/mach-msm/dal_remotetest.c b/arch/arm/mach-msm/dal_remotetest.c new file mode 100644 index 000000000000..bbe0f1a4475c --- /dev/null +++ b/arch/arm/mach-msm/dal_remotetest.c @@ -0,0 +1,454 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * DAL remote test device test suite. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/debugfs.h> + +#include "dal_remotetest.h" + +#define BYTEBUF_LEN 64 + +#define rpc_error(num) \ + do { \ + errmask |= (1 << num); \ + printk(KERN_INFO "%s: remote_unittest_%d failed (%d)\n", \ + __func__, num, ret); \ + } while (0) + +#define verify_error(num, field) \ + do { \ + errmask |= (1 << num); \ + printk(KERN_INFO "%s: remote_unittest_%d failed (%s)\n", \ + __func__, num, field); \ + } while (0) + + +static struct dentry *debugfs_dir_entry; +static struct dentry *debugfs_modem_entry; +static struct dentry *debugfs_dsp_entry; + +static uint8_t in_bytebuf[BYTEBUF_LEN]; +static uint8_t out_bytebuf[BYTEBUF_LEN]; +static uint8_t out_bytebuf2[BYTEBUF_LEN]; +static struct remote_test_data in_data; +static struct remote_test_data out_data; +static int block_until_cb = 1; + +static void init_data(struct remote_test_data *data) +{ + int i; + data->regular_event = REMOTE_UNITTEST_INPUT_HANDLE; + data->payload_event = REMOTE_UNITTEST_INPUT_HANDLE; + for (i = 0; i < 32; i++) + data->test[i] = i; +} + +static int verify_data(struct remote_test_data *data) +{ + int i; + if (data->regular_event != REMOTE_UNITTEST_INPUT_HANDLE || + data->payload_event != REMOTE_UNITTEST_INPUT_HANDLE) + return -1; + for (i = 0; i < 32; i++) + if (data->test[i] != i) + return -1; + + return 0; +} + +static int verify_uint32_buffer(uint32_t *buf) +{ + int i; + for (i = 0; i < 32; i++) + if (buf[i] != i) + return -1; + + return 0; +} + +static void init_bytebuf(uint8_t *bytebuf) +{ + int i; + for (i = 0; i < BYTEBUF_LEN; i++) + bytebuf[i] = i & 0xff; +} + +static int verify_bytebuf(uint8_t *bytebuf) +{ + int i; + for (i = 0; i < BYTEBUF_LEN; i++) + if (bytebuf[i] != (i & 0xff)) + return -1; + + return 0; +} + +static void test_cb(void *context, uint32_t param, void *data, uint32_t len) +{ + block_until_cb = 0; +} + +static int remotetest_exec(int dest, u64 *val) +{ + void *dev_handle; + void *event_handles[3]; + void *cb_handle; + int ret; + u64 errmask = 0; + uint32_t ouint; + uint32_t oalen; + + /* test daldevice_attach */ + ret = daldevice_attach(REMOTE_UNITTEST_DEVICEID, NULL, + dest, &dev_handle); + if (ret) { + printk(KERN_INFO "%s: failed to attach (%d)\n", __func__, ret); + *val = 0xffffffff; + return 0; + } + + /* test remote_unittest_0 */ + ret = remote_unittest_0(dev_handle, REMOTE_UNITTEST_INARG_1); + if (ret) + rpc_error(0); + + /* test remote_unittest_1 */ + ret = remote_unittest_1(dev_handle, REMOTE_UNITTEST_INARG_1, + REMOTE_UNITTEST_INARG_2); + if (ret) + rpc_error(1); + + /* test remote_unittest_2 */ + ouint = 0; + ret = remote_unittest_2(dev_handle, REMOTE_UNITTEST_INARG_1, &ouint); + if (ret) + rpc_error(2); + else if (ouint != REMOTE_UNITTEST_OUTARG_1) + verify_error(2, "ouint"); + + /* test remote_unittest_3 */ + ret = remote_unittest_3(dev_handle, REMOTE_UNITTEST_INARG_1, + REMOTE_UNITTEST_INARG_2, + REMOTE_UNITTEST_INARG_3); + if (ret) + rpc_error(3); + + /* test remote_unittest_4 */ + ouint = 0; + ret = remote_unittest_4(dev_handle, REMOTE_UNITTEST_INARG_1, + REMOTE_UNITTEST_INARG_2, &ouint); + if (ret) + rpc_error(4); + else if (ouint != REMOTE_UNITTEST_OUTARG_1) + verify_error(4, "ouint"); + + /* test remote_unittest_5 */ + init_data(&in_data); + ret = remote_unittest_5(dev_handle, &in_data, sizeof(in_data)); + if (ret) + rpc_error(5); + + /* test remote_unittest_6 */ + init_data(&in_data); + ret = remote_unittest_6(dev_handle, REMOTE_UNITTEST_INARG_1, + &in_data.test, sizeof(in_data.test)); + if (ret) + rpc_error(6); + + /* test remote_unittest_7 */ + init_data(&in_data); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_7(dev_handle, &in_data, sizeof(in_data), + &out_data.test, sizeof(out_data.test), + &oalen); + if (ret) + rpc_error(7); + else if (oalen != sizeof(out_data.test)) + verify_error(7, "oalen"); + else if (verify_uint32_buffer(out_data.test)) + verify_error(7, "obuf"); + + /* test remote_unittest_8 */ + init_bytebuf(in_bytebuf); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_8(dev_handle, in_bytebuf, sizeof(in_bytebuf), + &out_data, sizeof(out_data)); + if (ret) + rpc_error(8); + else if (verify_data(&out_data)) + verify_error(8, "obuf"); + + /* test remote_unittest_9 */ + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_9(dev_handle, out_bytebuf, sizeof(out_bytebuf)); + if (ret) + rpc_error(9); + else if (verify_bytebuf(out_bytebuf)) + verify_error(9, "obuf"); + + /* test remote_unittest_10 */ + init_bytebuf(in_bytebuf); + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_10(dev_handle, REMOTE_UNITTEST_INARG_1, + in_bytebuf, sizeof(in_bytebuf), + out_bytebuf, sizeof(out_bytebuf), &oalen); + if (ret) + rpc_error(10); + else if (oalen != sizeof(out_bytebuf)) + verify_error(10, "oalen"); + else if (verify_bytebuf(out_bytebuf)) + verify_error(10, "obuf"); + + /* test remote_unittest_11 */ + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_11(dev_handle, REMOTE_UNITTEST_INARG_1, + out_bytebuf, sizeof(out_bytebuf)); + if (ret) + rpc_error(11); + else if (verify_bytebuf(out_bytebuf)) + verify_error(11, "obuf"); + + /* test remote_unittest_12 */ + memset(&out_bytebuf, 0, sizeof(out_bytebuf)); + ret = remote_unittest_12(dev_handle, REMOTE_UNITTEST_INARG_1, + out_bytebuf, sizeof(out_bytebuf), &oalen); + if (ret) + rpc_error(12); + else if (oalen != sizeof(out_bytebuf)) + verify_error(12, "oalen"); + else if (verify_bytebuf(out_bytebuf)) + verify_error(12, "obuf"); + + /* test remote_unittest_13 */ + init_data(&in_data); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_13(dev_handle, in_data.test, sizeof(in_data.test), + &in_data, sizeof(in_data), + &out_data, sizeof(out_data)); + if (ret) + rpc_error(13); + else if (verify_data(&out_data)) + verify_error(13, "obuf"); + + /* test remote_unittest_14 */ + init_data(&in_data); + memset(out_bytebuf, 0, sizeof(out_bytebuf)); + memset(out_bytebuf2, 0, sizeof(out_bytebuf2)); + ret = remote_unittest_14(dev_handle, + in_data.test, sizeof(in_data.test), + out_bytebuf, sizeof(out_bytebuf), + out_bytebuf2, sizeof(out_bytebuf2), &oalen); + if (ret) + rpc_error(14); + else if (verify_bytebuf(out_bytebuf)) + verify_error(14, "obuf"); + else if (oalen != sizeof(out_bytebuf2)) + verify_error(14, "oalen"); + else if (verify_bytebuf(out_bytebuf2)) + verify_error(14, "obuf2"); + + /* test remote_unittest_15 */ + init_data(&in_data); + memset(out_bytebuf, 0, sizeof(out_bytebuf)); + memset(&out_data, 0, sizeof(out_data)); + ret = remote_unittest_15(dev_handle, + in_data.test, sizeof(in_data.test), + &in_data, sizeof(in_data), + &out_data, sizeof(out_data), &oalen, + out_bytebuf, sizeof(out_bytebuf)); + if (ret) + rpc_error(15); + else if (oalen != sizeof(out_data)) + verify_error(15, "oalen"); + else if (verify_bytebuf(out_bytebuf)) + verify_error(15, "obuf"); + else if (verify_data(&out_data)) + verify_error(15, "obuf2"); + + /* test setting up asynch events */ + event_handles[0] = dalrpc_alloc_event(dev_handle); + event_handles[1] = dalrpc_alloc_event(dev_handle); + event_handles[2] = dalrpc_alloc_event(dev_handle); + cb_handle = dalrpc_alloc_cb(dev_handle, test_cb, &out_data); + in_data.regular_event = (uint32_t)event_handles[2]; + in_data.payload_event = (uint32_t)cb_handle; + ret = remote_unittest_eventcfg(dev_handle, &in_data, sizeof(in_data)); + if (ret) { + errmask |= (1 << 16); + printk(KERN_INFO "%s: failed to configure asynch (%d)\n", + __func__, ret); + } + + /* test event */ + ret = remote_unittest_eventtrig(dev_handle, + REMOTE_UNITTEST_REGULAR_EVENT); + if (ret) { + errmask |= (1 << 17); + printk(KERN_INFO "%s: failed to trigger event (%d)\n", + __func__, ret); + } + ret = dalrpc_event_wait(event_handles[2], 1000); + if (ret) { + errmask |= (1 << 18); + printk(KERN_INFO "%s: failed to receive event (%d)\n", + __func__, ret); + } + + /* test event again */ + ret = remote_unittest_eventtrig(dev_handle, + REMOTE_UNITTEST_REGULAR_EVENT); + if (ret) { + errmask |= (1 << 19); + printk(KERN_INFO "%s: failed to trigger event (%d)\n", + __func__, ret); + } + ret = dalrpc_event_wait_multiple(3, event_handles, 1000); + if (ret != 2) { + errmask |= (1 << 20); + printk(KERN_INFO "%s: failed to receive event (%d)\n", + __func__, ret); + } + + /* test callback */ + ret = remote_unittest_eventtrig(dev_handle, + REMOTE_UNITTEST_CALLBACK_EVENT); + if (ret) { + errmask |= (1 << 21); + printk(KERN_INFO "%s: failed to trigger callback (%d)\n", + __func__, ret); + } else + while (block_until_cb) + ; + + dalrpc_dealloc_cb(dev_handle, cb_handle); + dalrpc_dealloc_event(dev_handle, event_handles[0]); + dalrpc_dealloc_event(dev_handle, event_handles[1]); + dalrpc_dealloc_event(dev_handle, event_handles[2]); + + /* test daldevice_detach */ + ret = daldevice_detach(dev_handle); + if (ret) { + errmask |= (1 << 22); + printk(KERN_INFO "%s: failed to detach (%d)\n", __func__, ret); + } + + printk(KERN_INFO "%s: remote_unittest complete\n", __func__); + + *val = errmask; + return 0; +} + +static int remotetest_modem_exec(void *data, u64 *val) +{ + return remotetest_exec(DALRPC_DEST_MODEM, val); +} + +static int remotetest_dsp_exec(void *data, u64 *val) +{ + return remotetest_exec(DALRPC_DEST_QDSP, val); +} + +DEFINE_SIMPLE_ATTRIBUTE(dal_modemtest_fops, remotetest_modem_exec, + NULL, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(dal_dsptest_fops, remotetest_dsp_exec, + NULL, "%llu\n"); + +static int __init remotetest_init(void) +{ + debugfs_dir_entry = debugfs_create_dir("dal", 0); + if (IS_ERR(debugfs_dir_entry)) + return PTR_ERR(debugfs_dir_entry); + + debugfs_modem_entry = debugfs_create_file("modem_test", 0444, + debugfs_dir_entry, + NULL, &dal_modemtest_fops); + if (IS_ERR(debugfs_modem_entry)) { + debugfs_remove(debugfs_dir_entry); + return PTR_ERR(debugfs_modem_entry); + } + + debugfs_dsp_entry = debugfs_create_file("dsp_test", 0444, + debugfs_dir_entry, + NULL, &dal_dsptest_fops); + if (IS_ERR(debugfs_dsp_entry)) { + debugfs_remove(debugfs_modem_entry); + debugfs_remove(debugfs_dir_entry); + return PTR_ERR(debugfs_dsp_entry); + } + + return 0; +} + +static void __exit remotetest_exit(void) +{ + debugfs_remove(debugfs_modem_entry); + debugfs_remove(debugfs_dsp_entry); + debugfs_remove(debugfs_dir_entry); +} + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Test for DAL RPC"); +MODULE_VERSION("1.0"); + +module_init(remotetest_init); +module_exit(remotetest_exit); diff --git a/arch/arm/mach-msm/dal_remotetest.h b/arch/arm/mach-msm/dal_remotetest.h new file mode 100644 index 000000000000..e24b480192ed --- /dev/null +++ b/arch/arm/mach-msm/dal_remotetest.h @@ -0,0 +1,187 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * DAL remote test device API. + */ + +#include <linux/kernel.h> + +#include <mach/dal.h> + +#define REMOTE_UNITTEST_DEVICEID 0xDA1DA1DA + +enum { + DALRPC_TEST_API_0 = DALDEVICE_FIRST_DEVICE_API_IDX, + DALRPC_TEST_API_1, + DALRPC_TEST_API_2, + DALRPC_TEST_API_3, + DALRPC_TEST_API_4, + DALRPC_TEST_API_5, + DALRPC_TEST_API_6, + DALRPC_TEST_API_7, + DALRPC_TEST_API_8, + DALRPC_TEST_API_9, + DALRPC_TEST_API_10, + DALRPC_TEST_API_11, + DALRPC_TEST_API_12, + DALRPC_TEST_API_13, + DALRPC_TEST_API_14, + DALRPC_TEST_API_15, + DALRPC_TEST_API_16, + DALRPC_TEST_API_17 +}; + +#define REMOTE_UNITTEST_INARG_1 0x01010101 +#define REMOTE_UNITTEST_INARG_2 0x20202020 +#define REMOTE_UNITTEST_INARG_3 0x12121212 +#define REMOTE_UNITTEST_INPUT_HANDLE 0xDA1FDA1F +#define REMOTE_UNITTEST_OUTARG_1 0xBEEFDEAD + +#define REMOTE_UNITTEST_REGULAR_EVENT 0 +#define REMOTE_UNITTEST_CALLBACK_EVENT 1 + +#define REMOTE_UNITTEST_BAD_PARAM 0x10 + +struct remote_test_data { + uint32_t regular_event; + uint32_t test[32]; + uint32_t payload_event; +}; + +static int remote_unittest_0(void *handle, uint32_t s1) +{ + return dalrpc_fcn_0(DALRPC_TEST_API_0, handle, s1); +} + +static int remote_unittest_1(void *handle, uint32_t s1, uint32_t s2) +{ + return dalrpc_fcn_1(DALRPC_TEST_API_1, handle, s1, s2); +} + +static int remote_unittest_2(void *handle, uint32_t s1, uint32_t *p_s2) +{ + return dalrpc_fcn_2(DALRPC_TEST_API_2, handle, s1, p_s2); +} + +static int remote_unittest_3(void *handle, uint32_t s1, uint32_t s2, + uint32_t s3) +{ + return dalrpc_fcn_3(DALRPC_TEST_API_3, handle, s1, s2, s3); +} + +static int remote_unittest_4(void *handle, uint32_t s1, uint32_t s2, + uint32_t *p_s3) +{ + return dalrpc_fcn_4(DALRPC_TEST_API_4, handle, s1, s2, p_s3); +} + +static int remote_unittest_5(void *handle, const void *ibuf, uint32_t ilen) +{ + return dalrpc_fcn_5(DALRPC_TEST_API_5, handle, ibuf, ilen); +} + +static int remote_unittest_6(void *handle, uint32_t s1, const void *ibuf, + uint32_t ilen) +{ + return dalrpc_fcn_6(DALRPC_TEST_API_6, handle, s1, ibuf, ilen); +} + +static int remote_unittest_7(void *handle, const void *ibuf, uint32_t ilen, + void *obuf, uint32_t olen, uint32_t *oalen) +{ + return dalrpc_fcn_7(DALRPC_TEST_API_7, handle, ibuf, ilen, obuf, + olen, oalen); +} + +static int remote_unittest_8(void *handle, const void *ibuf, uint32_t ilen, + void *obuf, uint32_t olen) +{ + return dalrpc_fcn_8(DALRPC_TEST_API_8, handle, ibuf, ilen, obuf, olen); +} + +static int remote_unittest_9(void *handle, void *obuf, uint32_t olen) +{ + return dalrpc_fcn_9(DALRPC_TEST_API_9, handle, obuf, olen); +} + +static int remote_unittest_10(void *handle, uint32_t s1, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + uint32_t *oalen) +{ + return dalrpc_fcn_10(DALRPC_TEST_API_10, handle, s1, ibuf, ilen, obuf, + olen, oalen); +} + +static int remote_unittest_11(void *handle, uint32_t s1, void *obuf, + uint32_t olen) +{ + return dalrpc_fcn_11(DALRPC_TEST_API_11, handle, s1, obuf, olen); +} + +static int remote_unittest_12(void *handle, uint32_t s1, void *obuf, + uint32_t olen, uint32_t *oalen) +{ + return dalrpc_fcn_12(DALRPC_TEST_API_12, handle, s1, obuf, olen, + oalen); +} + +static int remote_unittest_13(void *handle, const void *ibuf, uint32_t ilen, + const void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen) +{ + return dalrpc_fcn_13(DALRPC_TEST_API_13, handle, ibuf, ilen, ibuf2, + ilen2, obuf, olen); +} + +static int remote_unittest_14(void *handle, const void *ibuf, uint32_t ilen, + void *obuf, uint32_t olen, void *obuf2, + uint32_t olen2, uint32_t *oalen2) +{ + return dalrpc_fcn_14(DALRPC_TEST_API_14, handle, ibuf, ilen, obuf, + olen, obuf2, olen2, oalen2); +} + +static int remote_unittest_15(void *handle, const void *ibuf, uint32_t ilen, + const void *ibuf2, uint32_t ilen2, void *obuf, + uint32_t olen, uint32_t *oalen, void *obuf2, + uint32_t olen2) +{ + return dalrpc_fcn_15(DALRPC_TEST_API_15, handle, ibuf, ilen, ibuf2, + ilen2, obuf, olen, oalen, obuf2, olen2); +} + +static int remote_unittest_eventcfg(void *handle, const void *ibuf, + uint32_t ilen) +{ + return dalrpc_fcn_5(DALRPC_TEST_API_16, handle, ibuf, ilen); +} + +static int remote_unittest_eventtrig(void *handle, uint32_t event_idx) +{ + return dalrpc_fcn_0(DALRPC_TEST_API_17, handle, event_idx); +} diff --git a/arch/arm/mach-msm/devices.c b/arch/arm/mach-msm/devices.c index 31b6b30e98bf..737fd09d2841 100644 --- a/arch/arm/mach-msm/devices.c +++ b/arch/arm/mach-msm/devices.c @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/devices.c * * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,14 +17,20 @@ #include <linux/kernel.h> #include <linux/platform_device.h> +#include <linux/dma-mapping.h> #include <mach/irqs.h> #include <mach/msm_iomap.h> +#include <mach/dma.h> +#include <mach/board.h> + #include "devices.h" #include <asm/mach/flash.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <mach/mmc.h> + static struct resource resources_uart1[] = { { .start = INT_UART1, @@ -84,48 +91,14 @@ struct platform_device msm_device_uart3 = { .resource = resources_uart3, }; -static struct resource resources_i2c[] = { - { - .start = MSM_I2C_PHYS, - .end = MSM_I2C_PHYS + MSM_I2C_SIZE - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_PWB_I2C, - .end = INT_PWB_I2C, - .flags = IORESOURCE_IRQ, - }, -}; - -struct platform_device msm_device_i2c = { - .name = "msm_i2c", - .id = 0, - .num_resources = ARRAY_SIZE(resources_i2c), - .resource = resources_i2c, -}; - -static struct resource resources_hsusb[] = { - { - .start = MSM_HSUSB_PHYS, - .end = MSM_HSUSB_PHYS + MSM_HSUSB_SIZE, - .flags = IORESOURCE_MEM, - }, - { - .start = INT_USB_HS, - .end = INT_USB_HS, - .flags = IORESOURCE_IRQ, - }, -}; +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0900000 +#else +#define MSM_UART1DM_PHYS 0xA0200000 +#define MSM_UART2DM_PHYS 0xA0300000 +#endif -struct platform_device msm_device_hsusb = { - .name = "msm_hsusb", - .id = -1, - .num_resources = ARRAY_SIZE(resources_hsusb), - .resource = resources_hsusb, - .dev = { - .coherent_dma_mask = 0xffffffff, - }, -}; struct flash_platform_data msm_nand_data = { .parts = NULL, @@ -155,10 +128,27 @@ struct platform_device msm_device_smd = { .id = -1, }; +struct platform_device msm_device_dmov = { + .name = "msm_dmov", + .id = -1, +}; + +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define MSM_SDC1_BASE 0xA0300000 +#define MSM_SDC2_BASE 0xA0400000 +#define MSM_SDC3_BASE 0xA0500000 +#define MSM_SDC4_BASE 0xA0600000 +#else +#define MSM_SDC1_BASE 0xA0400000 +#define MSM_SDC2_BASE 0xA0500000 +#define MSM_SDC3_BASE 0xA0600000 +#define MSM_SDC4_BASE 0xA0700000 +#endif + static struct resource resources_sdc1[] = { { - .start = MSM_SDC1_PHYS, - .end = MSM_SDC1_PHYS + MSM_SDC1_SIZE - 1, + .start = MSM_SDC1_BASE, + .end = MSM_SDC1_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { @@ -175,8 +165,8 @@ static struct resource resources_sdc1[] = { static struct resource resources_sdc2[] = { { - .start = MSM_SDC2_PHYS, - .end = MSM_SDC2_PHYS + MSM_SDC2_SIZE - 1, + .start = MSM_SDC2_BASE, + .end = MSM_SDC2_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { @@ -193,8 +183,8 @@ static struct resource resources_sdc2[] = { static struct resource resources_sdc3[] = { { - .start = MSM_SDC3_PHYS, - .end = MSM_SDC3_PHYS + MSM_SDC3_SIZE - 1, + .start = MSM_SDC3_BASE, + .end = MSM_SDC3_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { @@ -211,8 +201,8 @@ static struct resource resources_sdc3[] = { static struct resource resources_sdc4[] = { { - .start = MSM_SDC4_PHYS, - .end = MSM_SDC4_PHYS + MSM_SDC4_SIZE - 1, + .start = MSM_SDC4_BASE, + .end = MSM_SDC4_BASE + SZ_4K - 1, .flags = IORESOURCE_MEM, }, { @@ -266,3 +256,273 @@ struct platform_device msm_device_sdc4 = { .coherent_dma_mask = 0xffffffff, }, }; + +static struct platform_device *msm_sdcc_devices[] __initdata = { + &msm_device_sdc1, + &msm_device_sdc2, + &msm_device_sdc3, + &msm_device_sdc4, +}; + +int __init msm_add_sdcc(unsigned int controller, struct mmc_platform_data *plat) +{ + struct platform_device *pdev; + + if (controller < 1 || controller > 4) + return -EINVAL; + + pdev = msm_sdcc_devices[controller-1]; + pdev->dev.platform_data = plat; + return platform_device_register(pdev); +} + +struct clk msm_clocks_7x01a[] = { + CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), + CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), + CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), + CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("gp_clk", GP_CLK, NULL, 0), + CLK_PCOM("grp_clk", GRP_CLK, NULL, OFF), + CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), + CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), + CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), + CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), + CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), + CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), + CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("tsif_clk", TSIF_CLK, NULL, 0), + CLK_PCOM("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), + CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), + CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), + CLK_PCOM("usb_hs_pclk", USB_HS_PCLK, NULL, OFF), + CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), + CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), + CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), + CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), + CLK_PCOM("grp_pclk", GRP_PCLK, NULL, 0), +}; + +unsigned msm_num_clocks_7x01a = ARRAY_SIZE(msm_clocks_7x01a); + +struct clk msm_clocks_7x25[] = { + CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), + CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), + CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), + CLK_PCOM("gp_clk", GP_CLK, NULL, 0), + CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), + CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), + CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), + CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), + CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), + CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), + CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), + CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), + CLK_PCOM("usb_hs_pclk", USB_HS_PCLK, NULL, OFF), + CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), + CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), + CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), + CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), + CLK_PCOM("grp_pclk", GRP_PCLK, NULL, 0), +}; + +unsigned msm_num_clocks_7x25 = ARRAY_SIZE(msm_clocks_7x25); + +struct clk msm_clocks_7x27[] = { + CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), + CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), + CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), + CLK_PCOM("gp_clk", GP_CLK, NULL, 0), + CLK_PCOM("grp_clk", GRP_CLK, NULL, 0), + CLK_PCOM("grp_pclk", GRP_PCLK, NULL, 0), + CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), + CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), + CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), + CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), + CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), + CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), + CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), + CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), + CLK_PCOM("usb_hs_pclk", USB_HS_PCLK, NULL, OFF), + CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), + CLK_PCOM("usb_phy_clk", USB_PHY_CLK, NULL, 0), + CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), + CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), + CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), +}; + +unsigned msm_num_clocks_7x27 = ARRAY_SIZE(msm_clocks_7x27); + +struct clk msm_clocks_7x30[] = { + CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), + CLK_PCOM("adsp_clk", ADSP_CLK, NULL, 0), + CLK_PCOM("cam_m_clk", CAM_MCLK_CLK, NULL, 0), + CLK_PCOM("camif_pad_pclk", CAMIF_PAD_PCLK, NULL, OFF), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), + CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), + CLK_PCOM("gp_clk", GP_CLK, NULL, 0), + CLK_PCOM("grp_2d_clk", GRP_2D_CLK, NULL, 0), + CLK_PCOM("grp_2d_pclk", GRP_2D_PCLK, NULL, 0), + CLK_PCOM("grp_clk", GRP_CLK, NULL, 0), + CLK_PCOM("grp_pclk", GRP_PCLK, NULL, 0), + CLK_PCOM("hdmi_clk", HDMI_CLK, NULL, 0), + CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), + CLK_PCOM("lpa_codec_clk", LPA_CODEC_CLK, NULL, 0), + CLK_PCOM("lpa_core_clk", LPA_CORE_CLK, NULL, 0), + CLK_PCOM("lpa_pclk", LPA_PCLK, NULL, 0), + CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mddi_pclk", PMDH_PCLK, NULL, 0), + CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), + CLK_PCOM("mdp_pclk", MDP_PCLK, NULL, 0), + CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), + CLK_PCOM("mfc_clk", MFC_CLK, NULL, 0), + CLK_PCOM("mfc_div2_clk", MFC_DIV2_CLK, NULL, 0), + CLK_PCOM("mfc_pclk", MFC_PCLK, NULL, 0), + CLK_PCOM("mi2s_codec_rx_m_clk", MI2S_CODEC_RX_MCLK, NULL, 0), + CLK_PCOM("mi2s_codec_rx_s_clk", MI2S_CODEC_RX_SCLK, NULL, 0), + CLK_PCOM("mi2s_codec_tx_m_clk", MI2S_CODEC_TX_MCLK, NULL, 0), + CLK_PCOM("mi2s_codec_tx_s_clk", MI2S_CODEC_TX_SCLK, NULL, 0), + CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), + CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), + CLK_PCOM("rotator_clk", AXI_ROTATOR_CLK, NULL, 0), + CLK_PCOM("rotator_imem_clk", ROTATOR_IMEM_CLK, NULL, OFF), + CLK_PCOM("rotator_pclk", ROTATOR_PCLK, NULL, OFF), + CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("spi_clk", SPI_CLK, NULL, 0), + CLK_PCOM("spi_pclk", SPI_PCLK, NULL, 0), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), + CLK_PCOM("usb_hs_pclk", USB_HS_PCLK, NULL, OFF), + CLK_PCOM("usb_hs_core_clk", USB_HS_CORE_CLK, NULL, OFF), + CLK_PCOM("usb_hs2_clk", USB_HS2_CLK, NULL, OFF), + CLK_PCOM("usb_hs2_pclk", USB_HS2_PCLK, NULL, OFF), + CLK_PCOM("usb_hs2_core_clk", USB_HS2_CORE_CLK, NULL, OFF), + CLK_PCOM("usb_hs3_clk", USB_HS3_CLK, NULL, OFF), + CLK_PCOM("usb_hs3_pclk", USB_HS3_PCLK, NULL, OFF), + CLK_PCOM("usb_hs3_core_clk", USB_HS3_CORE_CLK, NULL, OFF), + CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), + CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), + CLK_PCOM("vfe_camif_clk", VFE_CAMIF_CLK, NULL, 0), + CLK_PCOM("vfe_clk", VFE_CLK, NULL, 0), + CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, 0), + CLK_PCOM("vfe_pclk", VFE_PCLK, NULL, OFF), +}; + +unsigned msm_num_clocks_7x30 = ARRAY_SIZE(msm_clocks_7x30); + +struct clk msm_clocks_8x50[] = { + CLK_PCOM("adm_clk", ADM_CLK, NULL, 0), + CLK_PCOM("ebi1_clk", EBI1_CLK, NULL, CLK_MIN), + CLK_PCOM("ebi2_clk", EBI2_CLK, NULL, 0), + CLK_PCOM("ecodec_clk", ECODEC_CLK, NULL, 0), + CLK_PCOM("emdh_clk", EMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("gp_clk", GP_CLK, NULL, 0), + CLK_PCOM("grp_clk", GRP_CLK, NULL, 0), + CLK_PCOM("icodec_rx_clk", ICODEC_RX_CLK, NULL, 0), + CLK_PCOM("icodec_tx_clk", ICODEC_TX_CLK, NULL, 0), + CLK_PCOM("imem_clk", IMEM_CLK, NULL, OFF), + CLK_PCOM("mdc_clk", MDC_CLK, NULL, 0), + CLK_PCOM("mddi_clk", PMDH_CLK, NULL, OFF | CLK_MINMAX), + CLK_PCOM("mdp_clk", MDP_CLK, NULL, OFF), + CLK_PCOM("mdp_lcdc_pclk_clk", MDP_LCDC_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_lcdc_pad_pclk_clk", MDP_LCDC_PAD_PCLK_CLK, NULL, 0), + CLK_PCOM("mdp_vsync_clk", MDP_VSYNC_CLK, NULL, 0), + CLK_PCOM("pbus_clk", PBUS_CLK, NULL, CLK_MIN), + CLK_PCOM("pcm_clk", PCM_CLK, NULL, 0), + CLK_PCOM("sdac_clk", SDAC_CLK, NULL, OFF), + CLK_PCOM("sdc_clk", SDC1_CLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_pclk", SDC1_PCLK, &msm_device_sdc1.dev, OFF), + CLK_PCOM("sdc_clk", SDC2_CLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_pclk", SDC2_PCLK, &msm_device_sdc2.dev, OFF), + CLK_PCOM("sdc_clk", SDC3_CLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_pclk", SDC3_PCLK, &msm_device_sdc3.dev, OFF), + CLK_PCOM("sdc_clk", SDC4_CLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("sdc_pclk", SDC4_PCLK, &msm_device_sdc4.dev, OFF), + CLK_PCOM("spi_clk", SPI_CLK, NULL, 0), + CLK_PCOM("tsif_clk", TSIF_CLK, NULL, 0), + CLK_PCOM("tsif_ref_clk", TSIF_REF_CLK, NULL, 0), + CLK_PCOM("tv_dac_clk", TV_DAC_CLK, NULL, 0), + CLK_PCOM("tv_enc_clk", TV_ENC_CLK, NULL, 0), + CLK_PCOM("uart_clk", UART1_CLK, &msm_device_uart1.dev, OFF), + CLK_PCOM("uart_clk", UART2_CLK, &msm_device_uart2.dev, 0), + CLK_PCOM("uart_clk", UART3_CLK, &msm_device_uart3.dev, OFF), + CLK_PCOM("usb_hs_clk", USB_HS_CLK, NULL, OFF), + CLK_PCOM("usb_hs_pclk", USB_HS_PCLK, NULL, OFF), + CLK_PCOM("usb_otg_clk", USB_OTG_CLK, NULL, 0), + CLK_PCOM("vdc_clk", VDC_CLK, NULL, OFF | CLK_MIN), + CLK_PCOM("vfe_clk", VFE_CLK, NULL, OFF), + CLK_PCOM("vfe_mdc_clk", VFE_MDC_CLK, NULL, OFF), + CLK_PCOM("vfe_axi_clk", VFE_AXI_CLK, NULL, OFF), + CLK_PCOM("usb_hs2_clk", USB_HS2_CLK, NULL, OFF), + CLK_PCOM("usb_hs2_pclk", USB_HS2_PCLK, NULL, OFF), + CLK_PCOM("usb_hs3_clk", USB_HS3_CLK, NULL, OFF), + CLK_PCOM("usb_hs3_pclk", USB_HS3_PCLK, NULL, OFF), + CLK_PCOM("usb_phy_clk", USB_PHY_CLK, NULL, 0), +}; + +unsigned msm_num_clocks_8x50 = ARRAY_SIZE(msm_clocks_8x50); diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h index 0744c4a27d6a..9dd8d9b698c8 100644 --- a/arch/arm/mach-msm/devices.h +++ b/arch/arm/mach-msm/devices.h @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/devices.h * * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,6 +17,8 @@ #ifndef __ARCH_ARM_MACH_MSM_DEVICES_H #define __ARCH_ARM_MACH_MSM_DEVICES_H +#include "clock.h" + extern struct platform_device msm_device_uart1; extern struct platform_device msm_device_uart2; extern struct platform_device msm_device_uart3; @@ -25,12 +28,31 @@ extern struct platform_device msm_device_sdc2; extern struct platform_device msm_device_sdc3; extern struct platform_device msm_device_sdc4; -extern struct platform_device msm_device_hsusb; +extern struct platform_device msm_device_hsusb_otg; +extern struct platform_device msm_device_hsusb_peripheral; +extern struct platform_device msm_device_hsusb_host; extern struct platform_device msm_device_i2c; extern struct platform_device msm_device_smd; +extern struct platform_device msm_device_dmov; extern struct platform_device msm_device_nand; +extern struct clk msm_clocks_7x01a[]; +extern unsigned msm_num_clocks_7x01a; + +extern struct clk msm_clocks_7x25[]; +extern unsigned msm_num_clocks_7x25; + +extern struct clk msm_clocks_7x27[]; +extern unsigned msm_num_clocks_7x27; + +extern struct clk msm_clocks_7x30[]; +extern unsigned msm_num_clocks_7x30; + +extern struct clk msm_clocks_8x50[]; +extern unsigned msm_num_clocks_8x50; + + #endif diff --git a/arch/arm/mach-msm/devices_htc.c b/arch/arm/mach-msm/devices_htc.c new file mode 100644 index 000000000000..fad7dfb1bcc4 --- /dev/null +++ b/arch/arm/mach-msm/devices_htc.c @@ -0,0 +1,450 @@ +/* linux/arch/arm/mach-msm/devices.c + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> + +#include <linux/dma-mapping.h> +#include <mach/msm_iomap.h> +#include <mach/dma.h> +#include "gpio_chip.h" +#include "devices.h" +#include <mach/board.h> +#include <mach/board_htc.h> +#include <mach/msm_hsusb.h> +#include <linux/usb/mass_storage_function.h> +#include <linux/usb/android.h> + +#include <asm/mach/flash.h> +#include <asm/setup.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/delay.h> +#include <linux/android_pmem.h> +#include <mach/msm_rpcrouter.h> +#include <mach/msm_iomap.h> +#include <asm/mach/mmc.h> + +static char *df_serialno = "000000000000"; + +#if 0 +struct platform_device *devices[] __initdata = { + &msm_device_nand, + &msm_device_smd, + &msm_device_i2c, +}; + +void __init msm_add_devices(void) +{ + platform_add_devices(devices, ARRAY_SIZE(devices)); +} +#endif + +#define HSUSB_API_INIT_PHY_PROC 2 +#define HSUSB_API_PROG 0x30000064 +#define HSUSB_API_VERS 0x10001 +static void internal_phy_reset(void) +{ + struct msm_rpc_endpoint *usb_ep; + int rc; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + printk(KERN_INFO "msm_hsusb_phy_reset\n"); + + usb_ep = msm_rpc_connect(HSUSB_API_PROG, HSUSB_API_VERS, 0); + if (IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: init rpc failed! error: %ld\n", + __func__, PTR_ERR(usb_ep)); + goto close; + } + rc = msm_rpc_call(usb_ep, HSUSB_API_INIT_PHY_PROC, + &req, sizeof(req), 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: rpc call failed! (%d)\n", __func__, rc); + +close: + msm_rpc_close(usb_ep); +} + +/* adjust eye diagram, disable vbusvalid interrupts */ +static int hsusb_phy_init_seq[] = { 0x40, 0x31, 0x1D, 0x0D, 0x1D, 0x10, -1 }; + +#ifdef CONFIG_USB_FUNCTION +static char *usb_functions[] = { +#if defined(CONFIG_USB_FUNCTION_MASS_STORAGE) || defined(CONFIG_USB_FUNCTION_UMS) + "usb_mass_storage", +#endif +#ifdef CONFIG_USB_FUNCTION_ADB + "adb", +#endif +}; + +static struct msm_hsusb_product usb_products[] = { + { + .product_id = 0x0c01, + .functions = 0x00000041, /* usb_mass_storage */ + }, + { + .product_id = 0x0c02, + .functions = 0x00000043, /* usb_mass_storage + adb */ + }, +}; +#endif + +struct msm_hsusb_platform_data msm_hsusb_pdata = { + .phy_reset = internal_phy_reset, + .phy_init_seq = hsusb_phy_init_seq, +#ifdef CONFIG_USB_FUNCTION + .vendor_id = 0x0bb4, + .product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + + .functions = usb_functions, + .num_functions = ARRAY_SIZE(usb_functions), + .products = usb_products, + .num_products = ARRAY_SIZE(usb_products), +#endif +}; + +#ifdef CONFIG_USB_FUNCTION +static struct usb_mass_storage_platform_data mass_storage_pdata = { + .nluns = 1, + .buf_size = 16384, + .vendor = "HTC ", + .product = "Android Phone ", + .release = 0x0100, +}; + +static struct platform_device usb_mass_storage_device = { + .name = "usb_mass_storage", + .id = -1, + .dev = { + .platform_data = &mass_storage_pdata, + }, +}; +#endif + +#ifdef CONFIG_USB_ANDROID +static struct android_usb_platform_data android_usb_pdata = { + .vendor_id = 0x0bb4, + .product_id = 0x0c01, + .adb_product_id = 0x0c02, + .version = 0x0100, + .product_name = "Android Phone", + .manufacturer_name = "HTC", + .nluns = 1, +}; + +static struct platform_device android_usb_device = { + .name = "android_usb", + .id = -1, + .dev = { + .platform_data = &android_usb_pdata, + }, +}; +#endif + +void __init msm_add_usb_devices(void (*phy_reset) (void)) +{ + /* setup */ + if (phy_reset) + msm_hsusb_pdata.phy_reset = phy_reset; + msm_device_hsusb.dev.platform_data = &msm_hsusb_pdata; + platform_device_register(&msm_device_hsusb); +#ifdef CONFIG_USB_FUNCTION_MASS_STORAGE + platform_device_register(&usb_mass_storage_device); +#endif +#ifdef CONFIG_USB_ANDROID + platform_device_register(&android_usb_device); +#endif +} + +static struct android_pmem_platform_data pmem_pdata = { + .name = "pmem", + .no_allocator = 1, + .cached = 1, +}; + +static struct android_pmem_platform_data pmem_adsp_pdata = { + .name = "pmem_adsp", + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data pmem_camera_pdata = { + .name = "pmem_camera", + .no_allocator = 0, + .cached = 0, +}; + +static struct android_pmem_platform_data pmem_gpu0_pdata = { + .name = "pmem_gpu0", + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct android_pmem_platform_data pmem_gpu1_pdata = { + .name = "pmem_gpu1", + .no_allocator = 1, + .cached = 0, + .buffered = 1, +}; + +static struct platform_device pmem_device = { + .name = "android_pmem", + .id = 0, + .dev = { .platform_data = &pmem_pdata }, +}; + +static struct platform_device pmem_adsp_device = { + .name = "android_pmem", + .id = 1, + .dev = { .platform_data = &pmem_adsp_pdata }, +}; + +static struct platform_device pmem_gpu0_device = { + .name = "android_pmem", + .id = 2, + .dev = { .platform_data = &pmem_gpu0_pdata }, +}; + +static struct platform_device pmem_gpu1_device = { + .name = "android_pmem", + .id = 3, + .dev = { .platform_data = &pmem_gpu1_pdata }, +}; + +static struct platform_device pmem_camera_device = { + .name = "android_pmem", + .id = 4, + .dev = { .platform_data = &pmem_camera_pdata }, +}; + +static struct resource ram_console_resource[] = { + { + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device ram_console_device = { + .name = "ram_console", + .id = -1, + .num_resources = ARRAY_SIZE(ram_console_resource), + .resource = ram_console_resource, +}; + +void __init msm_add_mem_devices(struct msm_pmem_setting *setting) +{ + if (setting->pmem_size) { + pmem_pdata.start = setting->pmem_start; + pmem_pdata.size = setting->pmem_size; + platform_device_register(&pmem_device); + } + + if (setting->pmem_adsp_size) { + pmem_adsp_pdata.start = setting->pmem_adsp_start; + pmem_adsp_pdata.size = setting->pmem_adsp_size; + platform_device_register(&pmem_adsp_device); + } + + if (setting->pmem_gpu0_size) { + pmem_gpu0_pdata.start = setting->pmem_gpu0_start; + pmem_gpu0_pdata.size = setting->pmem_gpu0_size; + platform_device_register(&pmem_gpu0_device); + } + + if (setting->pmem_gpu1_size) { + pmem_gpu1_pdata.start = setting->pmem_gpu1_start; + pmem_gpu1_pdata.size = setting->pmem_gpu1_size; + platform_device_register(&pmem_gpu1_device); + } + + if (setting->pmem_camera_size) { + pmem_camera_pdata.start = setting->pmem_camera_start; + pmem_camera_pdata.size = setting->pmem_camera_size; + platform_device_register(&pmem_camera_device); + } + + if (setting->ram_console_size) { + ram_console_resource[0].start = setting->ram_console_start; + ram_console_resource[0].end = setting->ram_console_start + + setting->ram_console_size - 1; + platform_device_register(&ram_console_device); + } +} + +#define PM_LIBPROG 0x30000061 +#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define PM_LIBVERS 0xfb837d0b +#else +#define PM_LIBVERS 0x10001 +#endif + +#if 0 +static struct platform_device *msm_serial_devices[] __initdata = { + &msm_device_uart1, + &msm_device_uart2, + &msm_device_uart3, + #ifdef CONFIG_SERIAL_MSM_HS + &msm_device_uart_dm1, + &msm_device_uart_dm2, + #endif +}; + +int __init msm_add_serial_devices(unsigned num) +{ + if (num > MSM_SERIAL_NUM) + return -EINVAL; + + return platform_device_register(msm_serial_devices[num]); +} +#endif + +#define ATAG_SMI 0x4d534D71 +/* setup calls mach->fixup, then parse_tags, parse_cmdline + * We need to setup meminfo in mach->fixup, so this function + * will need to traverse each tag to find smi tag. + */ +int __init parse_tag_smi(const struct tag *tags) +{ + int smi_sz = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SMI) { + printk(KERN_DEBUG "find the smi tag\n"); + find = 1; + break; + } + } + if (!find) + return -1; + + printk(KERN_DEBUG "parse_tag_smi: smi size = %d\n", t->u.mem.size); + smi_sz = t->u.mem.size; + return smi_sz; +} +__tagtable(ATAG_SMI, parse_tag_smi); + + +#define ATAG_HWID 0x4d534D72 +int __init parse_tag_hwid(const struct tag *tags) +{ + int hwid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_HWID) { + printk(KERN_DEBUG "find the hwid tag\n"); + find = 1; + break; + } + } + + if (find) + hwid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_hwid: hwid = 0x%x\n", hwid); + return hwid; +} +__tagtable(ATAG_HWID, parse_tag_hwid); + +#define ATAG_SKUID 0x4d534D73 +int __init parse_tag_skuid(const struct tag *tags) +{ + int skuid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_SKUID) { + printk(KERN_DEBUG "find the skuid tag\n"); + find = 1; + break; + } + } + + if (find) + skuid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_skuid: hwid = 0x%x\n", skuid); + return skuid; +} +__tagtable(ATAG_SKUID, parse_tag_skuid); + +#define ATAG_ENGINEERID 0x4d534D75 +int __init parse_tag_engineerid(const struct tag *tags) +{ + int engineerid = 0, find = 0; + struct tag *t = (struct tag *)tags; + + for (; t->hdr.size; t = tag_next(t)) { + if (t->hdr.tag == ATAG_ENGINEERID) { + printk(KERN_DEBUG "find the engineer tag\n"); + find = 1; + break; + } + } + + if (find) + engineerid = t->u.revision.rev; + printk(KERN_DEBUG "parse_tag_engineerid: hwid = 0x%x\n", engineerid); + return engineerid; +} +__tagtable(ATAG_ENGINEERID, parse_tag_engineerid); + +static int mfg_mode; +int __init board_mfg_mode_init(char *s) +{ + if (!strcmp(s, "normal")) + mfg_mode = 0; + else if (!strcmp(s, "factory2")) + mfg_mode = 1; + else if (!strcmp(s, "recovery")) + mfg_mode = 2; + else if (!strcmp(s, "charge")) + mfg_mode = 3; + + return 1; +} +__setup("androidboot.mode=", board_mfg_mode_init); + + +int board_mfg_mode(void) +{ + return mfg_mode; +} + +static int __init board_serialno_setup(char *serialno) +{ + char *str; + + if (board_mfg_mode() || !strlen(serialno)) + str = df_serialno; + else + str = serialno; +#ifdef CONFIG_USB_FUNCTION + msm_hsusb_pdata.serial_number = str; +#endif +#ifdef CONFIG_USB_ANDROID + android_usb_pdata.serial_number = str; +#endif + return 1; +} + +__setup("androidboot.serialno=", board_serialno_setup); diff --git a/arch/arm/mach-msm/dma.c b/arch/arm/mach-msm/dma.c index f5420f9585c5..bbfab84938c6 100644 --- a/arch/arm/mach-msm/dma.c +++ b/arch/arm/mach-msm/dma.c @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/dma.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -13,10 +14,15 @@ * */ +#include <linux/clk.h> +#include <linux/err.h> #include <linux/io.h> #include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> #include <mach/dma.h> +#define MODULE_NAME "msm_dmov" #define MSM_DMOV_CHANNEL_COUNT 16 enum { @@ -25,11 +31,19 @@ enum { MSM_DMOV_PRINT_FLOW = 4 }; +enum { + CLK_DIS, + CLK_TO_BE_DIS, + CLK_EN +}; + static DEFINE_SPINLOCK(msm_dmov_lock); +static struct clk *msm_dmov_clk; static unsigned int channel_active; static struct list_head ready_commands[MSM_DMOV_CHANNEL_COUNT]; static struct list_head active_commands[MSM_DMOV_CHANNEL_COUNT]; unsigned int msm_dmov_print_mask = MSM_DMOV_PRINT_ERRORS; +unsigned int clk_ctl = CLK_DIS; #define MSM_DMOV_DPRINTF(mask, format, args...) \ do { \ @@ -48,12 +62,32 @@ void msm_dmov_stop_cmd(unsigned id, struct msm_dmov_cmd *cmd, int graceful) writel((graceful << 31), DMOV_FLUSH0(id)); } +static void timer_func(unsigned long func_paramter) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&msm_dmov_lock, irq_flags); + if (clk_ctl == CLK_TO_BE_DIS) { + BUG_ON(channel_active); + clk_disable(msm_dmov_clk); + clk_ctl = CLK_DIS; + } + spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); +} +DEFINE_TIMER(timer, timer_func, 0, 0); + void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) { unsigned long irq_flags; unsigned int status; spin_lock_irqsave(&msm_dmov_lock, irq_flags); + if (clk_ctl == CLK_DIS) + clk_enable(msm_dmov_clk); + else if (clk_ctl == CLK_TO_BE_DIS) + del_timer(&timer); + clk_ctl = CLK_EN; + status = readl(DMOV_STATUS(id)); if (list_empty(&ready_commands[id]) && (status & DMOV_STATUS_CMD_PTR_RDY)) { @@ -70,6 +104,10 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd) channel_active |= 1U << id; writel(cmd->cmdptr, DMOV_CMD_PTR(id)); } else { + if (!channel_active) { + clk_ctl = CLK_TO_BE_DIS; + mod_timer(&timer, jiffies + HZ); + } if (list_empty(&active_commands[id])) PRINT_ERROR("msm_dmov_enqueue_cmd(%d), error datamover stalled, status %x\n", id, status); @@ -123,6 +161,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr) PRINT_FLOW("dmov_exec_cmdptr(%d, %x) done\n", id, cmdptr); return 0; } +EXPORT_SYMBOL(msm_dmov_exec_cmd); static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id) @@ -219,28 +258,64 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id) PRINT_FLOW("msm_datamover_irq_handler id %d, status %x\n", id, ch_status); } - if (!channel_active) - disable_irq(INT_ADM_AARM); + if (!channel_active) { + disable_irq_nosync(INT_ADM_AARM); + clk_ctl = CLK_TO_BE_DIS; + mod_timer(&timer, jiffies + HZ); + } spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); return IRQ_HANDLED; } +static int msm_dmov_suspend_noirq(struct device *dev) +{ + unsigned long irq_flags; + + spin_lock_irqsave(&msm_dmov_lock, irq_flags); + if (clk_ctl == CLK_TO_BE_DIS) { + BUG_ON(channel_active); + del_timer(&timer); + clk_disable(msm_dmov_clk); + clk_ctl = CLK_DIS; + } + spin_unlock_irqrestore(&msm_dmov_lock, irq_flags); + return 0; +} + +static struct dev_pm_ops dmov_pm = { + .suspend_noirq = msm_dmov_suspend_noirq, +}; + +static struct platform_driver msm_dmov_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .pm = &dmov_pm, + }, +}; + static int __init msm_init_datamover(void) { int i; int ret; + for (i = 0; i < MSM_DMOV_CHANNEL_COUNT; i++) { INIT_LIST_HEAD(&ready_commands[i]); INIT_LIST_HEAD(&active_commands[i]); writel(DMOV_CONFIG_IRQ_EN | DMOV_CONFIG_FORCE_TOP_PTR_RSLT | DMOV_CONFIG_FORCE_FLUSH_RSLT, DMOV_CONFIG(i)); } + msm_dmov_clk = clk_get(NULL, "adm_clk"); + if (IS_ERR(msm_dmov_clk)) + return PTR_ERR(msm_dmov_clk); ret = request_irq(INT_ADM_AARM, msm_datamover_irq_handler, 0, "msmdatamover", NULL); if (ret) return ret; disable_irq(INT_ADM_AARM); + ret = platform_driver_register(&msm_dmov_driver); + if (ret) + return ret; return 0; } arch_initcall(msm_init_datamover); - diff --git a/arch/arm/mach-msm/dma_test.c b/arch/arm/mach-msm/dma_test.c new file mode 100644 index 000000000000..c4967f97f832 --- /dev/null +++ b/arch/arm/mach-msm/dma_test.c @@ -0,0 +1,404 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> + +#include <mach/dma.h> +#include <mach/dma_test.h> + + +/********************************************************************** + * User-space testing of the DMA driver. + * Intended to be loaded as a module. We have a bunch of static + * buffers that the user-side can refer to. The main DMA is simply + * used memory-to-memory. Device DMA is best tested with the specific + * device driver in question. + */ +#define MAX_TEST_BUFFERS 40 +#define MAX_TEST_BUFFER_SIZE 65536 +static void *(buffers[MAX_TEST_BUFFERS]); +static int sizes[MAX_TEST_BUFFERS]; + +/* Anything that allocates or deallocates buffers must lock with this + * mutex. */ +static DECLARE_MUTEX(buffer_lock); + +/* Each buffer has a semaphore associated with it that will be held + * for the duration of any operations on that buffer. It also must be + * available to free the given buffer. */ +static struct semaphore buffer_sems[MAX_TEST_BUFFERS]; + +#define buffer_up(num) up(&buffer_sems[num]) +#define buffer_down(num) down(&buffer_sems[num]) + +/* Use the General Purpose DMA channel as our test channel. This channel + * should be available on any target. */ +#define TEST_CHANNEL DMOV_GP_CHAN + +struct private { + /* Each open instance is allowed a single pending + * operation. */ + struct semaphore sem; + + /* Simple command buffer. Allocated and freed by driver. */ + /* TODO: Allocate these together. */ + dmov_s *command_ptr; + + /* Indirect. */ + u32 *command_ptr_ptr; + + /* Indicates completion with pending request. */ + struct completion complete; +}; + +static void free_buffers(void) +{ + int i; + + for (i = 0; i < MAX_TEST_BUFFERS; i++) { + if (sizes[i] > 0) { + kfree(buffers[i]); + sizes[i] = 0; + } + } +} + +/* Copy between two buffers, using the DMA. */ + +/* Allocate a buffer of a requested size. */ +static int buffer_req(struct msm_dma_alloc_req *req) +{ + int i; + + if (req->size <= 0 || req->size > MAX_TEST_BUFFER_SIZE) + return -EINVAL; + + down(&buffer_lock); + + /* Find a free buffer. */ + for (i = 0; i < MAX_TEST_BUFFERS; i++) + if (sizes[i] == 0) + break; + + if (i >= MAX_TEST_BUFFERS) + goto error; + + buffers[i] = kmalloc(req->size, GFP_KERNEL | __GFP_DMA); + if (buffers[i] == 0) + goto error; + sizes[i] = req->size; + + req->bufnum = i; + + up(&buffer_lock); + return 0; + +error: + up(&buffer_lock); + return -ENOSPC; +} + +static int dma_scopy(struct msm_dma_scopy *scopy, struct private *priv) +{ + int err = 0; + dma_addr_t mapped_cmd; + dma_addr_t mapped_cmd_ptr; + + buffer_down(scopy->srcbuf); + if (scopy->srcbuf != scopy->destbuf) + buffer_down(scopy->destbuf); + + priv->command_ptr->cmd = CMD_PTR_LP | CMD_MODE_SINGLE; + priv->command_ptr->src = dma_map_single(NULL, buffers[scopy->srcbuf], + scopy->size, DMA_TO_DEVICE); + priv->command_ptr->dst = dma_map_single(NULL, buffers[scopy->destbuf], + scopy->size, DMA_FROM_DEVICE); + priv->command_ptr->len = scopy->size; + + mapped_cmd = + dma_map_single(NULL, priv->command_ptr, sizeof(*priv->command_ptr), + DMA_TO_DEVICE); + *(priv->command_ptr_ptr) = CMD_PTR_ADDR(mapped_cmd) | CMD_PTR_LP; + + mapped_cmd_ptr = dma_map_single(NULL, priv->command_ptr_ptr, + sizeof(*priv->command_ptr_ptr), + DMA_TO_DEVICE); + + msm_dmov_exec_cmd(TEST_CHANNEL, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(mapped_cmd_ptr)); + + dma_unmap_single(NULL, (dma_addr_t) mapped_cmd_ptr, + sizeof(*priv->command_ptr_ptr), DMA_TO_DEVICE); + dma_unmap_single(NULL, (dma_addr_t) mapped_cmd, + sizeof(*priv->command_ptr), DMA_TO_DEVICE); + dma_unmap_single(NULL, (dma_addr_t) priv->command_ptr->dst, + scopy->size, DMA_FROM_DEVICE); + dma_unmap_single(NULL, (dma_addr_t) priv->command_ptr->src, + scopy->size, DMA_TO_DEVICE); + + if (scopy->srcbuf != scopy->destbuf) + buffer_up(scopy->destbuf); + buffer_up(scopy->srcbuf); + + return err; +} + +static int dma_test_open(struct inode *inode, struct file *file) +{ + struct private *priv; + + printk(KERN_ALERT "%s\n", __func__); + + priv = kmalloc(sizeof(struct private), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + file->private_data = priv; + + init_MUTEX(&priv->sem); + + /* Note, that these should be allocated together so we don't + * waste 32 bytes for each. */ + + /* Allocate the command pointer. */ + priv->command_ptr = kmalloc(sizeof(&priv->command_ptr), + GFP_KERNEL | __GFP_DMA); + if (priv->command_ptr == NULL) { + kfree(priv); + return -ENOSPC; + } + + /* And the indirect pointer. */ + priv->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA); + if (priv->command_ptr_ptr == NULL) { + kfree(priv->command_ptr); + kfree(priv); + return -ENOSPC; + } + + return 0; +} + +static int dma_test_release(struct inode *inode, struct file *file) +{ + struct private *priv; + + printk(KERN_ALERT "%s\n", __func__); + + if (file->private_data != NULL) { + priv = file->private_data; + kfree(priv->command_ptr_ptr); + kfree(priv->command_ptr); + } + kfree(file->private_data); + file->private_data = NULL; + + return 0; +} + +static int dma_test_ioctl(struct inode *inode, struct file *file, + unsigned cmd, unsigned long arg) +{ + int err = 0; + int tmp; + struct msm_dma_alloc_req alloc_req; + struct msm_dma_bufxfer xfer; + struct msm_dma_scopy scopy; + struct private *priv = file->private_data; + + /* Verify user arguments. */ + if (_IOC_TYPE(cmd) != MSM_DMA_IOC_MAGIC) + return -ENOTTY; + + switch (cmd) { + case MSM_DMA_IOALLOC: + if (!access_ok(VERIFY_WRITE, (void __user *)arg, + sizeof(alloc_req))) + return -EFAULT; + if (__copy_from_user(&alloc_req, (void __user *)arg, + sizeof(alloc_req))) + return -EFAULT; + err = buffer_req(&alloc_req); + if (err < 0) + return err; + if (__copy_to_user((void __user *)arg, &alloc_req, + sizeof(alloc_req))) + return -EFAULT; + break; + + case MSM_DMA_IOFREEALL: + down(&buffer_lock); + for (tmp = 0; tmp < MAX_TEST_BUFFERS; tmp++) { + buffer_down(tmp); + if (sizes[tmp] > 0) { + kfree(buffers[tmp]); + sizes[tmp] = 0; + } + buffer_up(tmp); + } + up(&buffer_lock); + break; + + case MSM_DMA_IOWBUF: + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) + return -EFAULT; + if (xfer.bufnum < 0 || xfer.bufnum >= MAX_TEST_BUFFERS) + return -EINVAL; + buffer_down(xfer.bufnum); + if (sizes[xfer.bufnum] == 0 || + xfer.size <= 0 || xfer.size > sizes[xfer.bufnum]) { + buffer_up(xfer.bufnum); + return -EINVAL; + } + if (copy_from_user(buffers[xfer.bufnum], + (void __user *)xfer.data, xfer.size)) + err = -EFAULT; + buffer_up(xfer.bufnum); + break; + + case MSM_DMA_IORBUF: + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) + return -EFAULT; + if (xfer.bufnum < 0 || xfer.bufnum >= MAX_TEST_BUFFERS) + return -EINVAL; + buffer_down(xfer.bufnum); + if (sizes[xfer.bufnum] == 0 || + xfer.size <= 0 || xfer.size > sizes[xfer.bufnum]) { + buffer_up(xfer.bufnum); + return -EINVAL; + } + if (copy_to_user((void __user *)xfer.data, buffers[xfer.bufnum], + xfer.size)) + err = -EFAULT; + buffer_up(xfer.bufnum); + break; + + case MSM_DMA_IOSCOPY: + if (copy_from_user(&scopy, (void __user *)arg, sizeof(scopy))) + return -EFAULT; + if (scopy.srcbuf < 0 || scopy.srcbuf >= MAX_TEST_BUFFERS || + sizes[scopy.srcbuf] == 0 || + scopy.destbuf < 0 || scopy.destbuf >= MAX_TEST_BUFFERS || + sizes[scopy.destbuf] == 0 || + scopy.size > sizes[scopy.destbuf] || + scopy.size > sizes[scopy.srcbuf]) + return -EINVAL; +#if 0 + /* Test interface using memcpy. */ + memcpy(buffers[scopy.destbuf], + buffers[scopy.srcbuf], scopy.size); +#else + err = dma_scopy(&scopy, priv); +#endif + break; + + default: + return -ENOTTY; + } + + return err; +} + +/********************************************************************** + * Register ourselves as a misc device to be able to test the DMA code + * from userspace. */ + +static const struct file_operations dma_test_fops = { + .owner = THIS_MODULE, + .ioctl = dma_test_ioctl, + .open = dma_test_open, + .release = dma_test_release, +}; + +static struct miscdevice dma_test_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "msmdma", + .fops = &dma_test_fops, +}; +static int dma_test_init(void) +{ + int ret, i; + + ret = misc_register(&dma_test_dev); + if (ret < 0) + return ret; + + for (i = 0; i < MAX_TEST_BUFFERS; i++) + init_MUTEX(&buffer_sems[i]); + + printk(KERN_ALERT "%s\n", __func__); + return 0; +} + +static void dma_test_exit(void) +{ + free_buffers(); + misc_deregister(&dma_test_dev); + printk(KERN_ALERT "%s\n", __func__); +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("David Brown, Qualcomm, Incorporated"); +MODULE_DESCRIPTION("Test for MSM DMA driver"); +MODULE_VERSION("1.01"); + +module_init(dma_test_init); +module_exit(dma_test_exit); diff --git a/arch/arm/mach-msm/fiq_glue.S b/arch/arm/mach-msm/fiq_glue.S new file mode 100644 index 000000000000..9ded61cc0de8 --- /dev/null +++ b/arch/arm/mach-msm/fiq_glue.S @@ -0,0 +1,64 @@ +/* arch/arm/mach-msm/fiq_glue.S + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + + .text + + .global fiq_glue_end + +ENTRY(fiq_glue) + adr r12, fiq_glue_ctxt_addr + ldr r8, [r12] + ldmia r8, {r9, r10, sp} + + /* store pc, cpsr from previous mode */ + sub r11, lr, #4 + mrs r12, spsr + stmfd sp!, {r11-r12,lr} + + /* store r8-r14 from previous mode */ + sub sp, sp, #(7 * 4) + stmia sp, {r8-r14}^ + nop + + /* store r0-r7 from previous mode */ + stmfd sp!, {r0-r7} + + /* call func(data,regs) */ + mov r0, r10 + mov r1, sp + blx r9 + + /* restore/discard saved state */ + ldmfd sp!, {r0-r7} + add sp, sp, #(9 * 4) + ldmfd sp!, {lr} + + subs pc, lr, #4 + +fiq_glue_ctxt_addr: + .long fiq_glue_ctxt +fiq_glue_end: + +ENTRY(fiq_glue_setup) /* func, data, sp */ + ldr r3, =fiq_glue_ctxt + stmia r3, {r0-r2} + bx lr + + .data +fiq_glue_ctxt: + .long 0, 0, 0 diff --git a/arch/arm/mach-msm/generic_gpio.c b/arch/arm/mach-msm/generic_gpio.c new file mode 100644 index 000000000000..fe24d38345d0 --- /dev/null +++ b/arch/arm/mach-msm/generic_gpio.c @@ -0,0 +1,274 @@ +/* arch/arm/mach-msm/generic_gpio.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <asm/gpio.h> +#include "gpio_chip.h" + +#define GPIO_NUM_TO_CHIP_INDEX(gpio) ((gpio)>>5) + +struct gpio_state { + unsigned long flags; + int refcount; +}; + +static DEFINE_SPINLOCK(gpio_chips_lock); +static LIST_HEAD(gpio_chip_list); +static struct gpio_chip **gpio_chip_array; +static unsigned long gpio_chip_array_size; + +int register_gpio_chip(struct gpio_chip *new_gpio_chip) +{ + int err = 0; + struct gpio_chip *gpio_chip; + int i; + unsigned long irq_flags; + unsigned int chip_array_start_index, chip_array_end_index; + + new_gpio_chip->state = kzalloc((new_gpio_chip->end + 1 - new_gpio_chip->start) * sizeof(new_gpio_chip->state[0]), GFP_KERNEL); + if (new_gpio_chip->state == NULL) { + printk(KERN_ERR "register_gpio_chip: failed to allocate state\n"); + return -ENOMEM; + } + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip_array_start_index = GPIO_NUM_TO_CHIP_INDEX(new_gpio_chip->start); + chip_array_end_index = GPIO_NUM_TO_CHIP_INDEX(new_gpio_chip->end); + if (chip_array_end_index >= gpio_chip_array_size) { + struct gpio_chip **new_gpio_chip_array; + unsigned long new_gpio_chip_array_size = chip_array_end_index + 1; + + new_gpio_chip_array = kmalloc(new_gpio_chip_array_size * sizeof(new_gpio_chip_array[0]), GFP_ATOMIC); + if (new_gpio_chip_array == NULL) { + printk(KERN_ERR "register_gpio_chip: failed to allocate array\n"); + err = -ENOMEM; + goto failed; + } + for (i = 0; i < gpio_chip_array_size; i++) + new_gpio_chip_array[i] = gpio_chip_array[i]; + for (i = gpio_chip_array_size; i < new_gpio_chip_array_size; i++) + new_gpio_chip_array[i] = NULL; + gpio_chip_array = new_gpio_chip_array; + gpio_chip_array_size = new_gpio_chip_array_size; + } + list_for_each_entry(gpio_chip, &gpio_chip_list, list) { + if (gpio_chip->start > new_gpio_chip->end) { + list_add_tail(&new_gpio_chip->list, &gpio_chip->list); + goto added; + } + if (gpio_chip->end >= new_gpio_chip->start) { + printk(KERN_ERR "register_gpio_source %u-%u overlaps with %u-%u\n", + new_gpio_chip->start, new_gpio_chip->end, + gpio_chip->start, gpio_chip->end); + err = -EBUSY; + goto failed; + } + } + list_add_tail(&new_gpio_chip->list, &gpio_chip_list); +added: + for (i = chip_array_start_index; i <= chip_array_end_index; i++) { + if (gpio_chip_array[i] == NULL || gpio_chip_array[i]->start > new_gpio_chip->start) + gpio_chip_array[i] = new_gpio_chip; + } +failed: + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + if (err) + kfree(new_gpio_chip->state); + return err; +} + +static struct gpio_chip *get_gpio_chip_locked(unsigned int gpio) +{ + unsigned long i; + struct gpio_chip *chip; + + i = GPIO_NUM_TO_CHIP_INDEX(gpio); + if (i >= gpio_chip_array_size) + return NULL; + chip = gpio_chip_array[i]; + if (chip == NULL) + return NULL; + list_for_each_entry_from(chip, &gpio_chip_list, list) { + if (gpio < chip->start) + return NULL; + if (gpio <= chip->end) + return chip; + } + return NULL; +} + +static int request_gpio(unsigned int gpio, unsigned long flags) +{ + int err = 0; + struct gpio_chip *chip; + unsigned long irq_flags; + unsigned long chip_index; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip == NULL) { + err = -EINVAL; + goto err; + } + chip_index = gpio - chip->start; + if (chip->state[chip_index].refcount == 0) { + chip->configure(chip, gpio, flags); + chip->state[chip_index].flags = flags; + chip->state[chip_index].refcount++; + } else if ((flags & IRQF_SHARED) && (chip->state[chip_index].flags & IRQF_SHARED)) + chip->state[chip_index].refcount++; + else + err = -EBUSY; +err: + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + return err; +} + +int gpio_request(unsigned gpio, const char *label) +{ + return request_gpio(gpio, 0); +} +EXPORT_SYMBOL(gpio_request); + +void gpio_free(unsigned gpio) +{ + struct gpio_chip *chip; + unsigned long irq_flags; + unsigned long chip_index; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip) { + chip_index = gpio - chip->start; + chip->state[chip_index].refcount--; + } + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); +} +EXPORT_SYMBOL(gpio_free); + +static int gpio_get_irq_num(unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) +{ + int ret = -ENOTSUPP; + struct gpio_chip *chip; + unsigned long irq_flags; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip && chip->get_irq_num) + ret = chip->get_irq_num(chip, gpio, irqp, irqnumflagsp); + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + return ret; +} + +int gpio_to_irq(unsigned gpio) +{ + int ret, irq; + ret = gpio_get_irq_num(gpio, &irq, NULL); + if (ret) + return ret; + return irq; +} +EXPORT_SYMBOL(gpio_to_irq); + +int gpio_configure(unsigned int gpio, unsigned long flags) +{ + int ret = -ENOTSUPP; + struct gpio_chip *chip; + unsigned long irq_flags; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip) + ret = chip->configure(chip, gpio, flags); + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + return ret; +} +EXPORT_SYMBOL(gpio_configure); + +int gpio_direction_input(unsigned gpio) +{ + return gpio_configure(gpio, GPIOF_INPUT); +} +EXPORT_SYMBOL(gpio_direction_input); + +int gpio_direction_output(unsigned gpio, int value) +{ + gpio_set_value(gpio, value); + return gpio_configure(gpio, GPIOF_DRIVE_OUTPUT); +} +EXPORT_SYMBOL(gpio_direction_output); + +int gpio_get_value(unsigned gpio) +{ + int ret = -ENOTSUPP; + struct gpio_chip *chip; + unsigned long irq_flags; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip && chip->read) + ret = chip->read(chip, gpio); + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + return ret; +} +EXPORT_SYMBOL(gpio_get_value); + +void gpio_set_value(unsigned gpio, int on) +{ + int ret = -ENOTSUPP; + struct gpio_chip *chip; + unsigned long irq_flags; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip && chip->write) + ret = chip->write(chip, gpio, on); + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); +} +EXPORT_SYMBOL(gpio_set_value); + +int gpio_read_detect_status(unsigned int gpio) +{ + int ret = -ENOTSUPP; + struct gpio_chip *chip; + unsigned long irq_flags; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip && chip->read_detect_status) + ret = chip->read_detect_status(chip, gpio); + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + return ret; +} +EXPORT_SYMBOL(gpio_read_detect_status); + +int gpio_clear_detect_status(unsigned int gpio) +{ + int ret = -ENOTSUPP; + struct gpio_chip *chip; + unsigned long irq_flags; + + spin_lock_irqsave(&gpio_chips_lock, irq_flags); + chip = get_gpio_chip_locked(gpio); + if (chip && chip->clear_detect_status) + ret = chip->clear_detect_status(chip, gpio); + spin_unlock_irqrestore(&gpio_chips_lock, irq_flags); + return ret; +} +EXPORT_SYMBOL(gpio_clear_detect_status); diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c new file mode 100644 index 000000000000..598022906be6 --- /dev/null +++ b/arch/arm/mach-msm/gpio.c @@ -0,0 +1,746 @@ +/* linux/arch/arm/mach-msm/gpio.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <asm/io.h> +#include <asm/gpio.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/debugfs.h> +#include "gpio_chip.h" +#include "gpio_hw.h" +#include "proc_comm.h" + +#include "smd_private.h" + +enum { + GPIO_DEBUG_SLEEP = 1U << 0, +}; +static int msm_gpio_debug_mask = 0; +module_param_named(debug_mask, msm_gpio_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +/* private gpio_configure flags */ +#define MSM_GPIOF_ENABLE_INTERRUPT 0x10000000 +#define MSM_GPIOF_DISABLE_INTERRUPT 0x20000000 +#define MSM_GPIOF_ENABLE_WAKE 0x40000000 +#define MSM_GPIOF_DISABLE_WAKE 0x80000000 + +static int msm_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags); +static int msm_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp); +static int msm_gpio_read(struct gpio_chip *chip, unsigned n); +static int msm_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on); +static int msm_gpio_read_detect_status(struct gpio_chip *chip, unsigned int gpio); +static int msm_gpio_clear_detect_status(struct gpio_chip *chip, unsigned int gpio); + +struct msm_gpio_chip msm_gpio_chips[] = { + { + .regs = { + .out = GPIO_OUT_0, + .in = GPIO_IN_0, + .int_status = GPIO_INT_STATUS_0, + .int_clear = GPIO_INT_CLEAR_0, + .int_en = GPIO_INT_EN_0, + .int_edge = GPIO_INT_EDGE_0, + .int_pos = GPIO_INT_POS_0, + .oe = GPIO_OE_0, + }, + .chip = { + .start = 0, + .end = 15, + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, + { + .regs = { + .out = GPIO_OUT_1, + .in = GPIO_IN_1, + .int_status = GPIO_INT_STATUS_1, + .int_clear = GPIO_INT_CLEAR_1, + .int_en = GPIO_INT_EN_1, + .int_edge = GPIO_INT_EDGE_1, + .int_pos = GPIO_INT_POS_1, + .oe = GPIO_OE_1, + }, + .chip = { + .start = 16, +#if defined(CONFIG_ARCH_MSM7X30) + .end = 43, +#else + .end = 42, +#endif + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, + { + .regs = { + .out = GPIO_OUT_2, + .in = GPIO_IN_2, + .int_status = GPIO_INT_STATUS_2, + .int_clear = GPIO_INT_CLEAR_2, + .int_en = GPIO_INT_EN_2, + .int_edge = GPIO_INT_EDGE_2, + .int_pos = GPIO_INT_POS_2, + .oe = GPIO_OE_2, + }, + .chip = { +#if defined(CONFIG_ARCH_MSM7X30) + .start = 44, +#else + .start = 43, +#endif + .end = 67, + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, + { + .regs = { + .out = GPIO_OUT_3, + .in = GPIO_IN_3, + .int_status = GPIO_INT_STATUS_3, + .int_clear = GPIO_INT_CLEAR_3, + .int_en = GPIO_INT_EN_3, + .int_edge = GPIO_INT_EDGE_3, + .int_pos = GPIO_INT_POS_3, + .oe = GPIO_OE_3, + }, + .chip = { + .start = 68, + .end = 94, + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, + { + .regs = { + .out = GPIO_OUT_4, + .in = GPIO_IN_4, + .int_status = GPIO_INT_STATUS_4, + .int_clear = GPIO_INT_CLEAR_4, + .int_en = GPIO_INT_EN_4, + .int_edge = GPIO_INT_EDGE_4, + .int_pos = GPIO_INT_POS_4, + .oe = GPIO_OE_4, + }, + .chip = { + .start = 95, +#if defined(CONFIG_ARCH_QSD8X50) + .end = 103, +#else + .end = 106, +#endif + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, + { + .regs = { + .out = GPIO_OUT_5, + .in = GPIO_IN_5, + .int_status = GPIO_INT_STATUS_5, + .int_clear = GPIO_INT_CLEAR_5, + .int_en = GPIO_INT_EN_5, + .int_edge = GPIO_INT_EDGE_5, + .int_pos = GPIO_INT_POS_5, + .oe = GPIO_OE_5, + }, + .chip = { +#if defined(CONFIG_ARCH_QSD8X50) + .start = 104, + .end = 121, +#elif defined(CONFIG_ARCH_MSM7X30) + .start = 107, + .end = 133, +#else + .start = 107, + .end = 132, +#endif + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, +#if defined(CONFIG_ARCH_MSM_SCORPION) + { + .regs = { + .out = GPIO_OUT_6, + .in = GPIO_IN_6, + .int_status = GPIO_INT_STATUS_6, + .int_clear = GPIO_INT_CLEAR_6, + .int_en = GPIO_INT_EN_6, + .int_edge = GPIO_INT_EDGE_6, + .int_pos = GPIO_INT_POS_6, + .oe = GPIO_OE_6, + }, + .chip = { +#if defined(CONFIG_ARCH_MSM7X30) + .start = 134, + .end = 150, +#else + .start = 122, + .end = 152, +#endif + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, + { + .regs = { + .out = GPIO_OUT_7, + .in = GPIO_IN_7, + .int_status = GPIO_INT_STATUS_7, + .int_clear = GPIO_INT_CLEAR_7, + .int_en = GPIO_INT_EN_7, + .int_edge = GPIO_INT_EDGE_7, + .int_pos = GPIO_INT_POS_7, + .oe = GPIO_OE_7, + }, + .chip = { +#if defined(CONFIG_ARCH_MSM7X30) + .start = 151, + .end = 181, +#else + .start = 153, + .end = 164, +#endif + .configure = msm_gpio_configure, + .get_irq_num = msm_gpio_get_irq_num, + .read = msm_gpio_read, + .write = msm_gpio_write, + .read_detect_status = msm_gpio_read_detect_status, + .clear_detect_status = msm_gpio_clear_detect_status + } + }, +#endif +}; + +static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip) +{ + int loop_limit = 100; + unsigned pol, val, val2, intstat; + do { + val = readl(msm_chip->regs.in); + pol = readl(msm_chip->regs.int_pos); + pol = (pol & ~msm_chip->both_edge_detect) | (~val & msm_chip->both_edge_detect); + writel(pol, msm_chip->regs.int_pos); + intstat = readl(msm_chip->regs.int_status); + val2 = readl(msm_chip->regs.in); + if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0) + return; + } while (loop_limit-- > 0); + printk(KERN_ERR "msm_gpio_update_both_edge_detect, failed to reach stable state %x != %x\n", val, val2); +} + +static int msm_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +{ + struct msm_gpio_chip *msm_chip = container_of(chip, struct msm_gpio_chip, chip); + unsigned b = 1U << (n - chip->start); + unsigned v; + + v = readl(msm_chip->regs.out); + if (on) { + writel(v | b, msm_chip->regs.out); + } else { + writel(v & (~b), msm_chip->regs.out); + } + return 0; +} + +static int msm_gpio_read(struct gpio_chip *chip, unsigned n) +{ + struct msm_gpio_chip *msm_chip = container_of(chip, struct msm_gpio_chip, chip); + unsigned b = 1U << (n - chip->start); + + return (readl(msm_chip->regs.in) & b) ? 1 : 0; +} + +static int msm_gpio_read_detect_status(struct gpio_chip *chip, unsigned int gpio) +{ + struct msm_gpio_chip *msm_chip = container_of(chip, struct msm_gpio_chip, chip); + unsigned b = 1U << (gpio - chip->start); + unsigned v; + + v = readl(msm_chip->regs.int_status); +#if MSM_GPIO_BROKEN_INT_CLEAR + v |= msm_chip->int_status_copy; +#endif + return (v & b) ? 1 : 0; +} + +static int msm_gpio_clear_detect_status(struct gpio_chip *chip, unsigned int gpio) +{ + struct msm_gpio_chip *msm_chip = container_of(chip, struct msm_gpio_chip, chip); + unsigned b = 1U << (gpio - chip->start); + +#if MSM_GPIO_BROKEN_INT_CLEAR + /* Save interrupts that already triggered before we loose them. */ + /* Any interrupt that triggers between the read of int_status */ + /* and the write to int_clear will still be lost though. */ + msm_chip->int_status_copy |= readl(msm_chip->regs.int_status); + msm_chip->int_status_copy &= ~b; +#endif + writel(b, msm_chip->regs.int_clear); + msm_gpio_update_both_edge_detect(msm_chip); + return 0; +} + +int msm_gpio_configure(struct gpio_chip *chip, unsigned int gpio, unsigned long flags) +{ + struct msm_gpio_chip *msm_chip = container_of(chip, struct msm_gpio_chip, chip); + unsigned b = 1U << (gpio - chip->start); + unsigned v; + + if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) + msm_gpio_write(chip, gpio, flags & GPIOF_OUTPUT_HIGH); + + if (flags & (GPIOF_INPUT | GPIOF_DRIVE_OUTPUT)) { + v = readl(msm_chip->regs.oe); + if (flags & GPIOF_DRIVE_OUTPUT) { + writel(v | b, msm_chip->regs.oe); + } else { + writel(v & (~b), msm_chip->regs.oe); + } + } + + if (flags & (IRQF_TRIGGER_MASK | GPIOF_IRQF_TRIGGER_NONE)) { + v = readl(msm_chip->regs.int_edge); + if (flags & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) { + writel(v | b, msm_chip->regs.int_edge); + irq_desc[MSM_GPIO_TO_INT(gpio)].handle_irq = handle_edge_irq; + } else { + writel(v & (~b), msm_chip->regs.int_edge); + irq_desc[MSM_GPIO_TO_INT(gpio)].handle_irq = handle_level_irq; + } + if ((flags & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) == (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) { + msm_chip->both_edge_detect |= b; + msm_gpio_update_both_edge_detect(msm_chip); + } else { + msm_chip->both_edge_detect &= ~b; + v = readl(msm_chip->regs.int_pos); + if (flags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) { + writel(v | b, msm_chip->regs.int_pos); + } else { + writel(v & (~b), msm_chip->regs.int_pos); + } + } + } + + /* used by msm_gpio_irq_mask and msm_gpio_irq_unmask */ + if (flags & (MSM_GPIOF_ENABLE_INTERRUPT | MSM_GPIOF_DISABLE_INTERRUPT)) { + v = readl(msm_chip->regs.int_edge); + /* level triggered interrupts are also latched */ + if (!(v & b)) + msm_gpio_clear_detect_status(chip, gpio); + if (flags & MSM_GPIOF_ENABLE_INTERRUPT) { + msm_chip->int_enable[0] |= b; + } else { + msm_chip->int_enable[0] &= ~b; + } + writel(msm_chip->int_enable[0], msm_chip->regs.int_en); + } + + if (flags & (MSM_GPIOF_ENABLE_WAKE | MSM_GPIOF_DISABLE_WAKE)) { + if (flags & MSM_GPIOF_ENABLE_WAKE) + msm_chip->int_enable[1] |= b; + else + msm_chip->int_enable[1] &= ~b; + } + + return 0; +} + +static int msm_gpio_get_irq_num(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp) +{ + *irqp = MSM_GPIO_TO_INT(gpio); + if (irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + + +static void msm_gpio_irq_ack(unsigned int irq) +{ + gpio_clear_detect_status(irq - NR_MSM_IRQS); +} + +static void msm_gpio_irq_mask(unsigned int irq) +{ + gpio_configure(irq - NR_MSM_IRQS, MSM_GPIOF_DISABLE_INTERRUPT); +} + +static void msm_gpio_irq_unmask(unsigned int irq) +{ + gpio_configure(irq - NR_MSM_IRQS, MSM_GPIOF_ENABLE_INTERRUPT); +} + +static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on) +{ + return gpio_configure(irq - NR_MSM_IRQS, on ? MSM_GPIOF_ENABLE_WAKE : MSM_GPIOF_DISABLE_WAKE); +} + + +static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) +{ + return gpio_configure(irq - NR_MSM_IRQS, flow_type); +} + +static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + int i, j, m; + unsigned v; + + for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { + struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i]; + v = readl(msm_chip->regs.int_status); + v &= msm_chip->int_enable[0]; + while (v) { + m = v & -v; + j = fls(m) - 1; + /* printk("msm_gpio_irq_handler %08x %08x bit %d gpio %d irq %d\n", v, m, j, msm_chip->chip.start + j, NR_MSM_IRQS + msm_chip->chip.start + j); */ + v &= ~m; + generic_handle_irq(NR_MSM_IRQS + msm_chip->chip.start + j); + } + } + desc->chip->ack(irq); +} + +static struct irq_chip msm_gpio_irq_chip = { + .name = "msmgpio", + .ack = msm_gpio_irq_ack, + .mask = msm_gpio_irq_mask, + .unmask = msm_gpio_irq_unmask, + .set_wake = msm_gpio_irq_set_wake, + .set_type = msm_gpio_irq_set_type, +}; + +#define NUM_GPIO_SMEM_BANKS 6 +#define GPIO_SMEM_NUM_GROUPS 2 +#define GPIO_SMEM_MAX_PC_INTERRUPTS 8 +struct tramp_gpio_smem +{ + uint16_t num_fired[GPIO_SMEM_NUM_GROUPS]; + uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS]; + uint32_t enabled[NUM_GPIO_SMEM_BANKS]; + uint32_t detection[NUM_GPIO_SMEM_BANKS]; + uint32_t polarity[NUM_GPIO_SMEM_BANKS]; +}; + +static void msm_gpio_sleep_int(unsigned long arg) +{ + int i, j; + struct tramp_gpio_smem *smem_gpio; + + BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32); + +#ifdef CONFIG_MACH_SMD + smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio)); +#else + smem_gpio = NULL; +#endif + if (smem_gpio == NULL) + return; + + local_irq_disable(); + for(i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) { + int count = smem_gpio->num_fired[i]; + for(j = 0; j < count; j++) { + /* TODO: Check mask */ + generic_handle_irq(MSM_GPIO_TO_INT(smem_gpio->fired[i][j])); + } + } + local_irq_enable(); +} + +static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0); + +void msm_gpio_enter_sleep(int from_idle) +{ + int i; + struct tramp_gpio_smem *smem_gpio; + +#ifdef CONFIG_MSM_SMD + smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio)); +#else + smem_gpio = NULL; +#endif + + if (smem_gpio) { + for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) { + smem_gpio->enabled[i] = 0; + smem_gpio->detection[i] = 0; + smem_gpio->polarity[i] = 0; + } + } + + for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { + writel(msm_gpio_chips[i].int_enable[!from_idle], msm_gpio_chips[i].regs.int_en); + if (smem_gpio) { + uint32_t tmp; + int start, index, shiftl, shiftr; + start = msm_gpio_chips[i].chip.start; + index = start / 32; + shiftl = start % 32; + shiftr = 32 - shiftl; + tmp = msm_gpio_chips[i].int_enable[!from_idle]; + smem_gpio->enabled[index] |= tmp << shiftl; + smem_gpio->enabled[index+1] |= tmp >> shiftr; + smem_gpio->detection[index] |= readl(msm_gpio_chips[i].regs.int_edge) << shiftl; + smem_gpio->detection[index+1] |= readl(msm_gpio_chips[i].regs.int_edge) >> shiftr; + smem_gpio->polarity[index] |= readl(msm_gpio_chips[i].regs.int_pos) << shiftl; + smem_gpio->polarity[index+1] |= readl(msm_gpio_chips[i].regs.int_pos) >> shiftr; + } + } + + if (smem_gpio) { + if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP) + for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) { + printk("msm_gpio_enter_sleep gpio %d-%d: enable" + " %08x, edge %08x, polarity %08x\n", + i * 32, i * 32 + 31, + smem_gpio->enabled[i], + smem_gpio->detection[i], + smem_gpio->polarity[i]); + } + for(i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) + smem_gpio->num_fired[i] = 0; + } +} + +void msm_gpio_exit_sleep(void) +{ + int i; + struct tramp_gpio_smem *smem_gpio; + +#ifdef CONFIG_MSM_SMD + smem_gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*smem_gpio)); +#else + smem_gpio = NULL; +#endif + + for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { + writel(msm_gpio_chips[i].int_enable[0], msm_gpio_chips[i].regs.int_en); + } + + if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) { + if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP) + printk(KERN_INFO "gpio: fired %x %x\n", + smem_gpio->num_fired[0], smem_gpio->num_fired[1]); + tasklet_schedule(&msm_gpio_sleep_int_tasklet); + } +} + +static int __init msm_init_gpio(void) +{ + int i; + + for (i = NR_MSM_IRQS; i < NR_MSM_IRQS + NR_GPIO_IRQS; i++) { + set_irq_chip(i, &msm_gpio_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { + writel(0, msm_gpio_chips[i].regs.int_en); + register_gpio_chip(&msm_gpio_chips[i].chip); + } + + set_irq_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler); + set_irq_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler); + set_irq_wake(INT_GPIO_GROUP1, 1); + set_irq_wake(INT_GPIO_GROUP2, 2); + return 0; +} + +postcore_initcall(msm_init_gpio); + +int gpio_tlmm_config(unsigned config, unsigned disable) +{ + return msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, &disable); +} +EXPORT_SYMBOL(gpio_tlmm_config); + +int msm_gpios_request_enable(const struct msm_gpio *table, int size) +{ + int rc = msm_gpios_request(table, size); + if (rc) + return rc; + rc = msm_gpios_enable(table, size); + if (rc) + msm_gpios_free(table, size); + return rc; +} +EXPORT_SYMBOL(msm_gpios_request_enable); + +void msm_gpios_disable_free(const struct msm_gpio *table, int size) +{ + msm_gpios_disable(table, size); + msm_gpios_free(table, size); +} +EXPORT_SYMBOL(msm_gpios_disable_free); + +int msm_gpios_request(const struct msm_gpio *table, int size) +{ + int rc; + int i; + const struct msm_gpio *g; + for (i = 0; i < size; i++) { + g = table + i; + rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label); + if (rc) { + pr_err("gpio_request(%d) <%s> failed: %d\n", + GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc); + goto err; + } + } + return 0; +err: + msm_gpios_free(table, i); + return rc; +} +EXPORT_SYMBOL(msm_gpios_request); + +void msm_gpios_free(const struct msm_gpio *table, int size) +{ + int i; + const struct msm_gpio *g; + for (i = size-1; i >= 0; i--) { + g = table + i; + gpio_free(GPIO_PIN(g->gpio_cfg)); + } +} +EXPORT_SYMBOL(msm_gpios_free); + +int msm_gpios_enable(const struct msm_gpio *table, int size) +{ + int rc; + int i; + const struct msm_gpio *g; + for (i = 0; i < size; i++) { + g = table + i; + rc = gpio_tlmm_config(g->gpio_cfg, GPIO_ENABLE); + if (rc) { + pr_err("gpio_tlmm_config(0x%08x, GPIO_ENABLE)" + " <%s> failed: %d\n", + g->gpio_cfg, g->label ?: "?", rc); + pr_err("pin %d func %d dir %d pull %d drvstr %d\n", + GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg), + GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg), + GPIO_DRVSTR(g->gpio_cfg)); + goto err; + } + } + return 0; +err: + msm_gpios_disable(table, i); + return rc; +} +EXPORT_SYMBOL(msm_gpios_enable); + +void msm_gpios_disable(const struct msm_gpio *table, int size) +{ + int rc; + int i; + const struct msm_gpio *g; + for (i = size-1; i >= 0; i--) { + g = table + i; + rc = gpio_tlmm_config(g->gpio_cfg, GPIO_DISABLE); + if (rc) { + pr_err("gpio_tlmm_config(0x%08x, GPIO_DISABLE)" + " <%s> failed: %d\n", + g->gpio_cfg, g->label ?: "?", rc); + pr_err("pin %d func %d dir %d pull %d drvstr %d\n", + GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg), + GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg), + GPIO_DRVSTR(g->gpio_cfg)); + } + } +} +EXPORT_SYMBOL(msm_gpios_disable); + +#if defined(CONFIG_DEBUG_FS) + +static int msm_gpio_debug_result = 1; + +static int gpio_enable_set(void *data, u64 val) +{ + msm_gpio_debug_result = gpio_tlmm_config(val, 0); + return 0; +} +static int gpio_disable_set(void *data, u64 val) +{ + msm_gpio_debug_result = gpio_tlmm_config(val, 1); + return 0; +} + +static int gpio_debug_get(void *data, u64 *val) +{ + unsigned int result = msm_gpio_debug_result; + msm_gpio_debug_result = 1; + if (result) + *val = 1; + else + *val = 0; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(gpio_enable_fops, gpio_debug_get, + gpio_enable_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(gpio_disable_fops, gpio_debug_get, + gpio_disable_set, "%llu\n"); + +static int __init gpio_debug_init(void) +{ + struct dentry *dent; + dent = debugfs_create_dir("gpio", 0); + if (IS_ERR(dent)) + return 0; + + debugfs_create_file("enable", 0644, dent, 0, &gpio_enable_fops); + debugfs_create_file("disable", 0644, dent, 0, &gpio_disable_fops); + return 0; +} + +device_initcall(gpio_debug_init); +#endif + diff --git a/arch/arm/mach-msm/gpio.h b/arch/arm/mach-msm/gpio.h new file mode 100644 index 000000000000..b411bc1e3c8f --- /dev/null +++ b/arch/arm/mach-msm/gpio.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_GPIO_H_ +#define _ARCH_ARM_MACH_MSM_GPIO_H_ + +void msm_gpio_enter_sleep(int from_idle); +void msm_gpio_exit_sleep(void); + +#endif diff --git a/arch/arm/mach-msm/gpio_chip.h b/arch/arm/mach-msm/gpio_chip.h new file mode 100644 index 000000000000..39ccc0f0ed8e --- /dev/null +++ b/arch/arm/mach-msm/gpio_chip.h @@ -0,0 +1,61 @@ +/* arch/arm/mach-msm/gpio_chip.h + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _LINUX_GPIO_CHIP_H +#define _LINUX_GPIO_CHIP_H + +#include <linux/list.h> + +struct gpio_chip { + struct list_head list; + struct gpio_state *state; + + unsigned int start; + unsigned int end; + + int (*configure)(struct gpio_chip *chip, unsigned int gpio, unsigned long flags); + int (*get_irq_num)(struct gpio_chip *chip, unsigned int gpio, unsigned int *irqp, unsigned long *irqnumflagsp); + int (*read)(struct gpio_chip *chip, unsigned int gpio); + int (*write)(struct gpio_chip *chip, unsigned int gpio, unsigned on); + int (*read_detect_status)(struct gpio_chip *chip, unsigned int gpio); + int (*clear_detect_status)(struct gpio_chip *chip, unsigned int gpio); +}; + +struct msm_gpio_regs { + void __iomem *out; + void __iomem *in; + void __iomem *int_status; + void __iomem *int_clear; + void __iomem *int_en; + void __iomem *int_edge; + void __iomem *int_pos; + void __iomem *oe; +}; + +#define MSM_GPIO_BROKEN_INT_CLEAR 1 + +struct msm_gpio_chip { + struct gpio_chip chip; + struct msm_gpio_regs regs; +#if MSM_GPIO_BROKEN_INT_CLEAR + unsigned int_status_copy; +#endif + unsigned int both_edge_detect; + unsigned int int_enable[2]; /* 0: awake, 1: sleep */ +}; + +int register_gpio_chip(struct gpio_chip *gpio_chip); + +#endif diff --git a/arch/arm/mach-msm/gpio_hw-7x30.h b/arch/arm/mach-msm/gpio_hw-7x30.h new file mode 100644 index 000000000000..9142e7e6045c --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw-7x30.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_7X30_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_7X30_H + +/* output value */ +#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */ +#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 43-16 */ +#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-44 */ +#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */ +#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 106-95 */ +#define GPIO_OUT_5 GPIO1_REG(0x50) /* gpio 133-107 */ +#define GPIO_OUT_6 GPIO1_REG(0xC4) /* gpio 150-134 */ +#define GPIO_OUT_7 GPIO1_REG(0x214) /* gpio 181-151 */ + +/* same pin map as above, output enable */ +#define GPIO_OE_0 GPIO1_REG(0x10) +#define GPIO_OE_1 GPIO2_REG(0x08) +#define GPIO_OE_2 GPIO1_REG(0x14) +#define GPIO_OE_3 GPIO1_REG(0x18) +#define GPIO_OE_4 GPIO1_REG(0x1C) +#define GPIO_OE_5 GPIO1_REG(0x54) +#define GPIO_OE_6 GPIO1_REG(0xC8) +#define GPIO_OE_7 GPIO1_REG(0x218) + +/* same pin map as above, input read */ +#define GPIO_IN_0 GPIO1_REG(0x34) +#define GPIO_IN_1 GPIO2_REG(0x20) +#define GPIO_IN_2 GPIO1_REG(0x38) +#define GPIO_IN_3 GPIO1_REG(0x3C) +#define GPIO_IN_4 GPIO1_REG(0x40) +#define GPIO_IN_5 GPIO1_REG(0x44) +#define GPIO_IN_6 GPIO1_REG(0xCC) +#define GPIO_IN_7 GPIO1_REG(0x21C) + +/* same pin map as above, 1=edge 0=level interrup */ +#define GPIO_INT_EDGE_0 GPIO1_REG(0x60) +#define GPIO_INT_EDGE_1 GPIO2_REG(0x50) +#define GPIO_INT_EDGE_2 GPIO1_REG(0x64) +#define GPIO_INT_EDGE_3 GPIO1_REG(0x68) +#define GPIO_INT_EDGE_4 GPIO1_REG(0x6C) +#define GPIO_INT_EDGE_5 GPIO1_REG(0xC0) +#define GPIO_INT_EDGE_6 GPIO1_REG(0xD0) +#define GPIO_INT_EDGE_7 GPIO1_REG(0x240) + +/* same pin map as above, 1=positive 0=negative */ +#define GPIO_INT_POS_0 GPIO1_REG(0x70) +#define GPIO_INT_POS_1 GPIO2_REG(0x58) +#define GPIO_INT_POS_2 GPIO1_REG(0x74) +#define GPIO_INT_POS_3 GPIO1_REG(0x78) +#define GPIO_INT_POS_4 GPIO1_REG(0x7C) +#define GPIO_INT_POS_5 GPIO1_REG(0xBC) +#define GPIO_INT_POS_6 GPIO1_REG(0xD4) +#define GPIO_INT_POS_7 GPIO1_REG(0x228) + +/* same pin map as above, interrupt enable */ +#define GPIO_INT_EN_0 GPIO1_REG(0x80) +#define GPIO_INT_EN_1 GPIO2_REG(0x60) +#define GPIO_INT_EN_2 GPIO1_REG(0x84) +#define GPIO_INT_EN_3 GPIO1_REG(0x88) +#define GPIO_INT_EN_4 GPIO1_REG(0x8C) +#define GPIO_INT_EN_5 GPIO1_REG(0xB8) +#define GPIO_INT_EN_6 GPIO1_REG(0xD8) +#define GPIO_INT_EN_7 GPIO1_REG(0x22C) + +/* same pin map as above, write 1 to clear interrupt */ +#define GPIO_INT_CLEAR_0 GPIO1_REG(0x90) +#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68) +#define GPIO_INT_CLEAR_2 GPIO1_REG(0x94) +#define GPIO_INT_CLEAR_3 GPIO1_REG(0x98) +#define GPIO_INT_CLEAR_4 GPIO1_REG(0x9C) +#define GPIO_INT_CLEAR_5 GPIO1_REG(0xB4) +#define GPIO_INT_CLEAR_6 GPIO1_REG(0xDC) +#define GPIO_INT_CLEAR_7 GPIO1_REG(0x230) + +/* same pin map as above, 1=interrupt pending */ +#define GPIO_INT_STATUS_0 GPIO1_REG(0xA0) +#define GPIO_INT_STATUS_1 GPIO2_REG(0x70) +#define GPIO_INT_STATUS_2 GPIO1_REG(0xA4) +#define GPIO_INT_STATUS_3 GPIO1_REG(0xA8) +#define GPIO_INT_STATUS_4 GPIO1_REG(0xAC) +#define GPIO_INT_STATUS_5 GPIO1_REG(0xB0) +#define GPIO_INT_STATUS_6 GPIO1_REG(0xE0) +#define GPIO_INT_STATUS_7 GPIO1_REG(0x234) + +#endif diff --git a/arch/arm/mach-msm/gpio_hw-7xxx.h b/arch/arm/mach-msm/gpio_hw-7xxx.h new file mode 100644 index 000000000000..17b4d80c2310 --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw-7xxx.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland <swetland@google.com> + */ + +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_7XXX_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_7XXX_H + +/* output value */ +#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */ +#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 42-16 */ +#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-43 */ +#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */ +#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 106-95 */ +#define GPIO_OUT_5 GPIO1_REG(0x50) /* gpio 107-121 */ + +/* same pin map as above, output enable */ +#define GPIO_OE_0 GPIO1_REG(0x10) +#define GPIO_OE_1 GPIO2_REG(0x08) +#define GPIO_OE_2 GPIO1_REG(0x14) +#define GPIO_OE_3 GPIO1_REG(0x18) +#define GPIO_OE_4 GPIO1_REG(0x1C) +#define GPIO_OE_5 GPIO1_REG(0x54) + +/* same pin map as above, input read */ +#define GPIO_IN_0 GPIO1_REG(0x34) +#define GPIO_IN_1 GPIO2_REG(0x20) +#define GPIO_IN_2 GPIO1_REG(0x38) +#define GPIO_IN_3 GPIO1_REG(0x3C) +#define GPIO_IN_4 GPIO1_REG(0x40) +#define GPIO_IN_5 GPIO1_REG(0x44) + +/* same pin map as above, 1=edge 0=level interrup */ +#define GPIO_INT_EDGE_0 GPIO1_REG(0x60) +#define GPIO_INT_EDGE_1 GPIO2_REG(0x50) +#define GPIO_INT_EDGE_2 GPIO1_REG(0x64) +#define GPIO_INT_EDGE_3 GPIO1_REG(0x68) +#define GPIO_INT_EDGE_4 GPIO1_REG(0x6C) +#define GPIO_INT_EDGE_5 GPIO1_REG(0xC0) + +/* same pin map as above, 1=positive 0=negative */ +#define GPIO_INT_POS_0 GPIO1_REG(0x70) +#define GPIO_INT_POS_1 GPIO2_REG(0x58) +#define GPIO_INT_POS_2 GPIO1_REG(0x74) +#define GPIO_INT_POS_3 GPIO1_REG(0x78) +#define GPIO_INT_POS_4 GPIO1_REG(0x7C) +#define GPIO_INT_POS_5 GPIO1_REG(0xBC) + +/* same pin map as above, interrupt enable */ +#define GPIO_INT_EN_0 GPIO1_REG(0x80) +#define GPIO_INT_EN_1 GPIO2_REG(0x60) +#define GPIO_INT_EN_2 GPIO1_REG(0x84) +#define GPIO_INT_EN_3 GPIO1_REG(0x88) +#define GPIO_INT_EN_4 GPIO1_REG(0x8C) +#define GPIO_INT_EN_5 GPIO1_REG(0xB8) + +/* same pin map as above, write 1 to clear interrupt */ +#define GPIO_INT_CLEAR_0 GPIO1_REG(0x90) +#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68) +#define GPIO_INT_CLEAR_2 GPIO1_REG(0x94) +#define GPIO_INT_CLEAR_3 GPIO1_REG(0x98) +#define GPIO_INT_CLEAR_4 GPIO1_REG(0x9C) +#define GPIO_INT_CLEAR_5 GPIO1_REG(0xB4) + +/* same pin map as above, 1=interrupt pending */ +#define GPIO_INT_STATUS_0 GPIO1_REG(0xA0) +#define GPIO_INT_STATUS_1 GPIO2_REG(0x70) +#define GPIO_INT_STATUS_2 GPIO1_REG(0xA4) +#define GPIO_INT_STATUS_3 GPIO1_REG(0xA8) +#define GPIO_INT_STATUS_4 GPIO1_REG(0xAC) +#define GPIO_INT_STATUS_5 GPIO1_REG(0xB0) + +#endif diff --git a/arch/arm/mach-msm/gpio_hw-8xxx.h b/arch/arm/mach-msm/gpio_hw-8xxx.h new file mode 100644 index 000000000000..91b1a43298fd --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw-8xxx.h @@ -0,0 +1,112 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_8XXX_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_8XXX_H + +/* output value */ +#define GPIO_OUT_0 GPIO1_REG(0x00) /* gpio 15-0 */ +#define GPIO_OUT_1 GPIO2_REG(0x00) /* gpio 42-16 */ +#define GPIO_OUT_2 GPIO1_REG(0x04) /* gpio 67-43 */ +#define GPIO_OUT_3 GPIO1_REG(0x08) /* gpio 94-68 */ +#define GPIO_OUT_4 GPIO1_REG(0x0C) /* gpio 103-95 */ +#define GPIO_OUT_5 GPIO1_REG(0x10) /* gpio 121-104 */ +#define GPIO_OUT_6 GPIO1_REG(0x14) /* gpio 152-122 */ +#define GPIO_OUT_7 GPIO1_REG(0x18) /* gpio 164-153 */ + +/* same pin map as above, output enable */ +#define GPIO_OE_0 GPIO1_REG(0x20) +#define GPIO_OE_1 GPIO2_REG(0x08) +#define GPIO_OE_2 GPIO1_REG(0x24) +#define GPIO_OE_3 GPIO1_REG(0x28) +#define GPIO_OE_4 GPIO1_REG(0x2C) +#define GPIO_OE_5 GPIO1_REG(0x30) +#define GPIO_OE_6 GPIO1_REG(0x34) +#define GPIO_OE_7 GPIO1_REG(0x38) + +/* same pin map as above, input read */ +#define GPIO_IN_0 GPIO1_REG(0x50) +#define GPIO_IN_1 GPIO2_REG(0x20) +#define GPIO_IN_2 GPIO1_REG(0x54) +#define GPIO_IN_3 GPIO1_REG(0x58) +#define GPIO_IN_4 GPIO1_REG(0x5C) +#define GPIO_IN_5 GPIO1_REG(0x60) +#define GPIO_IN_6 GPIO1_REG(0x64) +#define GPIO_IN_7 GPIO1_REG(0x68) + +/* same pin map as above, 1=edge 0=level interrup */ +#define GPIO_INT_EDGE_0 GPIO1_REG(0x70) +#define GPIO_INT_EDGE_1 GPIO2_REG(0x50) +#define GPIO_INT_EDGE_2 GPIO1_REG(0x74) +#define GPIO_INT_EDGE_3 GPIO1_REG(0x78) +#define GPIO_INT_EDGE_4 GPIO1_REG(0x7C) +#define GPIO_INT_EDGE_5 GPIO1_REG(0x80) +#define GPIO_INT_EDGE_6 GPIO1_REG(0x84) +#define GPIO_INT_EDGE_7 GPIO1_REG(0x88) + +/* same pin map as above, 1=positive 0=negative */ +#define GPIO_INT_POS_0 GPIO1_REG(0x90) +#define GPIO_INT_POS_1 GPIO2_REG(0x58) +#define GPIO_INT_POS_2 GPIO1_REG(0x94) +#define GPIO_INT_POS_3 GPIO1_REG(0x98) +#define GPIO_INT_POS_4 GPIO1_REG(0x9C) +#define GPIO_INT_POS_5 GPIO1_REG(0xA0) +#define GPIO_INT_POS_6 GPIO1_REG(0xA4) +#define GPIO_INT_POS_7 GPIO1_REG(0xA8) + +/* same pin map as above, interrupt enable */ +#define GPIO_INT_EN_0 GPIO1_REG(0xB0) +#define GPIO_INT_EN_1 GPIO2_REG(0x60) +#define GPIO_INT_EN_2 GPIO1_REG(0xB4) +#define GPIO_INT_EN_3 GPIO1_REG(0xB8) +#define GPIO_INT_EN_4 GPIO1_REG(0xBC) +#define GPIO_INT_EN_5 GPIO1_REG(0xC0) +#define GPIO_INT_EN_6 GPIO1_REG(0xC4) +#define GPIO_INT_EN_7 GPIO1_REG(0xC8) + +/* same pin map as above, write 1 to clear interrupt */ +#define GPIO_INT_CLEAR_0 GPIO1_REG(0xD0) +#define GPIO_INT_CLEAR_1 GPIO2_REG(0x68) +#define GPIO_INT_CLEAR_2 GPIO1_REG(0xD4) +#define GPIO_INT_CLEAR_3 GPIO1_REG(0xD8) +#define GPIO_INT_CLEAR_4 GPIO1_REG(0xDC) +#define GPIO_INT_CLEAR_5 GPIO1_REG(0xE0) +#define GPIO_INT_CLEAR_6 GPIO1_REG(0xE4) +#define GPIO_INT_CLEAR_7 GPIO1_REG(0xE8) + +/* same pin map as above, 1=interrupt pending */ +#define GPIO_INT_STATUS_0 GPIO1_REG(0xF0) +#define GPIO_INT_STATUS_1 GPIO2_REG(0x70) +#define GPIO_INT_STATUS_2 GPIO1_REG(0xF4) +#define GPIO_INT_STATUS_3 GPIO1_REG(0xF8) +#define GPIO_INT_STATUS_4 GPIO1_REG(0xFC) +#define GPIO_INT_STATUS_5 GPIO1_REG(0x100) +#define GPIO_INT_STATUS_6 GPIO1_REG(0x104) +#define GPIO_INT_STATUS_7 GPIO1_REG(0x108) + +#endif diff --git a/arch/arm/mach-msm/gpio_hw.h b/arch/arm/mach-msm/gpio_hw.h new file mode 100644 index 000000000000..cc6b036c1dbc --- /dev/null +++ b/arch/arm/mach-msm/gpio_hw.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_GPIO_HW_H +#define __ARCH_ARM_MACH_MSM_GPIO_HW_H + +#include <mach/msm_iomap.h> + +/* see 80-VA736-2 Rev C pp 695-751 +** +** These are actually the *shadow* gpio registers, since the +** real ones (which allow full access) are only available to the +** ARM9 side of the world. +** +** Since the _BASE need to be page-aligned when we're mapping them +** to virtual addresses, adjust for the additional offset in these +** macros. +*/ + +#if defined(CONFIG_ARCH_MSM7X30) +#define GPIO1_REG(off) (MSM_GPIO1_BASE + (off)) +#define GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off)) +#else +#define GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off)) +#define GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off)) +#endif + +#if defined(CONFIG_ARCH_QSD8X50) +#include "gpio_hw-8xxx.h" +#elif defined(CONFIG_ARCH_MSM7X30) +#include "gpio_hw-7x30.h" +#else +#include "gpio_hw-7xxx.h" +#endif + +#endif diff --git a/arch/arm/mach-msm/htc_acoustic.c b/arch/arm/mach-msm/htc_acoustic.c new file mode 100644 index 000000000000..3de71dddb589 --- /dev/null +++ b/arch/arm/mach-msm/htc_acoustic.c @@ -0,0 +1,239 @@ +/* arch/arm/mach-msm/htc_acoustic.c + * + * Copyright (C) 2007-2008 HTC Corporation + * Author: Laurence Chen <Laurence_Chen@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/uaccess.h> +#include <linux/mutex.h> +#include <linux/sched.h> + +#include <mach/msm_smd.h> +#include <mach/msm_rpcrouter.h> +#include <mach/msm_iomap.h> + +#include "smd_private.h" + +#define ACOUSTIC_IOCTL_MAGIC 'p' +#define ACOUSTIC_ARM11_DONE _IOW(ACOUSTIC_IOCTL_MAGIC, 22, unsigned int) + +#define HTCRPOG 0x30100002 +#define HTCVERS 0 +#define ONCRPC_SET_MIC_BIAS_PROC (1) +#define ONCRPC_ACOUSTIC_INIT_PROC (5) +#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (6) + +#define HTC_ACOUSTIC_TABLE_SIZE (0x10000) + +#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args) +#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args) + +struct set_smem_req { + struct rpc_request_hdr hdr; + uint32_t size; +}; + +struct set_smem_rep { + struct rpc_reply_hdr hdr; + int n; +}; + +struct set_acoustic_req { + struct rpc_request_hdr hdr; +}; + +struct set_acoustic_rep { + struct rpc_reply_hdr hdr; + int n; +}; + +static uint32_t htc_acoustic_vir_addr; +static struct msm_rpc_endpoint *endpoint; +static struct mutex api_lock; + +static int acoustic_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long pgoff, delta; + int rc = -EINVAL; + size_t size; + + D("mmap\n"); + + mutex_lock(&api_lock); + + size = vma->vm_end - vma->vm_start; + + if (vma->vm_pgoff != 0) { + E("mmap failed: page offset %lx\n", vma->vm_pgoff); + goto done; + } + + if (!htc_acoustic_vir_addr) { + E("mmap failed: smem region not allocated\n"); + rc = -EIO; + goto done; + } + + pgoff = MSM_SHARED_RAM_PHYS + + (htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE); + delta = PAGE_ALIGN(pgoff) - pgoff; + + if (size + delta > HTC_ACOUSTIC_TABLE_SIZE) { + E("mmap failed: size %d\n", size); + goto done; + } + + pgoff += delta; + vma->vm_flags |= VM_IO | VM_RESERVED; + + rc = io_remap_pfn_range(vma, vma->vm_start, pgoff >> PAGE_SHIFT, + size, vma->vm_page_prot); + + if (rc < 0) + E("mmap failed: remap error %d\n", rc); + +done: mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_open(struct inode *inode, struct file *file) +{ + int rc = -EIO; + struct set_smem_req req_smem; + struct set_smem_rep rep_smem; + + D("open\n"); + + mutex_lock(&api_lock); + + if (!htc_acoustic_vir_addr) { + if (endpoint == NULL) { + endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0); + if (IS_ERR(endpoint)) { + E("init rpc failed! rc = %ld\n", + PTR_ERR(endpoint)); + endpoint = NULL; + goto done; + } + } + + req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ALLOC_ACOUSTIC_MEM_PROC, + &req_smem, sizeof(req_smem), + &rep_smem, sizeof(rep_smem), + 5 * HZ); + + if (rep_smem.n != 0 || rc < 0) { + E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n", + rc); + goto done; + } + htc_acoustic_vir_addr = + (uint32_t)smem_alloc(SMEM_ID_VENDOR1, + HTC_ACOUSTIC_TABLE_SIZE); + if (!htc_acoustic_vir_addr) { + E("open failed: smem_alloc error\n"); + goto done; + } + } + + rc = 0; +done: + mutex_unlock(&api_lock); + return rc; +} + +static int acoustic_release(struct inode *inode, struct file *file) +{ + D("release\n"); + return 0; +} + +static long acoustic_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rc, reply_value; + struct set_acoustic_req req; + struct set_acoustic_rep rep; + + D("ioctl\n"); + + mutex_lock(&api_lock); + + switch (cmd) { + case ACOUSTIC_ARM11_DONE: + D("ioctl: ACOUSTIC_ARM11_DONE called %d.\n", current->pid); + rc = msm_rpc_call_reply(endpoint, + ONCRPC_ACOUSTIC_INIT_PROC, &req, + sizeof(req), &rep, sizeof(rep), + 5 * HZ); + + reply_value = be32_to_cpu(rep.n); + if (reply_value != 0 || rc < 0) { + E("ioctl failed: ONCRPC_ACOUSTIC_INIT_PROC "\ + "error %d.\n", rc); + if (rc >= 0) + rc = -EIO; + break; + } + D("ioctl: ONCRPC_ACOUSTIC_INIT_PROC success.\n"); + break; + default: + E("ioctl: invalid command\n"); + rc = -EINVAL; + } + + mutex_unlock(&api_lock); + return 0; +} + + +static struct file_operations acoustic_fops = { + .owner = THIS_MODULE, + .open = acoustic_open, + .release = acoustic_release, + .mmap = acoustic_mmap, + .unlocked_ioctl = acoustic_ioctl, +}; + +static struct miscdevice acoustic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "htc-acoustic", + .fops = &acoustic_fops, +}; + +static int __init acoustic_init(void) +{ + mutex_init(&api_lock); + return misc_register(&acoustic_misc); +} + +static void __exit acoustic_exit(void) +{ + misc_deregister(&acoustic_misc); +} + +module_init(acoustic_init); +module_exit(acoustic_exit); + +MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>"); +MODULE_DESCRIPTION("HTC acoustic driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_akm_cal.c b/arch/arm/mach-msm/htc_akm_cal.c new file mode 100644 index 000000000000..943083fe0fbe --- /dev/null +++ b/arch/arm/mach-msm/htc_akm_cal.c @@ -0,0 +1,64 @@ +/* arch/arm/mach-msm/htc_akm_cal.c + * + * Code to extract compass calibration information from ATAG set up + * by the bootloader. + * + * Copyright (C) 2007-2008 HTC Corporation + * Author: Farmer Tseng <farmer_tseng@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/setup.h> + +/* configuration tags specific to AKM8976 */ +#define ATAG_AKM8976 0x89768976 /* AKM8976 */ + +#define MAX_CALI_SIZE 0x1000U + +static char akm_cal_ram[MAX_CALI_SIZE]; + +char *get_akm_cal_ram(void) +{ + return(akm_cal_ram); +} +EXPORT_SYMBOL(get_akm_cal_ram); + +static int __init parse_tag_akm(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_CALI_SIZE); + + printk(KERN_INFO "AKM Data size = %d , 0x%x, size = %d\n", + tag->hdr.size, tag->hdr.tag, size); + +#ifdef ATAG_COMPASS_DEBUG + unsigned i; + unsigned char *ptr; + + ptr = dptr; + printk(KERN_INFO + "AKM Data size = %d , 0x%x\n", + tag->hdr.size, tag->hdr.tag); + for (i = 0; i < size; i++) + printk(KERN_INFO "%02x ", *ptr++); +#endif + memcpy((void *)akm_cal_ram, (void *)dptr, size); + return 0; +} + +__tagtable(ATAG_AKM8976, parse_tag_akm); diff --git a/arch/arm/mach-msm/htc_battery.c b/arch/arm/mach-msm/htc_battery.c new file mode 100644 index 000000000000..7320edbff1a9 --- /dev/null +++ b/arch/arm/mach-msm/htc_battery.c @@ -0,0 +1,769 @@ +/* arch/arm/mach-msm/htc_battery.c + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/power_supply.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/wakelock.h> +#include <asm/gpio.h> +#include <mach/msm_rpcrouter.h> +#include <mach/board.h> + +static struct wake_lock vbus_wake_lock; + +#define TRACE_BATT 0 + +#if TRACE_BATT +#define BATT(x...) printk(KERN_INFO "[BATT] " x) +#else +#define BATT(x...) do {} while (0) +#endif + +/* rpc related */ +#define APP_BATT_PDEV_NAME "rs30100001:00000000" +#define APP_BATT_PROG 0x30100001 +#define APP_BATT_VER 0 +#define HTC_PROCEDURE_BATTERY_NULL 0 +#define HTC_PROCEDURE_GET_BATT_LEVEL 1 +#define HTC_PROCEDURE_GET_BATT_INFO 2 +#define HTC_PROCEDURE_GET_CABLE_STATUS 3 +#define HTC_PROCEDURE_SET_BATT_DELTA 4 + +/* module debugger */ +#define HTC_BATTERY_DEBUG 1 +#define BATTERY_PREVENTION 1 + +/* Enable this will shut down if no battery */ +#define ENABLE_BATTERY_DETECTION 0 + +#define GPIO_BATTERY_DETECTION 21 +#define GPIO_BATTERY_CHARGER_EN 128 + +/* Charge current selection */ +#define GPIO_BATTERY_CHARGER_CURRENT 129 + +typedef enum { + DISABLE = 0, + ENABLE_SLOW_CHG, + ENABLE_FAST_CHG +} batt_ctl_t; + +/* This order is the same as htc_power_supplies[] + * And it's also the same as htc_cable_status_update() + */ +typedef enum { + CHARGER_BATTERY = 0, + CHARGER_USB, + CHARGER_AC +} charger_type_t; + +struct battery_info_reply { + u32 batt_id; /* Battery ID from ADC */ + u32 batt_vol; /* Battery voltage from ADC */ + u32 batt_temp; /* Battery Temperature (C) from formula and ADC */ + u32 batt_current; /* Battery current from ADC */ + u32 level; /* formula */ + u32 charging_source; /* 0: no cable, 1:usb, 2:AC */ + u32 charging_enabled; /* 0: Disable, 1: Enable */ + u32 full_bat; /* Full capacity of battery (mAh) */ +}; + +struct htc_battery_info { + int present; + unsigned long update_time; + + /* lock to protect the battery info */ + struct mutex lock; + + /* lock held while calling the arm9 to query the battery info */ + struct mutex rpc_lock; + struct battery_info_reply rep; +}; + +static struct msm_rpc_endpoint *endpoint; + +static struct htc_battery_info htc_batt_info; + +static unsigned int cache_time = 1000; + +static int htc_battery_initial = 0; + +static enum power_supply_property htc_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static enum power_supply_property htc_power_properties[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static char *supply_list[] = { + "battery", +}; + +/* HTC dedicated attributes */ +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf); + +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val); + +static struct power_supply htc_power_supplies[] = { + { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = htc_battery_properties, + .num_properties = ARRAY_SIZE(htc_battery_properties), + .get_property = htc_battery_get_property, + }, + { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, + { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .supplied_to = supply_list, + .num_supplicants = ARRAY_SIZE(supply_list), + .properties = htc_power_properties, + .num_properties = ARRAY_SIZE(htc_power_properties), + .get_property = htc_power_get_property, + }, +}; + + +/* -------------------------------------------------------------------------- */ + +#if defined(CONFIG_DEBUG_FS) +int htc_battery_set_charging(batt_ctl_t ctl); +static int batt_debug_set(void *data, u64 val) +{ + return htc_battery_set_charging((batt_ctl_t) val); +} + +static int batt_debug_get(void *data, u64 *val) +{ + return -ENOSYS; +} + +DEFINE_SIMPLE_ATTRIBUTE(batt_debug_fops, batt_debug_get, batt_debug_set, "%llu\n"); +static int __init batt_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("htc_battery", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("charger_state", 0644, dent, NULL, &batt_debug_fops); + + return 0; +} + +device_initcall(batt_debug_init); +#endif + +static int init_batt_gpio(void) +{ + if (gpio_request(GPIO_BATTERY_DETECTION, "batt_detect") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_EN, "charger_en") < 0) + goto gpio_failed; + if (gpio_request(GPIO_BATTERY_CHARGER_CURRENT, "charge_current") < 0) + goto gpio_failed; + + return 0; + +gpio_failed: + return -EINVAL; + +} + +/* + * battery_charging_ctrl - battery charing control. + * @ctl: battery control command + * + */ +static int battery_charging_ctrl(batt_ctl_t ctl) +{ + int result = 0; + + switch (ctl) { + case DISABLE: + BATT("charger OFF\n"); + /* 0 for enable; 1 disable */ + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 1); + break; + case ENABLE_SLOW_CHG: + BATT("charger ON (SLOW)\n"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 0); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + case ENABLE_FAST_CHG: + BATT("charger ON (FAST)\n"); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_CURRENT, 1); + result = gpio_direction_output(GPIO_BATTERY_CHARGER_EN, 0); + break; + default: + printk(KERN_ERR "Not supported battery ctr called.!\n"); + result = -EINVAL; + break; + } + + return result; +} + +int htc_battery_set_charging(batt_ctl_t ctl) +{ + int rc; + + if ((rc = battery_charging_ctrl(ctl)) < 0) + goto result; + + if (!htc_battery_initial) { + htc_batt_info.rep.charging_enabled = ctl & 0x3; + } else { + mutex_lock(&htc_batt_info.lock); + htc_batt_info.rep.charging_enabled = ctl & 0x3; + mutex_unlock(&htc_batt_info.lock); + } +result: + return rc; +} + +int htc_battery_status_update(u32 curr_level) +{ + int notify; + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + notify = (htc_batt_info.rep.level != curr_level); + htc_batt_info.rep.level = curr_level; + mutex_unlock(&htc_batt_info.lock); + + if (notify) + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + return 0; +} + +int htc_cable_status_update(int status) +{ + int rc = 0; + unsigned source; + + if (!htc_battery_initial) + return 0; + + mutex_lock(&htc_batt_info.lock); + switch(status) { + case CHARGER_BATTERY: + BATT("cable NOT PRESENT\n"); + htc_batt_info.rep.charging_source = CHARGER_BATTERY; + break; + case CHARGER_USB: + BATT("cable USB\n"); + htc_batt_info.rep.charging_source = CHARGER_USB; + break; + case CHARGER_AC: + BATT("cable AC\n"); + htc_batt_info.rep.charging_source = CHARGER_AC; + break; + default: + printk(KERN_ERR "%s: Not supported cable status received!\n", + __FUNCTION__); + rc = -EINVAL; + } + source = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + msm_hsusb_set_vbus_state(source == CHARGER_USB); + if (source == CHARGER_USB) { + wake_lock(&vbus_wake_lock); + } else { + /* give userspace some time to see the uevent and update + * LED state or whatnot... + */ + wake_lock_timeout(&vbus_wake_lock, HZ / 2); + } + + /* if the power source changes, all power supplies may change state */ + power_supply_changed(&htc_power_supplies[CHARGER_BATTERY]); + power_supply_changed(&htc_power_supplies[CHARGER_USB]); + power_supply_changed(&htc_power_supplies[CHARGER_AC]); + + return rc; +} + +static int htc_get_batt_info(struct battery_info_reply *buffer) +{ + struct rpc_request_hdr req; + + struct htc_get_batt_info_rep { + struct rpc_reply_hdr hdr; + struct battery_info_reply info; + } rep; + + int rc; + + if (buffer == NULL) + return -EINVAL; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_BATT_INFO, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if ( rc < 0 ) + return rc; + + mutex_lock(&htc_batt_info.lock); + buffer->batt_id = be32_to_cpu(rep.info.batt_id); + buffer->batt_vol = be32_to_cpu(rep.info.batt_vol); + buffer->batt_temp = be32_to_cpu(rep.info.batt_temp); + buffer->batt_current = be32_to_cpu(rep.info.batt_current); + buffer->level = be32_to_cpu(rep.info.level); + buffer->charging_source = be32_to_cpu(rep.info.charging_source); + buffer->charging_enabled = be32_to_cpu(rep.info.charging_enabled); + buffer->full_bat = be32_to_cpu(rep.info.full_bat); + mutex_unlock(&htc_batt_info.lock); + + return 0; +} + +#if 0 +static int htc_get_cable_status(void) +{ + + struct rpc_request_hdr req; + + struct htc_get_cable_status_rep { + struct rpc_reply_hdr hdr; + int status; + } rep; + + int rc; + + rc = msm_rpc_call_reply(endpoint, HTC_PROCEDURE_GET_CABLE_STATUS, + &req, sizeof(req), + &rep, sizeof(rep), + 5 * HZ); + if (rc < 0) + return rc; + + return be32_to_cpu(rep.status); +} +#endif + +/* -------------------------------------------------------------------------- */ +static int htc_power_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + charger_type_t charger; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + mutex_unlock(&htc_batt_info.lock); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + if (psy->type == POWER_SUPPLY_TYPE_MAINS) + val->intval = (charger == CHARGER_AC ? 1 : 0); + else if (psy->type == POWER_SUPPLY_TYPE_USB) + val->intval = (charger == CHARGER_USB ? 1 : 0); + else + val->intval = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int htc_battery_get_charging_status(void) +{ + u32 level; + charger_type_t charger; + int ret; + + mutex_lock(&htc_batt_info.lock); + charger = htc_batt_info.rep.charging_source; + + switch (charger) { + case CHARGER_BATTERY: + ret = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case CHARGER_USB: + case CHARGER_AC: + level = htc_batt_info.rep.level; + if (level == 100) + ret = POWER_SUPPLY_STATUS_FULL; + else + ret = POWER_SUPPLY_STATUS_CHARGING; + break; + default: + ret = POWER_SUPPLY_STATUS_UNKNOWN; + } + mutex_unlock(&htc_batt_info.lock); + return ret; +} + +static int htc_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = htc_battery_get_charging_status(); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = htc_batt_info.present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CAPACITY: + mutex_lock(&htc_batt_info.lock); + val->intval = htc_batt_info.rep.level; + mutex_unlock(&htc_batt_info.lock); + break; + default: + return -EINVAL; + } + + return 0; +} + +#define HTC_BATTERY_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IRUGO, .owner = THIS_MODULE }, \ + .show = htc_battery_show_property, \ + .store = NULL, \ +} + +static struct device_attribute htc_battery_attrs[] = { + HTC_BATTERY_ATTR(batt_id), + HTC_BATTERY_ATTR(batt_vol), + HTC_BATTERY_ATTR(batt_temp), + HTC_BATTERY_ATTR(batt_current), + HTC_BATTERY_ATTR(charging_source), + HTC_BATTERY_ATTR(charging_enabled), + HTC_BATTERY_ATTR(full_bat), +}; + +enum { + BATT_ID = 0, + BATT_VOL, + BATT_TEMP, + BATT_CURRENT, + CHARGING_SOURCE, + CHARGING_ENABLED, + FULL_BAT, +}; + +static int htc_rpc_set_delta(unsigned delta) +{ + struct set_batt_delta_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + req.data = cpu_to_be32(delta); + return msm_rpc_call(endpoint, HTC_PROCEDURE_SET_BATT_DELTA, + &req, sizeof(req), 5 * HZ); +} + + +static ssize_t htc_battery_set_delta(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long delta = 0; + + delta = simple_strtoul(buf, NULL, 10); + + if (delta > 100) + return -EINVAL; + + mutex_lock(&htc_batt_info.rpc_lock); + rc = htc_rpc_set_delta(delta); + mutex_unlock(&htc_batt_info.rpc_lock); + if (rc < 0) + return rc; + return count; +} + +static struct device_attribute htc_set_delta_attrs[] = { + __ATTR(delta, S_IWUSR | S_IWGRP, NULL, htc_battery_set_delta), +}; + +static int htc_battery_create_attrs(struct device * dev) +{ + int i, j, rc; + + for (i = 0; i < ARRAY_SIZE(htc_battery_attrs); i++) { + rc = device_create_file(dev, &htc_battery_attrs[i]); + if (rc) + goto htc_attrs_failed; + } + + for (j = 0; j < ARRAY_SIZE(htc_set_delta_attrs); j++) { + rc = device_create_file(dev, &htc_set_delta_attrs[j]); + if (rc) + goto htc_delta_attrs_failed; + } + + goto succeed; + +htc_attrs_failed: + while (i--) + device_remove_file(dev, &htc_battery_attrs[i]); +htc_delta_attrs_failed: + while (j--) + device_remove_file(dev, &htc_set_delta_attrs[i]); +succeed: + return rc; +} + +static ssize_t htc_battery_show_property(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i = 0; + const ptrdiff_t off = attr - htc_battery_attrs; + + /* rpc lock is used to prevent two threads from calling + * into the get info rpc at the same time + */ + + mutex_lock(&htc_batt_info.rpc_lock); + /* check cache time to decide if we need to update */ + if (htc_batt_info.update_time && + time_before(jiffies, htc_batt_info.update_time + + msecs_to_jiffies(cache_time))) + goto dont_need_update; + + if (htc_get_batt_info(&htc_batt_info.rep) < 0) { + printk(KERN_ERR "%s: rpc failed!!!\n", __FUNCTION__); + } else { + htc_batt_info.update_time = jiffies; + } +dont_need_update: + mutex_unlock(&htc_batt_info.rpc_lock); + + mutex_lock(&htc_batt_info.lock); + switch (off) { + case BATT_ID: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_id); + break; + case BATT_VOL: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_vol); + break; + case BATT_TEMP: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_temp); + break; + case BATT_CURRENT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.batt_current); + break; + case CHARGING_SOURCE: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_source); + break; + case CHARGING_ENABLED: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.charging_enabled); + break; + case FULL_BAT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + htc_batt_info.rep.full_bat); + break; + default: + i = -EINVAL; + } + mutex_unlock(&htc_batt_info.lock); + + return i; +} + +static int htc_battery_probe(struct platform_device *pdev) +{ + int i, rc; + + /* init battery gpio */ + if ((rc = init_batt_gpio()) < 0) { + printk(KERN_ERR "%s: init battery gpio failed!\n", __FUNCTION__); + return rc; + } + + /* init structure data member */ + htc_batt_info.update_time = jiffies; + htc_batt_info.present = gpio_get_value(GPIO_BATTERY_DETECTION); + + /* init rpc */ + endpoint = msm_rpc_connect(APP_BATT_PROG, APP_BATT_VER, 0); + if (IS_ERR(endpoint)) { + printk(KERN_ERR "%s: init rpc failed! rc = %ld\n", + __FUNCTION__, PTR_ERR(endpoint)); + return rc; + } + + /* init power supplier framework */ + for (i = 0; i < ARRAY_SIZE(htc_power_supplies); i++) { + rc = power_supply_register(&pdev->dev, &htc_power_supplies[i]); + if (rc) + printk(KERN_ERR "Failed to register power supply (%d)\n", rc); + } + + /* create htc detail attributes */ + htc_battery_create_attrs(htc_power_supplies[CHARGER_BATTERY].dev); + + /* After battery driver gets initialized, send rpc request to inquiry + * the battery status in case of we lost some info + */ + htc_battery_initial = 1; + + mutex_lock(&htc_batt_info.rpc_lock); + if (htc_get_batt_info(&htc_batt_info.rep) < 0) + printk(KERN_ERR "%s: get info failed\n", __FUNCTION__); + + htc_cable_status_update(htc_batt_info.rep.charging_source); + battery_charging_ctrl(htc_batt_info.rep.charging_enabled ? + ENABLE_SLOW_CHG : DISABLE); + + if (htc_rpc_set_delta(1) < 0) + printk(KERN_ERR "%s: set delta failed\n", __FUNCTION__); + htc_batt_info.update_time = jiffies; + mutex_unlock(&htc_batt_info.rpc_lock); + + if (htc_batt_info.rep.charging_enabled == 0) + battery_charging_ctrl(DISABLE); + + return 0; +} + +static struct platform_driver htc_battery_driver = { + .probe = htc_battery_probe, + .driver = { + .name = APP_BATT_PDEV_NAME, + .owner = THIS_MODULE, + }, +}; + +/* batt_mtoa server definitions */ +#define BATT_MTOA_PROG 0x30100000 +#define BATT_MTOA_VERS 0 +#define RPC_BATT_MTOA_NULL 0 +#define RPC_BATT_MTOA_SET_CHARGING_PROC 1 +#define RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC 2 +#define RPC_BATT_MTOA_LEVEL_UPDATE_PROC 3 + +struct rpc_batt_mtoa_set_charging_args { + int enable; +}; + +struct rpc_batt_mtoa_cable_status_update_args { + int status; +}; + +struct rpc_dem_battery_update_args { + uint32_t level; +}; + +static int handle_battery_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_BATT_MTOA_NULL: + return 0; + + case RPC_BATT_MTOA_SET_CHARGING_PROC: { + struct rpc_batt_mtoa_set_charging_args *args; + args = (struct rpc_batt_mtoa_set_charging_args *)(req + 1); + args->enable = be32_to_cpu(args->enable); + BATT("set_charging: enable=%d\n",args->enable); + htc_battery_set_charging(args->enable); + return 0; + } + case RPC_BATT_MTOA_CABLE_STATUS_UPDATE_PROC: { + struct rpc_batt_mtoa_cable_status_update_args *args; + args = (struct rpc_batt_mtoa_cable_status_update_args *)(req + 1); + args->status = be32_to_cpu(args->status); + BATT("cable_status_update: status=%d\n",args->status); + htc_cable_status_update(args->status); + return 0; + } + case RPC_BATT_MTOA_LEVEL_UPDATE_PROC: { + struct rpc_dem_battery_update_args *args; + args = (struct rpc_dem_battery_update_args *)(req + 1); + args->level = be32_to_cpu(args->level); + BATT("dem_battery_update: level=%d\n",args->level); + htc_battery_status_update(args->level); + return 0; + } + default: + printk(KERN_ERR "%s: program 0x%08x:%d: unknown procedure %d\n", + __FUNCTION__, req->prog, req->vers, req->procedure); + return -ENODEV; + } +} + +static struct msm_rpc_server battery_server = { + .prog = BATT_MTOA_PROG, + .vers = BATT_MTOA_VERS, + .rpc_call = handle_battery_call, +}; + +static int __init htc_battery_init(void) +{ + wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present"); + mutex_init(&htc_batt_info.lock); + mutex_init(&htc_batt_info.rpc_lock); + msm_rpc_create_server(&battery_server); + platform_driver_register(&htc_battery_driver); + return 0; +} + +module_init(htc_battery_init); +MODULE_DESCRIPTION("HTC Battery Driver"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/htc_headset.c b/arch/arm/mach-msm/htc_headset.c new file mode 100644 index 000000000000..a69a2e1ca5f8 --- /dev/null +++ b/arch/arm/mach-msm/htc_headset.c @@ -0,0 +1,1246 @@ +/* + * H2W device detection driver. + * + * Copyright (C) 2008 Google, Inc. + * Copyright (C) 2008 HTC, Inc. + * + * Authors: + * Laurence Chen <Laurence_Chen@htc.com> + * Nick Pelly <npelly@google.com> + * Thomas Tsai <thomas_tsai@htc.com> + * Farmer Tseng <farmer_tseng@htc.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + */ + +/* For detecting HTC 2 Wire devices, such as wired headset. + + Logically, the H2W driver is always present, and H2W state (hi->state) + indicates what is currently plugged into the H2W interface. + + When the headset is plugged in, CABLE_IN1 is pulled low. When the headset + button is pressed, CABLE_IN2 is pulled low. These two lines are shared with + the TX and RX (respectively) of UART3 - used for serial debugging. + + This headset driver keeps the CPLD configured as UART3 for as long as + possible, so that we can do serial FIQ debugging even when the kernel is + locked and this driver no longer runs. So it only configures the CPLD to + GPIO while the headset is plugged in, and for 10ms during detection work. + + Unfortunately we can't leave the CPLD as UART3 while a headset is plugged + in, UART3 is pullup on TX but the headset is pull-down, causing a 55 mA + drain on trout. + + The headset detection work involves setting CPLD to GPIO, and then pulling + CABLE_IN1 high with a stronger pullup than usual. A H2W headset will still + pull this line low, whereas other attachments such as a serial console + would get pulled up by this stronger pullup. + + Headset insertion/removal causes UEvent's to be sent, and + /sys/class/switch/h2w/state to be updated. + + Button presses are interpreted as input event (KEY_MEDIA). Button presses + are ignored if the headset is plugged in, so the buttons on 11 pin -> 3.5mm + jack adapters do not work until a headset is plugged into the adapter. This + is to avoid serial RX traffic causing spurious button press events. + + We tend to check the status of CABLE_IN1 a few more times than strictly + necessary during headset detection, to avoid spurious headset insertion + events caused by serial debugger TX traffic. +*/ + +#include <linux/module.h> +#include <linux/sysdev.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/hrtimer.h> +#include <linux/switch.h> +#include <linux/input.h> +#include <linux/debugfs.h> +#include <asm/gpio.h> +#include <asm/atomic.h> +#include <mach/board.h> +#include <mach/vreg.h> +#include <asm/mach-types.h> + +#include <mach/htc_headset.h> + +#define H2WI(fmt, arg...) \ + printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#define H2WE(fmt, arg...) \ + printk(KERN_ERR "[H2W] %s " fmt "\r\n", __func__, ## arg) + +#ifdef CONFIG_DEBUG_H2W +#define H2W_DBG(fmt, arg...) printk(KERN_INFO "[H2W] %s " fmt "\r\n", __func__, ## arg) +#else +#define H2W_DBG(fmt, arg...) do {} while (0) +#endif + +static struct workqueue_struct *g_detection_work_queue; +static void detection_work(struct work_struct *work); +static DECLARE_WORK(g_detection_work, detection_work); + +struct h2w_info { + struct switch_dev sdev; + struct input_dev *input; + struct mutex mutex_lock; + + atomic_t btn_state; + int ignore_btn; + + unsigned int irq; + unsigned int irq_btn; + + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + + void (*config_cpld) (int); + void (*init_cpld) (void); + /* for h2w */ + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); + + int htc_headset_flag; + + struct hrtimer timer; + ktime_t debounce_time; + + struct hrtimer btn_timer; + ktime_t btn_debounce_time; + + H2W_INFO h2w_info; + H2W_SPEED speed; + struct vreg *vreg_h2w; +}; +static struct h2w_info *hi; + +static ssize_t h2w_print_name(struct switch_dev *sdev, char *buf) +{ + switch (switch_get_state(&hi->sdev)) { + case H2W_NO_DEVICE: + return sprintf(buf, "No Device\n"); + case H2W_HTC_HEADSET: + return sprintf(buf, "Headset\n"); + } + return -EINVAL; +} + +static void button_pressed(void) +{ + H2W_DBG("button_pressed \n"); + atomic_set(&hi->btn_state, 1); + input_report_key(hi->input, KEY_MEDIA, 1); + input_sync(hi->input); +} + +static void button_released(void) +{ + H2W_DBG("button_released \n"); + atomic_set(&hi->btn_state, 0); + input_report_key(hi->input, KEY_MEDIA, 0); + input_sync(hi->input); +} + +/***************** + * H2W proctocol * + *****************/ +static inline void h2w_begin_command(void) +{ + /* Disable H2W interrupt */ + set_irq_type(hi->irq_btn, IRQF_TRIGGER_HIGH); + disable_irq(hi->irq); + disable_irq(hi->irq_btn); + + /* Set H2W_CLK as output low */ + hi->set_clk(0); + hi->set_clk_dir(1); +} + +static inline void h2w_end_command(void) +{ + /* Set H2W_CLK as input */ + hi->set_clk_dir(0); + + /* Enable H2W interrupt */ + enable_irq(hi->irq); + enable_irq(hi->irq_btn); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); +} + +/* + * One bit write data + * ________ + * SCLK O ______| |______O(L) + * + * + * SDAT I <XXXXXXXXXXXXXXXXXXXX> + */ +static inline void one_clock_write(unsigned short flag) +{ + if (flag) + hi->set_dat(1); + else + hi->set_dat(0); + + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + hi->set_clk(0); +} + +/* + * One bit write data R/W bit + * ________ + * SCLK ______| |______O(L) + * 1----> 1-----> + * 2-------> ______ + * SDAT <XXXXXXXXXXXXXX> I + * O(H/L) + */ +static inline void one_clock_write_RWbit(unsigned short flag) +{ + if (flag) + hi->set_dat(1); + else + hi->set_dat(0); + + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + hi->set_clk(0); + hi->set_dat_dir(0); + udelay(hi->speed); +} + +/* + * H2W Reset + * ___________ + * SCLK O(L)______| |___O(L) + * 1----> + * 4-->1-->1-->1us--> + * ____ + * SDAT O(L)________ | |_______O(L) + * + * H2w reset command needs to be issued before every access + */ +static inline void h2w_reset(void) +{ + /* Set H2W_DAT as output low */ + hi->set_dat(0); + hi->set_dat_dir(1); + + udelay(hi->speed); + hi->set_clk(1); + udelay(4 * hi->speed); + hi->set_dat(1); + udelay(hi->speed); + hi->set_dat(0); + udelay(hi->speed); + hi->set_clk(0); + udelay(hi->speed); +} + +/* + * H2W Start + * ___________ + * SCLK O(L)______| |___O(L) + * 1----> + * 2----------->1--> + * + * SDAT O(L)______________________O(L) + */ +static inline void h2w_start(void) +{ + udelay(hi->speed); + hi->set_clk(1); + udelay(2 * hi->speed); + hi->set_clk(0); + udelay(hi->speed); +} + +/* + * H2W Ack + * __________ + * SCLK _____| |_______O(L) + * 1----> 1------> + * 2---------> + * ________________________ + * SDAT become Input mode here I + */ +static inline int h2w_ack(void) +{ + int retry_times = 0; + +ack_resend: + if (retry_times == MAX_ACK_RESEND_TIMES) + return -1; + + udelay(hi->speed); + hi->set_clk(1); + udelay(2 * hi->speed); + + if (!hi->get_dat()) { + retry_times++; + hi->set_clk(0); + udelay(hi->speed); + goto ack_resend; + } + + hi->set_clk(0); + udelay(hi->speed); + return 0; +} + +/* + * One bit read data + * ________ + * SCLK ______| |______O(L) + * 2----> 2-----> + * 2-------> + * SDAT <XXXXXXXXXXXXXXXXXXXX>I + */ +static unsigned char h2w_readc(void) +{ + unsigned char h2w_read_data = 0x0; + int index; + + for (index = 0; index < 8; index++) { + hi->set_clk(0); + udelay(hi->speed); + hi->set_clk(1); + udelay(hi->speed); + if (hi->get_dat()) + h2w_read_data |= (1 << (7 - index)); + } + hi->set_clk(0); + udelay(hi->speed); + + return h2w_read_data; +} + +static int h2w_readc_cmd(H2W_ADDR address) +{ + int ret = -1, retry_times = 0; + unsigned char read_data; + +read_resend: + if (retry_times == MAX_HOST_RESEND_TIMES) + goto err_read; + + h2w_reset(); + h2w_start(); + /* Write address */ + one_clock_write(address & 0x1000); + one_clock_write(address & 0x0800); + one_clock_write(address & 0x0400); + one_clock_write(address & 0x0200); + one_clock_write(address & 0x0100); + one_clock_write(address & 0x0080); + one_clock_write(address & 0x0040); + one_clock_write(address & 0x0020); + one_clock_write(address & 0x0010); + one_clock_write(address & 0x0008); + one_clock_write(address & 0x0004); + one_clock_write(address & 0x0002); + one_clock_write(address & 0x0001); + one_clock_write_RWbit(1); + if (h2w_ack() < 0) { + H2W_DBG("Addr NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto read_resend; + } + + read_data = h2w_readc(); + + if (h2w_ack() < 0) { + H2W_DBG("Data NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto read_resend; + } + ret = (int)read_data; + +err_read: + if (ret < 0) + H2WE("NO ACK.\n"); + + return ret; +} + +static int h2w_writec_cmd(H2W_ADDR address, unsigned char data) +{ + int ret = -1; + int retry_times = 0; + +write_resend: + if (retry_times == MAX_HOST_RESEND_TIMES) + goto err_write; + + h2w_reset(); + h2w_start(); + + /* Write address */ + one_clock_write(address & 0x1000); + one_clock_write(address & 0x0800); + one_clock_write(address & 0x0400); + one_clock_write(address & 0x0200); + one_clock_write(address & 0x0100); + one_clock_write(address & 0x0080); + one_clock_write(address & 0x0040); + one_clock_write(address & 0x0020); + one_clock_write(address & 0x0010); + one_clock_write(address & 0x0008); + one_clock_write(address & 0x0004); + one_clock_write(address & 0x0002); + one_clock_write(address & 0x0001); + one_clock_write_RWbit(0); + if (h2w_ack() < 0) { + H2W_DBG("Addr NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto write_resend; + } + + /* Write data */ + hi->set_dat_dir(1); + one_clock_write(data & 0x0080); + one_clock_write(data & 0x0040); + one_clock_write(data & 0x0020); + one_clock_write(data & 0x0010); + one_clock_write(data & 0x0008); + one_clock_write(data & 0x0004); + one_clock_write(data & 0x0002); + one_clock_write_RWbit(data & 0x0001); + if (h2w_ack() < 0) { + H2W_DBG("Data NO ACK(%d).\n", retry_times); + retry_times++; + hi->set_clk(0); + mdelay(RESEND_DELAY); + goto write_resend; + } + ret = 0; + +err_write: + if (ret < 0) + H2WE("NO ACK.\n"); + + return ret; +} + +static int h2w_get_fnkey(void) +{ + int ret; + h2w_begin_command(); + ret = h2w_readc_cmd(H2W_FNKEY_UPDOWN); + h2w_end_command(); + return ret; +} + +static int h2w_dev_init(H2W_INFO *ph2w_info) +{ + int ret = -1; + unsigned char ascr0 = 0; + int h2w_sys = 0, maxgpadd = 0, maxadd = 0, key = 0; + + hi->speed = H2W_50KHz; + h2w_begin_command(); + + /* read H2W_SYSTEM */ + h2w_sys = h2w_readc_cmd(H2W_SYSTEM); + if (h2w_sys == -1) { + H2WE("read H2W_SYSTEM(0x0000) failed.\n"); + goto err_plugin; + } + ph2w_info->ACC_CLASS = (h2w_sys & 0x03); + ph2w_info->AUDIO_DEVICE = (h2w_sys & 0x04) > 0 ? 1 : 0; + ph2w_info->HW_REV = (h2w_sys & 0x18) >> 3; + ph2w_info->SLEEP_PR = (h2w_sys & 0x20) >> 5; + ph2w_info->CLK_SP = (h2w_sys & 0xC0) >> 6; + + /* enter init mode */ + if (h2w_writec_cmd(H2W_ASCR0, H2W_ASCR_DEVICE_INI) < 0) { + H2WE("write H2W_ASCR0(0x0002) failed.\n"); + goto err_plugin; + } + udelay(10); + + /* read H2W_MAX_GP_ADD */ + maxgpadd = h2w_readc_cmd(H2W_MAX_GP_ADD); + if (maxgpadd == -1) { + H2WE("write H2W_MAX_GP_ADD(0x0001) failed.\n"); + goto err_plugin; + } + ph2w_info->CLK_SP += (maxgpadd & 0x60) >> 3; + ph2w_info->MAX_GP_ADD = (maxgpadd & 0x1F); + + /* read key group */ + if (ph2w_info->MAX_GP_ADD >= 1) { + ph2w_info->KEY_MAXADD = h2w_readc_cmd(H2W_KEY_MAXADD); + if (ph2w_info->KEY_MAXADD == -1) + goto err_plugin; + if (ph2w_info->KEY_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_ASCII_DOWN); + if (key < 0) + goto err_plugin; + ph2w_info->ASCII_DOWN = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 2) { + key = h2w_readc_cmd(H2W_ASCII_UP); + if (key == -1) + goto err_plugin; + ph2w_info->ASCII_UP = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 3) { + key = h2w_readc_cmd(H2W_FNKEY_UPDOWN); + if (key == -1) + goto err_plugin; + ph2w_info->FNKEY_UPDOWN = (key == 0xFF) ? 1 : 0; + } + if (ph2w_info->KEY_MAXADD >= 4) { + key = h2w_readc_cmd(H2W_KD_STATUS); + if (key == -1) + goto err_plugin; + ph2w_info->KD_STATUS = (key == 0x01) ? 1 : 0; + } + } + + /* read led group */ + if (ph2w_info->MAX_GP_ADD >= 2) { + ph2w_info->LED_MAXADD = h2w_readc_cmd(H2W_LED_MAXADD); + if (ph2w_info->LED_MAXADD == -1) + goto err_plugin; + if (ph2w_info->LED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_LEDCT0); + if (key == -1) + goto err_plugin; + ph2w_info->LEDCT0 = (key == 0x02) ? 1 : 0; + } + } + + /* read group 3, 4, 5 */ + if (ph2w_info->MAX_GP_ADD >= 3) { + maxadd = h2w_readc_cmd(H2W_CRDL_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + if (ph2w_info->MAX_GP_ADD >= 4) { + maxadd = h2w_readc_cmd(H2W_CARKIT_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + if (ph2w_info->MAX_GP_ADD >= 5) { + maxadd = h2w_readc_cmd(H2W_USBHOST_MAXADD); + if (maxadd == -1) + goto err_plugin; + } + + /* read medical group */ + if (ph2w_info->MAX_GP_ADD >= 6) { + ph2w_info->MED_MAXADD = h2w_readc_cmd(H2W_MED_MAXADD); + if (ph2w_info->MED_MAXADD == -1) + goto err_plugin; + if (ph2w_info->MED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_MED_CONTROL); + if (key == -1) + goto err_plugin; + ph2w_info->DATA_EN = (key & 0x01); + ph2w_info->AP_EN = (key & 0x02) >> 1; + ph2w_info->AP_ID = (key & 0x1c) >> 2; + } + if (ph2w_info->MED_MAXADD >= 2) { + key = h2w_readc_cmd(H2W_MED_IN_DATA); + if (key == -1) + goto err_plugin; + } + } + + if (ph2w_info->AUDIO_DEVICE) + ascr0 = H2W_ASCR_AUDIO_IN | H2W_ASCR_ACT_EN; + else + ascr0 = H2W_ASCR_ACT_EN; + + if (h2w_writec_cmd(H2W_ASCR0, ascr0) < 0) + goto err_plugin; + udelay(10); + + ret = 0; + + /* adjust speed */ + if (ph2w_info->MAX_GP_ADD == 2) { + /* Remote control */ + hi->speed = H2W_250KHz; + } else if (ph2w_info->MAX_GP_ADD == 6) { + if (ph2w_info->MED_MAXADD >= 1) { + key = h2w_readc_cmd(H2W_MED_CONTROL); + if (key == -1) + goto err_plugin; + ph2w_info->DATA_EN = (key & 0x01); + ph2w_info->AP_EN = (key & 0x02) >> 1; + ph2w_info->AP_ID = (key & 0x1c) >> 2; + } + } + +err_plugin: + h2w_end_command(); + + return ret; +} + +static inline void h2w_dev_power_on(int on) +{ + if (!hi->vreg_h2w) + return; + + if (on) + vreg_enable(hi->vreg_h2w); + else + vreg_disable(hi->vreg_h2w); +} + +static int h2w_dev_detect(void) +{ + int ret = -1; + int retry_times; + + for (retry_times = 5; retry_times; retry_times--) { + /* Enable H2W Power */ + h2w_dev_power_on(1); + msleep(100); + memset(&hi->h2w_info, 0, sizeof(H2W_INFO)); + if (h2w_dev_init(&hi->h2w_info) < 0) { + h2w_dev_power_on(0); + msleep(100); + } else if (hi->h2w_info.MAX_GP_ADD == 2) { + ret = 0; + break; + } else { + printk(KERN_INFO "h2w_detect: detect error(%d)\n" + , hi->h2w_info.MAX_GP_ADD); + h2w_dev_power_on(0); + msleep(100); + } + printk(KERN_INFO "h2w_detect(%d)\n" + , hi->h2w_info.MAX_GP_ADD); + } + H2W_DBG("h2w_detect:(%d)\n", retry_times); + return ret; +} + +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, switch_get_state(&hi->sdev) & + ~(BIT_HEADSET | BIT_HEADSET_NO_MIC)); + mutex_unlock(&hi->mutex_lock); + hi->init_cpld(); + + /* Disable button */ + switch (hi->htc_headset_flag) { + case H2W_HTC_HEADSET: + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + break; + case H2W_DEVICE: + h2w_dev_power_on(0); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW); + disable_irq(hi->irq_btn); + /* 10ms (5-15 with 10ms tick) */ + hi->btn_debounce_time = ktime_set(0, 10000000); + hi->set_clk_dir(0); + hi->set_dat_dir(0); + break; + } + + hi->htc_headset_flag = 0; + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + +} + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER +extern void msm_serial_debug_enable(int); +#endif + +static void insert_headset(int type) +{ + unsigned long irq_flags; + int state; + + H2W_DBG(""); + + hi->htc_headset_flag = type; + state = BIT_HEADSET | BIT_HEADSET_NO_MIC; + + state = switch_get_state(&hi->sdev); + state &= ~(BIT_HEADSET_NO_MIC | BIT_HEADSET); + switch (type) { + case H2W_HTC_HEADSET: + printk(KERN_INFO "insert_headset H2W_HTC_HEADSET\n"); + state |= BIT_HEADSET; + hi->ignore_btn = !gpio_get_value(hi->cable_in2); + /* Enable button irq */ + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + hi->debounce_time = ktime_set(0, 200000000); /* 20 ms */ + break; + case H2W_DEVICE: + if (h2w_dev_detect() < 0) { + printk(KERN_INFO "H2W_DEVICE -- Non detect\n"); + remove_headset(); + } else { + printk(KERN_INFO "H2W_DEVICE -- detect\n"); + hi->btn_debounce_time = ktime_set(0, 0); + local_irq_save(irq_flags); + enable_irq(hi->irq_btn); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_RISING); + local_irq_restore(irq_flags); + state |= BIT_HEADSET; + } + break; + case H2W_USB_CRADLE: + state |= BIT_HEADSET_NO_MIC; + break; + case H2W_UART_DEBUG: + hi->config_cpld(hi->debug_uart); + printk(KERN_INFO "switch to H2W_UART_DEBUG\n"); + default: + return; + } + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, state); + mutex_unlock(&hi->mutex_lock); + +#ifdef CONFIG_MSM_SERIAL_DEBUGGER + msm_serial_debug_enable(false); +#endif + +} +#if 0 +static void remove_headset(void) +{ + unsigned long irq_flags; + + H2W_DBG(""); + + switch_set_state(&hi->sdev, H2W_NO_DEVICE); + + hi->init_cpld(); + + /* Disable button */ + local_irq_save(irq_flags); + disable_irq(hi->irq_btn); + local_irq_restore(irq_flags); + + if (atomic_read(&hi->btn_state)) + button_released(); + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ +} +#endif +static int is_accessary_pluged_in(void) +{ + int type = 0; + int clk1 = 0, dat1 = 0, clk2 = 0, dat2 = 0, clk3 = 0, dat3 = 0; + + /* Step1: save H2W_CLK and H2W_DAT */ + /* Delay 10ms for pin stable. */ + msleep(10); + clk1 = gpio_get_value(hi->h2w_clk); + dat1 = gpio_get_value(hi->h2w_data); + + /* + * Step2: set GPIO_CABLE_IN1 as output high and GPIO_CABLE_IN2 as + * input + */ + gpio_direction_output(hi->cable_in1, 1); + gpio_direction_input(hi->cable_in2); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Step 3: save H2W_CLK and H2W_DAT */ + clk2 = gpio_get_value(hi->h2w_clk); + dat2 = gpio_get_value(hi->h2w_data); + + /* + * Step 4: set GPIO_CABLE_IN1 as input and GPIO_CABLE_IN2 as output + * high + */ + gpio_direction_input(hi->cable_in1); + gpio_direction_output(hi->cable_in2, 1); + /* Delay 10ms for pin stable. */ + msleep(10); + /* Step 5: save H2W_CLK and H2W_DAT */ + clk3 = gpio_get_value(hi->h2w_clk); + dat3 = gpio_get_value(hi->h2w_data); + + /* Step 6: set both GPIO_CABLE_IN1 and GPIO_CABLE_IN2 as input */ + gpio_direction_input(hi->cable_in1); + gpio_direction_input(hi->cable_in2); + + H2W_DBG("(%d,%d) (%d,%d) (%d,%d)\n", + clk1, dat1, clk2, dat2, clk3, dat3); + + if ((clk1 == 0) && (dat1 == 1) && + (clk2 == 0) && (dat2 == 1) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_HTC_HEADSET; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 0) && (dat2 == 0) && + (clk3 == 0) && (dat3 == 0)) + type = NORMAL_HEARPHONE; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 1) && (dat2 == 0) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_DEVICE; + else if ((clk1 == 0) && (dat1 == 0) && + (clk2 == 1) && (dat2 == 1) && + (clk3 == 1) && (dat3 == 1)) + type = H2W_USB_CRADLE; + else if ((clk1 == 0) && (dat1 == 1) && + (clk2 == 1) && (dat2 == 1) && + (clk3 == 0) && (dat3 == 1)) + type = H2W_UART_DEBUG; + else + type = H2W_NO_DEVICE; + + return type; +} + + +static void detection_work(struct work_struct *work) +{ + unsigned long irq_flags; + int type; + + H2W_DBG(""); + + if (gpio_get_value(hi->cable_in1) != 0) { + /* Headset not plugged in */ + if (switch_get_state(&hi->sdev) != H2W_NO_DEVICE) + remove_headset(); + return; + } + + /* Something plugged in, lets make sure its a headset */ + + /* Switch CPLD to GPIO to do detection */ + hi->config_cpld(H2W_GPIO); + + /* Disable headset interrupt while detecting.*/ + local_irq_save(irq_flags); + disable_irq(hi->irq); + local_irq_restore(irq_flags); + + /* Something plugged in, lets make sure its a headset */ + type = is_accessary_pluged_in(); + + /* Restore IRQs */ + local_irq_save(irq_flags); + enable_irq(hi->irq); + local_irq_restore(irq_flags); + + insert_headset(type); +} + +static enum hrtimer_restart button_event_timer_func(struct hrtimer *data) +{ + int key, press, keyname, h2w_key = 1; + + H2W_DBG(""); + + if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) { + switch (hi->htc_headset_flag) { + case H2W_HTC_HEADSET: + if (gpio_get_value(hi->cable_in2)) { + if (hi->ignore_btn) + hi->ignore_btn = 0; + else if (atomic_read(&hi->btn_state)) + button_released(); + } else { + if (!hi->ignore_btn && + !atomic_read(&hi->btn_state)) + button_pressed(); + } + break; + case H2W_DEVICE: + if ((hi->get_dat() == 1) && (hi->get_clk() == 1)) { + /* Don't do anything because H2W pull out. */ + H2WE("Remote Control pull out.\n"); + } else { + key = h2w_get_fnkey(); + press = (key > 0x7F) ? 0 : 1; + keyname = key & 0x7F; + /* H2WI("key = %d, press = %d, + keyname = %d \n", + key, press, keyname); */ + switch (keyname) { + case H2W_KEY_PLAY: + H2WI("H2W_KEY_PLAY"); + key = KEY_PLAYPAUSE; + break; + case H2W_KEY_FORWARD: + H2WI("H2W_KEY_FORWARD"); + key = KEY_NEXTSONG; + break; + case H2W_KEY_BACKWARD: + H2WI("H2W_KEY_BACKWARD"); + key = KEY_PREVIOUSSONG; + break; + case H2W_KEY_VOLUP: + H2WI("H2W_KEY_VOLUP"); + key = KEY_VOLUMEUP; + break; + case H2W_KEY_VOLDOWN: + H2WI("H2W_KEY_VOLDOWN"); + key = KEY_VOLUMEDOWN; + break; + case H2W_KEY_PICKUP: + H2WI("H2W_KEY_PICKUP"); + key = KEY_SEND; + break; + case H2W_KEY_HANGUP: + H2WI("H2W_KEY_HANGUP"); + key = KEY_END; + break; + case H2W_KEY_MUTE: + H2WI("H2W_KEY_MUTE"); + key = KEY_MUTE; + break; + case H2W_KEY_HOLD: + H2WI("H2W_KEY_HOLD"); + break; + default: + H2WI("default"); + h2w_key = 0; + } + if (h2w_key) { + if (press) + H2WI("Press\n"); + else + H2WI("Release\n"); + input_report_key(hi->input, key, press); + } + } + break; + } /* end switch */ + } + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart detect_event_timer_func(struct hrtimer *data) +{ + H2W_DBG(""); + + queue_work(g_detection_work_queue, &g_detection_work); + return HRTIMER_NORESTART; +} + +static irqreturn_t detect_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + set_irq_type(hi->irq_btn, IRQF_TRIGGER_LOW); + do { + value1 = gpio_get_value(hi->cable_in1); + set_irq_type(hi->irq, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->cable_in1); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries), device=%d", + value2, (10-retry_limit), switch_get_state(&hi->sdev)); + + if ((switch_get_state(&hi->sdev) == H2W_NO_DEVICE) ^ value2) { + if (switch_get_state(&hi->sdev) == H2W_HTC_HEADSET) + hi->ignore_btn = 1; + /* Do the rest of the work in timer context */ + hrtimer_start(&hi->timer, hi->debounce_time, HRTIMER_MODE_REL); + } + + return IRQ_HANDLED; +} + +static irqreturn_t button_irq_handler(int irq, void *dev_id) +{ + int value1, value2; + int retry_limit = 10; + + H2W_DBG(""); + do { + value1 = gpio_get_value(hi->cable_in2); + if (hi->htc_headset_flag != H2W_DEVICE) + set_irq_type(hi->irq_btn, value1 ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH); + value2 = gpio_get_value(hi->cable_in2); + } while (value1 != value2 && retry_limit-- > 0); + + H2W_DBG("value2 = %d (%d retries)", value2, (10-retry_limit)); + + hrtimer_start(&hi->btn_timer, hi->btn_debounce_time, HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +#if defined(CONFIG_DEBUG_FS) +static int h2w_debug_set(void *data, u64 val) +{ + mutex_lock(&hi->mutex_lock); + switch_set_state(&hi->sdev, (int)val); + mutex_unlock(&hi->mutex_lock); + return 0; +} + +static int h2w_debug_get(void *data, u64 *val) +{ + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(h2w_debug_fops, h2w_debug_get, h2w_debug_set, "%llu\n"); +static int __init h2w_debug_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("h2w", 0); + if (IS_ERR(dent)) + return PTR_ERR(dent); + + debugfs_create_file("state", 0644, dent, NULL, &h2w_debug_fops); + + return 0; +} + +device_initcall(h2w_debug_init); +#endif + +static int h2w_probe(struct platform_device *pdev) +{ + int ret; + struct h2w_platform_data *pdata = pdev->dev.platform_data; + + printk(KERN_INFO "H2W: Registering H2W (headset) driver\n"); + hi = kzalloc(sizeof(struct h2w_info), GFP_KERNEL); + if (!hi) + return -ENOMEM; + + atomic_set(&hi->btn_state, 0); + hi->ignore_btn = 0; + + hi->debounce_time = ktime_set(0, 100000000); /* 100 ms */ + hi->btn_debounce_time = ktime_set(0, 10000000); /* 10 ms */ + + hi->htc_headset_flag = 0; + hi->cable_in1 = pdata->cable_in1; + hi->cable_in2 = pdata->cable_in2; + hi->h2w_clk = pdata->h2w_clk; + hi->h2w_data = pdata->h2w_data; + hi->debug_uart = pdata->debug_uart; + hi->config_cpld = pdata->config_cpld; + hi->init_cpld = pdata->init_cpld; + hi->set_dat = pdata->set_dat; + hi->set_clk = pdata->set_clk; + hi->set_dat_dir = pdata->set_dat_dir; + hi->set_clk_dir = pdata->set_clk_dir; + hi->get_dat = pdata->get_dat; + hi->get_clk = pdata->get_clk; + hi->speed = H2W_50KHz; + /* obtain needed VREGs */ + if (pdata->power_name) + hi->vreg_h2w = vreg_get(0, pdata->power_name); + + mutex_init(&hi->mutex_lock); + + hi->sdev.name = "h2w"; + hi->sdev.print_name = h2w_print_name; + + ret = switch_dev_register(&hi->sdev); + if (ret < 0) + goto err_switch_dev_register; + + g_detection_work_queue = create_workqueue("detection"); + if (g_detection_work_queue == NULL) { + ret = -ENOMEM; + goto err_create_work_queue; + } + + ret = gpio_request(hi->cable_in1, "h2w_detect"); + if (ret < 0) + goto err_request_detect_gpio; + + ret = gpio_request(hi->cable_in2, "h2w_button"); + if (ret < 0) + goto err_request_button_gpio; + + ret = gpio_direction_input(hi->cable_in1); + if (ret < 0) + goto err_set_detect_gpio; + + ret = gpio_direction_input(hi->cable_in2); + if (ret < 0) + goto err_set_button_gpio; + + hi->irq = gpio_to_irq(hi->cable_in1); + if (hi->irq < 0) { + ret = hi->irq; + goto err_get_h2w_detect_irq_num_failed; + } + + hi->irq_btn = gpio_to_irq(hi->cable_in2); + if (hi->irq_btn < 0) { + ret = hi->irq_btn; + goto err_get_button_irq_num_failed; + } + + /* Set CPLD MUX to H2W <-> CPLD GPIO */ + hi->init_cpld(); + + hrtimer_init(&hi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->timer.function = detect_event_timer_func; + hrtimer_init(&hi->btn_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hi->btn_timer.function = button_event_timer_func; + + ret = request_irq(hi->irq, detect_irq_handler, + IRQF_TRIGGER_LOW, "h2w_detect", NULL); + if (ret < 0) + goto err_request_detect_irq; + + /* Disable button until plugged in */ + set_irq_flags(hi->irq_btn, IRQF_VALID | IRQF_NOAUTOEN); + ret = request_irq(hi->irq_btn, button_irq_handler, + IRQF_TRIGGER_LOW, "h2w_button", NULL); + if (ret < 0) + goto err_request_h2w_headset_button_irq; + + ret = set_irq_wake(hi->irq, 1); + if (ret < 0) + goto err_request_input_dev; + + ret = set_irq_wake(hi->irq_btn, 1); + if (ret < 0) + goto err_request_input_dev; + + + + hi->input = input_allocate_device(); + if (!hi->input) { + ret = -ENOMEM; + goto err_request_input_dev; + } + + hi->input->name = "h2w headset"; + set_bit(EV_SYN, hi->input->evbit); + set_bit(EV_KEY, hi->input->evbit); + set_bit(KEY_MEDIA, hi->input->keybit); + set_bit(KEY_NEXTSONG, hi->input->keybit); + set_bit(KEY_PLAYPAUSE, hi->input->keybit); + set_bit(KEY_PREVIOUSSONG, hi->input->keybit); + set_bit(KEY_MUTE, hi->input->keybit); + set_bit(KEY_VOLUMEUP, hi->input->keybit); + set_bit(KEY_VOLUMEDOWN, hi->input->keybit); + set_bit(KEY_END, hi->input->keybit); + set_bit(KEY_SEND, hi->input->keybit); + + ret = input_register_device(hi->input); + if (ret < 0) + goto err_register_input_dev; + + return 0; + +err_register_input_dev: + input_free_device(hi->input); +err_request_input_dev: + free_irq(hi->irq_btn, 0); +err_request_h2w_headset_button_irq: + free_irq(hi->irq, 0); +err_request_detect_irq: +err_get_button_irq_num_failed: +err_get_h2w_detect_irq_num_failed: +err_set_button_gpio: +err_set_detect_gpio: + gpio_free(hi->cable_in2); +err_request_button_gpio: + gpio_free(hi->cable_in1); +err_request_detect_gpio: + destroy_workqueue(g_detection_work_queue); +err_create_work_queue: + switch_dev_unregister(&hi->sdev); +err_switch_dev_register: + printk(KERN_ERR "H2W: Failed to register driver\n"); + + return ret; +} + +static int h2w_remove(struct platform_device *pdev) +{ + H2W_DBG(""); + if (switch_get_state(&hi->sdev)) + remove_headset(); + input_unregister_device(hi->input); + gpio_free(hi->cable_in2); + gpio_free(hi->cable_in1); + free_irq(hi->irq_btn, 0); + free_irq(hi->irq, 0); + destroy_workqueue(g_detection_work_queue); + switch_dev_unregister(&hi->sdev); + + return 0; +} + + +static struct platform_driver h2w_driver = { + .probe = h2w_probe, + .remove = h2w_remove, + .driver = { + .name = "h2w", + .owner = THIS_MODULE, + }, +}; + +static int __init h2w_init(void) +{ + H2W_DBG(""); + return platform_driver_register(&h2w_driver); +} + +static void __exit h2w_exit(void) +{ + platform_driver_unregister(&h2w_driver); +} + +module_init(h2w_init); +module_exit(h2w_exit); + +MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>"); +MODULE_DESCRIPTION("HTC 2 Wire detection driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/htc_pwrsink.c b/arch/arm/mach-msm/htc_pwrsink.c new file mode 100644 index 000000000000..2ec2c7f4bb1b --- /dev/null +++ b/arch/arm/mach-msm/htc_pwrsink.c @@ -0,0 +1,281 @@ +/* arch/arm/mach-msm/htc_pwrsink.c + * + * Copyright (C) 2008 HTC Corporation + * Copyright (C) 2008 Google, Inc. + * Author: San Mehat <san@google.com> + * Kant Kang <kant_kang@htc.com> + * Eiven Peng <eiven_peng@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/debugfs.h> +#include <linux/earlysuspend.h> +#include <mach/msm_smd.h> +#include <mach/htc_pwrsink.h> + +#include "smd_private.h" + +enum { + PWRSINK_DEBUG_CURR_CHANGE = 1U << 0, + PWRSINK_DEBUG_CURR_CHANGE_AUDIO = 1U << 1, +}; +static int pwrsink_debug_mask; +module_param_named(debug_mask, pwrsink_debug_mask, int, + S_IRUGO | S_IWUSR | S_IWGRP); + +static int initialized; +static unsigned audio_path = 1; /* HTC_SND_DEVICE_SPEAKER = 1 */ +static struct pwr_sink_audio audio_sink_array[PWRSINK_AUDIO_LAST + 1]; +static struct pwr_sink *sink_array[PWRSINK_LAST + 1]; +static DEFINE_SPINLOCK(sink_lock); +static DEFINE_SPINLOCK(audio_sink_lock); +static unsigned long total_sink; +static uint32_t *smem_total_sink; + +int htc_pwrsink_set(pwrsink_id_type id, unsigned percent_utilized) +{ + unsigned long flags; + + if (!smem_total_sink) + smem_total_sink = smem_alloc(SMEM_ID_VENDOR0, sizeof(uint32_t)); + + if (!initialized) + return -EAGAIN; + + if (id < 0 || id > PWRSINK_LAST) + return -EINVAL; + + spin_lock_irqsave(&sink_lock, flags); + + if (!sink_array[id]) { + spin_unlock_irqrestore(&sink_lock, flags); + return -ENOENT; + } + + if (sink_array[id]->percent_util == percent_utilized) { + spin_unlock_irqrestore(&sink_lock, flags); + return 0; + } + + total_sink -= (sink_array[id]->ua_max * + sink_array[id]->percent_util / 100); + sink_array[id]->percent_util = percent_utilized; + total_sink += (sink_array[id]->ua_max * + sink_array[id]->percent_util / 100); + + if (smem_total_sink) + *smem_total_sink = total_sink / 1000; + + pr_debug("htc_pwrsink: ID %d, Util %d%%, Total %lu uA %s\n", + id, percent_utilized, total_sink, + smem_total_sink ? "SET" : ""); + + spin_unlock_irqrestore(&sink_lock, flags); + + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_set); + +static void compute_audio_current(void) +{ + /* unsigned long flags; */ + unsigned max_percent = 0; + int i, active_audio_sinks = 0; + pwrsink_audio_id_type last_active_audio_sink = 0; + + /* Make sure this segment will be spinlocked + before computing by calling function. */ + /* spin_lock_irqsave(&audio_sink_lock, flags); */ + for (i = 0; i <= PWRSINK_AUDIO_LAST; ++i) { + max_percent = (audio_sink_array[i].percent > max_percent) ? + audio_sink_array[i].percent : max_percent; + if (audio_sink_array[i].percent > 0) { + active_audio_sinks++; + last_active_audio_sink = i; + } + } + if (active_audio_sinks == 0) + htc_pwrsink_set(PWRSINK_AUDIO, 0); + else if (active_audio_sinks == 1) { + pwrsink_audio_id_type laas = last_active_audio_sink; + /* TODO: add volume and routing path current. */ + if (audio_path == 1) /* Speaker */ + htc_pwrsink_set(PWRSINK_AUDIO, + audio_sink_array[laas].percent); + else + htc_pwrsink_set(PWRSINK_AUDIO, + audio_sink_array[laas].percent * 9 / 10); + } else if (active_audio_sinks > 1) { + /* TODO: add volume and routing path current. */ + if (audio_path == 1) /* Speaker */ + htc_pwrsink_set(PWRSINK_AUDIO, max_percent); + else + htc_pwrsink_set(PWRSINK_AUDIO, max_percent * 9 / 10); + } + /* spin_unlock_irqrestore(&audio_sink_lock, flags); */ + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: active_audio_sinks=%d, audio_path=%d\n", __func__, + active_audio_sinks, audio_path); +} + +int htc_pwrsink_audio_set(pwrsink_audio_id_type id, unsigned percent_utilized) +{ + unsigned long flags; + + if (id < 0 || id > PWRSINK_AUDIO_LAST) + return -EINVAL; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: id=%d, percent=%d, percent_old=%d\n", __func__, + id, percent_utilized, audio_sink_array[id].percent); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_sink_array[id].percent == percent_utilized) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_sink_array[id].percent = percent_utilized; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_set); + +int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, unsigned volume) +{ + unsigned long flags; + + if (id < 0 || id > PWRSINK_AUDIO_LAST) + return -EINVAL; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: id=%d, volume=%d, volume_old=%d\n", __func__, + id, volume, audio_sink_array[id].volume); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_sink_array[id].volume == volume) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_sink_array[id].volume = volume; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_volume_set); + +int htc_pwrsink_audio_path_set(unsigned path) +{ + unsigned long flags; + + if (pwrsink_debug_mask & PWRSINK_DEBUG_CURR_CHANGE_AUDIO) + pr_info("%s: path=%d, path_old=%d\n", + __func__, path, audio_path); + + spin_lock_irqsave(&audio_sink_lock, flags); + if (audio_path == path) { + spin_unlock_irqrestore(&audio_sink_lock, flags); + return 0; + } + audio_path = path; + spin_unlock_irqrestore(&audio_sink_lock, flags); + compute_audio_current(); + return 0; +} +EXPORT_SYMBOL(htc_pwrsink_audio_path_set); + +void htc_pwrsink_suspend_early(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); +} + +int htc_pwrsink_suspend_late(struct platform_device *pdev, pm_message_t state) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + + if (pdata && pdata->suspend_late) + pdata->suspend_late(pdev, state); + else + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 13); + return 0; +} + +int htc_pwrsink_resume_early(struct platform_device *pdev) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + + if (pdata && pdata->resume_early) + pdata->resume_early(pdev); + else + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 70); + return 0; +} + +void htc_pwrsink_resume_late(struct early_suspend *h) +{ + htc_pwrsink_set(PWRSINK_SYSTEM_LOAD, 100); +} + +struct early_suspend htc_pwrsink_early_suspend = { + .level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 1, + .suspend = htc_pwrsink_suspend_early, + .resume = htc_pwrsink_resume_late, +}; + +static int __init htc_pwrsink_probe(struct platform_device *pdev) +{ + struct pwr_sink_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (!pdata) + return -EINVAL; + + total_sink = 0; + for (i = 0; i < pdata->num_sinks; i++) { + sink_array[pdata->sinks[i].id] = &pdata->sinks[i]; + total_sink += (pdata->sinks[i].ua_max * + pdata->sinks[i].percent_util / 100); + } + + initialized = 1; + + if (pdata->suspend_early) + htc_pwrsink_early_suspend.suspend = pdata->suspend_early; + if (pdata->resume_late) + htc_pwrsink_early_suspend.resume = pdata->resume_late; + register_early_suspend(&htc_pwrsink_early_suspend); + + return 0; +} + +static struct platform_driver htc_pwrsink_driver = { + .probe = htc_pwrsink_probe, + .suspend_late = htc_pwrsink_suspend_late, + .resume_early = htc_pwrsink_resume_early, + .driver = { + .name = "htc_pwrsink", + .owner = THIS_MODULE, + }, +}; + +static int __init htc_pwrsink_init(void) +{ + initialized = 0; + memset(sink_array, 0, sizeof(sink_array)); + return platform_driver_register(&htc_pwrsink_driver); +} + +module_init(htc_pwrsink_init); diff --git a/arch/arm/mach-msm/htc_wifi_nvs.c b/arch/arm/mach-msm/htc_wifi_nvs.c new file mode 100644 index 000000000000..95b8c3bbae9d --- /dev/null +++ b/arch/arm/mach-msm/htc_wifi_nvs.c @@ -0,0 +1,56 @@ +/* arch/arm/mach-msm/htc_wifi_nvs.c + * + * Code to extract WiFi calibration information from ATAG set up + * by the bootloader. + * + * Copyright (C) 2008 Google, Inc. + * Author: Dmitry Shmidt <dimitrysh@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/setup.h> + +/* configuration tags specific to msm */ +#define ATAG_MSM_WIFI 0x57494649 /* MSM WiFi */ + +#define MAX_NVS_SIZE 0x800U +static unsigned char wifi_nvs_ram[MAX_NVS_SIZE]; + +unsigned char *get_wifi_nvs_ram( void ) +{ + return( wifi_nvs_ram ); +} +EXPORT_SYMBOL(get_wifi_nvs_ram); + +static int __init parse_tag_msm_wifi(const struct tag *tag) +{ + unsigned char *dptr = (unsigned char *)(&tag->u); + unsigned size; + + size = min((tag->hdr.size - 2) * sizeof(__u32), MAX_NVS_SIZE); +#ifdef ATAG_MSM_WIFI_DEBUG + unsigned i; + + printk("WiFi Data size = %d , 0x%x\n", tag->hdr.size, tag->hdr.tag); + for(i=0;( i < size );i++) { + printk("%02x ", *dptr++); + } +#endif + memcpy( (void *)wifi_nvs_ram, (void *)dptr, size ); + return 0; +} + +__tagtable(ATAG_MSM_WIFI, parse_tag_msm_wifi); diff --git a/arch/arm/mach-msm/idle-v6.S b/arch/arm/mach-msm/idle-v6.S new file mode 100644 index 000000000000..1c74c6436a4e --- /dev/null +++ b/arch/arm/mach-msm/idle-v6.S @@ -0,0 +1,177 @@ +/* + * Idle processing for ARMv6-based Qualcomm SoCs. + * Work around bugs with SWFI. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +ENTRY(msm_arch_idle) + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 /* flush entire data cache */ + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ + bic r0, r1, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ + + mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + + mov pc, lr + +ENTRY(msm_pm_collapse) + ldr r0, =saved_state + stmia r0!, {r4-r14} + +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsid f +#endif + mrc p15, 0, r1, c1, c0, 0 /* MMU control */ + mrc p15, 0, r2, c2, c0, 0 /* ttb */ + mrc p15, 0, r3, c3, c0, 0 /* dacr */ + mrc p15, 0, ip, c13, c0, 1 /* context ID */ + stmia r0!, {r1-r3, ip} +#if defined(CONFIG_OPROFILE) + mrc p15, 0, r1, c15, c12, 0 /* pmnc */ + mrc p15, 0, r2, c15, c12, 1 /* ccnt */ + mrc p15, 0, r3, c15, c12, 2 /* pmn0 */ + mrc p15, 0, ip, c15, c12, 3 /* pmn1 */ + stmia r0!, {r1-r3, ip} +#endif + mrc p15, 0, r1, c1, c0, 2 /* read CACR */ + stmia r0!, {r1} + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 /* flush entire data cache */ + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ + bic r0, r1, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ + + mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsie f +#endif + + ldr r0, =saved_state /* restore registers */ + ldmfd r0, {r4-r14} + mov r0, #0 /* return power collapse failed */ + mov pc, lr + +ENTRY(msm_pm_collapse_exit) +#if 0 /* serial debug */ + mov r0, #0x80000016 + mcr p15, 0, r0, c15, c2, 4 + mov r0, #0xA9000000 + add r0, r0, #0x00A00000 /* UART1 */ + /*add r0, r0, #0x00C00000*/ /* UART3 */ + mov r1, #'A' + str r1, [r0, #0x00C] +#endif + ldr r1, =saved_state_end + ldr r2, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r1, r1, r3 + sub r1, r1, r2 + + ldmdb r1!, {r2} + mcr p15, 0, r2, c1, c0, 2 /* restore CACR */ +#if defined(CONFIG_OPROFILE) + ldmdb r1!, {r2-r5} + mcr p15, 0, r3, c15, c12, 1 /* ccnt */ + mcr p15, 0, r4, c15, c12, 2 /* pmn0 */ + mcr p15, 0, r5, c15, c12, 3 /* pmn1 */ + mcr p15, 0, r2, c15, c12, 0 /* pmnc */ +#endif + ldmdb r1!, {r2-r5} + mcr p15, 0, r4, c3, c0, 0 /* dacr */ + mcr p15, 0, r3, c2, c0, 0 /* ttb */ + mcr p15, 0, r5, c13, c0, 1 /* context ID */ + mov r0, #0 + mcr p15, 0, r0, c7, c5, 4 /* isb */ + ldmdb r1!, {r4-r14} + + /* Add 1:1 map in the PMD to allow smooth switch when turning on MMU */ + and r3, r3, #~0x7F /* mask off lower 7 bits of TTB */ + adr r0, msm_pm_mapped_pa /* get address of the mapped instr */ + lsr r1, r0, #20 /* get the addr range of addr in MB */ + lsl r1, r1, #2 /* multiply by 4 to get to the pg index */ + add r3, r3, r1 /* pgd + pgd_index(addr) */ + ldr r1, [r3] /* save current entry to r1 */ + lsr r0, #20 /* align current addr to 1MB boundary */ + lsl r0, #20 + /* Create new entry for this 1MB page */ + orr r0, r0, #0x400 /* PMD_SECT_AP_WRITE */ + orr r0, r0, #0x2 /* PMD_TYPE_SECT|PMD_DOMAIN(DOMAIN_KERNEL) */ + str r0, [r3] /* put new entry into the MMU table */ + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r2, c1, c0, 0 /* MMU control */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ +msm_pm_mapped_pa: + /* Switch to virtual */ + adr r2, msm_pm_pa_to_va + ldr r0, =msm_pm_pa_to_va + mov pc, r0 +msm_pm_pa_to_va: + sub r0, r0, r2 + /* Restore r1 in MMU table */ + add r3, r3, r0 + str r1, [r3] + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 /* flush entire data cache */ + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + mcr p15, 0, r0, c8, c7, 0 /* invalidate entire unified TLB */ + mcr p15, 0, r0, c7, c5, 6 /* invalidate entire branch target + * cache */ + mcr p15, 0, r0, c7, c7, 0 /* invalidate both data and instruction + * cache */ + mcr p15, 0, r0, c7, c10, 4 /* dsb */ + mcr p15, 0, r0, c7, c5, 4 /* isb */ + + mov r0, #1 + mov pc, lr + nop + nop + nop + nop + nop +1: b 1b + + + .data + +saved_state: + .space 4 * 11 /* r4-14 */ + .space 4 * 4 /* cp15 - MMU control, ttb, dacr, context ID */ +#if defined(CONFIG_OPROFILE) + .space 4 * 4 /* more cp15 - pmnc, ccnt, pmn0, pmn1 */ +#endif + .space 4 /* cacr */ +saved_state_end: + diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S new file mode 100644 index 000000000000..588062813e78 --- /dev/null +++ b/arch/arm/mach-msm/idle-v7.S @@ -0,0 +1,175 @@ +/* + * Idle processing for ARMv7-based Qualcomm SoCs. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> + +ENTRY(msm_arch_idle) + wfi + bx lr + +ENTRY(msm_pm_collapse) +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsid f +#endif + + ldr r0, =saved_state + stmia r0!, {r4-r14} + mrc p15, 0, r1, c1, c0, 0 /* MMU control */ + mrc p15, 0, r2, c2, c0, 0 /* TTBR0 */ + mrc p15, 0, r3, c3, c0, 0 /* dacr */ + mrc p15, 3, r4, c15, c0, 3 /* L2CR1 is the L2 cache control reg 1 */ + mrc p15, 0, r5, c10, c2, 0 /* PRRR */ + mrc p15, 0, r6, c10, c2, 1 /* NMRR */ + mrc p15, 0, r7, c1, c0, 1 /* ACTLR */ + mrc p15, 0, r8, c2, c0, 1 /* TTBR1 */ + mrc p15, 0, r9, c13, c0, 3 /* TPIDRURO */ + mrc p15, 0, ip, c13, c0, 1 /* context ID */ + stmia r0!, {r1-r9, ip} +#ifdef CONFIG_MSM_CPU_AVS + mrc p15, 7, r1, c15, c1, 7 /* AVSCSR is the Adaptive Voltage Scaling + * Control and Status Register */ + mrc p15, 7, r2, c15, c0, 6 /* AVSDSCR is the Adaptive Voltage + * Scaling Delay Synthesizer Control + * Register */ + mrc p15, 7, r3, c15, c1, 0 /* TSCSR is the Temperature Status and + * Control Register + */ + stmia r0!, {r1-r3} +#endif + +#ifdef CONFIG_MSM_JTAG_V7 + bl msm_save_jtag_debug +#endif + bl v7_flush_dcache_all + + mrc p15, 0, r1, c1, c0, 0 /* read current CR */ + bic r0, r1, #(1 << 2) /* clear dcache bit */ + bic r0, r0, #(1 << 12) /* clear icache bit */ + mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ + + dsb + wfi + + mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ + isb + +#if defined(CONFIG_MSM_FIQ_SUPPORT) + cpsie f +#endif + + ldr r0, =saved_state /* restore registers */ + ldmfd r0, {r4-r14} + mov r0, #0 /* return power collapse failed */ + bx lr + +ENTRY(msm_pm_collapse_exit) +#if 0 /* serial debug */ + mov r0, #0x80000016 + mcr p15, 0, r0, c15, c2, 4 + mov r0, #0xA9000000 + add r0, r0, #0x00A00000 /* UART1 */ + /*add r0, r0, #0x00C00000*/ /* UART3 */ + mov r1, #'A' + str r1, [r0, #0x00C] +#endif + ldr r1, =saved_state_end + ldr r2, =msm_pm_collapse_exit + adr r3, msm_pm_collapse_exit + add r1, r1, r3 + sub r1, r1, r2 +#ifdef CONFIG_MSM_CPU_AVS + ldmdb r1!, {r2-r4} + mcr p15, 7, r4, c15, c1, 0 /* TSCSR */ + mcr p15, 7, r3, c15, c0, 6 /* AVSDSCR */ + mcr p15, 7, r2, c15, c1, 7 /* AVSCSR */ +#endif + ldmdb r1!, {r2-r11} + mcr p15, 0, r4, c3, c0, 0 /* dacr */ + mcr p15, 0, r3, c2, c0, 0 /* TTBR0 */ + mcr p15, 3, r5, c15, c0, 3 /* L2CR1 */ + mcr p15, 0, r6, c10, c2, 0 /* PRRR */ + mcr p15, 0, r7, c10, c2, 1 /* NMRR */ + mcr p15, 0, r8, c1, c0, 1 /* ACTLR */ + mcr p15, 0, r9, c2, c0, 1 /* TTBR1 */ + mcr p15, 0, r10, c13, c0, 3 /* TPIDRURO */ + mcr p15, 0, r11, c13, c0, 1 /* context ID */ + isb + ldmdb r1!, {r4-r14} + /* Add 1:1 map in the PMD to allow smooth switch when turning on MMU */ + and r3, r3, #~0x7F /* mask off lower 7 bits of TTB */ + adr r0, msm_pm_mapped_pa /* get address of the mapped instr */ + lsr r1, r0, #20 /* get the addr range of addr in MB */ + lsl r1, r1, #2 /* multiply by 4 to get to the pg index */ + add r3, r3, r1 /* pgd + pgd_index(addr) */ + ldr r1, [r3] /* save current entry to r1 */ + lsr r0, #20 /* align current addr to 1MB boundary */ + lsl r0, #20 + /* Create new entry for this 1MB page */ + orr r0, r0, #0x4 /* PMD_SECT_BUFFERED */ + orr r0, r0, #0x400 /* PMD_SECT_AP_WRITE */ + orr r0, r0, #0x2 /* PMD_TYPE_SECT|PMD_DOMAIN(DOMAIN_KERNEL) */ + str r0, [r3] /* put new entry into the MMU table */ + mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */ + dsb + isb + mcr p15, 0, r2, c1, c0, 0 /* MMU control */ + isb +msm_pm_mapped_pa: + /* Switch to virtual */ + adr r2, msm_pm_pa_to_va + ldr r0, =msm_pm_pa_to_va + mov pc, r0 +msm_pm_pa_to_va: + sub r0, r0, r2 + /* Restore r1 in MMU table */ + add r3, r3, r0 + str r1, [r3] + mcr p15, 0, r3, c7, c10, 1 /* flush_pmd */ + dsb + isb + mcr p15, 0, r3, c8, c7, 0 /* UTLBIALL */ + mcr p15, 0, r3, c7, c5, 6 /* BPIALL */ + dsb + isb + stmfd sp!, {lr} + bl v7_flush_kern_cache_all +#ifdef CONFIG_MSM_JTAG_V7 + bl msm_restore_jtag_debug +#endif + ldmfd sp!, {lr} + mov r0, #1 + bx lr + nop + nop + nop + nop + nop +1: b 1b + + + .data + +saved_state: + .space 4 * 11 /* r4-14 */ + .space 4 * 10 /* cp15 */ +#ifdef CONFIG_MSM_CPU_AVS + .space 4 * 3 /* AVS control registers */ +#endif +saved_state_end: + + diff --git a/arch/arm/mach-msm/idle.S b/arch/arm/mach-msm/idle.S deleted file mode 100644 index 6a94f0527137..000000000000 --- a/arch/arm/mach-msm/idle.S +++ /dev/null @@ -1,36 +0,0 @@ -/* arch/arm/mach-msm/include/mach/idle.S - * - * Idle processing for MSM7K - work around bugs with SWFI. - * - * Copyright (c) 2007 QUALCOMM Incorporated. - * Copyright (C) 2007 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/linkage.h> -#include <asm/assembler.h> - -ENTRY(arch_idle) -#ifdef CONFIG_MSM7X00A_IDLE - mrc p15, 0, r1, c1, c0, 0 /* read current CR */ - bic r0, r1, #(1 << 2) /* clear dcache bit */ - bic r0, r0, #(1 << 12) /* clear icache bit */ - mcr p15, 0, r0, c1, c0, 0 /* disable d/i cache */ - - mov r0, #0 /* prepare wfi value */ - mcr p15, 0, r0, c7, c10, 0 /* flush the cache */ - mcr p15, 0, r0, c7, c10, 4 /* memory barrier */ - mcr p15, 0, r0, c7, c0, 4 /* wait for interrupt */ - - mcr p15, 0, r1, c1, c0, 0 /* restore d/i cache */ -#endif - mov pc, lr diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h new file mode 100644 index 000000000000..2e0371ed5b59 --- /dev/null +++ b/arch/arm/mach-msm/idle.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_IDLE_H_ +#define _ARCH_ARM_MACH_MSM_IDLE_H_ + +int msm_arch_idle(void); +int msm_pm_collapse(void); +void msm_pm_collapse_exit(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h index 264d62e519f3..163dffe87923 100644 --- a/arch/arm/mach-msm/include/mach/board.h +++ b/arch/arm/mach-msm/include/mach/board.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/board.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -18,6 +19,8 @@ #define __ASM_ARCH_MSM_BOARD_H #include <linux/types.h> +#include <linux/input.h> +#include <linux/clk.h> /* platform device data structures */ @@ -27,12 +30,35 @@ struct msm_mddi_platform_data unsigned has_vsync_irq:1; }; +struct msm_acpu_clock_platform_data +{ + uint32_t acpu_switch_time_us; + uint32_t max_speed_delta_khz; + uint32_t vdd_switch_time_us; + unsigned long power_collapse_khz; + unsigned long wait_for_irq_khz; + unsigned int max_axi_khz; + unsigned int max_vdd; + int (*acpu_set_vdd) (int mvolts); +}; + /* common init routines for use by arch/arm/mach-msm/board-*.c */ void __init msm_add_devices(void); void __init msm_map_common_io(void); +void __init msm_map_qsd8x50_io(void); +void __init msm_map_msm7x30_io(void); +void __init msm_map_comet_io(void); void __init msm_init_irq(void); -void __init msm_init_gpio(void); -void __init msm_clock_init(void); +void __init msm_clock_init(struct clk *clock_tbl, unsigned num_clocks); +void __init msm_acpu_clock_init(struct msm_acpu_clock_platform_data *); + +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +void msm_hsusb_set_vbus_state(int online); +#else +static inline void msm_hsusb_set_vbus_state(int online) {} +#endif + +extern int msm_shared_ram_phys; /* defined in arch/arm/mach-msm/io.c */ #endif diff --git a/arch/arm/mach-msm/include/mach/board_htc.h b/arch/arm/mach-msm/include/mach/board_htc.h new file mode 100644 index 000000000000..b537c91b957a --- /dev/null +++ b/arch/arm/mach-msm/include/mach/board_htc.h @@ -0,0 +1,78 @@ +/* arch/arm/mach-msm/include/mach/BOARD_HTC.h + * Copyright (C) 2007-2009 HTC Corporation. + * Author: Thomas Tsai <thomas_tsai@htc.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __ASM_ARCH_MSM_BOARD_HTC_H +#define __ASM_ARCH_MSM_BOARD_HTC_H + +#include <linux/types.h> +#include <linux/list.h> +#include <asm/setup.h> + +struct msm_pmem_setting{ + resource_size_t pmem_start; + resource_size_t pmem_size; + resource_size_t pmem_adsp_start; + resource_size_t pmem_adsp_size; + resource_size_t pmem_gpu0_start; + resource_size_t pmem_gpu0_size; + resource_size_t pmem_gpu1_start; + resource_size_t pmem_gpu1_size; + resource_size_t pmem_camera_start; + resource_size_t pmem_camera_size; + resource_size_t ram_console_start; + resource_size_t ram_console_size; +}; + +enum { + MSM_SERIAL_UART1 = 0, + MSM_SERIAL_UART2, + MSM_SERIAL_UART3, +#ifdef CONFIG_SERIAL_MSM_HS + MSM_SERIAL_UART1DM, + MSM_SERIAL_UART2DM, +#endif + MSM_SERIAL_NUM, +}; + + +/* common init routines for use by arch/arm/mach-msm/board-*.c */ + +void __init msm_add_usb_devices(void (*phy_reset) (void)); +void __init msm_add_mem_devices(struct msm_pmem_setting *setting); +void __init msm_init_pmic_vibrator(void); + +struct mmc_platform_data; +int __init msm_add_sdcc_devices(unsigned int controller, struct mmc_platform_data *plat); +int __init msm_add_serial_devices(unsigned uart); + +#if defined(CONFIG_USB_FUNCTION_MSM_HSUSB) +/* START: add USB connected notify function */ +struct t_usb_status_notifier{ + struct list_head notifier_link; + const char *name; + void (*func)(int online); +}; + int usb_register_notifier(struct t_usb_status_notifier *); + static LIST_HEAD(g_lh_usb_notifier_list); +/* END: add USB connected notify function */ +#endif + +int __init board_mfg_mode(void); +int __init parse_tag_smi(const struct tag *tags); +int __init parse_tag_hwid(const struct tag * tags); +int __init parse_tag_skuid(const struct tag * tags); +int parse_tag_engineerid(const struct tag * tags); + +char *board_serialno(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h new file mode 100644 index 000000000000..220fca53c7e5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/camera.h @@ -0,0 +1,297 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ASM__ARCH_CAMERA_H +#define __ASM__ARCH_CAMERA_H + +#include <linux/list.h> +#include <linux/poll.h> +#include <linux/cdev.h> +#include <linux/platform_device.h> +#include "linux/types.h" + +#include <mach/board.h> +#include <media/msm_camera.h> + +#undef CDBG +#ifdef CAMERA_DBG_MSG +#define CDBG(fmt, args...) printk(KERN_INFO "msm_camera: " fmt, ##args) +#else +#define CDBG(fmt, args...) +#endif + +#define MSM_CAMERA_MSG 0 +#define MSM_CAMERA_EVT 1 +#define NUM_WB_EXP_NEUTRAL_REGION_LINES 4 +#define NUM_WB_EXP_STAT_OUTPUT_BUFFERS 3 +#define NUM_AUTOFOCUS_MULTI_WINDOW_GRIDS 16 +#define NUM_AF_STAT_OUTPUT_BUFFERS 3 + +enum msm_queut_t { + MSM_CAM_Q_IVALID, + MSM_CAM_Q_CTRL, + MSM_CAM_Q_VFE_EVT, + MSM_CAM_Q_VFE_MSG, + MSM_CAM_Q_V4L2_REQ, + + MSM_CAM_Q_MAX +}; + +enum vfe_resp_msg_t { + VFE_EVENT, + VFE_MSG_GENERAL, + VFE_MSG_SNAPSHOT, + VFE_MSG_OUTPUT1, + VFE_MSG_OUTPUT2, + VFE_MSG_STATS_AF, + VFE_MSG_STATS_WE, + + VFE_MSG_INVALID +}; + +struct msm_vfe_phy_info { + uint32_t sbuf_phy; + uint32_t y_phy; + uint32_t cbcr_phy; +}; + +struct msm_vfe_resp_t { + enum vfe_resp_msg_t type; + struct msm_vfe_evt_msg_t evt_msg; + struct msm_vfe_phy_info phy; + void *extdata; + int32_t extlen; +}; + +struct msm_vfe_resp { + void (*vfe_resp)(struct msm_vfe_resp_t *, + enum msm_queut_t, void *syncdata); +}; + +struct msm_camvfe_fn_t { + int (*vfe_init) (struct msm_vfe_resp *, struct platform_device *); + int (*vfe_enable) (struct camera_enable_cmd_t *); + int (*vfe_config) (struct msm_vfe_cfg_cmd_t *, void *); + int (*vfe_disable) (struct camera_enable_cmd_t *, + struct platform_device *dev); + void (*vfe_release) (struct platform_device *); +}; + +struct msm_sensor_ctrl_t { + int (*s_init)(struct msm_camera_sensor_info *); + int (*s_release)(void); + int (*s_config)(void __user *); +}; + +struct msm_sync_t { + spinlock_t msg_event_queue_lock; + struct list_head msg_event_queue; + wait_queue_head_t msg_event_wait; + + spinlock_t prev_frame_q_lock; + struct list_head prev_frame_q; + wait_queue_head_t prev_frame_wait; + + spinlock_t pict_frame_q_lock; + struct list_head pict_frame_q; + wait_queue_head_t pict_frame_wait; + + spinlock_t ctrl_status_lock; + struct list_head ctrl_status_queue; + wait_queue_head_t ctrl_status_wait; + + struct hlist_head frame; + struct hlist_head stats; +}; + +struct msm_device_t { + struct msm_camvfe_fn_t vfefn; + struct device *device; + struct cdev cdev; + struct platform_device *pdev; + + struct mutex msm_lock; + uint8_t opencnt; + + const char *apps_id; + + void *cropinfo; + int croplen; + + struct mutex pict_pp_lock; + uint8_t pict_pp; + + int sidx; + struct msm_sensor_ctrl_t sctrl; + + struct mutex msm_sem; + struct msm_sync_t sync; +}; + +/* this structure is used in kernel */ +struct msm_queue_cmd_t { + struct list_head list; + + /* 1 - control command or control command status; + * 2 - adsp event; + * 3 - adsp message; + * 4 - v4l2 request; + */ + enum msm_queut_t type; + void *command; +}; + +struct register_address_value_pair_t { + uint16_t register_address; + uint16_t register_value; +}; + +struct msm_pmem_region { + struct hlist_node list; + enum msm_pmem_t type; + void *vaddr; + unsigned long paddr; + unsigned long len; + struct file *file; + uint32_t y_off; + uint32_t cbcr_off; + int fd; + uint8_t active; +}; + +struct axidata_t { + uint32_t bufnum1; + uint32_t bufnum2; + struct msm_pmem_region *region; +}; + +int32_t mt9d112_probe_init(void *, void *); +int32_t mt9t013_probe_init(void *, void *); +int32_t mt9p012_probe_init(void *, void *); +int32_t s5k3e2fx_probe_init(void *, void *); + +int32_t flash_set_led_state(enum msm_camera_led_state_t led_state); + +/* Below functions are added for V4L2 kernel APIs */ +struct msm_driver { + struct msm_device_t *vmsm; + long (*init)(struct msm_device_t *); + long (*ctrl)(struct msm_ctrl_cmd_t *, + struct msm_device_t *); + + long (*reg_pmem)(struct msm_pmem_info_t *, + struct msm_device_t *); + + long (*get_frame) (struct msm_frame_t *, + struct msm_device_t *); + + long (*put_frame) (struct msm_frame_t *, + struct msm_device_t *msm); + + long (*get_pict) (struct msm_ctrl_cmd_t *, + struct msm_device_t *msm); + + unsigned int (*drv_poll) (struct file *, struct poll_table_struct *, + struct msm_device_t *msm); +}; + +unsigned int msm_poll(struct file *, struct poll_table_struct *); + +long msm_register(struct msm_driver *, + const char *); +long msm_unregister(struct msm_driver *, + const char *); + +void msm_camvfe_init(void); +int msm_camvfe_check(void *); +void msm_camvfe_fn_init(struct msm_camvfe_fn_t *); +int msm_camera_drv_start(struct platform_device *); +int msm_camera_drv_remove(struct platform_device *); + +enum msm_camio_clk_type { + CAMIO_VFE_MDC_CLK, + CAMIO_MDC_CLK, + CAMIO_VFE_CLK, + CAMIO_VFE_AXI_CLK, + + CAMIO_MAX_CLK +}; + +enum msm_camio_clk_src_type { + MSM_CAMIO_CLK_SRC_INTERNAL, + MSM_CAMIO_CLK_SRC_EXTERNAL, + MSM_CAMIO_CLK_SRC_MAX +}; + +enum msm_s_test_mode_t { + S_TEST_OFF, + S_TEST_1, + S_TEST_2, + S_TEST_3 +}; + +enum msm_s_resolution_t { + S_QTR_SIZE, + S_FULL_SIZE, + S_INVALID_SIZE +}; + +enum msm_s_reg_update_t { + /* Sensor egisters that need to be updated during initialization */ + S_REG_INIT, + /* Sensor egisters that needs periodic I2C writes */ + S_UPDATE_PERIODIC, + /* All the sensor Registers will be updated */ + S_UPDATE_ALL, + /* Not valid update */ + S_UPDATE_INVALID +}; + +enum msm_s_setting_t { + S_RES_PREVIEW, + S_RES_CAPTURE +}; + +int msm_camio_enable(struct platform_device *dev); + +int msm_camio_clk_enable(enum msm_camio_clk_type clk); +int msm_camio_clk_disable(enum msm_camio_clk_type clk); +int msm_camio_clk_config(uint32_t freq); +void msm_camio_clk_rate_set(int rate); +void msm_camio_clk_axi_rate_set(int rate); + +void msm_camio_camif_pad_reg_reset(void); +void msm_camio_camif_pad_reg_reset_2(void); + +void msm_camio_vfe_blk_reset(void); + +void msm_camio_clk_sel(enum msm_camio_clk_src_type); +void msm_camio_disable(struct platform_device *); +int msm_camio_probe_on(struct platform_device *); +int msm_camio_probe_off(struct platform_device *); +#endif diff --git a/arch/arm/mach-msm/include/mach/clk.h b/arch/arm/mach-msm/include/mach/clk.h new file mode 100644 index 000000000000..ac70550cb41c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/clk.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef __MACH_CLK_H +#define __MACH_CLK_H + +/* Magic rate value for use with PM QOS to request the board's maximum + * supported AXI rate. PM QOS will only pass positive s32 rate values + * through to the clock driver, so INT_MAX is used. + */ +#define MSM_AXI_MAX_FREQ LONG_MAX + +enum clk_reset_action { + CLK_RESET_DEASSERT = 0, + CLK_RESET_ASSERT = 1 +}; + +struct clk; + +/* Rate is minimum clock rate in Hz */ +int clk_set_min_rate(struct clk *clk, unsigned long rate); + +/* Rate is maximum clock rate in Hz */ +int clk_set_max_rate(struct clk *clk, unsigned long rate); + +/* Assert/Deassert reset to a hardware block associated with a clock */ +int clk_reset(struct clk *clk, enum clk_reset_action action); + +#endif diff --git a/arch/arm/mach-msm/include/mach/dal.h b/arch/arm/mach-msm/include/mach/dal.h new file mode 100644 index 000000000000..a864f1469063 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/dal.h @@ -0,0 +1,155 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __DAL_H__ +#define __DAL_H__ + +#include <linux/kernel.h> +#include <mach/msm_smd.h> + +#define DALRPC_DEST_MODEM SMD_APPS_MODEM +#define DALRPC_DEST_QDSP SMD_APPS_QDSP + +#define DALRPC_TIMEOUT_INFINITE -1 + +enum { + DALDEVICE_ATTACH_IDX = 0, + DALDEVICE_DETACH_IDX, + DALDEVICE_INIT_IDX, + DALDEVICE_DEINIT_IDX, + DALDEVICE_OPEN_IDX, + DALDEVICE_CLOSE_IDX, + DALDEVICE_INFO_IDX, + DALDEVICE_POWEREVENT_IDX, + DALDEVICE_SYSREQUEST_IDX, + DALDEVICE_FIRST_DEVICE_API_IDX +}; + +struct daldevice_info_t { + uint32_t size; + uint32_t version; + char name[32]; +}; + +int daldevice_attach(uint32_t device_id, char *port, int cpu, + void **handle_ptr); + +/* The caller must ensure there are no outstanding dalrpc calls on + * the client before (and while) calling daldevice_detach. */ +int daldevice_detach(void *handle); + +uint32_t dalrpc_fcn_0(uint32_t ddi_idx, void *handle, uint32_t s1); +uint32_t dalrpc_fcn_1(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2); +uint32_t dalrpc_fcn_2(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t *p_s2); +uint32_t dalrpc_fcn_3(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t s3); +uint32_t dalrpc_fcn_4(uint32_t ddi_idx, void *handle, uint32_t s1, + uint32_t s2, uint32_t *p_s3); +uint32_t dalrpc_fcn_5(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen); +uint32_t dalrpc_fcn_6(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen); +uint32_t dalrpc_fcn_7(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen, + uint32_t *oalen); +uint32_t dalrpc_fcn_8(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf, uint32_t olen); +uint32_t dalrpc_fcn_9(uint32_t ddi_idx, void *handle, void *obuf, + uint32_t olen); +uint32_t dalrpc_fcn_10(uint32_t ddi_idx, void *handle, uint32_t s1, + const void *ibuf, uint32_t ilen, void *obuf, + uint32_t olen, uint32_t *oalen); +uint32_t dalrpc_fcn_11(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen); +uint32_t dalrpc_fcn_12(uint32_t ddi_idx, void *handle, uint32_t s1, + void *obuf, uint32_t olen, uint32_t *oalen); +uint32_t dalrpc_fcn_13(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen); +uint32_t dalrpc_fcn_14(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, void *obuf1, uint32_t olen1, + void *obuf2, uint32_t olen2, uint32_t *oalen2); +uint32_t dalrpc_fcn_15(uint32_t ddi_idx, void *handle, const void *ibuf, + uint32_t ilen, const void *ibuf2, uint32_t ilen2, + void *obuf, uint32_t olen, uint32_t *oalen, + void *obuf2, uint32_t olen2); + +static inline uint32_t daldevice_info(void *handle, + struct daldevice_info_t *info, + uint32_t info_size) +{ + return dalrpc_fcn_9(DALDEVICE_INFO_IDX, handle, info, info_size); +} + +static inline uint32_t daldevice_sysrequest(void *handle, uint32_t req_id, + const void *src_ptr, + uint32_t src_len, void *dest_ptr, + uint32_t dest_len, + uint32_t *dest_alen) +{ + return dalrpc_fcn_10(DALDEVICE_SYSREQUEST_IDX, handle, req_id, + src_ptr, src_len, dest_ptr, dest_len, dest_alen); +} + +static inline uint32_t daldevice_init(void *handle) +{ + return dalrpc_fcn_0(DALDEVICE_INIT_IDX, handle, 0); +} + +static inline uint32_t daldevice_deinit(void *handle) +{ + return dalrpc_fcn_0(DALDEVICE_DEINIT_IDX, handle, 0); +} + +static inline uint32_t daldevice_open(void *handle, uint32_t mode) +{ + return dalrpc_fcn_0(DALDEVICE_OPEN_IDX, handle, mode); +} + +static inline uint32_t daldevice_close(void *handle) +{ + return dalrpc_fcn_0(DALDEVICE_CLOSE_IDX, handle, 0); +} + +void *dalrpc_alloc_event(void *handle); +void *dalrpc_alloc_cb(void *handle, + void (*fn)(void *, uint32_t, void *, uint32_t), + void *context); +void dalrpc_dealloc_event(void *handle, + void *ev_h); +void dalrpc_dealloc_cb(void *handle, + void *cb_h); + +#define dalrpc_event_wait(ev_h, timeout) \ + dalrpc_event_wait_multiple(1, &ev_h, timeout) + +int dalrpc_event_wait_multiple(int num, void **ev_h, int timeout); + +#endif /* __DAL_H__ */ diff --git a/arch/arm/mach-msm/include/mach/dma.h b/arch/arm/mach-msm/include/mach/dma.h index 5ab5bdffab07..7a2fc563333b 100644 --- a/arch/arm/mach-msm/include/mach/dma.h +++ b/arch/arm/mach-msm/include/mach/dma.h @@ -1,6 +1,7 @@ /* linux/include/asm-arm/arch-msm/dma.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -41,46 +42,55 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr); #define DMOV_SD2(off, ch) (MSM_DMOV_BASE + 0x0800 + (off) + ((ch) << 2)) #define DMOV_SD3(off, ch) (MSM_DMOV_BASE + 0x0C00 + (off) + ((ch) << 2)) -/* only security domain 3 is available to the ARM11 - * SD0 -> mARM trusted, SD1 -> mARM nontrusted, SD2 -> aDSP, SD3 -> aARM - */ +#if defined(CONFIG_ARCH_MSM7X30) +#define DMOV_SD_AARM DMOV_SD2 +#else +#define DMOV_SD_AARM DMOV_SD3 +#endif -#define DMOV_CMD_PTR(ch) DMOV_SD3(0x000, ch) +#define DMOV_CMD_PTR(ch) DMOV_SD_AARM(0x000, ch) #define DMOV_CMD_LIST (0 << 29) /* does not work */ #define DMOV_CMD_PTR_LIST (1 << 29) /* works */ #define DMOV_CMD_INPUT_CFG (2 << 29) /* untested */ #define DMOV_CMD_OUTPUT_CFG (3 << 29) /* untested */ #define DMOV_CMD_ADDR(addr) ((addr) >> 3) -#define DMOV_RSLT(ch) DMOV_SD3(0x040, ch) +#define DMOV_RSLT(ch) DMOV_SD_AARM(0x040, ch) #define DMOV_RSLT_VALID (1 << 31) /* 0 == host has empties result fifo */ #define DMOV_RSLT_ERROR (1 << 3) #define DMOV_RSLT_FLUSH (1 << 2) #define DMOV_RSLT_DONE (1 << 1) /* top pointer done */ #define DMOV_RSLT_USER (1 << 0) /* command with FR force result */ -#define DMOV_FLUSH0(ch) DMOV_SD3(0x080, ch) -#define DMOV_FLUSH1(ch) DMOV_SD3(0x0C0, ch) -#define DMOV_FLUSH2(ch) DMOV_SD3(0x100, ch) -#define DMOV_FLUSH3(ch) DMOV_SD3(0x140, ch) -#define DMOV_FLUSH4(ch) DMOV_SD3(0x180, ch) -#define DMOV_FLUSH5(ch) DMOV_SD3(0x1C0, ch) +#define DMOV_FLUSH0(ch) DMOV_SD_AARM(0x080, ch) +#define DMOV_FLUSH1(ch) DMOV_SD_AARM(0x0C0, ch) +#define DMOV_FLUSH2(ch) DMOV_SD_AARM(0x100, ch) +#define DMOV_FLUSH3(ch) DMOV_SD_AARM(0x140, ch) +#define DMOV_FLUSH4(ch) DMOV_SD_AARM(0x180, ch) +#define DMOV_FLUSH5(ch) DMOV_SD_AARM(0x1C0, ch) -#define DMOV_STATUS(ch) DMOV_SD3(0x200, ch) +#define DMOV_STATUS(ch) DMOV_SD_AARM(0x200, ch) #define DMOV_STATUS_RSLT_COUNT(n) (((n) >> 29)) #define DMOV_STATUS_CMD_COUNT(n) (((n) >> 27) & 3) #define DMOV_STATUS_RSLT_VALID (1 << 1) #define DMOV_STATUS_CMD_PTR_RDY (1 << 0) +#define DMOV_ISR DMOV_SD_AARM(0x380, 0) -#define DMOV_ISR DMOV_SD3(0x380, 0) - -#define DMOV_CONFIG(ch) DMOV_SD3(0x300, ch) +#define DMOV_CONFIG(ch) DMOV_SD_AARM(0x300, ch) #define DMOV_CONFIG_FORCE_TOP_PTR_RSLT (1 << 2) #define DMOV_CONFIG_FORCE_FLUSH_RSLT (1 << 1) #define DMOV_CONFIG_IRQ_EN (1 << 0) /* channel assignments */ +#define DMOV_GP_CHAN 4 + +#define DMOV_CE_IN_CHAN 5 +#define DMOV_CE_IN_CRCI 1 + +#define DMOV_CE_OUT_CHAN 6 +#define DMOV_CE_OUT_CRCI 2 + #define DMOV_NAND_CHAN 7 #define DMOV_NAND_CRCI_CMD 5 #define DMOV_NAND_CRCI_DATA 4 diff --git a/arch/arm/mach-msm/include/mach/dma_test.h b/arch/arm/mach-msm/include/mach/dma_test.h new file mode 100644 index 000000000000..7f7dfe38a350 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/dma_test.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __MSM_DMA_TEST__ +#define __MSM_DMA_TEST__ + +#include <linux/ioctl.h> + +#define MSM_DMA_IOC_MAGIC 0x83 + +/* The testing driver can manage a series of buffers. These are + * allocated and freed using these calls. */ +struct msm_dma_alloc_req { + int size; /* Size of this request, in bytes. */ + int bufnum; /* OUT: Number of buffer allocated. */ +}; +#define MSM_DMA_IOALLOC _IOWR(MSM_DMA_IOC_MAGIC, 2, struct msm_dma_alloc_req) + +/* Free the specified buffer. */ +#define MSM_DMA_IOFREE _IOW(MSM_DMA_IOC_MAGIC, 3, int) + +/* Free all used buffers. */ +#define MSM_DMA_IOFREEALL _IO(MSM_DMA_IOC_MAGIC, 7) + +/* Read/write data into kernel buffer. */ +struct msm_dma_bufxfer { + void *data; + int size; + int bufnum; +}; +#define MSM_DMA_IOWBUF _IOW(MSM_DMA_IOC_MAGIC, 4, struct msm_dma_bufxfer) +#define MSM_DMA_IORBUF _IOW(MSM_DMA_IOC_MAGIC, 5, struct msm_dma_bufxfer) + +/* Use the data mover to copy from one buffer to another. */ +struct msm_dma_scopy { + int srcbuf; + int destbuf; + int size; +}; +#define MSM_DMA_IOSCOPY _IOW(MSM_DMA_IOC_MAGIC, 6, struct msm_dma_scopy) + +#endif /* __MSM_DMA_TEST__ */ diff --git a/arch/arm/mach-msm/include/mach/fiq.h b/arch/arm/mach-msm/include/mach/fiq.h new file mode 100644 index 000000000000..29a3ba1f33f3 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/fiq.h @@ -0,0 +1,33 @@ +/* linux/include/asm-arm/arch-msm/irqs.h + * + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_FIQ_H +#define __ASM_ARCH_MSM_FIQ_H + +/* cause an interrupt to be an FIQ instead of a regular IRQ */ +void msm_fiq_select(int number); +void msm_fiq_unselect(int number); + +/* enable/disable an interrupt that is an FIQ (not safe from FIQ context) */ +void msm_fiq_enable(int number); +void msm_fiq_disable(int number); + +/* install an FIQ handler */ +int msm_fiq_set_handler(void (*func)(void *data, void *regs), void *data); + +/* cause an edge triggered interrupt to fire (safe from FIQ context */ +void msm_trigger_irq(int number); + +#endif diff --git a/arch/arm/mach-msm/include/mach/gpio.h b/arch/arm/mach-msm/include/mach/gpio.h new file mode 100644 index 000000000000..713afb930d2d --- /dev/null +++ b/arch/arm/mach-msm/include/mach/gpio.h @@ -0,0 +1,167 @@ +/* linux/include/asm-arm/arch-msm/gpio.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Mike Lockwood <lockwood@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_GPIO_H +#define __ASM_ARCH_MSM_GPIO_H + +#include <linux/interrupt.h> + +/** + * struct msm_gpio - GPIO pin description + * @gpio_cfg - configuration bitmap, as per gpio_tlmm_config() + * @label - textual label + * + * Usually, GPIO's are operated by sets. + * This struct accumulate all GPIO information in single source + * and facilitete group operations provided by msm_gpios_xxx() + */ +struct msm_gpio { + u32 gpio_cfg; + const char *label; +}; + +/** + * msm_gpios_request_enable() - request and enable set of GPIOs + * + * Request and configure set of GPIO's + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_request_enable(const struct msm_gpio *table, int size); +/** + * msm_gpios_disable_free() - disable and free set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_disable_free(const struct msm_gpio *table, int size); +/** + * msm_gpios_request() - request set of GPIOs + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_request(const struct msm_gpio *table, int size); +/** + * msm_gpios_free() - free set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_free(const struct msm_gpio *table, int size); +/** + * msm_gpios_enable() - enable set of GPIOs + * In case of error, all operations rolled back. + * Return error code. + * + * @table: GPIO table + * @size: number of entries in @table + */ +int msm_gpios_enable(const struct msm_gpio *table, int size); +/** + * msm_gpios_disable() - disable set of GPIOs + * + * @table: GPIO table + * @size: number of entries in @table + */ +void msm_gpios_disable(const struct msm_gpio *table, int size); + +int gpio_request(unsigned gpio, const char *label); +void gpio_free(unsigned gpio); +int gpio_direction_input(unsigned gpio); +int gpio_direction_output(unsigned gpio, int value); +int gpio_get_value(unsigned gpio); +void gpio_set_value(unsigned gpio, int value); +int gpio_to_irq(unsigned gpio); + +#include <asm-generic/gpio.h> + +/* extended gpio api */ + +#define GPIOF_IRQF_MASK 0x0000ffff /* use to specify edge detection without */ +#define GPIOF_IRQF_TRIGGER_NONE 0x00010000 /* IRQF_TRIGGER_NONE is 0 which also means "as already configured" */ +#define GPIOF_INPUT 0x00020000 +#define GPIOF_DRIVE_OUTPUT 0x00040000 +#define GPIOF_OUTPUT_LOW 0x00080000 +#define GPIOF_OUTPUT_HIGH 0x00100000 + +#define GPIOIRQF_SHARED 0x00000001 /* the irq line is shared with other inputs */ + +extern int gpio_configure(unsigned int gpio, unsigned long flags); +extern int gpio_read_detect_status(unsigned int gpio); +extern int gpio_clear_detect_status(unsigned int gpio); + +/* GPIO TLMM (Top Level Multiplexing) Definitions */ + +/* GPIO TLMM: Function -- GPIO specific */ + +/* GPIO TLMM: Direction */ +enum { + GPIO_INPUT, + GPIO_OUTPUT, +}; + +/* GPIO TLMM: Pullup/Pulldown */ +enum { + GPIO_NO_PULL, + GPIO_PULL_DOWN, + GPIO_KEEPER, + GPIO_PULL_UP, +}; + +/* GPIO TLMM: Drive Strength */ +enum { + GPIO_2MA, + GPIO_4MA, + GPIO_6MA, + GPIO_8MA, + GPIO_10MA, + GPIO_12MA, + GPIO_14MA, + GPIO_16MA, +}; + +enum { + GPIO_ENABLE, + GPIO_DISABLE, +}; + +#define GPIO_CFG(gpio, func, dir, pull, drvstr) \ + ((((gpio) & 0x3FF) << 4) | \ + ((func) & 0xf) | \ + (((dir) & 0x1) << 14) | \ + (((pull) & 0x3) << 15) | \ + (((drvstr) & 0xF) << 17)) + +/** + * extract GPIO pin from bit-field used for gpio_tlmm_config + */ +#define GPIO_PIN(gpio_cfg) (((gpio_cfg) >> 4) & 0x3ff) +#define GPIO_FUNC(gpio_cfg) (((gpio_cfg) >> 0) & 0xf) +#define GPIO_DIR(gpio_cfg) (((gpio_cfg) >> 14) & 0x1) +#define GPIO_PULL(gpio_cfg) (((gpio_cfg) >> 15) & 0x3) +#define GPIO_DRVSTR(gpio_cfg) (((gpio_cfg) >> 17) & 0xf) + +int gpio_tlmm_config(unsigned config, unsigned disable); + +#endif + diff --git a/arch/arm/mach-msm/include/mach/htc_headset.h b/arch/arm/mach-msm/include/mach/htc_headset.h new file mode 100644 index 000000000000..2f4c18db2625 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_headset.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2008 HTC, Inc. + * Copyright (C) 2008 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_HTC_HEADSET_H +#define __ASM_ARCH_HTC_HEADSET_H + +struct h2w_platform_data { + char *power_name; + int cable_in1; + int cable_in2; + int h2w_clk; + int h2w_data; + int debug_uart; + void (*config_cpld)(int); + void (*init_cpld)(void); + void (*set_dat)(int); + void (*set_clk)(int); + void (*set_dat_dir)(int); + void (*set_clk_dir)(int); + int (*get_dat)(void); + int (*get_clk)(void); +}; + +#define BIT_HEADSET (1 << 0) +#define BIT_HEADSET_NO_MIC (1 << 1) +#define BIT_TTY (1 << 2) +#define BIT_FM_HEADSET (1 << 3) +#define BIT_FM_SPEAKER (1 << 4) + +enum { + H2W_NO_DEVICE = 0, + H2W_HTC_HEADSET = 1, +/* H2W_TTY_DEVICE = 2,*/ + NORMAL_HEARPHONE= 2, + H2W_DEVICE = 3, + H2W_USB_CRADLE = 4, + H2W_UART_DEBUG = 5, +}; + +enum { + H2W_GPIO = 0, + H2W_UART1 = 1, + H2W_UART3 = 2, + H2W_BT = 3 +}; + +#define RESEND_DELAY (3) /* ms */ +#define MAX_ACK_RESEND_TIMES (6) /* follow spec */ +#define MAX_HOST_RESEND_TIMES (3) /* follow spec */ +#define MAX_HYGEIA_RESEND_TIMES (5) + +#define H2W_ASCR_DEVICE_INI (0x01) +#define H2W_ASCR_ACT_EN (0x02) +#define H2W_ASCR_PHONE_IN (0x04) +#define H2W_ASCR_RESET (0x08) +#define H2W_ASCR_AUDIO_IN (0x10) + +#define H2W_LED_OFF (0x0) +#define H2W_LED_BKL (0x1) +#define H2W_LED_MTL (0x2) + +typedef enum { + /* === system group 0x0000~0x00FF === */ + /* (R) Accessory type register */ + H2W_SYSTEM = 0x0000, + /* (R) Maximum group address */ + H2W_MAX_GP_ADD = 0x0001, + /* (R/W) Accessory system control register0 */ + H2W_ASCR0 = 0x0002, + + /* === key group 0x0100~0x01FF === */ + /* (R) Key group maximum sub address */ + H2W_KEY_MAXADD = 0x0100, + /* (R) ASCII key press down flag */ + H2W_ASCII_DOWN = 0x0101, + /* (R) ASCII key release up flag */ + H2W_ASCII_UP = 0x0102, + /* (R) Function key status flag */ + H2W_FNKEY_UPDOWN = 0x0103, + /* (R/W) Key device status */ + H2W_KD_STATUS = 0x0104, + + /* === led group 0x0200~0x02FF === */ + /* (R) LED group maximum sub address */ + H2W_LED_MAXADD = 0x0200, + /* (R/W) LED control register0 */ + H2W_LEDCT0 = 0x0201, + + /* === crdl group 0x0300~0x03FF === */ + /* (R) Cardle group maximum sub address */ + H2W_CRDL_MAXADD = 0x0300, + /* (R/W) Cardle group function control register0 */ + H2W_CRDLCT0 = 0x0301, + + /* === car kit group 0x0400~0x04FF === */ + H2W_CARKIT_MAXADD = 0x0400, + + /* === usb host group 0x0500~0x05FF === */ + H2W_USBHOST_MAXADD = 0x0500, + + /* === medical group 0x0600~0x06FF === */ + H2W_MED_MAXADD = 0x0600, + H2W_MED_CONTROL = 0x0601, + H2W_MED_IN_DATA = 0x0602, +} H2W_ADDR; + + +typedef struct H2W_INFO { + /* system group */ + unsigned char CLK_SP; + int SLEEP_PR; + unsigned char HW_REV; + int AUDIO_DEVICE; + unsigned char ACC_CLASS; + unsigned char MAX_GP_ADD; + + /* key group */ + int KEY_MAXADD; + int ASCII_DOWN; + int ASCII_UP; + int FNKEY_UPDOWN; + int KD_STATUS; + + /* led group */ + int LED_MAXADD; + int LEDCT0; + + /* medical group */ + int MED_MAXADD; + unsigned char AP_ID; + unsigned char AP_EN; + unsigned char DATA_EN; +} H2W_INFO; + +typedef enum { + H2W_500KHz = 1, + H2W_250KHz = 2, + H2W_166KHz = 3, + H2W_125KHz = 4, + H2W_100KHz = 5, + H2W_83KHz = 6, + H2W_71KHz = 7, + H2W_62KHz = 8, + H2W_55KHz = 9, + H2W_50KHz = 10, +} H2W_SPEED; + +typedef enum { + H2W_KEY_INVALID = -1, + H2W_KEY_PLAY = 0, + H2W_KEY_FORWARD = 1, + H2W_KEY_BACKWARD = 2, + H2W_KEY_VOLUP = 3, + H2W_KEY_VOLDOWN = 4, + H2W_KEY_PICKUP = 5, + H2W_KEY_HANGUP = 6, + H2W_KEY_MUTE = 7, + H2W_KEY_HOLD = 8, + H2W_NUM_KEYFUNC = 9, +} KEYFUNC; +#endif diff --git a/arch/arm/mach-msm/include/mach/htc_pwrsink.h b/arch/arm/mach-msm/include/mach/htc_pwrsink.h new file mode 100644 index 000000000000..c7a91f1d906c --- /dev/null +++ b/arch/arm/mach-msm/include/mach/htc_pwrsink.h @@ -0,0 +1,87 @@ +/* include/asm/mach-msm/htc_pwrsink.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2008 HTC Corporation. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_ +#define _ARCH_ARM_MACH_MSM_HTC_PWRSINK_H_ + +#include <linux/platform_device.h> +#include <linux/earlysuspend.h> + +typedef enum { + PWRSINK_AUDIO_PCM = 0, + PWRSINK_AUDIO_MP3, + PWRSINK_AUDIO_AAC, + + PWRSINK_AUDIO_LAST = PWRSINK_AUDIO_AAC, + PWRSINK_AUDIO_INVALID +} pwrsink_audio_id_type; + +struct pwr_sink_audio { + unsigned volume; + unsigned percent; +}; + +typedef enum { + PWRSINK_SYSTEM_LOAD = 0, + PWRSINK_AUDIO, + PWRSINK_BACKLIGHT, + PWRSINK_LED_BUTTON, + PWRSINK_LED_KEYBOARD, + PWRSINK_GP_CLK, + PWRSINK_BLUETOOTH, + PWRSINK_CAMERA, + PWRSINK_SDCARD, + PWRSINK_VIDEO, + PWRSINK_WIFI, + + PWRSINK_LAST = PWRSINK_WIFI, + PWRSINK_INVALID +} pwrsink_id_type; + +struct pwr_sink { + pwrsink_id_type id; + unsigned ua_max; + unsigned percent_util; +}; + +struct pwr_sink_platform_data { + unsigned num_sinks; + struct pwr_sink *sinks; + int (*suspend_late)(struct platform_device *, pm_message_t state); + int (*resume_early)(struct platform_device *); + void (*suspend_early)(struct early_suspend *); + void (*resume_late)(struct early_suspend *); +}; + +#ifndef CONFIG_HTC_PWRSINK +static inline int htc_pwrsink_set(pwrsink_id_type id, unsigned percent) +{ + return 0; +} +static inline int htc_pwrsink_audio_set(pwrsink_audio_id_type id, + unsigned percent_utilized) { return 0; } +static inline int htc_pwrsink_audio_volume_set( + pwrsink_audio_id_type id, unsigned volume) { return 0; } +static inline int htc_pwrsink_audio_path_set(unsigned path) { return 0; } +#else +extern int htc_pwrsink_set(pwrsink_id_type id, unsigned percent); +extern int htc_pwrsink_audio_set(pwrsink_audio_id_type id, + unsigned percent_utilized); +extern int htc_pwrsink_audio_volume_set(pwrsink_audio_id_type id, + unsigned volume); +extern int htc_pwrsink_audio_path_set(unsigned path); +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/internal_power_rail.h b/arch/arm/mach-msm/include/mach/internal_power_rail.h new file mode 100644 index 000000000000..cd7ca765bde6 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/internal_power_rail.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _INTERNAL_POWER_RAIL_H +#define _INTERNAL_POWER_RAIL_H + +/* Clock power rail IDs */ +#define PWR_RAIL_GRP_CLK 8 +#define PWR_RAIL_VDC_CLK 39 +#define PWR_RAIL_VFE_CLK 41 +#define PWR_RAIL_MFC_CLK 68 + +enum rail_ctl_mode { + PWR_RAIL_CTL_AUTO = 0, + PWR_RAIL_CTL_MANUAL, +}; + +int internal_pwr_rail_ctl(unsigned rail_id, bool enable); +int internal_pwr_rail_mode(unsigned rail_id, enum rail_ctl_mode mode); + +#endif /* _INTERNAL_POWER_RAIL_H */ + diff --git a/arch/arm/mach-msm/include/mach/io.h b/arch/arm/mach-msm/include/mach/io.h index aab964591db4..bdac617f4204 100644 --- a/arch/arm/mach-msm/include/mach/io.h +++ b/arch/arm/mach-msm/include/mach/io.h @@ -23,7 +23,7 @@ void __iomem *__msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype); -#define __io(a) __typesafe_io(a) +#define __io(a) __typesafe_io(a) #define __mem_pci(a) (a) #endif diff --git a/arch/arm/mach-msm/include/mach/irqs-7x30.h b/arch/arm/mach-msm/include/mach/irqs-7x30.h new file mode 100644 index 000000000000..cd9b58572be3 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-7x30.h @@ -0,0 +1,158 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ASM_ARCH_MSM_IRQS_7X30_H +#define __ASM_ARCH_MSM_IRQS_7X30_H + +/* MSM ACPU Interrupt Numbers */ + +#define INT_DEBUG_TIMER_EXP 0 +#define INT_GPT0_TIMER_EXP 1 +#define INT_GPT1_TIMER_EXP 2 +#define INT_WDT0_ACCSCSSBARK 3 +#define INT_WDT1_ACCSCSSBARK 4 +#define INT_AVS_SVIC 5 +#define INT_AVS_SVIC_SW_DONE 6 +#define INT_SC_DBG_RX_FULL 7 +#define INT_SC_DBG_TX_EMPTY 8 +#define INT_SC_PERF_MON 9 +#define INT_AVS_REQ_DOWN 10 +#define INT_AVS_REQ_UP 11 +#define INT_SC_ACG 12 +/* SCSS_VICFIQSTS1[13:15] are RESERVED */ +#define INT_L2_SVICCPUIRPTREQ 16 +#define INT_L2_SVICDMANSIRPTREQ 17 +#define INT_L2_SVICDMASIRPTREQ 18 +#define INT_L2_SVICSLVIRPTREQ 19 +#define INT_AD5A_MPROC_APPS_0 20 +#define INT_AD5A_MPROC_APPS_1 21 +#define INT_A9_M2A_0 22 +#define INT_A9_M2A_1 23 +#define INT_A9_M2A_2 24 +#define INT_A9_M2A_3 25 +#define INT_A9_M2A_4 26 +#define INT_A9_M2A_5 27 +#define INT_A9_M2A_6 28 +#define INT_A9_M2A_7 29 +#define INT_A9_M2A_8 30 +#define INT_A9_M2A_9 31 + +#define INT_AXI_EBI1_SC (32 + 0) +#define INT_IMEM_ERR (32 + 1) +#define INT_AXI_EBI0_SC (32 + 2) +#define INT_PBUS_SC_IRQC (32 + 3) +#define INT_PERPH_BUS_BPM (32 + 4) +#define INT_CC_TEMP_SENSE (32 + 5) +#define INT_UXMC_EBI0 (32 + 6) +#define INT_UXMC_EBI1 (32 + 7) +#define INT_EBI2_OP_DONE (32 + 8) +#define INT_EBI2_WR_ER_DONE (32 + 9) +#define INT_TCSR_SPSS_CE (32 + 10) +#define INT_EMDH (32 + 11) +#define INT_PMDH (32 + 12) +#define INT_MDC (32 + 13) +#define INT_MIDI_TO_SUPSS (32 + 14) +#define INT_LPA_2 (32 + 15) +#define INT_GPIO_GROUP1_SECURE (32 + 16) +#define INT_GPIO_GROUP2_SECURE (32 + 17) +#define INT_GPIO_GROUP1 (32 + 18) +#define INT_GPIO_GROUP2 (32 + 19) +#define INT_MPRPH_SOFTRESET (32 + 20) +#define INT_PWB_I2C (32 + 21) +#define INT_PWB_I2C_2 (32 + 22) +#define INT_TSSC_SAMPLE (32 + 23) +#define INT_TSSC_PENUP (32 + 24) +#define INT_TCHSCRN_SSBI (32 + 25) +#define INT_FM_RDS (32 + 26) +#define INT_KEYSENSE (32 + 27) +#define INT_USB_OTG_HS (32 + 28) +#define INT_USB_OTG_HS2 (32 + 29) +#define INT_USB_OTG_HS3 (32 + 30) +#define INT_RESERVED_BIT31 (32 + 31) + +#define INT_SPI_OUTPUT (64 + 0) +#define INT_SPI_INPUT (64 + 1) +#define INT_SPI_ERROR (64 + 2) +#define INT_UART1 (64 + 3) +#define INT_UART1_RX (64 + 4) +#define INT_UART2 (64 + 5) +#define INT_UART2_RX (64 + 6) +#define INT_UART3 (64 + 7) +#define INT_UART3_RX (64 + 8) +#define INT_UART1DM_IRQ (64 + 9) +#define INT_UART1DM_RX (64 + 10) +#define INT_UART2DM_IRQ (64 + 11) +#define INT_UART2DM_RX (64 + 12) +#define INT_TSIF (64 + 13) +#define INT_ADM_SC1 (64 + 14) +#define INT_ADM_SC2 (64 + 15) +#define INT_MDP (64 + 16) +#define INT_VPE (64 + 17) +#define INT_GRP_2D (64 + 18) +#define INT_GRP_3D (64 + 19) +#define INT_ROTATOR (64 + 20) +#define INT_MFC720 (64 + 21) +#define INT_JPEG (64 + 22) +#define INT_VFE (64 + 23) +#define INT_TV_ENC (64 + 24) +#define INT_PMIC_SSBI (64 + 25) +#define INT_MPM_1 (64 + 26) +#define INT_TCSR_SPSS_SAMPLE (64 + 27) +#define INT_TCSR_SPSS_PENUP (64 + 28) +#define INT_MPM_2 (64 + 29) +#define INT_SDC1_0 (64 + 30) +#define INT_SDC1_1 (64 + 31) + +#define INT_SDC3_0 (96 + 0) +#define INT_SDC3_1 (96 + 1) +#define INT_SDC2_0 (96 + 2) +#define INT_SDC2_1 (96 + 3) +#define INT_SDC4_0 (96 + 4) +#define INT_SDC4_1 (96 + 5) +/* SCSS_VICFIQSTS3[6:31] are RESERVED */ + +/* Retrofit universal macro names */ +#define INT_ADM_AARM INT_ADM_SC2 +#define INT_USB_HS INT_USB_OTG_HS +#define INT_USB_OTG INT_USB_OTG_HS +#define INT_TCHSCRN1 INT_TSSC_PENUP +#define INT_TCHSCRN2 INT_TSSC_SAMPLE +#define INT_GP_TIMER_EXP INT_GPT0_TIMER_EXP +#define INT_ADSP_A11 INT_AD5A_MPROC_APPS_0 +#define INT_ADSP_A9_A11 INT_AD5A_MPROC_APPS_1 +#define INT_MDDI_EXT INT_EMDH +#define INT_MDDI_PRI INT_PMDH +#define INT_MDDI_CLIENT INT_MDC +#define INT_NAND_WR_ER_DONE INT_EBI2_WR_ER_DONE +#define INT_NAND_OP_DONE INT_EBI2_OP_DONE + +#define NR_GPIO_IRQS 181 +#define NR_MSM_IRQS 128 +#define NR_BOARD_IRQS 128 + +#endif /* __ASM_ARCH_MSM_IRQS_7X30_H */ diff --git a/arch/arm/mach-msm/include/mach/irqs-7xxx.h b/arch/arm/mach-msm/include/mach/irqs-7xxx.h new file mode 100644 index 000000000000..1b3c73d38073 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-7xxx.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + */ + +#ifndef __ASM_ARCH_MSM_IRQS_7XXX_H +#define __ASM_ARCH_MSM_IRQS_7XXX_H + +/* MSM ARM11 Interrupt Numbers */ +/* See 80-VE113-1 A, pp219-221 */ + +#define INT_A9_M2A_0 0 +#define INT_A9_M2A_1 1 +#define INT_A9_M2A_2 2 +#define INT_A9_M2A_3 3 +#define INT_A9_M2A_4 4 +#define INT_A9_M2A_5 5 +#define INT_A9_M2A_6 6 +#define INT_GP_TIMER_EXP 7 +#define INT_DEBUG_TIMER_EXP 8 +#define INT_UART1 9 +#define INT_UART2 10 +#define INT_UART3 11 +#define INT_UART1_RX 12 +#define INT_UART2_RX 13 +#define INT_UART3_RX 14 +#define INT_USB_OTG 15 +#define INT_MDDI_PRI 16 +#define INT_MDDI_EXT 17 +#define INT_MDDI_CLIENT 18 +#define INT_MDP 19 +#define INT_GRAPHICS 20 +#define INT_ADM_AARM 21 +#define INT_ADSP_A11 22 +#define INT_ADSP_A9_A11 23 +#define INT_SDC1_0 24 +#define INT_SDC1_1 25 +#define INT_SDC2_0 26 +#define INT_SDC2_1 27 +#define INT_KEYSENSE 28 +#define INT_TCHSCRN_SSBI 29 +#define INT_TCHSCRN1 30 +#define INT_TCHSCRN2 31 + +#define INT_GPIO_GROUP1 (32 + 0) +#define INT_GPIO_GROUP2 (32 + 1) +#define INT_PWB_I2C (32 + 2) +#define INT_SOFTRESET (32 + 3) +#define INT_NAND_WR_ER_DONE (32 + 4) +#define INT_NAND_OP_DONE (32 + 5) +#define INT_PBUS_ARM11 (32 + 6) +#define INT_AXI_MPU_SMI (32 + 7) +#define INT_AXI_MPU_EBI1 (32 + 8) +#define INT_AD_HSSD (32 + 9) +#define INT_ARM11_PMU (32 + 10) +#define INT_ARM11_DMA (32 + 11) +#define INT_TSIF_IRQ (32 + 12) +#define INT_UART1DM_IRQ (32 + 13) +#define INT_UART1DM_RX (32 + 14) +#define INT_USB_HS (32 + 15) +#define INT_SDC3_0 (32 + 16) +#define INT_SDC3_1 (32 + 17) +#define INT_SDC4_0 (32 + 18) +#define INT_SDC4_1 (32 + 19) +#define INT_UART2DM_IRQ (32 + 20) +#define INT_UART2DM_RX (32 + 21) + +/* 22-31 are reserved */ + +/* 7x00A uses 122, but 7x25 has up to 132. */ +#define NR_GPIO_IRQS 133 +#define NR_MSM_IRQS 64 +#define NR_BOARD_IRQS 64 + +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs-8xxx.h b/arch/arm/mach-msm/include/mach/irqs-8xxx.h new file mode 100644 index 000000000000..3ce01e9d01fa --- /dev/null +++ b/arch/arm/mach-msm/include/mach/irqs-8xxx.h @@ -0,0 +1,104 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ASM_ARCH_MSM_IRQS_8XXX_H +#define __ASM_ARCH_MSM_IRQS_8XXX_H + +/* MSM ACPU Interrupt Numbers */ + +#define INT_A9_M2A_0 0 +#define INT_A9_M2A_1 1 +#define INT_A9_M2A_2 2 +#define INT_A9_M2A_3 3 +#define INT_A9_M2A_4 4 +#define INT_A9_M2A_5 5 +#define INT_A9_M2A_6 6 +#define INT_GP_TIMER_EXP 7 +#define INT_DEBUG_TIMER_EXP 8 +#define INT_SIRC_0 9 +#define INT_SDC3_0 10 +#define INT_SDC3_1 11 +#define INT_SDC4_0 12 +#define INT_SDC4_1 13 +#define INT_AD6_EXT_VFR 14 +#define INT_USB_OTG 15 +#define INT_MDDI_PRI 16 +#define INT_MDDI_EXT 17 +#define INT_MDDI_CLIENT 18 +#define INT_MDP 19 +#define INT_GRAPHICS 20 +#define INT_ADM_AARM 21 +#define INT_ADSP_A11 22 +#define INT_ADSP_A9_A11 23 +#define INT_SDC1_0 24 +#define INT_SDC1_1 25 +#define INT_SDC2_0 26 +#define INT_SDC2_1 27 +#define INT_KEYSENSE 28 +#define INT_TCHSCRN_SSBI 29 +#define INT_TCHSCRN1 30 +#define INT_TCHSCRN2 31 + +#define INT_TCSR_MPRPH_SC1 (32 + 0) +#define INT_USB_FS2 (32 + 1) +#define INT_PWB_I2C (32 + 2) +#define INT_SOFTRESET (32 + 3) +#define INT_NAND_WR_ER_DONE (32 + 4) +#define INT_NAND_OP_DONE (32 + 5) +#define INT_TCSR_MPRPH_SC2 (32 + 6) +#define INT_OP_PEN (32 + 7) +#define INT_AD_HSSD (32 + 8) +#define INT_ARM11_PM (32 + 9) +#define INT_SDMA_NON_SECURE (32 + 10) +#define INT_TSIF_IRQ (32 + 11) +#define INT_UART1DM_IRQ (32 + 12) +#define INT_UART1DM_RX (32 + 13) +#define INT_SDMA_SECURE (32 + 14) +#define INT_SI2S_SLAVE (32 + 15) +#define INT_SC_I2CPU (32 + 16) +#define INT_SC_DBG_RDTRFULL (32 + 17) +#define INT_SC_DBG_WDTRFULL (32 + 18) +#define INT_SCPLL_CTL_DONE (32 + 19) +#define INT_UART2DM_IRQ (32 + 20) +#define INT_UART2DM_RX (32 + 21) +#define INT_VDC_MEC (32 + 22) +#define INT_VDC_DB (32 + 23) +#define INT_VDC_AXI (32 + 24) +#define INT_VFE (32 + 25) +#define INT_USB_HS (32 + 26) +#define INT_AUDIO_OUT0 (32 + 27) +#define INT_AUDIO_OUT1 (32 + 28) +#define INT_CRYPTO (32 + 29) +#define INT_AD6M_IDLE (32 + 30) +#define INT_SIRC_1 (32 + 31) + +#define NR_GPIO_IRQS 165 +#define NR_MSM_IRQS 64 +#define NR_BOARD_IRQS 64 + +#endif diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h index 9dd4cf8a2693..902910ad3b16 100644 --- a/arch/arm/mach-msm/include/mach/irqs.h +++ b/arch/arm/mach-msm/include/mach/irqs.h @@ -1,6 +1,6 @@ -/* arch/arm/mach-msm/include/mach/irqs.h - * +/* * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -17,74 +17,21 @@ #ifndef __ASM_ARCH_MSM_IRQS_H #define __ASM_ARCH_MSM_IRQS_H -/* MSM ARM11 Interrupt Numbers */ -/* See 80-VE113-1 A, pp219-221 */ - -#define INT_A9_M2A_0 0 -#define INT_A9_M2A_1 1 -#define INT_A9_M2A_2 2 -#define INT_A9_M2A_3 3 -#define INT_A9_M2A_4 4 -#define INT_A9_M2A_5 5 -#define INT_A9_M2A_6 6 -#define INT_GP_TIMER_EXP 7 -#define INT_DEBUG_TIMER_EXP 8 -#define INT_UART1 9 -#define INT_UART2 10 -#define INT_UART3 11 -#define INT_UART1_RX 12 -#define INT_UART2_RX 13 -#define INT_UART3_RX 14 -#define INT_USB_OTG 15 -#define INT_MDDI_PRI 16 -#define INT_MDDI_EXT 17 -#define INT_MDDI_CLIENT 18 -#define INT_MDP 19 -#define INT_GRAPHICS 20 -#define INT_ADM_AARM 21 -#define INT_ADSP_A11 22 -#define INT_ADSP_A9_A11 23 -#define INT_SDC1_0 24 -#define INT_SDC1_1 25 -#define INT_SDC2_0 26 -#define INT_SDC2_1 27 -#define INT_KEYSENSE 28 -#define INT_TCHSCRN_SSBI 29 -#define INT_TCHSCRN1 30 -#define INT_TCHSCRN2 31 - -#define INT_GPIO_GROUP1 (32 + 0) -#define INT_GPIO_GROUP2 (32 + 1) -#define INT_PWB_I2C (32 + 2) -#define INT_SOFTRESET (32 + 3) -#define INT_NAND_WR_ER_DONE (32 + 4) -#define INT_NAND_OP_DONE (32 + 5) -#define INT_PBUS_ARM11 (32 + 6) -#define INT_AXI_MPU_SMI (32 + 7) -#define INT_AXI_MPU_EBI1 (32 + 8) -#define INT_AD_HSSD (32 + 9) -#define INT_ARM11_PMU (32 + 10) -#define INT_ARM11_DMA (32 + 11) -#define INT_TSIF_IRQ (32 + 12) -#define INT_UART1DM_IRQ (32 + 13) -#define INT_UART1DM_RX (32 + 14) -#define INT_USB_HS (32 + 15) -#define INT_SDC3_0 (32 + 16) -#define INT_SDC3_1 (32 + 17) -#define INT_SDC4_0 (32 + 18) -#define INT_SDC4_1 (32 + 19) -#define INT_UART2DM_RX (32 + 20) -#define INT_UART2DM_IRQ (32 + 21) - -/* 22-31 are reserved */ - #define MSM_IRQ_BIT(irq) (1 << ((irq) & 31)) -#define NR_MSM_IRQS 64 -#define NR_GPIO_IRQS 122 -#define NR_BOARD_IRQS 64 -#define NR_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) +#if defined(CONFIG_ARCH_MSM7X30) +#include "irqs-7x30.h" +#elif defined(CONFIG_ARCH_QSD8X50) +#include "irqs-8xxx.h" +#include "sirc.h" +#elif defined(CONFIG_ARCH_MSM_ARM11) +#include "irqs-7xxx.h" +#else +#error "Unknown architecture specification" +#endif +#define NR_IRQS (NR_MSM_IRQS + NR_GPIO_IRQS + NR_BOARD_IRQS) #define MSM_GPIO_TO_INT(n) (NR_MSM_IRQS + (n)) +#define MSM_INT_TO_REG(base, irq) (base + irq / 32) #endif diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h index f4698baec976..dc278487c217 100644 --- a/arch/arm/mach-msm/include/mach/memory.h +++ b/arch/arm/mach-msm/include/mach/memory.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/memory.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -12,12 +13,38 @@ * GNU General Public License for more details. * */ - #ifndef __ASM_ARCH_MEMORY_H #define __ASM_ARCH_MEMORY_H /* physical offset of RAM */ +#ifdef CONFIG_MSM_STACKED_MEMORY + +#ifdef CONFIG_ARCH_MSM_SCORPION +#define PHYS_OFFSET UL(0x20000000) +#else #define PHYS_OFFSET UL(0x10000000) +#endif + +#else /* !CONFIG_MSM_STACKED_MEMORY */ + +#define PHYS_OFFSET UL(0x00200000) + +#endif + +#ifndef __ASSEMBLY__ +void *alloc_bootmem_aligned(unsigned long size, unsigned long alignment); + +#ifdef CONFIG_ARCH_MSM_ARM11 +void write_to_strongly_ordered_memory(void); + +#include <asm/mach-types.h> + +#define arch_barrier_extra() do \ + { if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) \ + write_to_strongly_ordered_memory(); \ + } while (0) +#endif +#endif #endif diff --git a/arch/arm/mach-msm/include/mach/mmc.h b/arch/arm/mach-msm/include/mach/mmc.h index 0ecf25426284..306de3d73fff 100644 --- a/arch/arm/mach-msm/include/mach/mmc.h +++ b/arch/arm/mach-msm/include/mach/mmc.h @@ -1,26 +1,16 @@ /* - * arch/arm/include/asm/mach/mmc.h + * arch/arm/mach-msm/include/mach/mmc.h */ -#ifndef ASMARM_MACH_MMC_H -#define ASMARM_MACH_MMC_H +#ifndef ASM_ARCH_MACH_MMC_H +#define ASM_ARCH_MACH_MMC_H #include <linux/mmc/host.h> -#include <linux/mmc/card.h> -#include <linux/mmc/sdio_func.h> - -struct embedded_sdio_data { - struct sdio_cis cis; - struct sdio_cccr cccr; - struct sdio_embedded_func *funcs; - int num_funcs; -}; struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ u32 (*translate_vdd)(struct device *, unsigned int); unsigned int (*status)(struct device *); - struct embedded_sdio_data *embedded_sdio; - int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); + unsigned long irq_flags; }; #endif diff --git a/arch/arm/mach-msm/include/mach/mpp.h b/arch/arm/mach-msm/include/mach/mpp.h new file mode 100644 index 000000000000..7af853bdca49 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/mpp.h @@ -0,0 +1,73 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_MPP_H +#define __ARCH_ARM_MACH_MSM_MPP_H + +struct mpp { + const char *name; + unsigned id; + int is_input; + int status; +}; + +/* Digital Logical Output Level */ +enum { + MPP_DLOGIC_LVL_MSME, + MPP_DLOGIC_LVL_MSMP, + MPP_DLOGIC_LVL_RUIM, + MPP_DLOGIC_LVL_MMC, + MPP_DLOGIC_LVL_VDD, +}; + +/* Digital Logical Output Control Value */ +enum { + MPP_DLOGIC_OUT_CTRL_LOW, + MPP_DLOGIC_OUT_CTRL_HIGH, + MPP_DLOGIC_OUT_CTRL_MPP, /* MPP Output = MPP Input */ + MPP_DLOGIC_OUT_CTRL_NOT_MPP, /* MPP Output = Inverted MPP Input */ +}; + +/* Digital Logical Input Value */ +enum { + MPP_DLOGIC_IN_DBUS_NONE, + MPP_DLOGIC_IN_DBUS_1, + MPP_DLOGIC_IN_DBUS_2, + MPP_DLOGIC_IN_DBUS_3, +}; + +#define MPP_CFG(level, control) ((((level) & 0x0FFFF) << 16) | \ + ((control) & 0x0FFFFF)) +#define MPP_CFG_INPUT(level, dbus) ((((level) & 0x0FFFF) << 16) | \ + ((control) & 0x0FFFFF)) + +struct mpp *mpp_get(struct device *dev, const char *id); +int mpp_config_digital_out(struct mpp *mpp, unsigned config); +int mpp_config_digital_in(struct mpp *mpp, unsigned config); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_handset.h b/arch/arm/mach-msm/include/mach/msm_handset.h new file mode 100644 index 000000000000..6947e03b8986 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_handset.h @@ -0,0 +1,34 @@ +/* arch/arm/mach-msm/include/mach/msm_handset.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MSM_HANDSET_H +#define _MSM_HANDSET_H + +#include <linux/input.h> + +#if defined(CONFIG_INPUT_MSM_HANDSET) +struct input_dev *msm_get_handset_input_dev(void); +#else +struct input_dev *msm_get_handset_input_dev(void) +{ + return NULL; +} +#endif + +struct msm_handset { + struct input_dev *ip_dev; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_i2ckbd.h b/arch/arm/mach-msm/include/mach/msm_i2ckbd.h new file mode 100644 index 000000000000..2ca51f261cac --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_i2ckbd.h @@ -0,0 +1,41 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _MSM_I2CKBD_H_ +#define _MSM_I2CKBD_H_ + +struct msm_i2ckbd_platform_data { + uint8_t hwrepeat; + uint8_t scanset1; + int gpioreset; + int gpioirq; + int (*gpio_setup) (void); + void (*gpio_shutdown)(void); +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h index 9dae1a98c77a..2f54547e1890 100644 --- a/arch/arm/mach-msm/include/mach/msm_iomap.h +++ b/arch/arm/mach-msm/include/mach/msm_iomap.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/msm_iomap.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -44,44 +45,98 @@ #endif #define MSM_VIC_BASE IOMEM(0xE0000000) +#if defined(CONFIG_ARCH_QSD8X50) +#define MSM_VIC_PHYS 0xAC000000 +#elif defined(CONFIG_ARCH_MSM7X30) +#define MSM_VIC_PHYS 0xC0080000 +#else #define MSM_VIC_PHYS 0xC0000000 +#endif #define MSM_VIC_SIZE SZ_4K #define MSM_CSR_BASE IOMEM(0xE0001000) +#if defined(CONFIG_ARCH_QSD8X50) +#define MSM_CSR_PHYS 0xAC100000 +#else #define MSM_CSR_PHYS 0xC0100000 +#endif #define MSM_CSR_SIZE SZ_4K -#define MSM_GPT_PHYS MSM_CSR_PHYS -#define MSM_GPT_BASE MSM_CSR_BASE -#define MSM_GPT_SIZE SZ_4K +#define MSM_TMR_PHYS MSM_CSR_PHYS +#define MSM_TMR_BASE MSM_CSR_BASE +#define MSM_TMR_SIZE SZ_4K #define MSM_DMOV_BASE IOMEM(0xE0002000) +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_DMOV_PHYS 0xAC400000 +#else #define MSM_DMOV_PHYS 0xA9700000 +#endif #define MSM_DMOV_SIZE SZ_4K #define MSM_GPIO1_BASE IOMEM(0xE0003000) +#if defined(CONFIG_ARCH_QSD8X50) +#define MSM_GPIO1_PHYS 0xA9000000 +#elif defined(CONFIG_ARCH_MSM7X30) +#define MSM_GPIO1_PHYS 0xAC001000 +#else #define MSM_GPIO1_PHYS 0xA9200000 +#endif #define MSM_GPIO1_SIZE SZ_4K #define MSM_GPIO2_BASE IOMEM(0xE0004000) + +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_GPIO2_PHYS 0xAC101000 +#elif defined(CONFIG_ARCH_QSD8X50) +#define MSM_GPIO2_PHYS 0xA9100000 +#else #define MSM_GPIO2_PHYS 0xA9300000 +#endif #define MSM_GPIO2_SIZE SZ_4K #define MSM_CLK_CTL_BASE IOMEM(0xE0005000) +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_CLK_CTL_PHYS 0xAB800000 +#else #define MSM_CLK_CTL_PHYS 0xA8600000 +#endif #define MSM_CLK_CTL_SIZE SZ_4K +#define MSM_L2CC_BASE IOMEM(0xE0006000) +#define MSM_L2CC_PHYS 0xC0400000 +#define MSM_L2CC_SIZE SZ_4K + +#define MSM_SIRC_BASE IOMEM(0xE1006000) +#define MSM_SIRC_PHYS 0xAC200000 +#define MSM_SIRC_SIZE SZ_4K + +#define MSM_SCPLL_BASE IOMEM(0xE1007000) +#define MSM_SCPLL_PHYS 0xA8800000 +#define MSM_SCPLL_SIZE SZ_4K + +#define MSM_ACC_BASE IOMEM(0xE0007000) +#define MSM_ACC_PHYS 0xC0101000 +#define MSM_ACC_SIZE SZ_4K + +#define MSM_GCC_BASE IOMEM(0xE0008000) +#define MSM_GCC_PHYS 0xC0182000 +#define MSM_GCC_SIZE SZ_4K + #define MSM_SHARED_RAM_BASE IOMEM(0xE0100000) -#define MSM_SHARED_RAM_PHYS 0x01F00000 #define MSM_SHARED_RAM_SIZE SZ_1M +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_UART1_PHYS 0xACA00000 +#define MSM_UART2_PHYS 0xACB00000 +#define MSM_UART3_PHYS 0xACC00000 +#else #define MSM_UART1_PHYS 0xA9A00000 -#define MSM_UART1_SIZE SZ_4K - #define MSM_UART2_PHYS 0xA9B00000 -#define MSM_UART2_SIZE SZ_4K - #define MSM_UART3_PHYS 0xA9C00000 +#endif +#define MSM_UART1_SIZE SZ_4K +#define MSM_UART2_SIZE SZ_4K #define MSM_UART3_SIZE SZ_4K #ifdef CONFIG_MSM_DEBUG_UART @@ -96,38 +151,27 @@ #define MSM_DEBUG_UART_SIZE SZ_4K #endif -#define MSM_SDC1_PHYS 0xA0400000 -#define MSM_SDC1_SIZE SZ_4K - -#define MSM_SDC2_PHYS 0xA0500000 -#define MSM_SDC2_SIZE SZ_4K - -#define MSM_SDC3_PHYS 0xA0600000 -#define MSM_SDC3_SIZE SZ_4K - -#define MSM_SDC4_PHYS 0xA0700000 -#define MSM_SDC4_SIZE SZ_4K - #define MSM_I2C_PHYS 0xA9900000 #define MSM_I2C_SIZE SZ_4K #define MSM_HSUSB_PHYS 0xA0800000 +#define MSM_HSUSB_BASE IOMEM(0xE0009000) #define MSM_HSUSB_SIZE SZ_4K -#define MSM_PMDH_PHYS 0xAA600000 -#define MSM_PMDH_SIZE SZ_4K - -#define MSM_EMDH_PHYS 0xAA700000 -#define MSM_EMDH_SIZE SZ_4K - -#define MSM_MDP_PHYS 0xAA200000 -#define MSM_MDP_SIZE 0x000F0000 - +#define MSM_MDC_BASE IOMEM(0xE0200000) #define MSM_MDC_PHYS 0xAA500000 #define MSM_MDC_SIZE SZ_1M +#define MSM_AD5_BASE IOMEM(0xE0300000) #define MSM_AD5_PHYS 0xAC000000 #define MSM_AD5_SIZE (SZ_1M*13) +#define MSM_SSBI_BASE IOMEM(0xE1004000) +#define MSM_SSBI_PHYS 0xA8100000 +#define MSM_SSBI_SIZE SZ_4K + +#define MSM_TSSC_BASE IOMEM(0xE1005000) +#define MSM_TSSC_PHYS 0xAA300000 +#define MSM_TSSC_SIZE SZ_4K #endif diff --git a/arch/arm/mach-msm/include/mach/msm_otg.h b/arch/arm/mach-msm/include/mach/msm_otg.h new file mode 100644 index 000000000000..6ab662921fee --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_otg.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_OTG_H +#define __ARCH_ARM_MACH_MSM_OTG_H + +/* + * The otg driver needs to interact with both device side and host side + * usb controllers. it decides which controller is active at a given + * moment, using the transceiver, ID signal. + */ + +struct msm_otg_transceiver { + struct device *dev; + struct clk *clk; + struct clk *pclk; + int in_lpm; + struct msm_otg_ops *dcd_ops; + struct msm_otg_ops *hcd_ops; + int irq; + int flags; + int state; + int active; + void __iomem *regs; /* device memory/io */ + struct work_struct work; + spinlock_t lock; + + /* bind/unbind the host controller */ + int (*set_host)(struct msm_otg_transceiver *otg, + struct msm_otg_ops *hcd_ops); + + /* bind/unbind the peripheral controller */ + int (*set_peripheral)(struct msm_otg_transceiver *otg, + struct msm_otg_ops *dcd_ops); + void (*set_suspend) (int on); + +}; + +struct msm_otg_ops { + void (*status_change)(int); +}; + +/* for usb host and peripheral controller drivers */ +#ifdef CONFIG_USB_MSM_OTG + +extern struct msm_otg_transceiver *msm_otg_get_transceiver(void); +extern void msm_otg_put_transceiver(struct msm_otg_transceiver *xceiv); + +#else + +static inline struct msm_otg_transceiver *msm_otg_get_transceiver(void) +{ + return NULL; +} + +static inline void msm_otg_put_transceiver(struct msm_otg_transceiver *xceiv) +{ +} + +#endif /*CONFIG_USB_MSM_OTG*/ + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_rpcrouter.h b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h new file mode 100644 index 000000000000..d0c01fb53047 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_rpcrouter.h @@ -0,0 +1,296 @@ +/** include/asm-arm/arch-msm/msm_rpcrouter.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM__ARCH_MSM_RPCROUTER_H +#define __ASM__ARCH_MSM_RPCROUTER_H + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/platform_device.h> +#include <linux/ioctl.h> + +struct rpcrouter_ioctl_server_args { + uint32_t prog; + uint32_t vers; +}; + +#define RPC_ROUTER_VERSION_V1 0x00010000 + +#define RPC_ROUTER_IOCTL_MAGIC (0xC1) + +#define RPC_ROUTER_IOCTL_GET_VERSION \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 0, unsigned int) + +#define RPC_ROUTER_IOCTL_GET_MTU \ + _IOR(RPC_ROUTER_IOCTL_MAGIC, 1, unsigned int) + +#define RPC_ROUTER_IOCTL_REGISTER_SERVER \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 2, unsigned int) + +#define RPC_ROUTER_IOCTL_UNREGISTER_SERVER \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 3, unsigned int) + +#define RPC_ROUTER_IOCTL_CLEAR_NETRESET \ + _IOWR(RPC_ROUTER_IOCTL_MAGIC, 4, unsigned int) + +/* RPC API version structure + * Version bit 31 : 1->hashkey versioning, + * 0->major-minor (backward compatible) versioning + * hashkey versioning: + * Version bits 31-0 hashkey + * major-minor (backward compatible) versioning + * Version bits 30-28 reserved (no match) + * Version bits 27-16 major (must match) + * Version bits 15-0 minor (greater or equal) + */ +#define RPC_VERSION_MODE_MASK 0x80000000 +#define RPC_VERSION_MAJOR_MASK 0x0fff0000 +#define RPC_VERSION_MINOR_MASK 0x0000ffff + +/* callback ID for NULL callback function is -1 */ +#define MSM_RPC_CLIENT_NULL_CB_ID 0xffffffff + +struct msm_rpc_endpoint; + +struct rpcsvr_platform_device +{ + struct platform_device base; + uint32_t prog; + uint32_t vers; +}; + +#define RPC_DATA_IN 0 +/* + * Structures for sending / receiving direct RPC requests + * XXX: Any cred/verif lengths > 0 not supported + */ + +struct rpc_request_hdr +{ + uint32_t xid; + uint32_t type; /* 0 */ + uint32_t rpc_vers; /* 2 */ + uint32_t prog; + uint32_t vers; + uint32_t procedure; + uint32_t cred_flavor; + uint32_t cred_length; + uint32_t verf_flavor; + uint32_t verf_length; +}; + +typedef struct +{ + uint32_t low; + uint32_t high; +} rpc_reply_progmismatch_data; + +typedef struct +{ +} rpc_denied_reply_hdr; + +typedef struct +{ + uint32_t verf_flavor; + uint32_t verf_length; + uint32_t accept_stat; +#define RPC_ACCEPTSTAT_SUCCESS 0 +#define RPC_ACCEPTSTAT_PROG_UNAVAIL 1 +#define RPC_ACCEPTSTAT_PROG_MISMATCH 2 +#define RPC_ACCEPTSTAT_PROC_UNAVAIL 3 +#define RPC_ACCEPTSTAT_GARBAGE_ARGS 4 +#define RPC_ACCEPTSTAT_SYSTEM_ERR 5 +#define RPC_ACCEPTSTAT_PROG_LOCKED 6 + /* + * Following data is dependant on accept_stat + * If ACCEPTSTAT == PROG_MISMATCH then there is a + * 'rpc_reply_progmismatch_data' structure following the header. + * Otherwise the data is procedure specific + */ +} rpc_accepted_reply_hdr; + +struct rpc_reply_hdr +{ + uint32_t xid; + uint32_t type; + uint32_t reply_stat; +#define RPCMSG_REPLYSTAT_ACCEPTED 0 +#define RPCMSG_REPLYSTAT_DENIED 1 + union { + rpc_accepted_reply_hdr acc_hdr; + rpc_denied_reply_hdr dny_hdr; + } data; +}; + +/* flags for msm_rpc_connect() */ +#define MSM_RPC_UNINTERRUPTIBLE 0x0001 + +/* use IS_ERR() to check for failure */ +struct msm_rpc_endpoint *msm_rpc_open(void); +/* Connect with the specified server version */ +struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags); +/* Connect with a compatible server version */ +struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog, + uint32_t vers, unsigned flags); +int msm_rpc_get_compatible_server(uint32_t prog, uint32_t vers, + uint32_t *found_vers); +/* check if server version can handle client requested version */ +int msm_rpc_is_compatible_version(uint32_t server_version, + uint32_t client_version); + +int msm_rpc_close(struct msm_rpc_endpoint *ept); +int msm_rpc_write(struct msm_rpc_endpoint *ept, + void *data, int len); +int msm_rpc_read(struct msm_rpc_endpoint *ept, + void **data, unsigned len, long timeout); +void msm_rpc_setup_req(struct rpc_request_hdr *hdr, + uint32_t prog, uint32_t vers, uint32_t proc); +int msm_rpc_register_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers); +int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers); + +int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept); +/* simple blocking rpc call + * + * request is mandatory and must have a rpc_request_hdr + * at the start. The header will be filled out for you. + * + * reply provides a buffer for replies of reply_max_size + */ +int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, + void *request, int request_size, + void *reply, int reply_max_size, + long timeout); +int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, + void *request, int request_size, + long timeout); + +struct msm_rpc_server +{ + struct list_head list; + uint32_t flags; + + uint32_t prog; + uint32_t vers; + + struct mutex cb_req_lock; + struct mutex reply_lock; + char *cb_req; + char *reply; + + struct msm_rpc_endpoint *cb_ept; + + int (*rpc_call)(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len); +}; + +int msm_rpc_create_server(struct msm_rpc_server *server); + +#define MSM_RPC_MSGSIZE_MAX 8192 + +struct msm_rpc_client; + +struct msm_rpc_client { + struct task_struct *read_thread; + struct task_struct *cb_thread; + + struct msm_rpc_endpoint *ept; + wait_queue_head_t reply_wait; + + uint32_t prog, ver; + + void *buf; + int read_avail; + + int (*cb_func)(struct msm_rpc_client *, void *, int); + void *cb_buf; + int cb_size; + + struct list_head cb_item_list; + struct mutex cb_item_list_lock; + + wait_queue_head_t cb_wait; + int cb_avail; + + atomic_t next_cb_id; + struct mutex cb_list_lock; + struct list_head cb_list; + + uint32_t exit_flag; + struct completion complete; + struct completion cb_complete; + + struct mutex req_lock; + struct mutex reply_lock; + char *req; + char *reply; +}; + +struct msm_rpc_client_info { + uint32_t pid; + uint32_t cid; + uint32_t prog; + uint32_t vers; +}; + +struct msm_rpc_client *msm_rpc_register_client( + const char *name, + uint32_t prog, uint32_t ver, + uint32_t create_cb_thread, + int (*cb_func)(struct msm_rpc_client *, void *, int)); + +int msm_rpc_unregister_client(struct msm_rpc_client *client); + +int msm_rpc_client_req(struct msm_rpc_client *client, uint32_t proc, + int (*arg_func)(struct msm_rpc_client *, + void *, void *), void *arg_data, + int (*result_func)(struct msm_rpc_client *, + void *, void *), void *result_data, + long timeout); + +void *msm_rpc_start_accepted_reply(struct msm_rpc_client *client, + uint32_t xid, uint32_t accept_status); + +int msm_rpc_send_accepted_reply(struct msm_rpc_client *client, uint32_t size); + +void *msm_rpc_server_start_accepted_reply(struct msm_rpc_server *server, + uint32_t xid, uint32_t accept_status); + +int msm_rpc_server_send_accepted_reply(struct msm_rpc_server *server, + uint32_t size); + +int msm_rpc_add_cb_func(struct msm_rpc_client *client, void *cb_func); + +void *msm_rpc_get_cb_func(struct msm_rpc_client *client, uint32_t cb_id); + +void msm_rpc_remove_cb_func(struct msm_rpc_client *client, void *cb_func); + +int msm_rpc_server_cb_req(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + uint32_t cb_proc, + int (*arg_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *ret_data, long timeout); + +void msm_rpc_server_get_requesting_client( + struct msm_rpc_client_info *clnt_info); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_smd.h b/arch/arm/mach-msm/include/mach/msm_smd.h new file mode 100644 index 000000000000..8ef65c04ea85 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_smd.h @@ -0,0 +1,82 @@ +/* linux/include/asm-arm/arch-msm/msm_smd.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ASM_ARCH_MSM_SMD_H +#define __ASM_ARCH_MSM_SMD_H + +typedef struct smd_channel smd_channel_t; + +/* warning: notify() may be called before open returns */ +int smd_open(const char *name, smd_channel_t **ch, void *priv, + void (*notify)(void *priv, unsigned event)); + +#define SMD_EVENT_DATA 1 +#define SMD_EVENT_OPEN 2 +#define SMD_EVENT_CLOSE 3 + +int smd_close(smd_channel_t *ch); + +/* passing a null pointer for data reads and discards */ +int smd_read(smd_channel_t *ch, void *data, int len); +int smd_read_from_cb(smd_channel_t *ch, void *data, int len); + +/* Write to stream channels may do a partial write and return +** the length actually written. +** Write to packet channels will never do a partial write -- +** it will return the requested length written or an error. +*/ +int smd_write(smd_channel_t *ch, const void *data, int len); + +int smd_write_avail(smd_channel_t *ch); +int smd_read_avail(smd_channel_t *ch); + +/* Returns the total size of the current packet being read. +** Returns 0 if no packets available or a stream channel. +*/ +int smd_cur_packet_size(smd_channel_t *ch); + + +#if 0 +/* these are interruptable waits which will block you until the specified +** number of bytes are readable or writable. +*/ +int smd_wait_until_readable(smd_channel_t *ch, int bytes); +int smd_wait_until_writable(smd_channel_t *ch, int bytes); +#endif + +/* these are used to get and set the IF sigs of a channel. + * DTR and RTS can be set; DSR, CTS, CD and RI can be read. + */ +int smd_tiocmget(smd_channel_t *ch); +int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear); + +#if defined(CONFIG_MSM_N_WAY_SMD) +enum { + SMD_APPS_MODEM = 0, + SMD_APPS_QDSP, + SMD_MODEM_QDSP +}; +#else +enum { + SMD_APPS_MODEM = 0 +}; +#endif + +int smd_named_open_on_edge(const char *name, uint32_t edge, smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)); + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_touch.h b/arch/arm/mach-msm/include/mach/msm_touch.h new file mode 100644 index 000000000000..763d6a8f1113 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_touch.h @@ -0,0 +1,26 @@ +/* arch/arm/mach-msm/include/mach/msm_touch.h + * + * Platform data for MSM touchscreen driver. + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MACH_MSM_TOUCH_H_ +#define _MACH_MSM_TOUCH_H_ + +struct msm_ts_platform_data { + unsigned int x_max; + unsigned int y_max; + unsigned int pressure_max; +}; + +#endif diff --git a/arch/arm/mach-msm/include/mach/msm_touchpad.h b/arch/arm/mach-msm/include/mach/msm_touchpad.h new file mode 100644 index 000000000000..5c02f15c5ca5 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/msm_touchpad.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Touchpad driver for QSD platform. + */ + +struct msm_touchpad_platform_data { + int gpioirq; + int gpiosuspend; + int (*gpio_setup) (void); + void (*gpio_shutdown)(void); +}; diff --git a/arch/arm/mach-msm/include/mach/oem_rapi_client.h b/arch/arm/mach-msm/include/mach/oem_rapi_client.h new file mode 100644 index 000000000000..4b340eb814c9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/oem_rapi_client.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ASM__ARCH_OEM_RAPI_CLIENT_H +#define __ASM__ARCH_OEM_RAPI_CLIENT_H + +/* + * OEM RAPI CLIENT Driver header file + */ + +#include <linux/types.h> +#include <mach/msm_rpcrouter.h> + +enum { + OEM_RAPI_CLIENT_EVENT_NONE = 0, + + /* + * list of oem rapi client events + */ + + OEM_RAPI_CLIENT_EVENT_MAX + +}; + +struct oem_rapi_client_streaming_func_cb_arg { + uint32_t event; + void *handle; + uint32_t in_len; + char *input; + uint32_t out_len_valid; + uint32_t output_valid; + uint32_t output_size; +}; + +struct oem_rapi_client_streaming_func_cb_ret { + uint32_t *out_len; + char *output; +}; + +struct oem_rapi_client_streaming_func_arg { + uint32_t event; + int (*cb_func)(struct oem_rapi_client_streaming_func_cb_arg *, + struct oem_rapi_client_streaming_func_cb_ret *); + void *handle; + uint32_t in_len; + char *input; + uint32_t out_len_valid; + uint32_t output_valid; + uint32_t output_size; +}; + +struct oem_rapi_client_streaming_func_ret { + uint32_t *out_len; + char *output; +}; + +int oem_rapi_client_streaming_function( + struct msm_rpc_client *client, + struct oem_rapi_client_streaming_func_arg *arg, + struct oem_rapi_client_streaming_func_ret *ret); + +int oem_rapi_client_close(void); + +struct msm_rpc_client *oem_rapi_client_init(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/pmic.h b/arch/arm/mach-msm/include/mach/pmic.h new file mode 100644 index 000000000000..c339e743c032 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/pmic.h @@ -0,0 +1,557 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ARCH_ARM_MACH_PMIC_H +#define __ARCH_ARM_MACH_PMIC_H + + +enum spkr_left_right { + LEFT_SPKR, + RIGHT_SPKR, +}; + +enum spkr_gain { + SPKR_GAIN_MINUS16DB, /* -16 db */ + SPKR_GAIN_MINUS12DB, /* -12 db */ + SPKR_GAIN_MINUS08DB, /* -08 db */ + SPKR_GAIN_MINUS04DB, /* -04 db */ + SPKR_GAIN_00DB, /* 00 db */ + SPKR_GAIN_PLUS04DB, /* +04 db */ + SPKR_GAIN_PLUS08DB, /* +08 db */ + SPKR_GAIN_PLUS12DB, /* +12 db */ +}; + +enum spkr_dly { + SPKR_DLY_10MS, /* ~10 ms delay */ + SPKR_DLY_100MS, /* ~100 ms delay */ +}; + +enum spkr_hpf_corner_freq { + SPKR_FREQ_1_39KHZ, /* 1.39 kHz */ + SPKR_FREQ_0_64KHZ, /* 0.64 kHz */ + SPKR_FREQ_0_86KHZ, /* 0.86 kHz */ + SPKR_FREQ_0_51KHZ, /* 0.51 kHz */ + SPKR_FREQ_1_06KHZ, /* 1.06 kHz */ + SPKR_FREQ_0_57KHZ, /* 0.57 kHz */ + SPKR_FREQ_0_73KHZ, /* 0.73 kHz */ + SPKR_FREQ_0_47KHZ, /* 0.47 kHz */ + SPKR_FREQ_1_20KHZ, /* 1.20 kHz */ + SPKR_FREQ_0_60KHZ, /* 0.60 kHz */ + SPKR_FREQ_0_76KHZ, /* 0.76 kHz */ + SPKR_FREQ_0_49KHZ, /* 0.49 kHz */ + SPKR_FREQ_0_95KHZ, /* 0.95 kHz */ + SPKR_FREQ_0_54KHZ, /* 0.54 kHz */ + SPKR_FREQ_0_68KHZ, /* 0.68 kHz */ + SPKR_FREQ_0_45KHZ, /* 0.45 kHz */ +}; + +/* Turn the speaker on or off and enables or disables mute.*/ +enum spkr_cmd { + SPKR_DISABLE, /* Enable Speaker */ + SPKR_ENABLE, /* Disable Speaker */ + SPKR_MUTE_OFF, /* turn speaker mute off, SOUND ON */ + SPKR_MUTE_ON, /* turn speaker mute on, SOUND OFF */ + SPKR_OFF, /* turn speaker OFF (speaker disable and mute on) */ + SPKR_ON, /* turn speaker ON (speaker enable and mute off) */ + SPKR_SET_FREQ_CMD, /* set speaker frequency */ + SPKR_GET_FREQ_CMD, /* get speaker frequency */ + SPKR_SET_GAIN_CMD, /* set speaker gain */ + SPKR_GET_GAIN_CMD, /* get speaker gain */ + SPKR_SET_DELAY_CMD, /* set speaker delay */ + SPKR_GET_DELAY_CMD, /* get speaker delay */ + SPKR_SET_PDM_MODE, + SPKR_SET_PWM_MODE, +}; + +struct spkr_config_mode { + uint32_t is_right_chan_en; + uint32_t is_left_chan_en; + uint32_t is_right_left_chan_added; + uint32_t is_stereo_en; + uint32_t is_usb_with_hpf_20hz; + uint32_t is_mux_bypassed; + uint32_t is_hpf_en; + uint32_t is_sink_curr_from_ref_volt_cir_en; +}; + +enum mic_volt { + MIC_VOLT_2_00V, /* 2.00 V */ + MIC_VOLT_1_93V, /* 1.93 V */ + MIC_VOLT_1_80V, /* 1.80 V */ + MIC_VOLT_1_73V, /* 1.73 V */ +}; + +enum ledtype { + LED_LCD, + LED_KEYPAD, +}; + +enum flash_led_mode { + FLASH_LED_MODE__MANUAL, + FLASH_LED_MODE__DBUS1, + FLASH_LED_MODE__DBUS2, + FLASH_LED_MODE__DBUS3, +}; + +enum flash_led_pol { + FLASH_LED_POL__ACTIVE_HIGH, + FLASH_LED_POL__ACTIVE_LOW, +}; + +enum switch_cmd { + OFF_CMD, + ON_CMD +}; + +enum vreg_lp_id { + PM_VREG_LP_MSMA_ID, + PM_VREG_LP_MSMP_ID, + PM_VREG_LP_MSME1_ID, + PM_VREG_LP_GP3_ID, + PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME2_ID, + PM_VREG_LP_GP4_ID, + PM_VREG_LP_GP1_ID, + PM_VREG_LP_RFTX_ID, + PM_VREG_LP_RFRX1_ID, + PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_WLAN_ID, + PM_VREG_LP_MMC_ID, + PM_VREG_LP_RUIM_ID, + PM_VREG_LP_MSMC0_ID, + PM_VREG_LP_GP2_ID, + PM_VREG_LP_GP5_ID, + PM_VREG_LP_GP6_ID, + PM_VREG_LP_MPLL_ID, + PM_VREG_LP_RFUBM_ID, + PM_VREG_LP_RFA_ID, + PM_VREG_LP_CDC2_ID, + PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_USIM_ID, + PM_VREG_LP_USB2P6_ID, + PM_VREG_LP_TCXO_ID, + PM_VREG_LP_USB3P3_ID, + + PM_VREG_LP_MSME_ID = PM_VREG_LP_MSME1_ID, + /* backward compatible enums only */ + PM_VREG_LP_CAM_ID = PM_VREG_LP_GP1_ID, + PM_VREG_LP_MDDI_ID = PM_VREG_LP_GP2_ID, + PM_VREG_LP_RUIM2_ID = PM_VREG_LP_GP3_ID, + PM_VREG_LP_AUX_ID = PM_VREG_LP_GP4_ID, + PM_VREG_LP_AUX2_ID = PM_VREG_LP_GP5_ID, + PM_VREG_LP_BT_ID = PM_VREG_LP_GP6_ID, + PM_VREG_LP_MSMC_LDO_ID = PM_VREG_LP_MSMC_ID, + PM_VREG_LP_MSME1_LDO_ID = PM_VREG_LP_MSME1_ID, + PM_VREG_LP_MSME2_LDO_ID = PM_VREG_LP_MSME2_ID, + PM_VREG_LP_RFA1_ID = PM_VREG_LP_RFRX2_ID, + PM_VREG_LP_RFA2_ID = PM_VREG_LP_RFTX2_ID, + PM_VREG_LP_XO_ID = PM_VREG_LP_TCXO_ID +}; + +enum vreg_id { + PM_VREG_MSMA_ID = 0, + PM_VREG_MSMP_ID, + PM_VREG_MSME1_ID, + PM_VREG_MSMC1_ID, + PM_VREG_MSMC2_ID, + PM_VREG_GP3_ID, + PM_VREG_MSME2_ID, + PM_VREG_GP4_ID, + PM_VREG_GP1_ID, + PM_VREG_TCXO_ID, + PM_VREG_PA_ID, + PM_VREG_RFTX_ID, + PM_VREG_RFRX1_ID, + PM_VREG_RFRX2_ID, + PM_VREG_SYNT_ID, + PM_VREG_WLAN_ID, + PM_VREG_USB_ID, + PM_VREG_BOOST_ID, + PM_VREG_MMC_ID, + PM_VREG_RUIM_ID, + PM_VREG_MSMC0_ID, + PM_VREG_GP2_ID, + PM_VREG_GP5_ID, + PM_VREG_GP6_ID, + PM_VREG_RF_ID, + PM_VREG_RF_VCO_ID, + PM_VREG_MPLL_ID, + PM_VREG_S2_ID, + PM_VREG_S3_ID, + PM_VREG_RFUBM_ID, + PM_VREG_NCP_ID, + PM_VREG_RF2_ID, + PM_VREG_RFA_ID, + PM_VREG_CDC2_ID, + PM_VREG_RFTX2_ID, + PM_VREG_USIM_ID, + PM_VREG_USB2P6_ID, + PM_VREG_USB3P3_ID, + PM_VREG_EXTCDC1_ID, + PM_VREG_EXTCDC2_ID, + + /* backward compatible enums only */ + PM_VREG_MSME_ID = PM_VREG_MSME1_ID, + PM_VREG_MSME_BUCK_SMPS_ID = PM_VREG_MSME1_ID, + PM_VREG_MSME1_LDO_ID = PM_VREG_MSME1_ID, + PM_VREG_MSMC_ID = PM_VREG_MSMC1_ID, + PM_VREG_MSMC_LDO_ID = PM_VREG_MSMC1_ID, + PM_VREG_MSMC1_BUCK_SMPS_ID = PM_VREG_MSMC1_ID, + PM_VREG_MSME2_LDO_ID = PM_VREG_MSME2_ID, + PM_VREG_CAM_ID = PM_VREG_GP1_ID, + PM_VREG_MDDI_ID = PM_VREG_GP2_ID, + PM_VREG_RUIM2_ID = PM_VREG_GP3_ID, + PM_VREG_AUX_ID = PM_VREG_GP4_ID, + PM_VREG_AUX2_ID = PM_VREG_GP5_ID, + PM_VREG_BT_ID = PM_VREG_GP6_ID, + PM_VREG_RF1_ID = PM_VREG_RF_ID, + PM_VREG_S1_ID = PM_VREG_RF1_ID, + PM_VREG_5V_ID = PM_VREG_BOOST_ID, + PM_VREG_RFA1_ID = PM_VREG_RFRX2_ID, + PM_VREG_RFA2_ID = PM_VREG_RFTX2_ID, + PM_VREG_XO_ID = PM_VREG_TCXO_ID +}; + +enum vreg_pdown_id { + PM_VREG_PDOWN_MSMA_ID, + PM_VREG_PDOWN_MSMP_ID, + PM_VREG_PDOWN_MSME1_ID, + PM_VREG_PDOWN_MSMC1_ID, + PM_VREG_PDOWN_MSMC2_ID, + PM_VREG_PDOWN_GP3_ID, + PM_VREG_PDOWN_MSME2_ID, + PM_VREG_PDOWN_GP4_ID, + PM_VREG_PDOWN_GP1_ID, + PM_VREG_PDOWN_TCXO_ID, + PM_VREG_PDOWN_PA_ID, + PM_VREG_PDOWN_RFTX_ID, + PM_VREG_PDOWN_RFRX1_ID, + PM_VREG_PDOWN_RFRX2_ID, + PM_VREG_PDOWN_SYNT_ID, + PM_VREG_PDOWN_WLAN_ID, + PM_VREG_PDOWN_USB_ID, + PM_VREG_PDOWN_MMC_ID, + PM_VREG_PDOWN_RUIM_ID, + PM_VREG_PDOWN_MSMC0_ID, + PM_VREG_PDOWN_GP2_ID, + PM_VREG_PDOWN_GP5_ID, + PM_VREG_PDOWN_GP6_ID, + PM_VREG_PDOWN_RF_ID, + PM_VREG_PDOWN_RF_VCO_ID, + PM_VREG_PDOWN_MPLL_ID, + PM_VREG_PDOWN_S2_ID, + PM_VREG_PDOWN_S3_ID, + PM_VREG_PDOWN_RFUBM_ID, + /* new for HAN */ + PM_VREG_PDOWN_RF1_ID, + PM_VREG_PDOWN_RF2_ID, + PM_VREG_PDOWN_RFA_ID, + PM_VREG_PDOWN_CDC2_ID, + PM_VREG_PDOWN_RFTX2_ID, + PM_VREG_PDOWN_USIM_ID, + PM_VREG_PDOWN_USB2P6_ID, + PM_VREG_PDOWN_USB3P3_ID, + + /* backward compatible enums only */ + PM_VREG_PDOWN_CAM_ID = PM_VREG_PDOWN_GP1_ID, + PM_VREG_PDOWN_MDDI_ID = PM_VREG_PDOWN_GP2_ID, + PM_VREG_PDOWN_RUIM2_ID = PM_VREG_PDOWN_GP3_ID, + PM_VREG_PDOWN_AUX_ID = PM_VREG_PDOWN_GP4_ID, + PM_VREG_PDOWN_AUX2_ID = PM_VREG_PDOWN_GP5_ID, + PM_VREG_PDOWN_BT_ID = PM_VREG_PDOWN_GP6_ID, + PM_VREG_PDOWN_MSME_ID = PM_VREG_PDOWN_MSME1_ID, + PM_VREG_PDOWN_MSMC_ID = PM_VREG_PDOWN_MSMC1_ID, + PM_VREG_PDOWN_RFA1_ID = PM_VREG_PDOWN_RFRX2_ID, + PM_VREG_PDOWN_RFA2_ID = PM_VREG_PDOWN_RFTX2_ID, + PM_VREG_PDOWN_XO_ID = PM_VREG_PDOWN_TCXO_ID +}; + +enum mpp_which { + PM_MPP_1, + PM_MPP_2, + PM_MPP_3, + PM_MPP_4, + PM_MPP_5, + PM_MPP_6, + PM_MPP_7, + PM_MPP_8, + PM_MPP_9, + PM_MPP_10, + PM_MPP_11, + PM_MPP_12, + PM_MPP_13, + PM_MPP_14, + PM_MPP_15, + PM_MPP_16, + PM_MPP_17, + PM_MPP_18, + PM_MPP_19, + PM_MPP_20, + PM_MPP_21, + PM_MPP_22, + + PM_NUM_MPP_HAN = PM_MPP_4 + 1, + PM_NUM_MPP_KIP = PM_MPP_4 + 1, + PM_NUM_MPP_EPIC = PM_MPP_4 + 1, + PM_NUM_MPP_PM7500 = PM_MPP_22 + 1, + PM_NUM_MPP_PM6650 = PM_MPP_12 + 1, + PM_NUM_MPP_PM6658 = PM_MPP_12 + 1, + PM_NUM_MPP_PANORAMIX = PM_MPP_2 + 1, + PM_NUM_MPP_PM6640 = PM_NUM_MPP_PANORAMIX, + PM_NUM_MPP_PM6620 = PM_NUM_MPP_PANORAMIX +}; + +enum mpp_dlogic_level { + PM_MPP__DLOGIC__LVL_MSME, + PM_MPP__DLOGIC__LVL_MSMP, + PM_MPP__DLOGIC__LVL_RUIM, + PM_MPP__DLOGIC__LVL_MMC, + PM_MPP__DLOGIC__LVL_VDD, +}; + +enum mpp_dlogic_in_dbus { + PM_MPP__DLOGIC_IN__DBUS_NONE, + PM_MPP__DLOGIC_IN__DBUS1, + PM_MPP__DLOGIC_IN__DBUS2, + PM_MPP__DLOGIC_IN__DBUS3, +}; + +enum mpp_dlogic_out_ctrl { + PM_MPP__DLOGIC_OUT__CTRL_LOW, + PM_MPP__DLOGIC_OUT__CTRL_HIGH, + PM_MPP__DLOGIC_OUT__CTRL_MPP, + PM_MPP__DLOGIC_OUT__CTRL_NOT_MPP, +}; + +enum mpp_i_sink_level { + PM_MPP__I_SINK__LEVEL_5mA, + PM_MPP__I_SINK__LEVEL_10mA, + PM_MPP__I_SINK__LEVEL_15mA, + PM_MPP__I_SINK__LEVEL_20mA, + PM_MPP__I_SINK__LEVEL_25mA, + PM_MPP__I_SINK__LEVEL_30mA, + PM_MPP__I_SINK__LEVEL_35mA, + PM_MPP__I_SINK__LEVEL_40mA, +}; + +enum mpp_i_sink_switch { + PM_MPP__I_SINK__SWITCH_DIS, + PM_MPP__I_SINK__SWITCH_ENA, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_HIGH, + PM_MPP__I_SINK__SWITCH_ENA_IF_MPP_LOW, +}; + +enum pm_vib_mot_mode { + PM_VIB_MOT_MODE__MANUAL, + PM_VIB_MOT_MODE__DBUS1, + PM_VIB_MOT_MODE__DBUS2, + PM_VIB_MOT_MODE__DBUS3, +}; + +enum pm_vib_mot_pol { + PM_VIB_MOT_POL__ACTIVE_HIGH, + PM_VIB_MOT_POL__ACTIVE_LOW, +}; + +struct rtc_time { + uint sec; +}; + +enum rtc_alarm { + PM_RTC_ALARM_1, +}; + +enum hsed_controller { + PM_HSED_CONTROLLER_0, + PM_HSED_CONTROLLER_1, + PM_HSED_CONTROLLER_2, +}; + +enum hsed_switch { + PM_HSED_SC_SWITCH_TYPE, + PM_HSED_OC_SWITCH_TYPE, +}; + +enum hsed_enable { + PM_HSED_ENABLE_OFF, + PM_HSED_ENABLE_TCXO, + PM_HSED_ENABLE_PWM_TCXO, + PM_HSED_ENABLE_ALWAYS, +}; + +enum hsed_hyst_pre_div { + PM_HSED_HYST_PRE_DIV_1, + PM_HSED_HYST_PRE_DIV_2, + PM_HSED_HYST_PRE_DIV_4, + PM_HSED_HYST_PRE_DIV_8, + PM_HSED_HYST_PRE_DIV_16, + PM_HSED_HYST_PRE_DIV_32, + PM_HSED_HYST_PRE_DIV_64, + PM_HSED_HYST_PRE_DIV_128, +}; + +enum hsed_hyst_time { + PM_HSED_HYST_TIME_1_CLK_CYCLES, + PM_HSED_HYST_TIME_2_CLK_CYCLES, + PM_HSED_HYST_TIME_3_CLK_CYCLES, + PM_HSED_HYST_TIME_4_CLK_CYCLES, + PM_HSED_HYST_TIME_5_CLK_CYCLES, + PM_HSED_HYST_TIME_6_CLK_CYCLES, + PM_HSED_HYST_TIME_7_CLK_CYCLES, + PM_HSED_HYST_TIME_8_CLK_CYCLES, + PM_HSED_HYST_TIME_9_CLK_CYCLES, + PM_HSED_HYST_TIME_10_CLK_CYCLES, + PM_HSED_HYST_TIME_11_CLK_CYCLES, + PM_HSED_HYST_TIME_12_CLK_CYCLES, + PM_HSED_HYST_TIME_13_CLK_CYCLES, + PM_HSED_HYST_TIME_14_CLK_CYCLES, + PM_HSED_HYST_TIME_15_CLK_CYCLES, + PM_HSED_HYST_TIME_16_CLK_CYCLES, +}; + +enum hsed_period_pre_div { + PM_HSED_PERIOD_PRE_DIV_2, + PM_HSED_PERIOD_PRE_DIV_4, + PM_HSED_PERIOD_PRE_DIV_8, + PM_HSED_PERIOD_PRE_DIV_16, + PM_HSED_PERIOD_PRE_DIV_32, + PM_HSED_PERIOD_PRE_DIV_64, + PM_HSED_PERIOD_PRE_DIV_128, + PM_HSED_PERIOD_PRE_DIV_256, +}; + +enum hsed_period_time { + PM_HSED_PERIOD_TIME_1_CLK_CYCLES, + PM_HSED_PERIOD_TIME_2_CLK_CYCLES, + PM_HSED_PERIOD_TIME_3_CLK_CYCLES, + PM_HSED_PERIOD_TIME_4_CLK_CYCLES, + PM_HSED_PERIOD_TIME_5_CLK_CYCLES, + PM_HSED_PERIOD_TIME_6_CLK_CYCLES, + PM_HSED_PERIOD_TIME_7_CLK_CYCLES, + PM_HSED_PERIOD_TIME_8_CLK_CYCLES, + PM_HSED_PERIOD_TIME_9_CLK_CYCLES, + PM_HSED_PERIOD_TIME_10_CLK_CYCLES, + PM_HSED_PERIOD_TIME_11_CLK_CYCLES, + PM_HSED_PERIOD_TIME_12_CLK_CYCLES, + PM_HSED_PERIOD_TIME_13_CLK_CYCLES, + PM_HSED_PERIOD_TIME_14_CLK_CYCLES, + PM_HSED_PERIOD_TIME_15_CLK_CYCLES, + PM_HSED_PERIOD_TIME_16_CLK_CYCLES, +}; + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id); +int pmic_vreg_set_level(enum vreg_id vreg, int level); +int pmic_vreg_pull_down_switch(enum switch_cmd cmd, enum vreg_pdown_id id); +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_out_ctrl out); +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, enum mpp_i_sink_switch onoff); +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, enum mpp_dlogic_in_dbus dbus); +int pmic_rtc_start(struct rtc_time *time); +int pmic_rtc_stop(void); +int pmic_rtc_get_time(struct rtc_time *time); +int pmic_rtc_enable_alarm(enum rtc_alarm alarm, + struct rtc_time *time); +int pmic_rtc_disable_alarm(enum rtc_alarm alarm); +int pmic_rtc_get_alarm_time(enum rtc_alarm alarm, + struct rtc_time *time); +int pmic_rtc_get_alarm_status(uint *status); +int pmic_rtc_set_time_adjust(uint adjust); +int pmic_rtc_get_time_adjust(uint *adjust); +int pmic_speaker_cmd(const enum spkr_cmd cmd); +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg); +int pmic_get_spkr_configuration(struct spkr_config_mode *cfg); +int pmic_spkr_en_right_chan(uint enable); +int pmic_spkr_is_right_chan_en(uint *enabled); +int pmic_spkr_en_left_chan(uint enable); +int pmic_spkr_is_left_chan_en(uint *enabled); +int pmic_spkr_en(enum spkr_left_right left_right, uint enabled); +int pmic_spkr_is_en(enum spkr_left_right left_right, uint *enabled); +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain); +int pmic_spkr_get_gain(enum spkr_left_right left_right, enum spkr_gain *gain); +int pmic_set_speaker_gain(enum spkr_gain gain); +int pmic_set_speaker_delay(enum spkr_dly delay); +int pmic_speaker_1k6_zin_enable(uint enable); +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq); +int pmic_spkr_get_mux_hpf_corner_freq(enum spkr_hpf_corner_freq *freq); +int pmic_spkr_select_usb_with_hpf_20hz(uint enable); +int pmic_spkr_is_usb_with_hpf_20hz(uint *enabled); +int pmic_spkr_bypass_mux(uint enable); +int pmic_spkr_is_mux_bypassed(uint *enabled); +int pmic_spkr_en_hpf(uint enable); +int pmic_spkr_is_hpf_en(uint *enabled); +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable); +int pmic_spkr_is_sink_curr_from_ref_volt_cir_en(uint *enabled); +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay); +int pmic_spkr_get_delay(enum spkr_left_right left_right, enum spkr_dly *delay); +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled); +int pmic_spkr_is_mute_en(enum spkr_left_right left_right, uint *enabled); +int pmic_mic_en(uint enable); +int pmic_mic_is_en(uint *enabled); +int pmic_mic_set_volt(enum mic_volt vol); +int pmic_mic_get_volt(enum mic_volt *voltage); +int pmic_set_led_intensity(enum ledtype type, int level); +int pmic_flash_led_set_current(uint16_t milliamps); +int pmic_flash_led_set_mode(enum flash_led_mode mode); +int pmic_flash_led_set_polarity(enum flash_led_pol pol); +int pmic_spkr_add_right_left_chan(uint enable); +int pmic_spkr_is_right_left_chan_added(uint *enabled); +int pmic_spkr_en_stereo(uint enable); +int pmic_spkr_is_stereo_en(uint *enabled); +int pmic_vib_mot_set_volt(uint vol); +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode); +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol); +int pmic_vid_en(uint enable); +int pmic_vid_is_en(uint *enabled); +int pmic_vid_load_detect_en(uint enable); + +int pmic_hsed_set_period( + enum hsed_controller controller, + enum hsed_period_pre_div period_pre_div, + enum hsed_period_time period_time +); + +int pmic_hsed_set_hysteresis( + enum hsed_controller controller, + enum hsed_hyst_pre_div hyst_pre_div, + enum hsed_hyst_time hyst_time +); + +int pmic_hsed_set_current_threshold( + enum hsed_controller controller, + enum hsed_switch switch_hsed, + uint32_t current_threshold +); + +int pmic_hsed_enable( + enum hsed_controller controller, + enum hsed_enable enable +); + +#endif diff --git a/arch/arm/mach-msm/include/mach/remote_spinlock.h b/arch/arm/mach-msm/include/mach/remote_spinlock.h new file mode 100644 index 000000000000..015b6ffd89af --- /dev/null +++ b/arch/arm/mach-msm/include/mach/remote_spinlock.h @@ -0,0 +1,165 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * Part of this this code is based on the standard ARM spinlock + * implementation (asm/spinlock.h) found in the 2.6.29 kernel. + */ + +#ifndef __ASM__ARCH_QC_REMOTE_SPINLOCK_H +#define __ASM__ARCH_QC_REMOTE_SPINLOCK_H + +#include <linux/types.h> +#include <linux/spinlock.h> + +typedef struct { + volatile uint32_t lock; +} raw_remote_spinlock_t; + +typedef raw_remote_spinlock_t *_remote_spinlock_t; + +#define remote_spin_lock_id_t uint32_t + +static inline void __raw_remote_ex_spin_lock(raw_remote_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( +"1: ldrex %0, [%1]\n" +" teq %0, #0\n" +" strexeq %0, %2, [%1]\n" +" teqeq %0, #0\n" +" bne 1b" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + smp_mb(); +} + +static inline void __raw_remote_ex_spin_unlock(raw_remote_spinlock_t *lock) +{ + smp_mb(); + + __asm__ __volatile__( +" str %1, [%0]\n" + : + : "r" (&lock->lock), "r" (0) + : "cc"); +} + +static inline void __raw_remote_swp_spin_lock(raw_remote_spinlock_t *lock) +{ + unsigned long tmp; + + __asm__ __volatile__( +"1: swp %0, %2, [%1]\n" +" teq %0, #0\n" +" bne 1b" + : "=&r" (tmp) + : "r" (&lock->lock), "r" (1) + : "cc"); + + smp_mb(); +} + +static inline void __raw_remote_swp_spin_unlock(raw_remote_spinlock_t *lock) +{ + smp_mb(); + + __asm__ __volatile__( +" str %1, [%0]" + : + : "r" (&lock->lock), "r" (0) + : "cc"); +} + + +int _remote_spin_lock_init(remote_spin_lock_id_t id, _remote_spinlock_t *lock); + +/* Only use SWP-based spinlocks for ARM11 apps processors where the LDREX/STREX + * instructions are unable to lock shared memory for exclusive access. */ +#if defined(CONFIG_ARCH_MSM_ARM11) +#define _remote_spin_lock(lock) __raw_remote_swp_spin_lock(*lock) +#define _remote_spin_unlock(lock) __raw_remote_swp_spin_unlock(*lock) +#else +#define _remote_spin_lock(lock) __raw_remote_ex_spin_lock(*lock) +#define _remote_spin_unlock(lock) __raw_remote_ex_spin_unlock(*lock) +#endif /* CONFIG_ARCH_MSM_ARM11 */ + + +/* Grabbing a local spin lock before going for a remote lock has several + * advantages: + * 1. Get calls to preempt enable/disable and IRQ save/restore for free. + * 2. For UP kernel, there is no overhead. + * 3. Reduces the possibility of executing the remote spin lock code. This is + * especially useful when the remote CPUs' mutual exclusion instructions + * don't work with the local CPUs' instructions. In such cases, one has to + * use software based mutex algorithms (e.g. Lamport's bakery algorithm) + * which could get expensive when the no. of contending CPUs is high. + * 4. In the case of software based mutex algorithm the exection time will be + * smaller since the no. of contending CPUs is reduced by having just one + * contender for all the local CPUs. + * 5. Get most of the spin lock debug features for free. + * 6. The code will continue to work "gracefully" even when the remote spin + * lock code is stubbed out for debug purposes or when there is no remote + * CPU in some board/machine types. + */ +typedef struct { + spinlock_t local; + _remote_spinlock_t remote; +} remote_spinlock_t; + +#define remote_spin_lock_init(lock,id) \ + do { \ + spin_lock_init(&((lock)->local)); \ + _remote_spin_lock_init(id, &((lock)->remote)); \ + } while (0) +#define remote_spin_lock(lock) \ + do { \ + spin_lock(&((lock)->local)); \ + _remote_spin_lock(&((lock)->remote)); \ + } while (0) +#define remote_spin_unlock(lock) \ + do { \ + _remote_spin_unlock(&((lock)->remote)); \ + spin_unlock(&((lock)->local)); \ + } while (0) +#define remote_spin_lock_irqsave(lock, flags) \ + do { \ + spin_lock_irqsave(&((lock)->local), flags); \ + _remote_spin_lock(&((lock)->remote)); \ + } while (0) +#define remote_spin_unlock_irqrestore(lock, flags) \ + do { \ + _remote_spin_unlock(&((lock)->remote)); \ + spin_unlock_irqrestore(&((lock)->local), flags); \ + } while (0) + +#endif /* __ASM__ARCH_QC_REMOTE_SPINLOCK_H */ + diff --git a/arch/arm/mach-msm/include/mach/rpc_hsusb.h b/arch/arm/mach-msm/include/mach/rpc_hsusb.h new file mode 100644 index 000000000000..9ef304be2189 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/rpc_hsusb.h @@ -0,0 +1,45 @@ +/* linux/include/mach/rpc_hsusb.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#ifndef __ASM_ARCH_MSM_RPC_HSUSB_H +#define __ASM_ARCH_MSM_RPC_HSUSB_H + +#include <mach/msm_rpcrouter.h> + +int msm_hsusb_rpc_connect(void); +int msm_hsusb_phy_reset(void); +int msm_hsusb_vbus_powerup(void); +int msm_hsusb_vbus_shutdown(void); +int msm_hsusb_send_productID(uint32_t product_id); +int msm_hsusb_send_serial_number(char *serial_number); +int msm_hsusb_is_serial_num_null(uint32_t val); +int msm_hsusb_reset_rework_installed(void); +int msm_hsusb_enable_pmic_ulpidata0(void); +int msm_hsusb_disable_pmic_ulpidata0(void); +int msm_hsusb_rpc_close(void); + +int msm_chg_rpc_connect(void); +int msm_chg_usb_charger_connected(uint32_t type); +int msm_chg_usb_i_is_available(uint32_t sample); +int msm_chg_usb_i_is_not_available(void); +int msm_chg_usb_charger_disconnected(void); +int msm_chg_rpc_close(void); + +#endif diff --git a/arch/arm/mach-msm/include/mach/sirc.h b/arch/arm/mach-msm/include/mach/sirc.h new file mode 100644 index 000000000000..133f37ef1546 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/sirc.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef __ASM_ARCH_MSM_SIRC_H +#define __ASM_ARCH_MSM_SIRC_H + +struct sirc_regs_t { + void *int_enable; + void *int_enable_clear; + void *int_enable_set; + void *int_type; + void *int_polarity; + void *int_clear; +}; + +struct sirc_cascade_regs { + void *int_status; + unsigned int cascade_irq; +}; + +void msm_init_sirc(void); +void msm_sirc_enter_sleep(void); +void msm_sirc_exit_sleep(void); + +#if defined(CONFIG_ARCH_MSM_SCORPION) + +#include <mach/msm_iomap.h> + +/* + * Secondary interrupt controller interrupts + */ + +#define FIRST_SIRC_IRQ (NR_MSM_IRQS + NR_GPIO_IRQS) + +#define INT_UART1 (FIRST_SIRC_IRQ + 0) +#define INT_UART2 (FIRST_SIRC_IRQ + 1) +#define INT_UART3 (FIRST_SIRC_IRQ + 2) +#define INT_UART1_RX (FIRST_SIRC_IRQ + 3) +#define INT_UART2_RX (FIRST_SIRC_IRQ + 4) +#define INT_UART3_RX (FIRST_SIRC_IRQ + 5) +#define INT_SPI_INPUT (FIRST_SIRC_IRQ + 6) +#define INT_SPI_OUTPUT (FIRST_SIRC_IRQ + 7) +#define INT_SPI_ERROR (FIRST_SIRC_IRQ + 8) +#define INT_GPIO_GROUP1 (FIRST_SIRC_IRQ + 9) +#define INT_GPIO_GROUP2 (FIRST_SIRC_IRQ + 10) +#define INT_GPIO_GROUP1_SECURE (FIRST_SIRC_IRQ + 11) +#define INT_GPIO_GROUP2_SECURE (FIRST_SIRC_IRQ + 12) +#define INT_AVS_SVIC (FIRST_SIRC_IRQ + 13) +#define INT_AVS_REQ_UP (FIRST_SIRC_IRQ + 14) +#define INT_AVS_REQ_DOWN (FIRST_SIRC_IRQ + 15) +#define INT_PBUS_ERR (FIRST_SIRC_IRQ + 16) +#define INT_AXI_ERR (FIRST_SIRC_IRQ + 17) +#define INT_SMI_ERR (FIRST_SIRC_IRQ + 18) +#define INT_EBI1_ERR (FIRST_SIRC_IRQ + 19) +#define INT_IMEM_ERR (FIRST_SIRC_IRQ + 20) +#define INT_SC_TEMP_SENSOR (FIRST_SIRC_IRQ + 21) +#define INT_TV_ENC (FIRST_SIRC_IRQ + 22) + +#define NR_SIRC_IRQS 23 +#define SIRC_MASK 0x007FFFFF +#define LAST_SIRC_IRQ (FIRST_SIRC_IRQ + NR_SIRC_IRQS - 1) + +#define SPSS_SIRC_INT_SELECT (MSM_SIRC_BASE + 0x00) +#define SPSS_SIRC_INT_ENABLE (MSM_SIRC_BASE + 0x04) +#define SPSS_SIRC_INT_ENABLE_CLEAR (MSM_SIRC_BASE + 0x08) +#define SPSS_SIRC_INT_ENABLE_SET (MSM_SIRC_BASE + 0x0C) +#define SPSS_SIRC_INT_TYPE (MSM_SIRC_BASE + 0x10) +#define SPSS_SIRC_INT_POLARITY (MSM_SIRC_BASE + 0x14) +#define SPSS_SIRC_SECURITY (MSM_SIRC_BASE + 0x18) +#define SPSS_SIRC_IRQ_STATUS (MSM_SIRC_BASE + 0x1C) +#define SPSS_SIRC_IRQ1_STATUS (MSM_SIRC_BASE + 0x20) +#define SPSS_SIRC_RAW_STATUS (MSM_SIRC_BASE + 0x24) +#define SPSS_SIRC_INT_CLEAR (MSM_SIRC_BASE + 0x28) +#define SPSS_SIRC_SOFT_INT (MSM_SIRC_BASE + 0x2C) + +#endif + +#endif diff --git a/arch/arm/mach-msm/include/mach/smem_log.h b/arch/arm/mach-msm/include/mach/smem_log.h new file mode 100644 index 000000000000..bd94bacc4809 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/smem_log.h @@ -0,0 +1,245 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/ioctl.h> +#include <linux/types.h> + +#define SMEM_LOG_BASE 0x30 + +#define SMIOC_SETMODE _IOW(SMEM_LOG_BASE, 1, int) +#define SMIOC_SETLOG _IOW(SMEM_LOG_BASE, 2, int) + +#define SMIOC_TEXT 0x00000001 +#define SMIOC_BINARY 0x00000002 +#define SMIOC_LOG 0x00000003 +#define SMIOC_STATIC_LOG 0x00000004 + +/* Event indentifier format: + * bit 31-28 is processor ID 8 => apps, 4 => Q6, 0 => modem + * bits 27-16 are subsystem id (event base) + * bits 15-0 are event id + */ + +#define PROC 0xF0000000 +#define SUB 0x0FFF0000 +#define ID 0x0000FFFF + +#define SMEM_LOG_PROC_ID_MODEM 0x00000000 +#define SMEM_LOG_PROC_ID_Q6 0x40000000 +#define SMEM_LOG_PROC_ID_APPS 0x80000000 + +#define SMEM_LOG_CONT 0x10000000 + +#define SMEM_LOG_DEBUG_EVENT_BASE 0x00000000 +#define SMEM_LOG_ONCRPC_EVENT_BASE 0x00010000 +#define SMEM_LOG_SMEM_EVENT_BASE 0x00020000 +#define SMEM_LOG_TMC_EVENT_BASE 0x00030000 +#define SMEM_LOG_TIMETICK_EVENT_BASE 0x00040000 +#define SMEM_LOG_DEM_EVENT_BASE 0x00050000 +#define SMEM_LOG_ERROR_EVENT_BASE 0x00060000 +#define SMEM_LOG_DCVS_EVENT_BASE 0x00070000 +#define SMEM_LOG_SLEEP_EVENT_BASE 0x00080000 +#define SMEM_LOG_RPC_ROUTER_EVENT_BASE 0x00090000 +#if defined(CONFIG_MSM_N_WAY_SMSM) +#define DEM_SMSM_ISR (SMEM_LOG_DEM_EVENT_BASE + 0x1) +#define DEM_STATE_CHANGE (SMEM_LOG_DEM_EVENT_BASE + 0x2) +#define DEM_STATE_MACHINE_ENTER (SMEM_LOG_DEM_EVENT_BASE + 0x3) +#define DEM_ENTER_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x4) +#define DEM_END_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x5) +#define DEM_SETUP_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x6) +#define DEM_SETUP_POWER_COLLAPSE (SMEM_LOG_DEM_EVENT_BASE + 0x7) +#define DEM_SETUP_SUSPEND (SMEM_LOG_DEM_EVENT_BASE + 0x8) +#define DEM_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 0x9) +#define DEM_WAKEUP_REASON (SMEM_LOG_DEM_EVENT_BASE + 0xA) +#define DEM_DETECT_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0xB) +#define DEM_DETECT_RESET (SMEM_LOG_DEM_EVENT_BASE + 0xC) +#define DEM_DETECT_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 0xD) +#define DEM_DETECT_RUN (SMEM_LOG_DEM_EVENT_BASE + 0xE) +#define DEM_APPS_SWFI (SMEM_LOG_DEM_EVENT_BASE + 0xF) +#define DEM_SEND_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0x10) +#define DEM_ASSERT_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x11) +#define DEM_NEGATE_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x12) +#define DEM_PROC_COMM_CMD (SMEM_LOG_DEM_EVENT_BASE + 0x13) +#define DEM_REMOVE_PROC_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x14) +#define DEM_RESTORE_PROC_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x15) +#define DEM_SMI_CLK_DISABLED (SMEM_LOG_DEM_EVENT_BASE + 0x16) +#define DEM_SMI_CLK_ENABLED (SMEM_LOG_DEM_EVENT_BASE + 0x17) +#define DEM_MAO_INTS (SMEM_LOG_DEM_EVENT_BASE + 0x18) +#define DEM_APPS_WAKEUP_INT (SMEM_LOG_DEM_EVENT_BASE + 0x19) +#define DEM_PROC_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0x1A) +#define DEM_PROC_POWERUP (SMEM_LOG_DEM_EVENT_BASE + 0x1B) +#define DEM_TIMER_EXPIRED (SMEM_LOG_DEM_EVENT_BASE + 0x1C) +#define DEM_SEND_BATTERY_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x1D) +#define DEM_REMOTE_PWR_CB (SMEM_LOG_DEM_EVENT_BASE + 0x24) +#define DEM_TIME_SYNC_START (SMEM_LOG_DEM_EVENT_BASE + 0x1E) +#define DEM_TIME_SYNC_SEND_VALUE (SMEM_LOG_DEM_EVENT_BASE + 0x1F) +#define DEM_TIME_SYNC_DONE (SMEM_LOG_DEM_EVENT_BASE + 0x20) +#define DEM_TIME_SYNC_REQUEST (SMEM_LOG_DEM_EVENT_BASE + 0x21) +#define DEM_TIME_SYNC_POLL (SMEM_LOG_DEM_EVENT_BASE + 0x22) +#define DEM_TIME_SYNC_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x23) +#define DEM_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x25) +#else +#define DEM_NO_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 1) +#define DEM_INSUF_TIME (SMEM_LOG_DEM_EVENT_BASE + 2) +#define DEMAPPS_ENTER_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 3) +#define DEMAPPS_DETECT_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 4) +#define DEMAPPS_END_APPS_TCXO (SMEM_LOG_DEM_EVENT_BASE + 5) +#define DEMAPPS_ENTER_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 6) +#define DEMAPPS_END_APPS_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 7) +#define DEMAPPS_SETUP_APPS_PWRCLPS (SMEM_LOG_DEM_EVENT_BASE + 8) +#define DEMAPPS_PWRCLPS_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 9) +#define DEMMOD_SEND_WAKEUP (SMEM_LOG_DEM_EVENT_BASE + 0xA) +#define DEMMOD_NO_APPS_VOTE (SMEM_LOG_DEM_EVENT_BASE + 0xB) +#define DEMMOD_NO_TCXO_SLEEP (SMEM_LOG_DEM_EVENT_BASE + 0xC) +#define DEMMOD_BT_CLOCK (SMEM_LOG_DEM_EVENT_BASE + 0xD) +#define DEMMOD_UART_CLOCK (SMEM_LOG_DEM_EVENT_BASE + 0xE) +#define DEMMOD_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0xF) +#define DEM_SLEEP_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x10) +#define DEMMOD_TCXO_END (SMEM_LOG_DEM_EVENT_BASE + 0x11) +#define DEMMOD_END_SLEEP_SIG (SMEM_LOG_DEM_EVENT_BASE + 0x12) +#define DEMMOD_SETUP_APPSSLEEP (SMEM_LOG_DEM_EVENT_BASE + 0x13) +#define DEMMOD_ENTER_TCXO (SMEM_LOG_DEM_EVENT_BASE + 0x14) +#define DEMMOD_WAKE_APPS (SMEM_LOG_DEM_EVENT_BASE + 0x15) +#define DEMMOD_POWER_COLLAPSE_APPS (SMEM_LOG_DEM_EVENT_BASE + 0x16) +#define DEMMOD_RESTORE_APPS_PWR (SMEM_LOG_DEM_EVENT_BASE + 0x17) +#define DEMAPPS_ASSERT_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x18) +#define DEMAPPS_RESTART_START_TIMER (SMEM_LOG_DEM_EVENT_BASE + 0x19) +#define DEMAPPS_ENTER_RUN (SMEM_LOG_DEM_EVENT_BASE + 0x1A) +#define DEMMOD_MAO_INTS (SMEM_LOG_DEM_EVENT_BASE + 0x1B) +#define DEMMOD_POWERUP_APPS_CALLED (SMEM_LOG_DEM_EVENT_BASE + 0x1C) +#define DEMMOD_PC_TIMER_EXPIRED (SMEM_LOG_DEM_EVENT_BASE + 0x1D) +#define DEM_DETECT_SLEEPEXIT (SMEM_LOG_DEM_EVENT_BASE + 0x1E) +#define DEM_DETECT_RUN (SMEM_LOG_DEM_EVENT_BASE + 0x1F) +#define DEM_SET_APPS_TIMER (SMEM_LOG_DEM_EVENT_BASE + 0x20) +#define DEM_NEGATE_OKTS (SMEM_LOG_DEM_EVENT_BASE + 0x21) +#define DEMMOD_APPS_WAKEUP_INT (SMEM_LOG_DEM_EVENT_BASE + 0x22) +#define DEMMOD_APPS_SWFI (SMEM_LOG_DEM_EVENT_BASE + 0x23) +#define DEM_SEND_BATTERY_INFO (SMEM_LOG_DEM_EVENT_BASE + 0x24) +#define DEM_SMI_CLK_DISABLED (SMEM_LOG_DEM_EVENT_BASE + 0x25) +#define DEM_SMI_CLK_ENABLED (SMEM_LOG_DEM_EVENT_BASE + 0x26) +#define DEMAPPS_SETUP_APPS_SUSPEND (SMEM_LOG_DEM_EVENT_BASE + 0x27) +#define DEM_RPC_EARLY_EXIT (SMEM_LOG_DEM_EVENT_BASE + 0x28) +#define DEMAPPS_WAKEUP_REASON (SMEM_LOG_DEM_EVENT_BASE + 0x29) +#define DEM_INIT (SMEM_LOG_DEM_EVENT_BASE + 0x30) +#endif +#define DEMMOD_UMTS_BASE (SMEM_LOG_DEM_EVENT_BASE + 0x8000) +#define DEMMOD_GL1_GO_TO_SLEEP (DEMMOD_UMTS_BASE + 0x0000) +#define DEMMOD_GL1_SLEEP_START (DEMMOD_UMTS_BASE + 0x0001) +#define DEMMOD_GL1_AFTER_GSM_CLK_ON (DEMMOD_UMTS_BASE + 0x0002) +#define DEMMOD_GL1_BEFORE_RF_ON (DEMMOD_UMTS_BASE + 0x0003) +#define DEMMOD_GL1_AFTER_RF_ON (DEMMOD_UMTS_BASE + 0x0004) +#define DEMMOD_GL1_FRAME_TICK (DEMMOD_UMTS_BASE + 0x0005) +#define DEMMOD_GL1_WCDMA_START (DEMMOD_UMTS_BASE + 0x0006) +#define DEMMOD_GL1_WCDMA_ENDING (DEMMOD_UMTS_BASE + 0x0007) +#define DEMMOD_UMTS_NOT_OKTS (DEMMOD_UMTS_BASE + 0x0008) +#define DEMMOD_UMTS_START_TCXO_SHUTDOWN (DEMMOD_UMTS_BASE + 0x0009) +#define DEMMOD_UMTS_END_TCXO_SHUTDOWN (DEMMOD_UMTS_BASE + 0x000A) +#define DEMMOD_UMTS_START_ARM_HALT (DEMMOD_UMTS_BASE + 0x000B) +#define DEMMOD_UMTS_END_ARM_HALT (DEMMOD_UMTS_BASE + 0x000C) +#define DEMMOD_UMTS_NEXT_WAKEUP_SCLK (DEMMOD_UMTS_BASE + 0x000D) +#define TIME_REMOTE_LOG_EVENT_START (SMEM_LOG_TIMETICK_EVENT_BASE + 0) +#define TIME_REMOTE_LOG_EVENT_GOTO_WAIT (SMEM_LOG_TIMETICK_EVENT_BASE + 1) +#define TIME_REMOTE_LOG_EVENT_GOTO_INIT (SMEM_LOG_TIMETICK_EVENT_BASE + 2) +#define ERR_ERROR_FATAL (SMEM_LOG_ERROR_EVENT_BASE + 1) +#define ERR_ERROR_FATAL_TASK (SMEM_LOG_ERROR_EVENT_BASE + 2) +#define DCVSAPPS_LOG_IDLE (SMEM_LOG_DCVS_EVENT_BASE + 0x0) +#define DCVSAPPS_LOG_ERR (SMEM_LOG_DCVS_EVENT_BASE + 0x1) +#define DCVSAPPS_LOG_CHG (SMEM_LOG_DCVS_EVENT_BASE + 0x2) +#define DCVSAPPS_LOG_REG (SMEM_LOG_DCVS_EVENT_BASE + 0x3) +#define DCVSAPPS_LOG_DEREG (SMEM_LOG_DCVS_EVENT_BASE + 0x4) +#define SMEM_LOG_EVENT_CB (SMEM_LOG_SMEM_EVENT_BASE + 0) +#define SMEM_LOG_EVENT_START (SMEM_LOG_SMEM_EVENT_BASE + 1) +#define SMEM_LOG_EVENT_INIT (SMEM_LOG_SMEM_EVENT_BASE + 2) +#define SMEM_LOG_EVENT_RUNNING (SMEM_LOG_SMEM_EVENT_BASE + 3) +#define SMEM_LOG_EVENT_STOP (SMEM_LOG_SMEM_EVENT_BASE + 4) +#define SMEM_LOG_EVENT_RESTART (SMEM_LOG_SMEM_EVENT_BASE + 5) +#define SMEM_LOG_EVENT_SS (SMEM_LOG_SMEM_EVENT_BASE + 6) +#define SMEM_LOG_EVENT_READ (SMEM_LOG_SMEM_EVENT_BASE + 7) +#define SMEM_LOG_EVENT_WRITE (SMEM_LOG_SMEM_EVENT_BASE + 8) +#define SMEM_LOG_EVENT_SIGS1 (SMEM_LOG_SMEM_EVENT_BASE + 9) +#define SMEM_LOG_EVENT_SIGS2 (SMEM_LOG_SMEM_EVENT_BASE + 10) +#define SMEM_LOG_EVENT_WRITE_DM (SMEM_LOG_SMEM_EVENT_BASE + 11) +#define SMEM_LOG_EVENT_READ_DM (SMEM_LOG_SMEM_EVENT_BASE + 12) +#define SMEM_LOG_EVENT_SKIP_DM (SMEM_LOG_SMEM_EVENT_BASE + 13) +#define SMEM_LOG_EVENT_STOP_DM (SMEM_LOG_SMEM_EVENT_BASE + 14) +#define SMEM_LOG_EVENT_ISR (SMEM_LOG_SMEM_EVENT_BASE + 15) +#define SMEM_LOG_EVENT_TASK (SMEM_LOG_SMEM_EVENT_BASE + 16) +#define SMEM_LOG_EVENT_RS (SMEM_LOG_SMEM_EVENT_BASE + 17) +#define ONCRPC_LOG_EVENT_SMD_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 0) +#define ONCRPC_LOG_EVENT_RPC_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 1) +#define ONCRPC_LOG_EVENT_RPC_BOTH_WAIT (SMEM_LOG_ONCRPC_EVENT_BASE + 2) +#define ONCRPC_LOG_EVENT_RPC_INIT (SMEM_LOG_ONCRPC_EVENT_BASE + 3) +#define ONCRPC_LOG_EVENT_RUNNING (SMEM_LOG_ONCRPC_EVENT_BASE + 4) +#define ONCRPC_LOG_EVENT_APIS_INITED (SMEM_LOG_ONCRPC_EVENT_BASE + 5) +#define ONCRPC_LOG_EVENT_AMSS_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 6) +#define ONCRPC_LOG_EVENT_SMD_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 7) +#define ONCRPC_LOG_EVENT_ONCRPC_RESET (SMEM_LOG_ONCRPC_EVENT_BASE + 8) +#define ONCRPC_LOG_EVENT_CB (SMEM_LOG_ONCRPC_EVENT_BASE + 9) +#define ONCRPC_LOG_EVENT_STD_CALL (SMEM_LOG_ONCRPC_EVENT_BASE + 10) +#define ONCRPC_LOG_EVENT_STD_REPLY (SMEM_LOG_ONCRPC_EVENT_BASE + 11) +#define ONCRPC_LOG_EVENT_STD_CALL_ASYNC (SMEM_LOG_ONCRPC_EVENT_BASE + 12) +#define NO_SLEEP_OLD (SMEM_LOG_SLEEP_EVENT_BASE + 0x1) +#define INSUF_TIME (SMEM_LOG_SLEEP_EVENT_BASE + 0x2) +#define MOD_UART_CLOCK (SMEM_LOG_SLEEP_EVENT_BASE + 0x3) +#define SLEEP_INFO (SMEM_LOG_SLEEP_EVENT_BASE + 0x4) +#define MOD_TCXO_END (SMEM_LOG_SLEEP_EVENT_BASE + 0x5) +#define MOD_ENTER_TCXO (SMEM_LOG_SLEEP_EVENT_BASE + 0x6) +#define NO_SLEEP_NEW (SMEM_LOG_SLEEP_EVENT_BASE + 0x7) +#define RPC_ROUTER_LOG_EVENT_UNKNOWN (SMEM_LOG_RPC_ROUTER_EVENT_BASE) +#define RPC_ROUTER_LOG_EVENT_MSG_READ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 1) +#define RPC_ROUTER_LOG_EVENT_MSG_WRITTEN (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 2) +#define RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 3) +#define RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 4) +#define RPC_ROUTER_LOG_EVENT_MID_READ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 5) +#define RPC_ROUTER_LOG_EVENT_MID_WRITTEN (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 6) +#define RPC_ROUTER_LOG_EVENT_MID_CFM_REQ (SMEM_LOG_RPC_ROUTER_EVENT_BASE + 7) + +#ifdef CONFIG_MSM_SMD_LOGGING +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3); +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6); +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3); +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6); +#else +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) { } +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) { } +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) { } +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) { } +#endif + diff --git a/arch/arm/mach-msm/include/mach/system.h b/arch/arm/mach-msm/include/mach/system.h index 574ccc493daf..d2e83f42ba16 100644 --- a/arch/arm/mach-msm/include/mach/system.h +++ b/arch/arm/mach-msm/include/mach/system.h @@ -21,3 +21,8 @@ static inline void arch_reset(char mode, const char *cmd) { for (;;) ; /* depends on IPC w/ other core */ } + +/* low level hardware reset hook -- for example, hitting the + * PSHOLD line on the PMIC to hard reset the system + */ +extern void (*msm_hw_reset_hook)(void); diff --git a/arch/arm/mach-msm/include/mach/uncompress.h b/arch/arm/mach-msm/include/mach/uncompress.h index d94292c29d8e..6d1a2e82fc3f 100644 --- a/arch/arm/mach-msm/include/mach/uncompress.h +++ b/arch/arm/mach-msm/include/mach/uncompress.h @@ -15,7 +15,6 @@ #ifndef __ASM_ARCH_MSM_UNCOMPRESS_H -#include "hardware.h" #include "linux/io.h" #include "mach/msm_iomap.h" diff --git a/arch/arm/mach-msm/include/mach/usbdiag.h b/arch/arm/mach-msm/include/mach/usbdiag.h new file mode 100644 index 000000000000..a2b89650f4c9 --- /dev/null +++ b/arch/arm/mach-msm/include/mach/usbdiag.h @@ -0,0 +1,44 @@ +/* include/asm-arm/arch-msm/usbdiag.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#ifndef _DRIVERS_USB_DIAG_H_ +#define _DRIVERS_USB_DIAG_H_ +#define ENOREQ -1 +struct diag_operations { + + int (*diag_connect)(void); + int (*diag_disconnect)(void); + int (*diag_char_write_complete)(unsigned char * , int, int); + int (*diag_char_read_complete)(unsigned char *, int , int); +}; + +struct diag_request { + char *buf; + int length; +}; +int diag_open(int); +void diag_close(void); +int diag_read(unsigned char *, int); +int diag_write(unsigned char *, int); + +int diag_usb_register(struct diag_operations *); +int diag_usb_unregister(void); +int diag_read_from_cb(unsigned char * , int); +#endif diff --git a/arch/arm/mach-msm/include/mach/vmalloc.h b/arch/arm/mach-msm/include/mach/vmalloc.h index 05f81fd8623c..9d8c8375b206 100644 --- a/arch/arm/mach-msm/include/mach/vmalloc.h +++ b/arch/arm/mach-msm/include/mach/vmalloc.h @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/include/mach/vmalloc.h * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,7 +17,11 @@ #ifndef __ASM_ARCH_MSM_VMALLOC_H #define __ASM_ARCH_MSM_VMALLOC_H -#define VMALLOC_END (PAGE_OFFSET + 0x10000000) +#ifdef CONFIG_VMSPLIT_2G +#define VMALLOC_END (PAGE_OFFSET + 0x60000000) +#else +#define VMALLOC_END (PAGE_OFFSET + 0x20000000) +#endif #endif diff --git a/arch/arm/mach-msm/include/mach/vreg.h b/arch/arm/mach-msm/include/mach/vreg.h index 9f9e25cb718e..6626e7864e28 100644 --- a/arch/arm/mach-msm/include/mach/vreg.h +++ b/arch/arm/mach-msm/include/mach/vreg.h @@ -23,7 +23,7 @@ struct vreg *vreg_get(struct device *dev, const char *id); void vreg_put(struct vreg *vreg); int vreg_enable(struct vreg *vreg); -void vreg_disable(struct vreg *vreg); +int vreg_disable(struct vreg *vreg); int vreg_set_level(struct vreg *vreg, unsigned mv); #endif diff --git a/arch/arm/mach-msm/internal_power_rail.c b/arch/arm/mach-msm/internal_power_rail.c new file mode 100644 index 000000000000..c2c5e5bed9ce --- /dev/null +++ b/arch/arm/mach-msm/internal_power_rail.c @@ -0,0 +1,90 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include <mach/internal_power_rail.h> + +#include "proc_comm.h" + +/* Enable or disable an internal power rail */ +int internal_pwr_rail_ctl(unsigned rail_id, bool enable) +{ + int cmd, rc; + + cmd = enable ? PCOM_CLKCTL_RPC_RAIL_ENABLE : + PCOM_CLKCTL_RPC_RAIL_DISABLE; + + rc = msm_proc_comm(cmd, &rail_id, NULL); + + return rc; + +} +EXPORT_SYMBOL(internal_pwr_rail_ctl); + +/* Specify an internal power rail control mode (ex. auto, manual) */ +int internal_pwr_rail_mode(unsigned rail_id, enum rail_ctl_mode mode) +{ + int rc; + + rc = msm_proc_comm(PCOM_CLKCTL_RPC_RAIL_CONTROL, &rail_id, &mode); + + return rc; +} +EXPORT_SYMBOL(internal_pwr_rail_mode); + diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c index 05f96b780aa6..39a99a3a0f6b 100644 --- a/arch/arm/mach-msm/io.c +++ b/arch/arm/mach-msm/io.c @@ -1,8 +1,9 @@ /* arch/arm/mach-msm/io.c * - * MSM7K io support + * MSM7K, QSD io support * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -19,6 +20,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/module.h> #include <mach/hardware.h> #include <asm/page.h> @@ -34,10 +36,28 @@ .type = MT_DEVICE_NONSHARED, \ } +/* msm_shared_ram_phys default value of 0x00100000 is the most common value + * and should work as-is for any target without stacked memory. + */ +int msm_shared_ram_phys = 0x00100000; + +static void msm_map_io(struct map_desc *io_desc, int size) +{ + int i; + + BUG_ON(!size); + for (i = 0; i < size; i++) + if (io_desc[i].virtual == (unsigned long)MSM_SHARED_RAM_BASE) + io_desc[i].pfn = __phys_to_pfn(msm_shared_ram_phys); + + iotable_init(io_desc, size); +} + +#if defined(CONFIG_ARCH_MSM7X01A) || defined(CONFIG_ARCH_MSM7X27) static struct map_desc msm_io_desc[] __initdata = { MSM_DEVICE(VIC), MSM_DEVICE(CSR), - MSM_DEVICE(GPT), + MSM_DEVICE(TMR), MSM_DEVICE(DMOV), MSM_DEVICE(GPIO1), MSM_DEVICE(GPIO2), @@ -45,9 +65,16 @@ static struct map_desc msm_io_desc[] __initdata = { #ifdef CONFIG_MSM_DEBUG_UART MSM_DEVICE(DEBUG_UART), #endif +#ifdef CONFIG_CACHE_L2X0 + { + .virtual = (unsigned long) MSM_L2CC_BASE, + .pfn = __phys_to_pfn(MSM_L2CC_PHYS), + .length = MSM_L2CC_SIZE, + .type = MT_DEVICE, + }, +#endif { .virtual = (unsigned long) MSM_SHARED_RAM_BASE, - .pfn = __phys_to_pfn(MSM_SHARED_RAM_PHYS), .length = MSM_SHARED_RAM_SIZE, .type = MT_DEVICE, }, @@ -60,9 +87,98 @@ void __init msm_map_common_io(void) * pages are peripheral interface or not. */ asm("mcr p15, 0, %0, c15, c2, 4" : : "r" (0)); + msm_map_io(msm_io_desc, ARRAY_SIZE(msm_io_desc)); +} +#endif + +#ifdef CONFIG_ARCH_QSD8X50 +static struct map_desc qsd8x50_io_desc[] __initdata = { + MSM_DEVICE(VIC), + MSM_DEVICE(CSR), + MSM_DEVICE(TMR), + MSM_DEVICE(DMOV), + MSM_DEVICE(GPIO1), + MSM_DEVICE(GPIO2), + MSM_DEVICE(CLK_CTL), + MSM_DEVICE(SIRC), + MSM_DEVICE(SCPLL), + MSM_DEVICE(AD5), + MSM_DEVICE(MDC), +#ifdef CONFIG_MSM_DEBUG_UART + MSM_DEVICE(DEBUG_UART), +#endif + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +}; + +void __init msm_map_qsd8x50_io(void) +{ + msm_map_io(qsd8x50_io_desc, ARRAY_SIZE(qsd8x50_io_desc)); +} +#endif /* CONFIG_ARCH_QSD8X50 */ + +#ifdef CONFIG_ARCH_MSM7X30 +static struct map_desc msm7x30_io_desc[] __initdata = { + MSM_DEVICE(VIC), + MSM_DEVICE(CSR), + MSM_DEVICE(TMR), + MSM_DEVICE(DMOV), + MSM_DEVICE(GPIO1), + MSM_DEVICE(GPIO2), + MSM_DEVICE(CLK_CTL), + MSM_DEVICE(SIRC), + MSM_DEVICE(SCPLL), + MSM_DEVICE(AD5), + MSM_DEVICE(MDC), + MSM_DEVICE(ACC), + MSM_DEVICE(GCC), +#ifdef CONFIG_MSM_DEBUG_UART + MSM_DEVICE(DEBUG_UART), +#endif + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +}; + +void __init msm_map_msm7x30_io(void) +{ + msm_map_io(msm7x30_io_desc, ARRAY_SIZE(msm7x30_io_desc)); +} +#endif /* CONFIG_ARCH_MSM7X30 */ - iotable_init(msm_io_desc, ARRAY_SIZE(msm_io_desc)); +#ifdef CONFIG_MACH_QSD8X50_COMET +static struct map_desc comet_io_desc[] __initdata = { + MSM_DEVICE(VIC), + MSM_DEVICE(CSR), + MSM_DEVICE(TMR), + MSM_DEVICE(DMOV), + MSM_DEVICE(GPIO1), + MSM_DEVICE(GPIO2), + MSM_DEVICE(CLK_CTL), + MSM_DEVICE(SIRC), + MSM_DEVICE(SCPLL), + MSM_DEVICE(AD5), + MSM_DEVICE(MDC), +#ifdef CONFIG_MSM_DEBUG_UART + MSM_DEVICE(DEBUG_UART), +#endif + { + .virtual = (unsigned long) MSM_SHARED_RAM_BASE, + .length = MSM_SHARED_RAM_SIZE, + .type = MT_DEVICE, + }, +}; + +void __init msm_map_comet_io(void) +{ + msm_map_io(comet_io_desc, ARRAY_SIZE(comet_io_desc)); } +#endif /* CONFIG_MACH_QSD8X50_COMET */ void __iomem * __msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) @@ -79,3 +195,4 @@ __msm_ioremap(unsigned long phys_addr, size_t size, unsigned int mtype) return __arm_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); } +EXPORT_SYMBOL(__msm_ioremap); diff --git a/arch/arm/mach-msm/irq.c b/arch/arm/mach-msm/irq.c index 69ca0dd79bdf..03687ab2c223 100644 --- a/arch/arm/mach-msm/irq.c +++ b/arch/arm/mach-msm/irq.c @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/irq.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,96 +23,504 @@ #include <linux/irq.h> #include <linux/io.h> +#include <asm/cacheflush.h> + #include <mach/hardware.h> #include <mach/msm_iomap.h> +#include <mach/fiq.h> + +#include "smd_private.h" + +enum { + IRQ_DEBUG_SLEEP_INT_TRIGGER = 1U << 0, + IRQ_DEBUG_SLEEP_INT = 1U << 1, + IRQ_DEBUG_SLEEP_ABORT = 1U << 2, + IRQ_DEBUG_SLEEP = 1U << 3, + IRQ_DEBUG_SLEEP_REQUEST = 1U << 4, +}; +static int msm_irq_debug_mask; +module_param_named(debug_mask, msm_irq_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define VIC_REG(off) (MSM_VIC_BASE + (off)) +#define VIC_INT_TO_REG_ADDR(base, irq) (base + (irq / 32) * 4) +#define VIC_INT_TO_REG_INDEX(irq) ((irq >> 5) & 3) #define VIC_INT_SELECT0 VIC_REG(0x0000) /* 1: FIQ, 0: IRQ */ #define VIC_INT_SELECT1 VIC_REG(0x0004) /* 1: FIQ, 0: IRQ */ +#define VIC_INT_SELECT2 VIC_REG(0x0008) /* 1: FIQ, 0: IRQ */ +#define VIC_INT_SELECT3 VIC_REG(0x000C) /* 1: FIQ, 0: IRQ */ #define VIC_INT_EN0 VIC_REG(0x0010) #define VIC_INT_EN1 VIC_REG(0x0014) +#define VIC_INT_EN2 VIC_REG(0x0018) +#define VIC_INT_EN3 VIC_REG(0x001C) #define VIC_INT_ENCLEAR0 VIC_REG(0x0020) #define VIC_INT_ENCLEAR1 VIC_REG(0x0024) +#define VIC_INT_ENCLEAR2 VIC_REG(0x0028) +#define VIC_INT_ENCLEAR3 VIC_REG(0x002C) #define VIC_INT_ENSET0 VIC_REG(0x0030) #define VIC_INT_ENSET1 VIC_REG(0x0034) +#define VIC_INT_ENSET2 VIC_REG(0x0038) +#define VIC_INT_ENSET3 VIC_REG(0x003C) #define VIC_INT_TYPE0 VIC_REG(0x0040) /* 1: EDGE, 0: LEVEL */ #define VIC_INT_TYPE1 VIC_REG(0x0044) /* 1: EDGE, 0: LEVEL */ +#define VIC_INT_TYPE2 VIC_REG(0x0048) /* 1: EDGE, 0: LEVEL */ +#define VIC_INT_TYPE3 VIC_REG(0x004C) /* 1: EDGE, 0: LEVEL */ #define VIC_INT_POLARITY0 VIC_REG(0x0050) /* 1: NEG, 0: POS */ #define VIC_INT_POLARITY1 VIC_REG(0x0054) /* 1: NEG, 0: POS */ +#define VIC_INT_POLARITY2 VIC_REG(0x0058) /* 1: NEG, 0: POS */ +#define VIC_INT_POLARITY3 VIC_REG(0x005C) /* 1: NEG, 0: POS */ #define VIC_NO_PEND_VAL VIC_REG(0x0060) + +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define VIC_NO_PEND_VAL_FIQ VIC_REG(0x0064) +#define VIC_INT_MASTEREN VIC_REG(0x0068) /* 1: IRQ, 2: FIQ */ +#define VIC_CONFIG VIC_REG(0x006C) /* 1: USE SC VIC */ +#else #define VIC_INT_MASTEREN VIC_REG(0x0064) /* 1: IRQ, 2: FIQ */ #define VIC_PROTECTION VIC_REG(0x006C) /* 1: ENABLE */ #define VIC_CONFIG VIC_REG(0x0068) /* 1: USE ARM1136 VIC */ +#endif + #define VIC_IRQ_STATUS0 VIC_REG(0x0080) #define VIC_IRQ_STATUS1 VIC_REG(0x0084) +#define VIC_IRQ_STATUS2 VIC_REG(0x0088) +#define VIC_IRQ_STATUS3 VIC_REG(0x008C) #define VIC_FIQ_STATUS0 VIC_REG(0x0090) #define VIC_FIQ_STATUS1 VIC_REG(0x0094) +#define VIC_FIQ_STATUS2 VIC_REG(0x0098) +#define VIC_FIQ_STATUS3 VIC_REG(0x009C) #define VIC_RAW_STATUS0 VIC_REG(0x00A0) #define VIC_RAW_STATUS1 VIC_REG(0x00A4) +#define VIC_RAW_STATUS2 VIC_REG(0x00A8) +#define VIC_RAW_STATUS3 VIC_REG(0x00AC) #define VIC_INT_CLEAR0 VIC_REG(0x00B0) #define VIC_INT_CLEAR1 VIC_REG(0x00B4) +#define VIC_INT_CLEAR2 VIC_REG(0x00B8) +#define VIC_INT_CLEAR3 VIC_REG(0x00BC) #define VIC_SOFTINT0 VIC_REG(0x00C0) #define VIC_SOFTINT1 VIC_REG(0x00C4) +#define VIC_SOFTINT2 VIC_REG(0x00C8) +#define VIC_SOFTINT3 VIC_REG(0x00CC) #define VIC_IRQ_VEC_RD VIC_REG(0x00D0) /* pending int # */ #define VIC_IRQ_VEC_PEND_RD VIC_REG(0x00D4) /* pending vector addr */ #define VIC_IRQ_VEC_WR VIC_REG(0x00D8) + +#if defined(CONFIG_ARCH_MSM_SCORPION) +#define VIC_FIQ_VEC_RD VIC_REG(0x00DC) +#define VIC_FIQ_VEC_PEND_RD VIC_REG(0x00E0) +#define VIC_FIQ_VEC_WR VIC_REG(0x00E4) +#define VIC_IRQ_IN_SERVICE VIC_REG(0x00E8) +#define VIC_IRQ_IN_STACK VIC_REG(0x00EC) +#define VIC_FIQ_IN_SERVICE VIC_REG(0x00F0) +#define VIC_FIQ_IN_STACK VIC_REG(0x00F4) +#define VIC_TEST_BUS_SEL VIC_REG(0x00F8) +#define VIC_IRQ_CTRL_CONFIG VIC_REG(0x00FC) +#else #define VIC_IRQ_IN_SERVICE VIC_REG(0x00E0) #define VIC_IRQ_IN_STACK VIC_REG(0x00E4) #define VIC_TEST_BUS_SEL VIC_REG(0x00E8) +#endif #define VIC_VECTPRIORITY(n) VIC_REG(0x0200+((n) * 4)) #define VIC_VECTADDR(n) VIC_REG(0x0400+((n) * 4)) +#if defined(CONFIG_ARCH_MSM7X30) +#define VIC_NUM_REGS 4 +#else +#define VIC_NUM_REGS 2 +#endif + +#if VIC_NUM_REGS == 2 +#define DPRINT_REGS(base_reg, format, ...) \ + printk(KERN_INFO format " %x %x\n", ##__VA_ARGS__, \ + readl(base_reg ## 0), readl(base_reg ## 1)) +#define DPRINT_ARRAY(array, format, ...) \ + printk(KERN_INFO format " %x %x\n", ##__VA_ARGS__, \ + array[0], array[1]) +#elif VIC_NUM_REGS == 4 +#define DPRINT_REGS(base_reg, format, ...) \ + printk(KERN_INFO format " %x %x %x %x\n", ##__VA_ARGS__, \ + readl(base_reg ## 0), readl(base_reg ## 1), \ + readl(base_reg ## 2), readl(base_reg ## 3)) +#define DPRINT_ARRAY(array, format, ...) \ + printk(KERN_INFO format " %x %x %x %x\n", ##__VA_ARGS__, \ + array[0], array[1], \ + array[2], array[3]) +#else +#error "VIC_NUM_REGS set to illegal value" +#endif + +static uint32_t msm_irq_smsm_wake_enable[2]; +static struct { + uint32_t int_en[2]; + uint32_t int_type; + uint32_t int_polarity; + uint32_t int_select; +} msm_irq_shadow_reg[VIC_NUM_REGS]; +static uint32_t msm_irq_idle_disable[VIC_NUM_REGS]; + +#define SMSM_FAKE_IRQ (0xff) +static uint8_t msm_irq_to_smsm[NR_IRQS] = { + [INT_MDDI_EXT] = 1, + [INT_MDDI_PRI] = 2, + [INT_MDDI_CLIENT] = 3, + [INT_USB_OTG] = 4, + + [INT_PWB_I2C] = 5, + [INT_SDC1_0] = 6, + [INT_SDC1_1] = 7, + [INT_SDC2_0] = 8, + + [INT_SDC2_1] = 9, + [INT_ADSP_A9_A11] = 10, + [INT_UART1] = 11, + [INT_UART2] = 12, + + [INT_UART3] = 13, + [INT_UART1_RX] = 14, + [INT_UART2_RX] = 15, + [INT_UART3_RX] = 16, + + [INT_UART1DM_IRQ] = 17, + [INT_UART1DM_RX] = 18, + [INT_KEYSENSE] = 19, +#if !defined(CONFIG_ARCH_MSM7X30) + [INT_AD_HSSD] = 20, +#endif + + [INT_NAND_WR_ER_DONE] = 21, + [INT_NAND_OP_DONE] = 22, + [INT_TCHSCRN1] = 23, + [INT_TCHSCRN2] = 24, + + [INT_TCHSCRN_SSBI] = 25, + [INT_USB_HS] = 26, + [INT_UART2DM_RX] = 27, + [INT_UART2DM_IRQ] = 28, + + [INT_SDC4_1] = 29, + [INT_SDC4_0] = 30, + [INT_SDC3_1] = 31, + [INT_SDC3_0] = 32, + + /* fake wakeup interrupts */ + [INT_GPIO_GROUP1] = SMSM_FAKE_IRQ, + [INT_GPIO_GROUP2] = SMSM_FAKE_IRQ, + [INT_A9_M2A_0] = SMSM_FAKE_IRQ, + [INT_A9_M2A_1] = SMSM_FAKE_IRQ, + [INT_A9_M2A_5] = SMSM_FAKE_IRQ, + [INT_GP_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_DEBUG_TIMER_EXP] = SMSM_FAKE_IRQ, + [INT_ADSP_A11] = SMSM_FAKE_IRQ, +#ifdef CONFIG_ARCH_QSD8X50 + [INT_SIRC_0] = SMSM_FAKE_IRQ, + [INT_SIRC_1] = SMSM_FAKE_IRQ, +#endif +}; + +static inline void msm_irq_write_all_regs(void __iomem *base, unsigned int val) +{ + int i; + + for (i = 0; i < VIC_NUM_REGS; i++) + writel(val, base + (i * 4)); +} + static void msm_irq_ack(unsigned int irq) { - void __iomem *reg = VIC_INT_CLEAR0 + ((irq & 32) ? 4 : 0); + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_CLEAR0, irq); irq = 1 << (irq & 31); writel(irq, reg); } static void msm_irq_mask(unsigned int irq) { - void __iomem *reg = VIC_INT_ENCLEAR0 + ((irq & 32) ? 4 : 0); - writel(1 << (irq & 31), reg); + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENCLEAR0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + msm_irq_shadow_reg[index].int_en[0] &= ~mask; + writel(mask, reg); + if (smsm_irq == 0) + msm_irq_idle_disable[index] &= ~mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] &= ~mask; + } } static void msm_irq_unmask(unsigned int irq) { - void __iomem *reg = VIC_INT_ENSET0 + ((irq & 32) ? 4 : 0); - writel(1 << (irq & 31), reg); + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_ENSET0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + msm_irq_shadow_reg[index].int_en[0] |= mask; + writel(mask, reg); + + if (smsm_irq == 0) + msm_irq_idle_disable[index] |= mask; + else { + mask = 1UL << (smsm_irq - 1); + msm_irq_smsm_wake_enable[0] |= mask; + } } static int msm_irq_set_wake(unsigned int irq, unsigned int on) { - return -EINVAL; + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + int smsm_irq = msm_irq_to_smsm[irq]; + + if (smsm_irq == 0) { + printk(KERN_ERR "msm_irq_set_wake: bad wakeup irq %d\n", irq); + return -EINVAL; + } + if (on) + msm_irq_shadow_reg[index].int_en[1] |= mask; + else + msm_irq_shadow_reg[index].int_en[1] &= ~mask; + + if (smsm_irq == SMSM_FAKE_IRQ) + return 0; + + mask = 1UL << (smsm_irq - 1); + if (on) + msm_irq_smsm_wake_enable[1] |= mask; + else + msm_irq_smsm_wake_enable[1] &= ~mask; + return 0; } static int msm_irq_set_type(unsigned int irq, unsigned int flow_type) { - void __iomem *treg = VIC_INT_TYPE0 + ((irq & 32) ? 4 : 0); - void __iomem *preg = VIC_INT_POLARITY0 + ((irq & 32) ? 4 : 0); + void __iomem *treg = VIC_INT_TO_REG_ADDR(VIC_INT_TYPE0, irq); + void __iomem *preg = VIC_INT_TO_REG_ADDR(VIC_INT_POLARITY0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); int b = 1 << (irq & 31); + uint32_t polarity; + uint32_t type; + polarity = msm_irq_shadow_reg[index].int_polarity; if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW)) - writel(readl(preg) | b, preg); + polarity |= b; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) - writel(readl(preg) & (~b), preg); + polarity &= ~b; + writel(polarity, preg); + msm_irq_shadow_reg[index].int_polarity = polarity; + type = msm_irq_shadow_reg[index].int_type; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - writel(readl(treg) | b, treg); - set_irq_handler(irq, handle_edge_irq); + type |= b; + irq_desc[irq].handle_irq = handle_edge_irq; } if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) { - writel(readl(treg) & (~b), treg); - set_irq_handler(irq, handle_level_irq); + type &= ~b; + irq_desc[irq].handle_irq = handle_level_irq; + } + writel(type, treg); + msm_irq_shadow_reg[index].int_type = type; + return 0; +} + +unsigned int msm_irq_pending(void) +{ + unsigned int i, pending = 0; + + for (i = 0; (i < VIC_NUM_REGS) && !pending; i++) + pending |= readl(VIC_IRQ_STATUS0 + (i * 4)); + + return pending; +} + +int msm_irq_idle_sleep_allowed(void) +{ + uint32_t i, disable = 0; + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_REQUEST) + DPRINT_ARRAY(msm_irq_idle_disable, + "msm_irq_idle_sleep_allowed: disable"); + + for (i = 0; i < VIC_NUM_REGS; i++) + disable |= msm_irq_idle_disable[i]; + + return !disable; +} + +/* + * Prepare interrupt subsystem for entering sleep -- phase 1. + * If modem_wake is true, return currently enabled interrupts in *irq_mask. + */ +void msm_irq_enter_sleep1(bool modem_wake, int from_idle, uint32_t *irq_mask) +{ + if (modem_wake) { + *irq_mask = msm_irq_smsm_wake_enable[!from_idle]; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + printk(KERN_INFO + "%s irq_mask %x\n", __func__, *irq_mask); + } +} + +/* + * Prepare interrupt subsystem for entering sleep -- phase 2. + * Detect any pending interrupts and configure interrupt hardware. + * + * Return value: + * -EAGAIN: there are pending interrupt(s); interrupt configuration + * is not changed. + * 0: success + */ +int msm_irq_enter_sleep2(bool modem_wake, int from_idle) +{ + int i, limit = 10; + uint32_t pending[VIC_NUM_REGS]; + + if (from_idle && !modem_wake) + return 0; + + /* edge triggered interrupt may get lost if this mode is used */ + WARN_ON_ONCE(!modem_wake && !from_idle); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s change irq, pend", __func__); + + for (i = 0; i < VIC_NUM_REGS; i++) { + pending[i] = readl(VIC_IRQ_STATUS0 + (i * 4)); + pending[i] &= msm_irq_shadow_reg[i].int_en[!from_idle]; } + + /* Clear INT_A9_M2A_5 since requesting sleep triggers it */ + pending[0] &= ~(1U << INT_A9_M2A_5); + + for (i = 0; i < VIC_NUM_REGS; i++) { + if (pending[i]) { + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_ABORT) + DPRINT_ARRAY(pending, "%s abort", + __func__); + return -EAGAIN; + } + } + + msm_irq_write_all_regs(VIC_INT_EN0, 0); + + while (limit-- > 0) { + int pend_irq; + int irq = readl(VIC_IRQ_VEC_RD); + if (irq == -1) + break; + pend_irq = readl(VIC_IRQ_VEC_PEND_RD); + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + printk(KERN_INFO "%s cleared int %d (%d)\n", + __func__, irq, pend_irq); + } + + if (modem_wake) { + msm_irq_set_type(INT_A9_M2A_6, IRQF_TRIGGER_RISING); + writel(1U << INT_A9_M2A_6, VIC_INT_ENSET0); + } else { + for (i = 0; i < VIC_NUM_REGS; i++) + writel(msm_irq_shadow_reg[i].int_en[1], + VIC_INT_ENSET0 + (i * 4)); + } + return 0; } +/* + * Restore interrupt subsystem from sleep -- phase 1. + * Configure interrupt hardware. + */ +void msm_irq_exit_sleep1(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs) +{ + int i; + + msm_irq_ack(INT_A9_M2A_6); + + for (i = 0; i < VIC_NUM_REGS; i++) { + writel(msm_irq_shadow_reg[i].int_type, + VIC_INT_TYPE0 + i * 4); + writel(msm_irq_shadow_reg[i].int_polarity, + VIC_INT_POLARITY0 + i * 4); + writel(msm_irq_shadow_reg[i].int_en[0], + VIC_INT_EN0 + i * 4); + writel(msm_irq_shadow_reg[i].int_select, + VIC_INT_SELECT0 + i * 4); + } + + writel(3, VIC_INT_MASTEREN); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s %x %x %x now", + __func__, irq_mask, pending_irqs, wakeup_reason); +} + +/* + * Restore interrupt subsystem from sleep -- phase 2. + * Poke the specified pending interrupts into interrupt hardware. + */ +void msm_irq_exit_sleep2(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending) +{ + int i; + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s %x %x %x now", + __func__, irq_mask, pending, wakeup_reason); + + for (i = 0; pending && i < ARRAY_SIZE(msm_irq_to_smsm); i++) { + unsigned reg_offset = VIC_INT_TO_REG_ADDR(0, i); + uint32_t reg_mask = 1UL << (i & 31); + int smsm_irq = msm_irq_to_smsm[i]; + uint32_t smsm_mask; + + if (smsm_irq == 0) + continue; + + smsm_mask = 1U << (smsm_irq - 1); + if (!(pending & smsm_mask)) + continue; + + pending &= ~smsm_mask; + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT) + DPRINT_REGS(VIC_IRQ_STATUS, + "%s: irq %d still pending %x now", + __func__, i, pending); +#if 0 /* debug intetrrupt trigger */ + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + writel(reg_mask, VIC_INT_CLEAR0 + reg_offset); +#endif + if (readl(VIC_IRQ_STATUS0 + reg_offset) & reg_mask) + continue; + + writel(reg_mask, VIC_SOFTINT0 + reg_offset); + + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP_INT_TRIGGER) + DPRINT_REGS(VIC_IRQ_STATUS, + "%s: irq %d need trigger, now", + __func__, i); + } +} + +/* + * Restore interrupt subsystem from sleep -- phase 3. + * Print debug information. + */ +void msm_irq_exit_sleep3(uint32_t irq_mask, uint32_t wakeup_reason, + uint32_t pending_irqs) +{ + if (msm_irq_debug_mask & IRQ_DEBUG_SLEEP) + DPRINT_REGS(VIC_IRQ_STATUS, "%s %x %x %x state %x now", + __func__, irq_mask, pending_irqs, wakeup_reason, + smsm_get_state(SMSM_MODEM_STATE)); +} + static struct irq_chip msm_irq_chip = { .name = "msm", + .disable = msm_irq_mask, .ack = msm_irq_ack, .mask = msm_irq_mask, .unmask = msm_irq_unmask, @@ -124,26 +533,22 @@ void __init msm_init_irq(void) unsigned n; /* select level interrupts */ - writel(0, VIC_INT_TYPE0); - writel(0, VIC_INT_TYPE1); + msm_irq_write_all_regs(VIC_INT_TYPE0, 0); /* select highlevel interrupts */ - writel(0, VIC_INT_POLARITY0); - writel(0, VIC_INT_POLARITY1); + msm_irq_write_all_regs(VIC_INT_POLARITY0, 0); /* select IRQ for all INTs */ - writel(0, VIC_INT_SELECT0); - writel(0, VIC_INT_SELECT1); + msm_irq_write_all_regs(VIC_INT_SELECT0, 0); /* disable all INTs */ - writel(0, VIC_INT_EN0); - writel(0, VIC_INT_EN1); + msm_irq_write_all_regs(VIC_INT_EN0, 0); - /* don't use 1136 vic */ + /* don't use vic */ writel(0, VIC_CONFIG); /* enable interrupt controller */ - writel(1, VIC_INT_MASTEREN); + writel(3, VIC_INT_MASTEREN); for (n = 0; n < NR_MSM_IRQS; n++) { set_irq_chip(n, &msm_irq_chip); @@ -151,3 +556,85 @@ void __init msm_init_irq(void) set_irq_flags(n, IRQF_VALID); } } + +#if defined(CONFIG_MSM_FIQ_SUPPORT) +void msm_trigger_irq(int irq) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_SOFTINT0, irq); + uint32_t mask = 1UL << (irq & 31); + writel(mask, reg); +} + +void msm_fiq_enable(int irq) +{ + unsigned long flags; + local_irq_save(flags); + msm_irq_unmask(irq); + local_irq_restore(flags); +} + +void msm_fiq_disable(int irq) +{ + unsigned long flags; + local_irq_save(flags); + msm_irq_mask(irq); + local_irq_restore(flags); +} + +void msm_fiq_select(int irq) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_SELECT0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select |= mask; + writel(msm_irq_shadow_reg[index].int_select, reg); + local_irq_restore(flags); +} + +void msm_fiq_unselect(int irq) +{ + void __iomem *reg = VIC_INT_TO_REG_ADDR(VIC_INT_SELECT0, irq); + unsigned index = VIC_INT_TO_REG_INDEX(irq); + uint32_t mask = 1UL << (irq & 31); + unsigned long flags; + + local_irq_save(flags); + msm_irq_shadow_reg[index].int_select &= (!mask); + writel(msm_irq_shadow_reg[index].int_select, reg); + local_irq_restore(flags); +} +/* set_fiq_handler originally from arch/arm/kernel/fiq.c */ +static void set_fiq_handler(void *start, unsigned int length) +{ + memcpy((void *)0xffff001c, start, length); + flush_icache_range(0xffff001c, 0xffff001c + length); + if (!vectors_high()) + flush_icache_range(0x1c, 0x1c + length); +} + +extern unsigned char fiq_glue, fiq_glue_end; + +static void (*fiq_func)(void *data, void *regs); +static unsigned long long fiq_stack[256]; + +void fiq_glue_setup(void *func, void *data, void *sp); + +int msm_fiq_set_handler(void (*func)(void *data, void *regs), void *data) +{ + unsigned long flags; + int ret = -ENOMEM; + + local_irq_save(flags); + if (fiq_func == 0) { + fiq_func = func; + fiq_glue_setup(func, data, fiq_stack + 255); + set_fiq_handler(&fiq_glue, (&fiq_glue_end - &fiq_glue)); + ret = 0; + } + local_irq_restore(flags); + return ret; +} +#endif diff --git a/arch/arm/mach-msm/irq.h b/arch/arm/mach-msm/irq.h new file mode 100644 index 000000000000..ad4cb92ca570 --- /dev/null +++ b/arch/arm/mach-msm/irq.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_IRQ_H_ +#define _ARCH_ARM_MACH_MSM_IRQ_H_ + +int msm_irq_idle_sleep_allowed(void); +unsigned int msm_irq_pending(void); +void msm_irq_enter_sleep1(bool arm9_wake, int from_idle, uint32_t *irq_mask); +int msm_irq_enter_sleep2(bool arm9_wake, int from_idle); +void msm_irq_exit_sleep1 + (uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs); +void msm_irq_exit_sleep2 + (uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending); +void msm_irq_exit_sleep3 + (uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs); + +#endif diff --git a/arch/arm/mach-msm/jtag-v7.S b/arch/arm/mach-msm/jtag-v7.S new file mode 100644 index 000000000000..ebac7b254549 --- /dev/null +++ b/arch/arm/mach-msm/jtag-v7.S @@ -0,0 +1,117 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * JTAG support functions for ARMv7-based Qualcomm SoCs. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> + +ENTRY(msm_save_jtag_debug) + /* lock debug and preserve registers through power collapse */ + ldr r3, =dbg_state /* store state at dbg_state */ + + ldr r1, =0xc5ACCE55 /* set DBGOSLAR lock */ + mcr p14,0,r1,c1,c0,4 + isb + + mrc p14,0,r1,c1,c2,4 /* DBGOSSRR state register count */ + + cmp r1, #(0x20-1) /* check for state overflow */ + movge r1, #0 /* if not enough space, don't save */ + + str r1,[r3],#4 /* save count for restore */ + +1: cmp r1,#0 + mrcne p14,0,r2,c1,c2,4 /* DBGOSSRR state value */ + strne r2,[r3],#4 /* push value */ + subne r1,r1,#1 + bne 1b + + /* unlock JTAG. Works better than leaving locked. */ + stmfd sp!, {lr} + bl msm_unlock_jtag_debug + ldmfd sp!, {lr} + bx lr + +ENTRY(msm_unlock_jtag_debug) + mov r0, #0 /* unlock value */ + mcr p14,0,r0,c1,c0,4 /* unlock DBGOSLAR */ + isb + bx lr + +ENTRY(msm_restore_jtag_debug) + /* restore debug registers after power collapse */ + ldr r3, =dbg_state /* load state from dbg_state */ + + ldr r1, =0xc5ACCE55 /* set DBGOSLAR lock */ + mcr p14,0,r1,c1,c0,4 + isb + + mrc p14,0,r1,c1,c2,4 /* DBGOSSRR dummy read (required)*/ + ldr r1,[r3],#4 /* load saved count */ + cmp r1,#0 /* skip if none stored + beq msm_pm_dbg_restore_end + + /* restores debug state except DBGDSCR */ +1: ldr r2,[r3],#4 + cmp r1,#0x10 /* DBGDSCR special case */ + biceq r2,r2,#0xc000 /* DBGDSCR = DBGDSCR & ~0xc000 */ + mcr p14,0,r2,c1,c2,4 /* DBGOSSRR write state value */ + subs r1,r1,#1 + bne 1b + isb + + /* second loop to restore DBGDSCR after other state restored */ + ldr r3, =dbg_state /* load state from dbg_state */ + + ldr r1, =0xc5ACCE55 /* set DBGOSLAR lock */ + mcr p14,0,r1,c1,c0,4 + isb + + mrc p14,0,r1,c1,c5,4 /* clear sticky power down bit */ + isb + + mrc p14,0,r1,c1,c2,4 /* DBGOSSRR dummy read (required)*/ + ldr r1,[r3],#4 /* load saved count */ + +1: ldr r2,[r3],#4 + mcr p14,0,r2,c1,c2,4 /* DBGOSSRR write state value */ + subs r1,r1,#1 + bne 1b +msm_pm_dbg_restore_end: + mcr p14,0,r1,c1,c0,4 /* unlock DBGOSLAR */ + isb + bx lr + + + .data + +dbg_state: + .space 4 * 0x20 + diff --git a/arch/arm/mach-msm/keypad-surf-ffa.c b/arch/arm/mach-msm/keypad-surf-ffa.c new file mode 100644 index 000000000000..1ea72af71a47 --- /dev/null +++ b/arch/arm/mach-msm/keypad-surf-ffa.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/platform_device.h> +#include <linux/gpio_event.h> + +#include <asm/mach-types.h> + +/* don't turn this on without updating the ffa support */ +#define SCAN_FUNCTION_KEYS 0 + +/* FFA: + 36: KEYSENSE_N(0) + 37: KEYSENSE_N(1) + 38: KEYSENSE_N(2) + 39: KEYSENSE_N(3) + 40: KEYSENSE_N(4) + + 31: KYPD_17 + 32: KYPD_15 + 33: KYPD_13 + 34: KYPD_11 + 35: KYPD_9 + 41: KYPD_MEMO +*/ + +static unsigned int keypad_row_gpios[] = { + 31, 32, 33, 34, 35, 41 +#if SCAN_FUNCTION_KEYS + , 42 +#endif +}; + +static unsigned int keypad_col_gpios[] = { 36, 37, 38, 39, 40 }; + +static unsigned int keypad_row_gpios_8k_ffa[] = {31, 32, 33, 34, 35, 36}; +static unsigned int keypad_col_gpios_8k_ffa[] = {38, 39, 40, 41, 42}; + +#define KEYMAP_INDEX(row, col) ((row)*ARRAY_SIZE(keypad_col_gpios) + (col)) +#define FFA_8K_KEYMAP_INDEX(row, col) ((row)* \ + ARRAY_SIZE(keypad_col_gpios_8k_ffa) + (col)) + +static const unsigned short keypad_keymap_surf[ARRAY_SIZE(keypad_col_gpios) * + ARRAY_SIZE(keypad_row_gpios)] = { + [KEYMAP_INDEX(0, 0)] = KEY_5, + [KEYMAP_INDEX(0, 1)] = KEY_9, + [KEYMAP_INDEX(0, 2)] = 229, /* SOFT1 */ + [KEYMAP_INDEX(0, 3)] = KEY_6, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_0, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_1, + [KEYMAP_INDEX(1, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(1, 4)] = KEY_SEND, + + [KEYMAP_INDEX(2, 0)] = KEY_VOLUMEUP, + [KEYMAP_INDEX(2, 1)] = KEY_HOME, /* FA */ + [KEYMAP_INDEX(2, 2)] = KEY_F8, /* QCHT */ + [KEYMAP_INDEX(2, 3)] = KEY_F6, /* R+ */ + [KEYMAP_INDEX(2, 4)] = KEY_F7, /* R- */ + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = KEY_CLEAR, + [KEYMAP_INDEX(3, 2)] = KEY_4, + [KEYMAP_INDEX(3, 3)] = KEY_MUTE, /* SPKR */ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = 230, /* SOFT2 */ + [KEYMAP_INDEX(4, 1)] = 232, /* KEY_CENTER */ + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_BACK, /* FB */ + [KEYMAP_INDEX(4, 4)] = KEY_8, + + [KEYMAP_INDEX(5, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = KEY_MAIL, /* MESG */ + [KEYMAP_INDEX(5, 3)] = KEY_3, + [KEYMAP_INDEX(5, 4)] = KEY_7, + +#if SCAN_FUNCTION_KEYS + [KEYMAP_INDEX(6, 0)] = KEY_F5, + [KEYMAP_INDEX(6, 1)] = KEY_F4, + [KEYMAP_INDEX(6, 2)] = KEY_F3, + [KEYMAP_INDEX(6, 3)] = KEY_F2, + [KEYMAP_INDEX(6, 4)] = KEY_F1 +#endif +}; + +static const unsigned short keypad_keymap_ffa[ARRAY_SIZE(keypad_col_gpios) * + ARRAY_SIZE(keypad_row_gpios)] = { + /*[KEYMAP_INDEX(0, 0)] = ,*/ + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [KEYMAP_INDEX(0, 2)] = KEY_1, + [KEYMAP_INDEX(0, 3)] = KEY_SEND, + [KEYMAP_INDEX(0, 4)] = KEY_LEFT, + + [KEYMAP_INDEX(1, 0)] = KEY_3, + [KEYMAP_INDEX(1, 1)] = KEY_RIGHT, + [KEYMAP_INDEX(1, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [KEYMAP_INDEX(1, 4)] = KEY_6, + + [KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [KEYMAP_INDEX(2, 2)] = KEY_0, + [KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [KEYMAP_INDEX(2, 4)] = KEY_9, + + [KEYMAP_INDEX(3, 0)] = KEY_UP, + [KEYMAP_INDEX(3, 1)] = 232, /* KEY_CENTER */ /* i */ + [KEYMAP_INDEX(3, 2)] = KEY_4, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [KEYMAP_INDEX(3, 4)] = KEY_2, + + [KEYMAP_INDEX(4, 0)] = KEY_VOLUMEDOWN, + [KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [KEYMAP_INDEX(4, 2)] = KEY_DOWN, + [KEYMAP_INDEX(4, 3)] = KEY_8, + [KEYMAP_INDEX(4, 4)] = KEY_5, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +#define QSD8x50_FFA_KEYMAP_SIZE (ARRAY_SIZE(keypad_col_gpios_8k_ffa) * \ + ARRAY_SIZE(keypad_row_gpios_8k_ffa)) + +static const unsigned short keypad_keymap_8k_ffa[QSD8x50_FFA_KEYMAP_SIZE] = { + + [FFA_8K_KEYMAP_INDEX(0, 0)] = KEY_VOLUMEDOWN, + /*[KEYMAP_INDEX(0, 1)] = ,*/ + [FFA_8K_KEYMAP_INDEX(0, 2)] = KEY_DOWN, + [FFA_8K_KEYMAP_INDEX(0, 3)] = KEY_8, + [FFA_8K_KEYMAP_INDEX(0, 4)] = KEY_5, + + [FFA_8K_KEYMAP_INDEX(1, 0)] = KEY_UP, + [FFA_8K_KEYMAP_INDEX(1, 1)] = KEY_CLEAR, + [FFA_8K_KEYMAP_INDEX(1, 2)] = KEY_4, + /*[KEYMAP_INDEX(1, 3)] = ,*/ + [FFA_8K_KEYMAP_INDEX(1, 4)] = KEY_2, + + [FFA_8K_KEYMAP_INDEX(2, 0)] = KEY_HOME, /* A */ + [FFA_8K_KEYMAP_INDEX(2, 1)] = KEY_BACK, /* B */ + [FFA_8K_KEYMAP_INDEX(2, 2)] = KEY_0, + [FFA_8K_KEYMAP_INDEX(2, 3)] = 228, /* KEY_SHARP */ + [FFA_8K_KEYMAP_INDEX(2, 4)] = KEY_9, + + [FFA_8K_KEYMAP_INDEX(3, 0)] = KEY_3, + [FFA_8K_KEYMAP_INDEX(3, 1)] = KEY_RIGHT, + [FFA_8K_KEYMAP_INDEX(3, 2)] = KEY_VOLUMEUP, + /*[KEYMAP_INDEX(3, 3)] = ,*/ + [FFA_8K_KEYMAP_INDEX(3, 4)] = KEY_6, + + [FFA_8K_KEYMAP_INDEX(4, 0)] = 232, /* OK */ + [FFA_8K_KEYMAP_INDEX(4, 1)] = KEY_SOUND, + [FFA_8K_KEYMAP_INDEX(4, 2)] = KEY_1, + [FFA_8K_KEYMAP_INDEX(4, 3)] = KEY_SEND, + [FFA_8K_KEYMAP_INDEX(4, 4)] = KEY_LEFT, + + /*[KEYMAP_INDEX(5, 0)] = ,*/ + [FFA_8K_KEYMAP_INDEX(5, 1)] = 227, /* KEY_STAR */ + [FFA_8K_KEYMAP_INDEX(5, 2)] = 230, /*SOFT2*/ /* 2 */ + [FFA_8K_KEYMAP_INDEX(5, 3)] = KEY_MENU, /* 1 */ + [FFA_8K_KEYMAP_INDEX(5, 4)] = KEY_7, +}; + +static const unsigned short keypad_virtual_keys[] = { + KEY_END, + KEY_POWER +}; + +static int keypad_gpio_event_matrix_func(struct input_dev *input_dev, + struct gpio_event_info *info, + void **data, int func); + +/* SURF keypad platform device information */ +static struct gpio_event_matrix_info surf_keypad_matrix_info = { + .info.func = keypad_gpio_event_matrix_func, + .keymap = keypad_keymap_surf, + .output_gpios = keypad_row_gpios, + .input_gpios = keypad_col_gpios, + .noutputs = ARRAY_SIZE(keypad_row_gpios), + .ninputs = ARRAY_SIZE(keypad_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS +}; + +static struct gpio_event_info *surf_keypad_info[] = { + &surf_keypad_matrix_info.info +}; + +static struct gpio_event_platform_data surf_keypad_data = { + .name = "surf_keypad", + .info = surf_keypad_info, + .info_count = ARRAY_SIZE(surf_keypad_info) +}; + +struct platform_device keypad_device_surf = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &surf_keypad_data, + }, +}; + +/* 8k FFA keypad platform device information */ +static struct gpio_event_matrix_info keypad_matrix_info_8k_ffa = { + .info.func = keypad_gpio_event_matrix_func, + .keymap = keypad_keymap_8k_ffa, + .output_gpios = keypad_row_gpios_8k_ffa, + .input_gpios = keypad_col_gpios_8k_ffa, + .noutputs = ARRAY_SIZE(keypad_row_gpios_8k_ffa), + .ninputs = ARRAY_SIZE(keypad_col_gpios_8k_ffa), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS +}; + +static struct gpio_event_info *keypad_info_8k_ffa[] = { + &keypad_matrix_info_8k_ffa.info +}; + +static struct gpio_event_platform_data keypad_data_8k_ffa = { + .name = "8k_ffa_keypad", + .info = keypad_info_8k_ffa, + .info_count = ARRAY_SIZE(keypad_info_8k_ffa) +}; + +struct platform_device keypad_device_8k_ffa = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &keypad_data_8k_ffa, + }, +}; + +/* 7k FFA keypad platform device information */ +static struct gpio_event_matrix_info keypad_matrix_info_7k_ffa = { + .info.func = keypad_gpio_event_matrix_func, + .keymap = keypad_keymap_ffa, + .output_gpios = keypad_row_gpios, + .input_gpios = keypad_col_gpios, + .noutputs = ARRAY_SIZE(keypad_row_gpios), + .ninputs = ARRAY_SIZE(keypad_col_gpios), + .settle_time.tv.nsec = 0, + .poll_time.tv.nsec = 20 * NSEC_PER_MSEC, + .flags = GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_DRIVE_INACTIVE | + GPIOKPF_PRINT_UNMAPPED_KEYS +}; + +static struct gpio_event_info *keypad_info_7k_ffa[] = { + &keypad_matrix_info_7k_ffa.info +}; + +static struct gpio_event_platform_data keypad_data_7k_ffa = { + .name = "7k_ffa_keypad", + .info = keypad_info_7k_ffa, + .info_count = ARRAY_SIZE(keypad_info_7k_ffa) +}; + +struct platform_device keypad_device_7k_ffa = { + .name = GPIO_EVENT_DEV_NAME, + .id = -1, + .dev = { + .platform_data = &keypad_data_7k_ffa, + }, +}; + +static struct input_dev *keypad_dev; + +static int keypad_gpio_event_matrix_func(struct input_dev *input_dev, + struct gpio_event_info *info, + void **data, int func) +{ + int err; + int i; + + err = gpio_event_matrix_func(input_dev, info, data, func); + + if (func == GPIO_EVENT_FUNC_INIT && !err) { + keypad_dev = input_dev; + for (i = 0; i < ARRAY_SIZE(keypad_virtual_keys); i++) + set_bit(keypad_virtual_keys[i] & KEY_MAX, + input_dev->keybit); + } else if (func == GPIO_EVENT_FUNC_UNINIT) { + keypad_dev = NULL; + } + + return err; +} + +struct input_dev *msm_keypad_get_input_dev(void) +{ + return keypad_dev; +} + diff --git a/arch/arm/mach-msm/keypad-surf-ffa.h b/arch/arm/mach-msm/keypad-surf-ffa.h new file mode 100644 index 000000000000..2a29307fddfb --- /dev/null +++ b/arch/arm/mach-msm/keypad-surf-ffa.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _KEYPAD_SURF_FFA_H +#define _KEYPAD_SURF_FFA_H + +#include <linux/input.h> + +#if defined(CONFIG_SURF_FFA_GPIO_KEYPAD) +struct input_dev *msm_keypad_get_input_dev(void); +#else +static struct input_dev *msm_keypad_get_input_dev(void) +{ + return NULL; +} +#endif + +#endif diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c new file mode 100644 index 000000000000..e87bbddc58b0 --- /dev/null +++ b/arch/arm/mach-msm/memory.c @@ -0,0 +1,86 @@ +/* arch/arm/mach-msm/memory.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/mm.h> +#include <linux/mm_types.h> +#include <linux/bootmem.h> +#include <asm/pgtable.h> +#include <asm/io.h> +#include <asm/mach/map.h> + +int arch_io_remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, + unsigned long pfn, unsigned long size, pgprot_t prot) +{ + unsigned long pfn_addr = pfn << PAGE_SHIFT; + if ((pfn_addr >= 0x88000000) && (pfn_addr < 0xD0000000)) { + prot = pgprot_device(prot); + printk("remapping device %lx\n", prot); + } + return remap_pfn_range(vma, addr, pfn, size, prot); +} + +void *zero_page_strongly_ordered; + +static void map_zero_page_strongly_ordered(void) +{ + if (zero_page_strongly_ordered) + return; + + zero_page_strongly_ordered = + ioremap_strongly_ordered(page_to_pfn(empty_zero_page) + << PAGE_SHIFT, PAGE_SIZE); +} + +void write_to_strongly_ordered_memory(void) +{ + map_zero_page_strongly_ordered(); + *(int *)zero_page_strongly_ordered = 0; +} + +void flush_axi_bus_buffer(void) +{ + __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" \ + : : "r" (0) : "memory"); + write_to_strongly_ordered_memory(); +} + +void *alloc_bootmem_aligned(unsigned long size, unsigned long alignment) +{ + void *unused_addr = NULL; + unsigned long addr, tmp_size, unused_size; + + /* Allocate maximum size needed, see where it ends up. + * Then free it -- in this path there are no other allocators + * so we can depend on getting the same address back + * when we allocate a smaller piece that is aligned + * at the end (if necessary) and the piece we really want, + * then free the unused first piece. + */ + + tmp_size = size + alignment - PAGE_SIZE; + addr = (unsigned long)alloc_bootmem(tmp_size); + free_bootmem(__pa(addr), tmp_size); + + unused_size = alignment - (addr % alignment); + if (unused_size) + unused_addr = alloc_bootmem(unused_size); + + addr = (unsigned long)alloc_bootmem(size); + if (unused_size) + free_bootmem(__pa(unused_addr), unused_size); + + return (void *)addr; +} diff --git a/arch/arm/mach-msm/modem_notifier.c b/arch/arm/mach-msm/modem_notifier.c new file mode 100644 index 000000000000..532757065aa9 --- /dev/null +++ b/arch/arm/mach-msm/modem_notifier.c @@ -0,0 +1,237 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Modem Restart Notifier -- Provides notification + * of modem restart events. + */ + +#include <linux/notifier.h> +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/workqueue.h> + +#include "modem_notifier.h" + +#define DEBUG + +static struct srcu_notifier_head modem_notifier_list; +static struct workqueue_struct *modem_notifier_wq; + +static void notify_work_start_reset(struct work_struct *work) +{ + modem_notify(0, MODEM_NOTIFIER_START_RESET); +} +static DECLARE_WORK(modem_notifier_start_reset_work, ¬ify_work_start_reset); + +void modem_queue_start_reset_notify(void) +{ + int ret; + + ret = queue_work(modem_notifier_wq, &modem_notifier_start_reset_work); + + if (!ret) + printk(KERN_ERR "%s\n", __func__); +} +EXPORT_SYMBOL(modem_queue_start_reset_notify); + +static void notify_work_end_reset(struct work_struct *work) +{ + modem_notify(0, MODEM_NOTIFIER_END_RESET); +} +static DECLARE_WORK(modem_notifier_end_reset_work, ¬ify_work_end_reset); + +void modem_queue_end_reset_notify(void) +{ + int ret; + + ret = queue_work(modem_notifier_wq, &modem_notifier_end_reset_work); + + if (!ret) + printk(KERN_ERR "%s\n", __func__); +} +EXPORT_SYMBOL(modem_queue_end_reset_notify); + +int modem_register_notifier(struct notifier_block *nb) +{ + int ret; + + ret = srcu_notifier_chain_register( + &modem_notifier_list, nb); + + return ret; +} +EXPORT_SYMBOL(modem_register_notifier); + +int modem_unregister_notifier(struct notifier_block *nb) +{ + int ret; + + ret = srcu_notifier_chain_unregister( + &modem_notifier_list, nb); + + return ret; +} +EXPORT_SYMBOL(modem_unregister_notifier); + +void modem_notify(void *data, unsigned int state) +{ + srcu_notifier_call_chain(&modem_notifier_list, state, data); +} +EXPORT_SYMBOL(modem_notify); + +#if defined(CONFIG_DEBUG_FS) +static int debug_reset_start(const char __user *buf, int count) +{ + modem_queue_start_reset_notify(); + return 0; +} + +static int debug_reset_end(const char __user *buf, int count) +{ + modem_queue_end_reset_notify(); + return 0; +} + +static ssize_t debug_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fling)(const char __user *buf, int max) = file->private_data; + fling(buf, count); + return count; +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .write = debug_write, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fling)(const char __user *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fling, &debug_ops); +} + +static void modem_notifier_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("modem_notifier", 0); + if (IS_ERR(dent)) + return; + + debug_create("reset_start", 0444, dent, debug_reset_start); + debug_create("reset_end", 0444, dent, debug_reset_end); +} +#else +static void modem_notifier_debugfs_init(void) {} +#endif + +#if defined(DEBUG) +static int modem_notifier_test_call(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + switch (code) { + case MODEM_NOTIFIER_START_RESET: + printk(KERN_ERR "Notify: start reset\n"); + break; + case MODEM_NOTIFIER_END_RESET: + printk(KERN_ERR "Notify: end reset\n"); + break; + default: + printk(KERN_ERR "Notify: general\n"); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block nb = { + .notifier_call = modem_notifier_test_call, +}; + +static void register_test_notifier(void) +{ + modem_register_notifier(&nb); +} +#endif + +static int __init init_modem_notifier_list(void) +{ + srcu_init_notifier_head(&modem_notifier_list); + modem_notifier_debugfs_init(); +#if defined(DEBUG) + register_test_notifier(); +#endif + + /* Create the workqueue */ + modem_notifier_wq = create_singlethread_workqueue("modem_notifier"); + if (!modem_notifier_wq) { + srcu_cleanup_notifier_head(&modem_notifier_list); + return -ENOMEM; + } + + return 0; +} +module_init(init_modem_notifier_list); diff --git a/arch/arm/mach-msm/modem_notifier.h b/arch/arm/mach-msm/modem_notifier.h new file mode 100644 index 000000000000..627f5a142b73 --- /dev/null +++ b/arch/arm/mach-msm/modem_notifier.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Modem Restart Notifier API + * + */ + +#ifndef _MODEM_NOTIFIER_H +#define _MODEM_NOTIFIER_H + +#include <linux/notifier.h> + +#define MODEM_NOTIFIER_START_RESET 0x1 +#define MODEM_NOTIFIER_END_RESET 0x2 + +extern int modem_register_notifier(struct notifier_block *nb); +extern int modem_unregister_notifier(struct notifier_block *nb); +extern void modem_notify(void *data, unsigned int state); +extern void modem_queue_start_reset_notify(void); +extern void modem_queue_end_reset_notify(void); + + +#endif /* _MODEM_NOTIFIER_H */ diff --git a/arch/arm/mach-msm/mpp.c b/arch/arm/mach-msm/mpp.c new file mode 100644 index 000000000000..eb6f533d61d0 --- /dev/null +++ b/arch/arm/mach-msm/mpp.c @@ -0,0 +1,176 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* Qualcomm PMIC Multi-Purpose Pin Configurations */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/debugfs.h> + +#include <mach/mpp.h> + +#include "proc_comm.h" + +#define MPP(_name, _id, _is_input, _status) \ + { .name = _name, .id = _id, .is_input = _is_input, .status = _status} + +static struct mpp mpps[] = { + MPP("mpp1", 0, 0, 0), + MPP("mpp2", 1, 0, 0), + MPP("mpp3", 2, 0, 0), + MPP("mpp4", 3, 0, 0), + MPP("mpp5", 4, 0, 0), + MPP("mpp6", 5, 0, 0), + MPP("mpp7", 6, 0, 0), + MPP("mpp8", 7, 0, 0), + MPP("mpp9", 8, 0, 0), + MPP("mpp10", 9, 0, 0), + MPP("mpp11", 10, 0, 0), + MPP("mpp12", 11, 0, 0), + MPP("mpp13", 12, 0, 0), + MPP("mpp14", 13, 0, 0), + MPP("mpp15", 14, 0, 0), + MPP("mpp16", 15, 0, 0), + MPP("mpp17", 16, 0, 0), + MPP("mpp18", 17, 0, 0), + MPP("mpp19", 18, 0, 0), + MPP("mpp20", 19, 0, 0), + MPP("mpp21", 20, 0, 0), + MPP("mpp22", 21, 0, 0), +}; + +struct mpp *mpp_get(struct device *dev, const char *id) +{ + int n; + for (n = 0; n < ARRAY_SIZE(mpps); n++) { + if (!strcmp(mpps[n].name, id)) + return mpps + n; + } + return NULL; +} +EXPORT_SYMBOL(mpp_get); + +int mpp_config_digital_out(struct mpp *mpp, unsigned config) +{ + unsigned id = mpp->id; + int err; + err = msm_proc_comm(PCOM_PM_MPP_CONFIG, &id, &config); + mpp->status = err; + mpp->is_input = 0; + return err; +} +EXPORT_SYMBOL(mpp_config_digital_out); + +int mpp_config_digital_in(struct mpp *mpp, unsigned config) +{ + unsigned id = mpp->id; + int err; + err = msm_proc_comm(PCOM_PM_MPP_CONFIG_DIGITAL_INPUT, &id, &config); + mpp->status = err; + mpp->is_input = 1; + return err; +} +EXPORT_SYMBOL(mpp_config_digital_in); + +#if defined(CONFIG_DEBUG_FS) +static int mpp_debug_set(void *data, u64 val) +{ + int err; + struct mpp *mpp = data; + + err = mpp_config_digital_out(mpp, (unsigned)val); + if (err) { + printk(KERN_ERR + "%s: mpp_config_digital_out \ + [%s(%d) = 0x%x] failed\n", + __func__, mpp->name, mpp->id, (unsigned)val); + } + return 0; +} + +static int mpp_debug_get(void *data, u64 *val) +{ + struct mpp *mpp = data; + int status = mpp->status; + if (!status) + *val = 0; + else + *val = 1; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(mpp_fops, mpp_debug_get, mpp_debug_set, "%llu\n"); + +static int __init mpp_debug_init(void) +{ + struct dentry *dent; + int n; + + dent = debugfs_create_dir("mpp", 0); + if (IS_ERR(dent)) + return 0; + + for (n = 0; n < ARRAY_SIZE(mpps); n++) + debugfs_create_file(mpps[n].name, 0644, dent, mpps + n, + &mpp_fops); + + return 0; +} + +device_initcall(mpp_debug_init); +#endif diff --git a/arch/arm/mach-msm/msm-keypad-devices.h b/arch/arm/mach-msm/msm-keypad-devices.h new file mode 100644 index 000000000000..469564a754ad --- /dev/null +++ b/arch/arm/mach-msm/msm-keypad-devices.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _MSM_KEYPAD_DEVICES_H +#define _MSM_KEYPAD_DEVICES_H + +extern struct platform_device keypad_device_7k_ffa; +extern struct platform_device keypad_device_8k_ffa; +extern struct platform_device keypad_device_surf; + +#endif diff --git a/arch/arm/mach-msm/msm_vibrator.c b/arch/arm/mach-msm/msm_vibrator.c new file mode 100644 index 000000000000..f4da4363aa98 --- /dev/null +++ b/arch/arm/mach-msm/msm_vibrator.c @@ -0,0 +1,137 @@ +/* include/asm/mach-msm/htc_pwrsink.h + * + * Copyright (C) 2008 HTC Corporation. + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/hrtimer.h> +#include <../../../drivers/staging/android/timed_output.h> +#include <linux/sched.h> + +#include <mach/msm_rpcrouter.h> + +#define PM_LIBPROG 0x30000061 +#if (CONFIG_MSM_AMSS_VERSION == 6220) || (CONFIG_MSM_AMSS_VERSION == 6225) +#define PM_LIBVERS 0xfb837d0b +#else +#define PM_LIBVERS 0x10001 +#endif + +#define HTC_PROCEDURE_SET_VIB_ON_OFF 21 +#define PMIC_VIBRATOR_LEVEL (3000) + +static struct work_struct work_vibrator_on; +static struct work_struct work_vibrator_off; +static struct hrtimer vibe_timer; + +static void set_pmic_vibrator(int on) +{ + static struct msm_rpc_endpoint *vib_endpoint; + struct set_vib_on_off_req { + struct rpc_request_hdr hdr; + uint32_t data; + } req; + + if (!vib_endpoint) { + vib_endpoint = msm_rpc_connect(PM_LIBPROG, PM_LIBVERS, 0); + if (IS_ERR(vib_endpoint)) { + printk(KERN_ERR "init vib rpc failed!\n"); + vib_endpoint = 0; + return; + } + } + + + if (on) + req.data = cpu_to_be32(PMIC_VIBRATOR_LEVEL); + else + req.data = cpu_to_be32(0); + + msm_rpc_call(vib_endpoint, HTC_PROCEDURE_SET_VIB_ON_OFF, &req, + sizeof(req), 5 * HZ); +} + +static void pmic_vibrator_on(struct work_struct *work) +{ + set_pmic_vibrator(1); +} + +static void pmic_vibrator_off(struct work_struct *work) +{ + set_pmic_vibrator(0); +} + +static void timed_vibrator_on(struct timed_output_dev *sdev) +{ + schedule_work(&work_vibrator_on); +} + +static void timed_vibrator_off(struct timed_output_dev *sdev) +{ + schedule_work(&work_vibrator_off); +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + hrtimer_cancel(&vibe_timer); + + if (value == 0) + timed_vibrator_off(dev); + else { + value = (value > 15000 ? 15000 : value); + + timed_vibrator_on(dev); + + hrtimer_start(&vibe_timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } +} + +static int vibrator_get_time(struct timed_output_dev *dev) +{ + if (hrtimer_active(&vibe_timer)) { + ktime_t r = hrtimer_get_remaining(&vibe_timer); + return r.tv.sec * 1000 + r.tv.nsec / 1000000; + } else + return 0; +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + timed_vibrator_off(NULL); + return HRTIMER_NORESTART; +} + +static struct timed_output_dev pmic_vibrator = { + .name = "vibrator", + .get_time = vibrator_get_time, + .enable = vibrator_enable, +}; + +void __init msm_init_pmic_vibrator(void) +{ + INIT_WORK(&work_vibrator_on, pmic_vibrator_on); + INIT_WORK(&work_vibrator_off, pmic_vibrator_off); + + hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vibe_timer.function = vibrator_timer_func; + + timed_output_dev_register(&pmic_vibrator); +} + +MODULE_DESCRIPTION("timed output pmic vibrator device"); +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-msm/nand_partitions.c b/arch/arm/mach-msm/nand_partitions.c new file mode 100644 index 000000000000..fc874470339b --- /dev/null +++ b/arch/arm/mach-msm/nand_partitions.c @@ -0,0 +1,187 @@ +/* arch/arm/mach-msm/nand_partitions.c + * + * Code to extract partition information from ATAG set up by the + * bootloader. + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/mach/flash.h> +#include <linux/io.h> + +#include <asm/setup.h> + +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <mach/msm_iomap.h> + +#include <mach/board.h> +#include "smd_private.h" + +/* configuration tags specific to msm */ + +#define ATAG_MSM_PARTITION 0x4d534D70 /* MSMp */ + +struct msm_ptbl_entry { + char name[16]; + __u32 offset; + __u32 size; + __u32 flags; +}; + +#define MSM_MAX_PARTITIONS 8 + +static struct mtd_partition msm_nand_partitions[MSM_MAX_PARTITIONS]; +static char msm_nand_names[MSM_MAX_PARTITIONS * 16]; + +extern struct flash_platform_data msm_nand_data; + +static int __init parse_tag_msm_partition(const struct tag *tag) +{ + struct mtd_partition *ptn = msm_nand_partitions; + char *name = msm_nand_names; + struct msm_ptbl_entry *entry = (void *) &tag->u; + unsigned count, n; + + count = (tag->hdr.size - 2) / + (sizeof(struct msm_ptbl_entry) / sizeof(__u32)); + + if (count > MSM_MAX_PARTITIONS) + count = MSM_MAX_PARTITIONS; + + for (n = 0; n < count; n++) { + memcpy(name, entry->name, 15); + name[15] = 0; + + ptn->name = name; + ptn->offset = entry->offset * 64 * 2048; + ptn->size = entry->size * 64 * 2048; + + printk(KERN_INFO "Partition (from atag) %s " + "-- Offset:%llx Size:%llx\n", + ptn->name, ptn->offset, ptn->size); + + name += 16; + entry++; + ptn++; + } + + msm_nand_data.nr_parts = count; + msm_nand_data.parts = msm_nand_partitions; + + return 0; +} + +__tagtable(ATAG_MSM_PARTITION, parse_tag_msm_partition); + +#define FLASH_PART_MAGIC1 0x55EE73AA +#define FLASH_PART_MAGIC2 0xE35EBDDB +#define FLASH_PARTITION_VERSION 0x3 + +#define LINUX_FS_PARTITION_NAME "0:EFS2APPS" + +struct flash_partition_entry { + char name[16]; + u32 offset; /* Offset in blocks from beginning of device */ + u32 length; /* Length of the partition in blocks */ + u8 attrib1; + u8 attrib2; + u8 attrib3; + u8 which_flash; /* Numeric ID (first = 0, second = 1) */ +}; +struct flash_partition_table { + u32 magic1; + u32 magic2; + u32 version; + u32 numparts; + struct flash_partition_entry part_entry[16]; +}; + +static int get_nand_partitions(void) +{ + struct flash_partition_table *partition_table; + struct flash_partition_entry *part_entry; + struct mtd_partition *ptn = msm_nand_partitions; + char *name = msm_nand_names; + int part; + + if (msm_nand_data.nr_parts) + return 0; + + partition_table = (struct flash_partition_table *) + smem_alloc(SMEM_AARM_PARTITION_TABLE, + sizeof(struct flash_partition_table)); + + if (!partition_table) { + printk(KERN_WARNING "%s: no flash partition table in shared " + "memory\n", __func__); + return -ENOENT; + } + + if ((partition_table->magic1 != (u32) FLASH_PART_MAGIC1) || + (partition_table->magic2 != (u32) FLASH_PART_MAGIC2) || + (partition_table->version != (u32) FLASH_PARTITION_VERSION)) { + printk(KERN_WARNING "%s: version mismatch -- magic1=%#x, " + "magic2=%#x, version=%#x\n", __func__, + partition_table->magic1, + partition_table->magic2, + partition_table->version); + return -EFAULT; + } + + msm_nand_data.nr_parts = 0; + + /* Get the LINUX FS partition info */ + for (part = 0; part < partition_table->numparts; part++) { + part_entry = &partition_table->part_entry[part]; + + /* Find a match for the Linux file system partition */ + if (strcmp(part_entry->name, LINUX_FS_PARTITION_NAME) == 0) { + strcpy(name, part_entry->name); + ptn->name = name; + + /*TODO: Get block count and size info */ + ptn->offset = part_entry->offset * 64 * 2048; + + /* For SMEM, -1 indicates remaining space in flash, + * but for MTD it is 0 + */ + if (part_entry->length == (u32)-1) + ptn->size = 0; + else + ptn->size = part_entry->length * 64 * 2048; + + msm_nand_data.nr_parts = 1; + msm_nand_data.parts = msm_nand_partitions; + + printk(KERN_INFO "Partition(from smem) %s " + "-- Offset:%llx Size:%llx\n", + ptn->name, ptn->offset, ptn->size); + + return 0; + } + } + + printk(KERN_WARNING "%s: no partition table found!", __func__); + + return -ENODEV; +} + +device_initcall(get_nand_partitions); diff --git a/arch/arm/mach-msm/nohlt.c b/arch/arm/mach-msm/nohlt.c new file mode 100644 index 000000000000..a680fbb8f82d --- /dev/null +++ b/arch/arm/mach-msm/nohlt.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * MSM architecture driver to control arm halt behavior + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/fs.h> +#include <asm/system.h> + +#ifdef CONFIG_DEBUG_FS +static int set_nohalt(void *data, u64 val) +{ + if (val) + disable_hlt(); + else + enable_hlt(); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(nohalt_ops, NULL, set_nohalt, "%llu\n"); + +static int __init init_hlt_debug(void) +{ + debugfs_create_file("nohlt", 0200, NULL, NULL, &nohalt_ops); + + return 0; +} + +late_initcall(init_hlt_debug); +#endif diff --git a/arch/arm/mach-msm/oem_rapi_client.c b/arch/arm/mach-msm/oem_rapi_client.c new file mode 100644 index 000000000000..e31de45f2920 --- /dev/null +++ b/arch/arm/mach-msm/oem_rapi_client.c @@ -0,0 +1,489 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * OEM RAPI CLIENT Driver source file + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> +#include <mach/msm_rpcrouter.h> +#include <mach/oem_rapi_client.h> + +#define OEM_RAPI_PROG 0x3000006B +#define OEM_RAPI_VERS 0x00010001 + +#define OEM_RAPI_NULL_PROC 0 +#define OEM_RAPI_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define OEM_RAPI_STREAMING_FUNCTION_PROC 2 + +#define OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE 128 + +static struct msm_rpc_client *rpc_client; +static uint32_t open_count; +static DEFINE_MUTEX(oem_rapi_client_lock); + +static int oem_rapi_client_cb(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + struct rpc_request_hdr *req; + void *buf, *cb_func, *reply; + uint32_t cb_id, accept_status, size; + int rc; + + struct oem_rapi_client_streaming_func_cb_arg arg; + struct oem_rapi_client_streaming_func_cb_ret ret; + + arg.input = NULL; + ret.out_len = NULL; + ret.output = NULL; + + req = (struct rpc_request_hdr *)buffer; + buf = (void *)(req + 1); + + /* cb_id */ + cb_id = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + /* enum */ + arg.event = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + /* handle */ + arg.handle = (void *)be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + /* in_len */ + arg.in_len = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + /* input */ + size = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + if (size) { + arg.input = kmalloc(size, GFP_KERNEL); + if (arg.input) + memcpy(arg.input, buf, size); + else { + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto oem_rapi_send_ack; + } + } + buf += size; + if (size & 0x3) + buf += 4 - (size & 0x3); + + /* out_len */ + arg.out_len_valid = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + if (arg.out_len_valid) { + ret.out_len = kmalloc(sizeof(*ret.out_len), GFP_KERNEL); + if (!ret.out_len) { + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto oem_rapi_send_ack; + } + } + + /* out */ + arg.output_valid = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + if (arg.output_valid) { + arg.output_size = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + ret.output = kmalloc(arg.output_size, GFP_KERNEL); + if (!ret.output) { + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + goto oem_rapi_send_ack; + } + } + + cb_func = msm_rpc_get_cb_func(client, cb_id); + if (cb_func) { + rc = ((int (*)(struct oem_rapi_client_streaming_func_cb_arg *, + struct oem_rapi_client_streaming_func_cb_ret *)) + cb_func)(&arg, &ret); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + oem_rapi_send_ack: + reply = msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + accept_status); + + size = 0; + if (accept_status == RPC_ACCEPTSTAT_SUCCESS) { + *(uint32_t *)reply = cpu_to_be32((uint32_t)(ret.out_len != 0)); + reply += sizeof(uint32_t); + size += sizeof(uint32_t); + + if (ret.out_len) { + *(uint32_t *)reply = cpu_to_be32(*ret.out_len); + reply += sizeof(uint32_t); + size += sizeof(uint32_t); + } + + if (ret.output && ret.out_len) { + *(uint32_t *)reply = + cpu_to_be32((uint32_t)(*ret.out_len)); + reply += sizeof(uint32_t); + size += sizeof(uint32_t); + + memcpy(reply, ret.output, *ret.out_len); + reply += *ret.out_len; + size += *ret.out_len; + if (*ret.out_len & 0x3) { + memset(reply, 0, 4 - (*ret.out_len & 0x3)); + reply += 4 - (*ret.out_len & 0x3); + size += 4 - (*ret.out_len & 0x3); + } + } else { + *(uint32_t *)reply = cpu_to_be32(0); + reply += sizeof(uint32_t); + size += sizeof(uint32_t); + } + } + rc = msm_rpc_send_accepted_reply(client, size); + if (rc) + pr_err("%s: sending reply failed: %d\n", __func__, rc); + + kfree(arg.input); + kfree(ret.out_len); + kfree(ret.output); + + return 0; +} + +static int oem_rapi_client_streaming_function_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + int size = 0; + int cb_id; + struct oem_rapi_client_streaming_func_arg *arg = data; + + /* enum */ + *((uint32_t *)buf) = cpu_to_be32(arg->event); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + /* cb_id */ + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + *((uint32_t *)buf) = cpu_to_be32((uint32_t)cb_id); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + /* handle */ + *((uint32_t *)buf) = cpu_to_be32((uint32_t)arg->handle); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + /* in_len */ + *((uint32_t *)buf) = cpu_to_be32(arg->in_len); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + /* input */ + *((uint32_t *)buf) = cpu_to_be32(arg->in_len); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + memcpy(buf, arg->input, arg->in_len); + size += arg->in_len; + buf += arg->in_len; + if (arg->in_len & 0x3) { + memset(buf, 0, 4 - (arg->in_len & 0x3)); + buf += 4 - (arg->in_len & 0x3); + size += 4 - (arg->in_len & 0x3); + } + + /* out_len */ + *((uint32_t *)buf) = cpu_to_be32((uint32_t)(arg->out_len_valid)); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + /* output */ + *((uint32_t *)buf) = cpu_to_be32((uint32_t)(arg->output_valid)); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + if (arg->output_valid) { + *((uint32_t *)buf) = cpu_to_be32(arg->output_size); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + } + + return size; +} + +static int oem_rapi_client_streaming_function_ret(struct msm_rpc_client *client, + void *buf, void *data) +{ + uint32_t data_present, size; + struct oem_rapi_client_streaming_func_ret *ret = data; + + /* out_len */ + data_present = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + if (data_present && ret->out_len) { + *ret->out_len = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + } + + /* output */ + size = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + if (size && ret->output) + memcpy(ret->output, buf, size); + buf += size; + if (size & 0x3) + buf += 4 - (size & 0x3); + + return 0; +} + +int oem_rapi_client_streaming_function( + struct msm_rpc_client *client, + struct oem_rapi_client_streaming_func_arg *arg, + struct oem_rapi_client_streaming_func_ret *ret) +{ + return msm_rpc_client_req(client, + OEM_RAPI_STREAMING_FUNCTION_PROC, + oem_rapi_client_streaming_function_arg, arg, + oem_rapi_client_streaming_function_ret, + ret, -1); +} +EXPORT_SYMBOL(oem_rapi_client_streaming_function); + +int oem_rapi_client_close(void) +{ + mutex_lock(&oem_rapi_client_lock); + if (--open_count == 0) { + msm_rpc_unregister_client(rpc_client); + pr_info("%s: disconnected from remote oem rapi server\n", + __func__); + } + mutex_unlock(&oem_rapi_client_lock); + return 0; +} +EXPORT_SYMBOL(oem_rapi_client_close); + +struct msm_rpc_client *oem_rapi_client_init(void) +{ + mutex_lock(&oem_rapi_client_lock); + if (open_count == 0) { + rpc_client = msm_rpc_register_client("oemrapiclient", + OEM_RAPI_PROG, + OEM_RAPI_VERS, 0, + oem_rapi_client_cb); + if (!IS_ERR(rpc_client)) + open_count++; + } + mutex_unlock(&oem_rapi_client_lock); + return rpc_client; +} +EXPORT_SYMBOL(oem_rapi_client_init); + +#if defined(CONFIG_DEBUG_FS) + +static struct dentry *dent; +static int oem_rapi_client_test_res; + +static int oem_rapi_client_null(struct msm_rpc_client *client, + void *arg, void *ret) +{ + return msm_rpc_client_req(client, OEM_RAPI_NULL_PROC, + NULL, NULL, NULL, NULL, -1); +} + +static int oem_rapi_client_test_streaming_cb_func( + struct oem_rapi_client_streaming_func_cb_arg *arg, + struct oem_rapi_client_streaming_func_cb_ret *ret) +{ + uint32_t size; + pr_info("oem rapi client test cb func\n"); + + size = (arg->in_len < OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE) ? + arg->in_len : OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE; + + if (ret->out_len != 0) + *ret->out_len = size; + + if (ret->output != 0) + memcpy(ret->output, arg->input, size); + + return 0; +} + +static ssize_t debug_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char _buf[16]; + + snprintf(_buf, sizeof(_buf), "%i\n", oem_rapi_client_test_res); + + return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf)); +} + +static ssize_t debug_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + char input[OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE]; + char output[OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE]; + uint32_t out_len; + struct oem_rapi_client_streaming_func_arg arg; + struct oem_rapi_client_streaming_func_ret ret; + + unsigned char cmd[64]; + int len; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "null", 64)) { + oem_rapi_client_test_res = oem_rapi_client_null(rpc_client, + NULL, NULL); + } else if (!strncmp(cmd, "streaming_func", 64)) { + memset(input, 5, 16); + arg.event = 0; + arg.cb_func = oem_rapi_client_test_streaming_cb_func; + arg.handle = (void *)20; + arg.in_len = 16; + arg.input = input; + arg.out_len_valid = 1; + arg.output_valid = 1; + arg.output_size = OEM_RAPI_CLIENT_MAX_OUT_BUFF_SIZE; + + ret.out_len = &out_len; + ret.output = output; + oem_rapi_client_test_res = oem_rapi_client_streaming_function( + rpc_client, &arg, &ret); + } else + oem_rapi_client_test_res = -EINVAL; + + if (oem_rapi_client_test_res) + pr_err("oem rapi client test fail %d\n", + oem_rapi_client_test_res); + else + pr_info("oem rapi client test passed\n"); + + return count; +} + +static int debug_release(struct inode *ip, struct file *fp) +{ + return oem_rapi_client_close(); +} + +static int debug_open(struct inode *ip, struct file *fp) +{ + struct msm_rpc_client *client; + client = oem_rapi_client_init(); + if (IS_ERR(client)) { + pr_err("%s: couldn't open oem rapi client\n", __func__); + return PTR_ERR(client); + } else + pr_info("%s: connected to remote oem rapi server\n", __func__); + + return 0; +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = debug_open, + .release = debug_release, + .read = debug_read, + .write = debug_write, +}; + +static void __exit oem_rapi_client_mod_exit(void) +{ + debugfs_remove(dent); +} + +static int __init oem_rapi_client_mod_init(void) +{ + dent = debugfs_create_file("oem_rapi", 0444, 0, NULL, &debug_ops); + open_count = 0; + oem_rapi_client_test_res = -1; + return 0; +} + +module_init(oem_rapi_client_mod_init); +module_exit(oem_rapi_client_mod_exit); + +#endif + +MODULE_DESCRIPTION("OEM RAPI CLIENT Driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/arch/arm/mach-msm/ping_mdm_rpc_client.c b/arch/arm/mach-msm/ping_mdm_rpc_client.c new file mode 100644 index 000000000000..b70527e4875d --- /dev/null +++ b/arch/arm/mach-msm/ping_mdm_rpc_client.c @@ -0,0 +1,772 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * SMD RPC PING MODEM Driver + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/debugfs.h> +#include <linux/uaccess.h> +#include <mach/msm_rpcrouter.h> + +#define PING_TEST_BASE 0x31 + +#define PTIOC_NULL_TEST _IO(PING_TEST_BASE, 1) +#define PTIOC_REG_TEST _IO(PING_TEST_BASE, 2) +#define PTIOC_DATA_REG_TEST _IO(PING_TEST_BASE, 3) +#define PTIOC_DATA_CB_REG_TEST _IO(PING_TEST_BASE, 4) + +#define PING_MDM_PROG 0x30000081 +#define PING_MDM_VERS 0x00010001 +#define PING_MDM_CB_PROG 0x31000081 +#define PING_MDM_CB_VERS 0x00010001 + +#define PING_MDM_NULL_PROC 0 +#define PING_MDM_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define PING_MDM_REGISTER_PROC 2 +#define PING_MDM_UNREGISTER_PROC 3 +#define PING_MDM_REGISTER_DATA_PROC 4 +#define PING_MDM_UNREGISTER_DATA_CB_PROC 5 +#define PING_MDM_REGISTER_DATA_CB_PROC 6 + +#define PING_MDM_DATA_CB_PROC 1 +#define PING_MDM_CB_PROC 2 + +static struct msm_rpc_client *rpc_client; +static uint32_t open_count; +static DEFINE_MUTEX(ping_mdm_lock); + +struct ping_mdm_register_cb_arg { + uint32_t cb_id; + int val; +}; + +struct ping_mdm_register_data_cb_cb_arg { + uint32_t cb_id; + uint32_t *data; + uint32_t size; + uint32_t sum; +}; + +struct ping_mdm_register_data_cb_cb_ret { + uint32_t result; +}; + +static int ping_mdm_register_cb(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + int rc; + uint32_t accept_status; + struct rpc_request_hdr *req; + struct ping_mdm_register_cb_arg arg, *buf_ptr; + void *cb_func; + + req = (struct rpc_request_hdr *)buffer; + buf_ptr = (struct ping_mdm_register_cb_arg *)(req + 1); + + arg.cb_id = be32_to_cpu(buf_ptr->cb_id); + arg.val = be32_to_cpu(buf_ptr->val); + + cb_func = msm_rpc_get_cb_func(client, arg.cb_id); + if (cb_func) { + rc = ((int (*)(struct ping_mdm_register_cb_arg *, void *)) + cb_func)(&arg, NULL); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + accept_status); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int ping_mdm_data_cb(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + struct rpc_request_hdr *req; + int rc, i; + void *buf, *cb_func, *reply; + uint32_t size, accept_status; + struct ping_mdm_register_data_cb_cb_arg arg; + struct ping_mdm_register_data_cb_cb_ret ret; + + req = (struct rpc_request_hdr *)buffer; + buf = (void *)(req + 1); + + arg.cb_id = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + size = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + if (size) { + arg.data = kmalloc((size * sizeof(*arg.data)), GFP_KERNEL); + if (arg.data) + for (i = 0; i < size; i++) + arg.data[i] = + be32_to_cpu(*((uint32_t *)buf + i)); + } + buf += sizeof(uint32_t) * size; + + arg.size = be32_to_cpu(*(uint32_t *)buf); + buf += sizeof(uint32_t); + + arg.sum = be32_to_cpu(*(uint32_t *)buf); + + cb_func = msm_rpc_get_cb_func(client, arg.cb_id); + if (cb_func) { + rc = ((int (*) + (struct ping_mdm_register_data_cb_cb_arg *, + struct ping_mdm_register_data_cb_cb_ret *)) + cb_func)(&arg, &ret); + if (rc) + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + else + accept_status = RPC_ACCEPTSTAT_SUCCESS; + } else + accept_status = RPC_ACCEPTSTAT_SYSTEM_ERR; + + reply = msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + accept_status); + + size = 0; + if (accept_status == RPC_ACCEPTSTAT_SUCCESS) { + *(uint32_t *)reply = cpu_to_be32(ret.result); + size = sizeof(uint32_t); + } + rc = msm_rpc_send_accepted_reply(client, size); + if (rc) + pr_err("%s: send accepted reply failed: %d\n", __func__, rc); + + return rc; +} + +static int ping_mdm_cb_func(struct msm_rpc_client *client, + void *buffer, int in_size) +{ + int rc = 0; + struct rpc_request_hdr *req; + + req = (struct rpc_request_hdr *)buffer; + + switch (be32_to_cpu(req->procedure)) { + case PING_MDM_CB_PROC: + rc = ping_mdm_register_cb(client, buffer, in_size); + break; + case PING_MDM_DATA_CB_PROC: + rc = ping_mdm_data_cb(client, buffer, in_size); + break; + default: + pr_err("%s: procedure not supported %d\n", __func__, + be32_to_cpu(req->procedure)); + msm_rpc_start_accepted_reply(client, be32_to_cpu(req->xid), + RPC_ACCEPTSTAT_PROC_UNAVAIL); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) + pr_err("%s: sending reply failed: %d\n", __func__, rc); + break; + } + return rc; +} + +struct ping_mdm_unregister_data_cb_arg { + int (*cb_func)( + struct ping_mdm_register_data_cb_cb_arg *arg, + struct ping_mdm_register_data_cb_cb_ret *ret); +}; + +struct ping_mdm_register_data_cb_arg { + int (*cb_func)( + struct ping_mdm_register_data_cb_cb_arg *arg, + struct ping_mdm_register_data_cb_cb_ret *ret); + uint32_t num; + uint32_t size; + uint32_t interval_ms; + uint32_t num_tasks; +}; + +struct ping_mdm_register_data_cb_ret { + uint32_t result; +}; + +struct ping_mdm_unregister_data_cb_ret { + uint32_t result; +}; + +static int ping_mdm_data_cb_register_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_register_data_cb_arg *arg; + int cb_id, size = 0; + + arg = (struct ping_mdm_register_data_cb_arg *)data; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + *((uint32_t *)buf) = cpu_to_be32((uint32_t)cb_id); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + *((uint32_t *)buf) = cpu_to_be32(arg->num); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + *((uint32_t *)buf) = cpu_to_be32(arg->size); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + *((uint32_t *)buf) = cpu_to_be32(arg->interval_ms); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + *((uint32_t *)buf) = cpu_to_be32(arg->num_tasks); + size += sizeof(uint32_t); + + return size; +} + +static int ping_mdm_data_cb_unregister_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_unregister_data_cb_arg *arg; + int cb_id; + + arg = (struct ping_mdm_unregister_data_cb_arg *)data; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + *((uint32_t *)buf) = cpu_to_be32((uint32_t)cb_id); + + return sizeof(uint32_t); +} + +static int ping_mdm_data_cb_register_ret(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_register_data_cb_ret *data_ptr, *buf_ptr; + + data_ptr = (struct ping_mdm_register_data_cb_ret *)data; + buf_ptr = (struct ping_mdm_register_data_cb_ret *)buf; + + data_ptr->result = be32_to_cpu(buf_ptr->result); + return 0; +} + +static int ping_mdm_register_data_cb( + struct msm_rpc_client *client, + struct ping_mdm_register_data_cb_arg *arg, + struct ping_mdm_register_data_cb_ret *ret) +{ + return msm_rpc_client_req(client, + PING_MDM_REGISTER_DATA_CB_PROC, + ping_mdm_data_cb_register_arg, arg, + ping_mdm_data_cb_register_ret, ret, -1); +} + +static int ping_mdm_unregister_data_cb( + struct msm_rpc_client *client, + struct ping_mdm_unregister_data_cb_arg *arg, + struct ping_mdm_unregister_data_cb_ret *ret) +{ + return msm_rpc_client_req(client, + PING_MDM_UNREGISTER_DATA_CB_PROC, + ping_mdm_data_cb_unregister_arg, arg, + ping_mdm_data_cb_register_ret, ret, -1); +} + +struct ping_mdm_data_arg { + uint32_t *data; + uint32_t size; +}; + +struct ping_mdm_data_ret { + uint32_t result; +}; + +static int ping_mdm_data_register_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + int i; + struct ping_mdm_data_arg *data_ptr; + + data_ptr = (struct ping_mdm_data_arg *)data; + + *((uint32_t *)buf) = cpu_to_be32(data_ptr->size); + buf += sizeof(data_ptr->size); + for (i = 0; i < data_ptr->size; i++) { + *((uint32_t *)buf) = cpu_to_be32(data_ptr->data[i]); + buf += sizeof(*data_ptr->data); + } + + *((uint32_t *)buf) = cpu_to_be32(data_ptr->size); + + return (data_ptr->size * sizeof(uint32_t)) + + (sizeof(data_ptr->size) * 2); +} + +static int ping_mdm_data_register_ret(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_data_ret *data_ptr, *buf_ptr; + + data_ptr = (struct ping_mdm_data_ret *)data; + buf_ptr = (struct ping_mdm_data_ret *)buf; + + data_ptr->result = be32_to_cpu(buf_ptr->result); + return 0; +} + +static int ping_mdm_data_register( + struct msm_rpc_client *client, + struct ping_mdm_data_arg *arg, + struct ping_mdm_data_ret *ret) +{ + return msm_rpc_client_req(client, + PING_MDM_REGISTER_DATA_PROC, + ping_mdm_data_register_arg, arg, + ping_mdm_data_register_ret, ret, -1); +} + +struct ping_mdm_register_arg { + int (*cb_func)(struct ping_mdm_register_cb_arg *, void *); + int num; +}; + +struct ping_mdm_unregister_arg { + int (*cb_func)(struct ping_mdm_register_cb_arg *, void *); +}; + +struct ping_mdm_register_ret { + uint32_t result; +}; + +struct ping_mdm_unregister_ret { + uint32_t result; +}; + +static int ping_mdm_register_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_register_arg *arg; + int cb_id, size = 0; + + arg = (struct ping_mdm_register_arg *)data; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + *((uint32_t *)buf) = cpu_to_be32((uint32_t)cb_id); + size += sizeof(uint32_t); + buf += sizeof(uint32_t); + + *((int32_t *)buf) = cpu_to_be32(arg->num); + size += sizeof(uint32_t); + + return size; +} + +static int ping_mdm_unregister_arg(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_unregister_arg *arg; + int cb_id; + + arg = (struct ping_mdm_unregister_arg *)data; + + cb_id = msm_rpc_add_cb_func(client, (void *)arg->cb_func); + if ((cb_id < 0) && (cb_id != MSM_RPC_CLIENT_NULL_CB_ID)) + return cb_id; + + *((uint32_t *)buf) = cpu_to_be32((uint32_t)cb_id); + + return sizeof(uint32_t); +} + +static int ping_mdm_register_ret(struct msm_rpc_client *client, + void *buf, void *data) +{ + struct ping_mdm_register_ret *data_ptr, *buf_ptr; + + data_ptr = (struct ping_mdm_register_ret *)data; + buf_ptr = (struct ping_mdm_register_ret *)buf; + + data_ptr->result = be32_to_cpu(buf_ptr->result); + + return 0; +} + +static int ping_mdm_register( + struct msm_rpc_client *client, + struct ping_mdm_register_arg *arg, + struct ping_mdm_register_ret *ret) +{ + return msm_rpc_client_req(client, + PING_MDM_REGISTER_PROC, + ping_mdm_register_arg, arg, + ping_mdm_register_ret, ret, -1); +} + +static int ping_mdm_unregister( + struct msm_rpc_client *client, + struct ping_mdm_unregister_arg *arg, + struct ping_mdm_unregister_ret *ret) +{ + return msm_rpc_client_req(client, + PING_MDM_UNREGISTER_PROC, + ping_mdm_unregister_arg, arg, + ping_mdm_register_ret, ret, -1); +} + +static int ping_mdm_null(struct msm_rpc_client *client, + void *arg, void *ret) +{ + return msm_rpc_client_req(client, PING_MDM_NULL_PROC, + NULL, NULL, NULL, NULL, -1); +} + +static int ping_mdm_close(void) +{ + mutex_lock(&ping_mdm_lock); + if (--open_count == 0) { + msm_rpc_unregister_client(rpc_client); + pr_info("%s: disconnected from remote ping server\n", + __func__); + } + mutex_unlock(&ping_mdm_lock); + return 0; +} + +static struct msm_rpc_client *ping_mdm_init(void) +{ + mutex_lock(&ping_mdm_lock); + if (open_count == 0) { + rpc_client = msm_rpc_register_client("pingdef", + PING_MDM_PROG, + PING_MDM_VERS, 1, + ping_mdm_cb_func); + if (!IS_ERR(rpc_client)) + open_count++; + } + mutex_unlock(&ping_mdm_lock); + return rpc_client; +} + +static struct dentry *dent; + +static DEFINE_MUTEX(ping_mdm_cb_lock); +static LIST_HEAD(ping_mdm_cb_list); +static uint32_t test_res; + +static int reg_cb_num, reg_cb_num_req; +static int data_cb_num, data_cb_num_req; +static int reg_done_flag, data_cb_done_flag; +static DECLARE_WAIT_QUEUE_HEAD(reg_test_wait); +static DECLARE_WAIT_QUEUE_HEAD(data_cb_test_wait); + +static int ping_mdm_data_register_test(void) +{ + int i, rc = 0; + uint32_t my_data[64]; + uint32_t my_sum = 0; + struct ping_mdm_data_arg data_arg; + struct ping_mdm_data_ret data_ret; + + for (i = 0; i < 64; i++) { + my_data[i] = (42 + i); + my_sum ^= (42 + i); + } + + data_arg.data = my_data; + data_arg.size = 64; + + rc = ping_mdm_data_register(rpc_client, &data_arg, &data_ret); + if (rc) + return rc; + + if (my_sum != data_ret.result) { + pr_err("%s: sum mismatch %d %d\n", + __func__, my_sum, data_ret.result); + rc = -1; + } + + return rc; +} + +static int ping_mdm_test_register_data_cb( + struct ping_mdm_register_data_cb_cb_arg *arg, + struct ping_mdm_register_data_cb_cb_ret *ret) +{ + uint32_t i, sum = 0; + + pr_info("%s: received cb_id %d, size = %d, sum = %u\n", + __func__, arg->cb_id, arg->size, arg->sum); + + if (arg->data) + for (i = 0; i < arg->size; i++) + sum ^= arg->data[i]; + + if (sum != arg->sum) + pr_err("%s: sum mismatch %d %d\n", __func__, sum, arg->sum); + + data_cb_num++; + if (data_cb_num == data_cb_num_req) { + data_cb_done_flag = 1; + wake_up(&data_cb_test_wait); + } + + ret->result = 1; + return 0; +} + +static int ping_mdm_data_cb_register_test(void) +{ + int rc = 0; + struct ping_mdm_register_data_cb_arg reg_arg; + struct ping_mdm_unregister_data_cb_arg unreg_arg; + struct ping_mdm_register_data_cb_ret reg_ret; + struct ping_mdm_unregister_data_cb_ret unreg_ret; + + data_cb_num = 0; + data_cb_num_req = 10; + data_cb_done_flag = 0; + + reg_arg.cb_func = ping_mdm_test_register_data_cb; + reg_arg.num = 10; + reg_arg.size = 64; + reg_arg.interval_ms = 10; + reg_arg.num_tasks = 1; + + rc = ping_mdm_register_data_cb(rpc_client, ®_arg, ®_ret); + if (rc) + return rc; + + pr_info("%s: data_cb_register result: 0x%x\n", + __func__, reg_ret.result); + wait_event(data_cb_test_wait, data_cb_done_flag); + + unreg_arg.cb_func = reg_arg.cb_func; + rc = ping_mdm_unregister_data_cb(rpc_client, &unreg_arg, &unreg_ret); + if (rc) + return rc; + + pr_info("%s: data_cb_unregister result: 0x%x\n", + __func__, unreg_ret.result); + + return 0; +} + +static int ping_mdm_test_register_cb( + struct ping_mdm_register_cb_arg *arg, void *ret) +{ + pr_info("%s: received cb_id %d, val = %d\n", + __func__, arg->cb_id, arg->val); + + reg_cb_num++; + if (reg_cb_num == reg_cb_num_req) { + reg_done_flag = 1; + wake_up(®_test_wait); + } + return 0; +} + +static int ping_mdm_register_test(void) +{ + int rc = 0; + struct ping_mdm_register_arg reg_arg; + struct ping_mdm_unregister_arg unreg_arg; + struct ping_mdm_register_ret reg_ret; + struct ping_mdm_unregister_ret unreg_ret; + + reg_cb_num = 0; + reg_cb_num_req = 10; + reg_done_flag = 0; + + reg_arg.num = 10; + reg_arg.cb_func = ping_mdm_test_register_cb; + + rc = ping_mdm_register(rpc_client, ®_arg, ®_ret); + if (rc) + return rc; + + pr_info("%s: register result: 0x%x\n", + __func__, reg_ret.result); + + wait_event(reg_test_wait, reg_done_flag); + + unreg_arg.cb_func = ping_mdm_test_register_cb; + rc = ping_mdm_unregister(rpc_client, &unreg_arg, &unreg_ret); + if (rc) + return rc; + + pr_info("%s: unregister result: 0x%x\n", + __func__, unreg_ret.result); + + return 0; +} + +static int ping_mdm_null_test(void) +{ + return ping_mdm_null(rpc_client, NULL, NULL); +} + +static int ping_test_release(struct inode *ip, struct file *fp) +{ + return ping_mdm_close(); +} + +static int ping_test_open(struct inode *ip, struct file *fp) +{ + struct msm_rpc_client *client; + + client = ping_mdm_init(); + if (IS_ERR(client)) { + pr_err("%s: couldn't open ping client\n", __func__); + return PTR_ERR(client); + } else + pr_info("%s: connected to remote ping server\n", + __func__); + + return 0; +} + +static ssize_t ping_test_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char _buf[16]; + + snprintf(_buf, sizeof(_buf), "%i\n", test_res); + + return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf)); +} + +static ssize_t ping_test_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + unsigned char cmd[64]; + int len; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "null_test", 64)) + test_res = ping_mdm_null_test(); + else if (!strncmp(cmd, "reg_test", 64)) + test_res = ping_mdm_register_test(); + else if (!strncmp(cmd, "data_reg_test", 64)) + test_res = ping_mdm_data_register_test(); + else if (!strncmp(cmd, "data_cb_reg_test", 64)) + test_res = ping_mdm_data_cb_register_test(); + else + test_res = -EINVAL; + + return count; +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = ping_test_open, + .read = ping_test_read, + .write = ping_test_write, + .release = ping_test_release, +}; + +static void __exit ping_test_exit(void) +{ + debugfs_remove(dent); +} + +static int __init ping_test_init(void) +{ + dent = debugfs_create_file("ping_mdm", 0444, 0, NULL, &debug_ops); + test_res = 0; + open_count = 0; + return 0; +} + +module_init(ping_test_init); +module_exit(ping_test_exit); + +MODULE_DESCRIPTION("PING TEST Driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/arch/arm/mach-msm/pm.c b/arch/arm/mach-msm/pm.c new file mode 100644 index 000000000000..d5d26cc021ed --- /dev/null +++ b/arch/arm/mach-msm/pm.c @@ -0,0 +1,954 @@ +/* arch/arm/mach-msm/pm.c + * + * MSM Power Management Routines + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/pm_qos_params.h> +#include <linux/proc_fs.h> +#include <linux/suspend.h> +#include <linux/reboot.h> +#include <linux/uaccess.h> +#include <mach/msm_iomap.h> +#include <mach/system.h> +#include <asm/io.h> + +#ifdef CONFIG_HAS_WAKELOCK +#include <linux/wakelock.h> +#endif + +#include "smd_private.h" +#include "acpuclock.h" +#include "clock.h" +#include "proc_comm.h" +#include "idle.h" +#include "irq.h" +#include "gpio.h" +#include "timer.h" +#include "pm.h" + +enum { + MSM_PM_DEBUG_SUSPEND = 1U << 0, + MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1, + MSM_PM_DEBUG_STATE = 1U << 2, + MSM_PM_DEBUG_CLOCK = 1U << 3, + MSM_PM_DEBUG_RESET_VECTOR = 1U << 4, + MSM_PM_DEBUG_SMSM_STATE = 1U << 5, + MSM_PM_DEBUG_IDLE = 1U << 6, +}; +static int msm_pm_debug_mask; +module_param_named(debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE +static int msm_pm_sleep_time_override; +module_param_named(sleep_time_override, + msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP); +#endif + +static int msm_pm_sleep_mode = CONFIG_MSM7X00A_SLEEP_MODE; +module_param_named(sleep_mode, msm_pm_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP); +static int msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE; +module_param_named(idle_sleep_mode, msm_pm_idle_sleep_mode, int, S_IRUGO | S_IWUSR | S_IWGRP); +static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME; +module_param_named(idle_sleep_min_time, msm_pm_idle_sleep_min_time, int, S_IRUGO | S_IWUSR | S_IWGRP); +static int msm_pm_idle_spin_time = CONFIG_MSM7X00A_IDLE_SPIN_TIME; +module_param_named(idle_spin_time, msm_pm_idle_spin_time, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define A11S_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c) +#define A11S_PWRDOWN (MSM_CSR_BASE + 0x440) +#define A11S_STANDBY_CTL (MSM_CSR_BASE + 0x108) +#define A11RAMBACKBIAS (MSM_CSR_BASE + 0x508) + +enum { + SLEEP_LIMIT_NONE = 0, + SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2 +}; + +static atomic_t msm_pm_init_done = ATOMIC_INIT(0); +struct smsm_interrupt_info_ext { + uint32_t aArm_en_mask; + uint32_t aArm_interrupts_pending; + uint32_t aArm_wakeup_reason; + uint32_t aArm_rpc_prog; + uint32_t aArm_rpc_proc; + char aArm_smd_port_name[20]; + uint32_t aArm_gpio_info; +}; +static struct msm_pm_smem_addr_t { + uint32_t *sleep_delay; + uint32_t *limit_sleep; + struct smsm_interrupt_info *int_info; + struct smsm_interrupt_info_ext *int_info_ext; +} msm_pm_sma; + +static uint32_t *msm_pm_reset_vector; +static uint32_t msm_pm_max_sleep_time; +static struct msm_pm_platform_data *msm_pm_modes; + +#ifdef CONFIG_MSM_IDLE_STATS +enum msm_pm_time_stats_id { + MSM_PM_STAT_REQUESTED_IDLE, + MSM_PM_STAT_IDLE_SPIN, + MSM_PM_STAT_IDLE_WFI, + MSM_PM_STAT_IDLE_SLEEP, + MSM_PM_STAT_IDLE_FAILED_SLEEP, + MSM_PM_STAT_IDLE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE, + MSM_PM_STAT_SUSPEND, + MSM_PM_STAT_FAILED_SUSPEND, + MSM_PM_STAT_NOT_IDLE, + MSM_PM_STAT_COUNT +}; + +static struct msm_pm_time_stats { + const char *name; + int64_t first_bucket_time; + int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int count; + int64_t total_time; +} msm_pm_stats[MSM_PM_STAT_COUNT] = { + [MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request", + [MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_SPIN].name = "idle-spin", + [MSM_PM_STAT_IDLE_SPIN].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_WFI].name = "idle-wfi", + [MSM_PM_STAT_IDLE_WFI].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_SLEEP].name = "idle-sleep", + [MSM_PM_STAT_IDLE_SLEEP].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_FAILED_SLEEP].name = "idle-failed-sleep", + [MSM_PM_STAT_IDLE_FAILED_SLEEP].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_POWER_COLLAPSE].name = "idle-power-collapse", + [MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name = + "idle-failed-power-collapse", + [MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_SUSPEND].name = "suspend", + [MSM_PM_STAT_SUSPEND].first_bucket_time = + CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend", + [MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_NOT_IDLE].name = "not-idle", + [MSM_PM_STAT_NOT_IDLE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, +}; + +static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) +{ + int i; + int64_t bt; + msm_pm_stats[id].total_time += t; + msm_pm_stats[id].count++; + bt = t; + do_div(bt, msm_pm_stats[id].first_bucket_time); + if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT * + (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1))) + i = DIV_ROUND_UP(fls((uint32_t)bt), + CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT); + else + i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; + msm_pm_stats[id].bucket[i]++; + if (t < msm_pm_stats[id].min_time[i] || !msm_pm_stats[id].max_time[i]) + msm_pm_stats[id].min_time[i] = t; + if (t > msm_pm_stats[id].max_time[i]) + msm_pm_stats[id].max_time[i] = t; +} + +static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE; +static DECLARE_BITMAP(msm_pm_clocks_no_tcxo_shutdown, NR_CLKS); +#endif + +static int +msm_pm_wait_state(uint32_t wait_state_all_set, uint32_t wait_state_all_clear, + uint32_t wait_state_any_set, uint32_t wait_state_any_clear) +{ + int i; + uint32_t state; + + for (i = 0; i < 2000000; i++) { + state = smsm_get_state(SMSM_MODEM_STATE); + if (((state & wait_state_all_set) == wait_state_all_set) && + ((~state & wait_state_all_clear) == wait_state_all_clear) && + (wait_state_any_set == 0 || (state & wait_state_any_set) || + wait_state_any_clear == 0 || (state & wait_state_any_clear))) + return 0; + } + printk(KERN_ERR "msm_pm_wait_state(%x, %x, %x, %x) failed %x\n", + wait_state_all_set, wait_state_all_clear, + wait_state_any_set, wait_state_any_clear, state); + return -ETIMEDOUT; +} + +/* + * Respond to timing out waiting for Modem + * + * NOTE: The function never returns. + */ +static void msm_pm_timeout(void) +{ +#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP) + printk(KERN_EMERG "%s(): resetting chip\n", __func__); + msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL); +#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM) + printk(KERN_EMERG "%s(): resetting modem\n", __func__); + msm_proc_comm_reset_modem_now(); +#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT) + printk(KERN_EMERG "%s(): halting\n", __func__); +#endif + for (;;) + ; +} + +static int msm_sleep(int sleep_mode, uint32_t sleep_delay, + uint32_t sleep_limit, int from_idle) +{ + uint32_t saved_vector[2]; + int collapsed; + uint32_t enter_state; + uint32_t enter_wait_set = 0; + uint32_t enter_wait_clear = 0; + uint32_t exit_state; + uint32_t exit_wait_clear = 0; + uint32_t exit_wait_set = 0; + unsigned long pm_saved_acpu_clk_rate = 0; + int ret; + int rv = -EINTR; + + if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) + printk(KERN_INFO "msm_sleep(): " + "mode %d delay %u limit %u idle %d\n", + sleep_mode, sleep_delay, sleep_limit, from_idle); + + switch (sleep_mode) { + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + enter_state = SMSM_PWRC; + enter_wait_set = SMSM_RSA; + exit_state = SMSM_WFPI; + exit_wait_clear = SMSM_RSA; + break; + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: + enter_state = SMSM_PWRC_SUSPEND; + enter_wait_set = SMSM_RSA; + exit_state = SMSM_WFPI; + exit_wait_clear = SMSM_RSA; + break; + case MSM_PM_SLEEP_MODE_APPS_SLEEP: + enter_state = SMSM_SLEEP; + exit_state = SMSM_SLEEPEXIT; + exit_wait_set = SMSM_SLEEPEXIT; + break; + default: + enter_state = 0; + exit_state = 0; + } + + if (enter_state && !(smsm_get_state(SMSM_MODEM_STATE) & SMSM_RUN)) { + if ((MSM_PM_DEBUG_POWER_COLLAPSE | MSM_PM_DEBUG_SUSPEND) & + msm_pm_debug_mask) + printk(KERN_INFO "msm_sleep(): modem not ready\n"); + rv = -EBUSY; + goto check_failed; + } + + memset(msm_pm_sma.int_info, 0, sizeof(*msm_pm_sma.int_info)); + msm_irq_enter_sleep1(!!enter_state, from_idle, + &msm_pm_sma.int_info->aArm_en_mask); + msm_gpio_enter_sleep(from_idle); + + if (enter_state) { + if (sleep_delay == 0 && sleep_mode >= MSM_PM_SLEEP_MODE_APPS_SLEEP) + sleep_delay = 192000*5; /* APPS_SLEEP does not allow infinite timeout */ + + *msm_pm_sma.sleep_delay = sleep_delay; + *msm_pm_sma.limit_sleep = sleep_limit; + ret = smsm_change_state(SMSM_APPS_STATE, SMSM_RUN, enter_state); + if (ret) { + printk(KERN_ERR "msm_sleep(): smsm_change_state %x failed\n", enter_state); + enter_state = 0; + exit_state = 0; + } + ret = msm_pm_wait_state(enter_wait_set, enter_wait_clear, 0, 0); + if (ret) { + printk(KERN_EMERG "msm_sleep(): power collapse entry " + "timed out waiting for Modem's response\n"); + msm_pm_timeout(); + } + } + if (msm_irq_enter_sleep2(!!enter_state, from_idle)) + goto enter_failed; + + if (enter_state) { + writel(0x1f, A11S_CLK_SLEEP_EN); + writel(1, A11S_PWRDOWN); + + writel(0, A11S_STANDBY_CTL); + writel(0, A11RAMBACKBIAS); + + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): enter " + "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " + "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), + readl(A11S_PWRDOWN), + smsm_get_state(SMSM_MODEM_STATE)); + } + + if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { + pm_saved_acpu_clk_rate = acpuclk_power_collapse(); + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_INFO "msm_sleep(): %ld enter power collapse" + "\n", pm_saved_acpu_clk_rate); + if (pm_saved_acpu_clk_rate == 0) + goto ramp_down_failed; + } + if (sleep_mode < MSM_PM_SLEEP_MODE_APPS_SLEEP) { +#ifdef CONFIG_MSM_ADM_OFF_AT_POWER_COLLAPSE + /* XXX: Temp workaround that needs to be removed soon. The + * right fix will probably involve the DMA driver taking + * ownership of the ADM clock. */ + /* id is set to denote ADM clock. */ + unsigned id = 1; + msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); +#endif + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) + smsm_print_sleep_info(*msm_pm_sma.sleep_delay, + *msm_pm_sma.limit_sleep, + msm_pm_sma.int_info->aArm_en_mask, + msm_pm_sma.int_info->aArm_wakeup_reason, + msm_pm_sma.int_info->aArm_interrupts_pending); + saved_vector[0] = msm_pm_reset_vector[0]; + saved_vector[1] = msm_pm_reset_vector[1]; + msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */ + msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit); + if (msm_pm_debug_mask & MSM_PM_DEBUG_RESET_VECTOR) + printk(KERN_INFO "msm_sleep(): vector %x %x -> " + "%x %x\n", saved_vector[0], saved_vector[1], + msm_pm_reset_vector[0], msm_pm_reset_vector[1]); + collapsed = msm_pm_collapse(); + msm_pm_reset_vector[0] = saved_vector[0]; + msm_pm_reset_vector[1] = saved_vector[1]; + if (collapsed) { + cpu_init(); + local_fiq_enable(); + rv = 0; + } + if (msm_pm_debug_mask & MSM_PM_DEBUG_POWER_COLLAPSE) + printk(KERN_INFO "msm_pm_collapse(): returned %d\n", + collapsed); + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) + smsm_print_sleep_info(*msm_pm_sma.sleep_delay, + *msm_pm_sma.limit_sleep, + msm_pm_sma.int_info->aArm_en_mask, + msm_pm_sma.int_info->aArm_wakeup_reason, + msm_pm_sma.int_info->aArm_interrupts_pending); +#ifdef CONFIG_MSM_ADM_OFF_AT_POWER_COLLAPSE + /* id is set to denote ADM clock. */ + id = 1; + if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL) < 0 || + id < 0) + printk(KERN_ERR + "msm_sleep(): failed to turn on ADM clock\n"); +#endif + } else { + msm_arch_idle(); + rv = 0; + } + + if (sleep_mode <= MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT) { + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_INFO "msm_sleep(): exit power collapse %ld" + "\n", pm_saved_acpu_clk_rate); + if (acpuclk_set_rate(pm_saved_acpu_clk_rate, SETRATE_PC) < 0) + printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " + "failed\n", pm_saved_acpu_clk_rate); + } + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): exit A11S_CLK_SLEEP_EN %x, " + "A11S_PWRDOWN %x, smsm_get_state %x\n", + readl(A11S_CLK_SLEEP_EN), readl(A11S_PWRDOWN), + smsm_get_state(SMSM_MODEM_STATE)); +ramp_down_failed: + msm_irq_exit_sleep1(msm_pm_sma.int_info->aArm_en_mask, + msm_pm_sma.int_info->aArm_wakeup_reason, + msm_pm_sma.int_info->aArm_interrupts_pending); +enter_failed: + if (enter_state) { + writel(0x00, A11S_CLK_SLEEP_EN); + writel(0, A11S_PWRDOWN); + smsm_change_state(SMSM_APPS_STATE, enter_state, exit_state); + if (msm_pm_wait_state(exit_wait_set, exit_wait_clear, 0, 0)) { + printk(KERN_EMERG "msm_sleep(): power collapse exit " + "timed out waiting for Modem's response\n"); + msm_pm_timeout(); + } + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): sleep exit " + "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " + "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), + readl(A11S_PWRDOWN), + smsm_get_state(SMSM_MODEM_STATE)); + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) + smsm_print_sleep_info(*msm_pm_sma.sleep_delay, + *msm_pm_sma.limit_sleep, + msm_pm_sma.int_info->aArm_en_mask, + msm_pm_sma.int_info->aArm_wakeup_reason, + msm_pm_sma.int_info->aArm_interrupts_pending); + } + msm_irq_exit_sleep2(msm_pm_sma.int_info->aArm_en_mask, + msm_pm_sma.int_info->aArm_wakeup_reason, + msm_pm_sma.int_info->aArm_interrupts_pending); + if (enter_state) { + smsm_change_state(SMSM_APPS_STATE, exit_state, SMSM_RUN); + if (msm_pm_debug_mask & MSM_PM_DEBUG_STATE) + printk(KERN_INFO "msm_sleep(): sleep exit " + "A11S_CLK_SLEEP_EN %x, A11S_PWRDOWN %x, " + "smsm_get_state %x\n", readl(A11S_CLK_SLEEP_EN), + readl(A11S_PWRDOWN), + smsm_get_state(SMSM_MODEM_STATE)); + } + msm_irq_exit_sleep3(msm_pm_sma.int_info->aArm_en_mask, + msm_pm_sma.int_info->aArm_wakeup_reason, + msm_pm_sma.int_info->aArm_interrupts_pending); + msm_gpio_exit_sleep(); + smd_sleep_exit(); + +check_failed: + return rv; +} + +void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) +{ + int64_t max_sleep_time_bs = max_sleep_time_ns; + + /* Convert from ns -> BS units */ + do_div(max_sleep_time_bs, NSEC_PER_SEC / 32768); + + if (max_sleep_time_bs > 0x6DDD000) + msm_pm_max_sleep_time = (uint32_t) 0x6DDD000; + else + msm_pm_max_sleep_time = (uint32_t) max_sleep_time_bs; + + if (msm_pm_debug_mask & MSM_PM_DEBUG_SUSPEND) + printk(KERN_INFO "%s: Requested %lldns (%lldbs), Giving %ubs\n", + __func__, max_sleep_time_ns, + max_sleep_time_bs, + msm_pm_max_sleep_time); +} +EXPORT_SYMBOL(msm_pm_set_max_sleep_time); + +void arch_idle(void) +{ + int ret; + int spin; + int64_t sleep_time; + int low_power = 0; + struct msm_pm_platform_data *mode; +#ifdef CONFIG_MSM_IDLE_STATS + DECLARE_BITMAP(clk_ids, NR_CLKS); + int64_t t1; + static int64_t t2; + int exit_stat; +#endif + int latency_qos = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY); + uint32_t sleep_limit = SLEEP_LIMIT_NONE; + int allow_sleep = + msm_pm_idle_sleep_mode < MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT && +#ifdef CONFIG_HAS_WAKELOCK + !has_wake_lock(WAKE_LOCK_IDLE) && +#endif + msm_irq_idle_sleep_allowed(); + + if (!atomic_read(&msm_pm_init_done)) + return; + + sleep_time = msm_timer_enter_idle(); + +#ifdef CONFIG_MSM_IDLE_STATS + t1 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2); + msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, sleep_time); +#endif + + mode = &msm_pm_modes[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]; + if (mode->latency >= latency_qos) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; + + mode = &msm_pm_modes[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]; + if (mode->latency >= latency_qos) + allow_sleep = false; + + mode = &msm_pm_modes[ + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]; + if (mode->latency >= latency_qos) { + /* no time even for SWFI */ + while (!msm_irq_pending()) + udelay(1); +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif + goto abort_idle; + } + + if (msm_pm_debug_mask & MSM_PM_DEBUG_IDLE) + printk(KERN_INFO "arch_idle: sleep time %llu, allow_sleep %d\n", + sleep_time, allow_sleep); + spin = msm_pm_idle_spin_time >> 10; + while (spin-- > 0) { + if (msm_irq_pending()) { +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif + goto abort_idle; + } + udelay(1); + } + if (sleep_time < msm_pm_idle_sleep_min_time || !allow_sleep) { + unsigned long saved_rate; + saved_rate = acpuclk_wait_for_irq(); + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_DEBUG "arch_idle: clk %ld -> swfi\n", + saved_rate); + if (saved_rate) { + msm_arch_idle(); +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_WFI; +#endif + } else { + while (!msm_irq_pending()) + udelay(1); +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif + } + if (msm_pm_debug_mask & MSM_PM_DEBUG_CLOCK) + printk(KERN_DEBUG "msm_sleep: clk swfi -> %ld\n", + saved_rate); + if (saved_rate + && acpuclk_set_rate(saved_rate, SETRATE_SWFI) < 0) + printk(KERN_ERR "msm_sleep(): clk_set_rate %ld " + "failed\n", saved_rate); + } else { +#ifdef CONFIG_MSM_IDLE_STATS + ret = msm_clock_require_tcxo(clk_ids, NR_CLKS); +#elif defined(CONFIG_CLOCK_BASED_SLEEP_LIMIT) + ret = msm_clock_require_tcxo(NULL, 0); +#endif + +#ifdef CONFIG_CLOCK_BASED_SLEEP_LIMIT + if (ret) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; +#endif + + low_power = 1; + do_div(sleep_time, NSEC_PER_SEC / 32768); + if (sleep_time > 0x6DDD000) { + printk("sleep_time too big %lld\n", sleep_time); + sleep_time = 0x6DDD000; + } + ret = msm_sleep(msm_pm_idle_sleep_mode, sleep_time, + sleep_limit, 1); +#ifdef CONFIG_MSM_IDLE_STATS + switch (msm_pm_idle_sleep_mode) { + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + if (ret) + exit_stat = + MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE; + else { + exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE; + msm_pm_sleep_limit = sleep_limit; + bitmap_copy(msm_pm_clocks_no_tcxo_shutdown, + clk_ids, NR_CLKS); + } + break; + case MSM_PM_SLEEP_MODE_APPS_SLEEP: + if (ret) + exit_stat = MSM_PM_STAT_IDLE_FAILED_SLEEP; + else + exit_stat = MSM_PM_STAT_IDLE_SLEEP; + break; + default: + exit_stat = MSM_PM_STAT_IDLE_WFI; + } +#endif + } +abort_idle: + msm_timer_exit_idle(low_power); +#ifdef CONFIG_MSM_IDLE_STATS + t2 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(exit_stat, t2 - t1); +#endif +} + +static int msm_pm_enter(suspend_state_t state) +{ + uint32_t sleep_limit; + int ret; +#ifdef CONFIG_MSM_IDLE_STATS + DECLARE_BITMAP(clk_ids, NR_CLKS); + int64_t period = 0; + int64_t time = 0; + + time = msm_timer_get_sclk_time(&period); + ret = msm_clock_require_tcxo(clk_ids, NR_CLKS); +#elif defined(CONFIG_CLOCK_BASED_SLEEP_LIMIT) + ret = msm_clock_require_tcxo(NULL, 0); +#endif /* CONFIG_MSM_IDLE_STATS */ + +#ifdef CONFIG_CLOCK_BASED_SLEEP_LIMIT + sleep_limit = ret ? SLEEP_LIMIT_NO_TCXO_SHUTDOWN : SLEEP_LIMIT_NONE; +#else + sleep_limit = SLEEP_LIMIT_NONE; +#endif + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE + if (msm_pm_sleep_time_override > 0) { + int64_t ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override; + msm_pm_set_max_sleep_time(ns); + msm_pm_sleep_time_override = 0; + } +#endif + + ret = msm_sleep(msm_pm_sleep_mode, + msm_pm_max_sleep_time, sleep_limit, 0); + +#ifdef CONFIG_MSM_IDLE_STATS + if (msm_pm_sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND || + msm_pm_sleep_mode == MSM_PM_SLEEP_MODE_POWER_COLLAPSE) { + enum msm_pm_time_stats_id id; + int64_t end_time; + + if (ret) + id = MSM_PM_STAT_FAILED_SUSPEND; + else { + id = MSM_PM_STAT_SUSPEND; + msm_pm_sleep_limit = sleep_limit; + bitmap_copy(msm_pm_clocks_no_tcxo_shutdown, clk_ids, + NR_CLKS); + } + + if (time != 0) { + end_time = msm_timer_get_sclk_time(NULL); + if (end_time != 0) { + time = end_time - time; + if (time < 0) + time += period; + } else + time = 0; + } + + msm_pm_add_stat(id, time); + } +#endif + + return 0; +} + +static struct platform_suspend_ops msm_pm_ops = { + .enter = msm_pm_enter, + .valid = suspend_valid_only_mem, +}; + +static uint32_t restart_reason = 0x776655AA; + +static void msm_pm_power_off(void) +{ + msm_proc_comm(PCOM_POWER_DOWN, 0, 0); + for (;;) ; +} + +static void msm_pm_restart(char str, const char *cmd) +{ + msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0); + + for (;;) ; +} + +static int msm_reboot_call(struct notifier_block *this, unsigned long code, void *_cmd) +{ + if((code == SYS_RESTART) && _cmd) { + char *cmd = _cmd; + if (!strcmp(cmd, "bootloader")) { + restart_reason = 0x77665500; + } else if (!strcmp(cmd, "recovery")) { + restart_reason = 0x77665502; + } else if (!strcmp(cmd, "eraseflash")) { + restart_reason = 0x776655EF; + } else if (!strncmp(cmd, "oem-", 4)) { + unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff; + restart_reason = 0x6f656d00 | code; + } else { + restart_reason = 0x77665501; + } + } + return NOTIFY_DONE; +} + +static struct notifier_block msm_reboot_notifier = +{ + .notifier_call = msm_reboot_call, +}; + +#ifdef CONFIG_MSM_IDLE_STATS +/* + * Helper function of snprintf where buf is auto-incremented, size is auto- + * decremented, and there is no return value. + * + * NOTE: buf and size must be l-values (e.g. variables) + */ +#define SNPRINTF(buf, size, format, ...) \ + do { \ + if (size > 0) { \ + int ret; \ + ret = snprintf(buf, size, format, ## __VA_ARGS__); \ + if (ret > size) { \ + buf += size; \ + size = 0; \ + } else { \ + buf += ret; \ + size -= ret; \ + } \ + } \ + } while (0) + +/* + * Write out the power management statistics. + */ +static int msm_pm_read_proc( + char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int i; + char *p = page; + char clk_name[16]; + + if (count < 1024) { + *start = (char *) 0; + *eof = 0; + return 0; + } + + if (!off) { + SNPRINTF(p, count, "Clocks against last TCXO shutdown:\n"); + for_each_bit(i, msm_pm_clocks_no_tcxo_shutdown, NR_CLKS) { + clk_name[0] = '\0'; + msm_clock_get_name(i, clk_name, sizeof(clk_name)); + SNPRINTF(p, count, " %s (id=%d)\n", clk_name, i); + } + + SNPRINTF(p, count, "Last power collapse voted "); + if (msm_pm_sleep_limit == SLEEP_LIMIT_NONE) + SNPRINTF(p, count, "for TCXO shutdown\n\n"); + else + SNPRINTF(p, count, "against TCXO shutdown\n\n"); + + *start = (char *) 1; + *eof = 0; + } else if (--off < ARRAY_SIZE(msm_pm_stats)) { + int64_t bucket_time; + int64_t s; + uint32_t ns; + + s = msm_pm_stats[off].total_time; + ns = do_div(s, NSEC_PER_SEC); + SNPRINTF(p, count, + "%s:\n" + " count: %7d\n" + " total_time: %lld.%09u\n", + msm_pm_stats[off].name, + msm_pm_stats[off].count, + s, ns); + + bucket_time = msm_pm_stats[off].first_bucket_time; + for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) { + s = bucket_time; + ns = do_div(s, NSEC_PER_SEC); + SNPRINTF(p, count, + " <%6lld.%09u: %7d (%lld-%lld)\n", + s, ns, msm_pm_stats[off].bucket[i], + msm_pm_stats[off].min_time[i], + msm_pm_stats[off].max_time[i]); + + bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT; + } + + SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n", + s, ns, msm_pm_stats[off].bucket[i], + msm_pm_stats[off].min_time[i], + msm_pm_stats[off].max_time[i]); + + *start = (char *) 1; + *eof = (off + 1 >= ARRAY_SIZE(msm_pm_stats)); + } + + return p - page; +} +#undef SNPRINTF + +#define MSM_PM_STATS_RESET "reset" + +/* + * Reset the power management statistics values. + */ +static int msm_pm_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char buf[sizeof(MSM_PM_STATS_RESET)]; + int ret; + unsigned long flags; + int i; + + if (count < strlen(MSM_PM_STATS_RESET)) { + ret = -EINVAL; + goto write_proc_failed; + } + + if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) { + ret = -EFAULT; + goto write_proc_failed; + } + + if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) { + ret = -EINVAL; + goto write_proc_failed; + } + + local_irq_save(flags); + for (i = 0; i < ARRAY_SIZE(msm_pm_stats); i++) { + memset(msm_pm_stats[i].bucket, + 0, sizeof(msm_pm_stats[i].bucket)); + memset(msm_pm_stats[i].min_time, + 0, sizeof(msm_pm_stats[i].min_time)); + memset(msm_pm_stats[i].max_time, + 0, sizeof(msm_pm_stats[i].max_time)); + msm_pm_stats[i].count = 0; + msm_pm_stats[i].total_time = 0; + } + + msm_pm_sleep_limit = SLEEP_LIMIT_NONE; + bitmap_zero(msm_pm_clocks_no_tcxo_shutdown, NR_CLKS); + local_irq_restore(flags); + + return count; + +write_proc_failed: + return ret; +} +#undef MSM_PM_STATS_RESET +#endif /* CONFIG_MSM_IDLE_STATS */ + +static int __init msm_pm_init(void) +{ +#ifdef CONFIG_MSM_IDLE_STATS + struct proc_dir_entry *d_entry; +#endif + + pm_power_off = msm_pm_power_off; + arm_pm_restart = msm_pm_restart; + msm_pm_max_sleep_time = 0; + + register_reboot_notifier(&msm_reboot_notifier); + + msm_pm_sma.sleep_delay = smem_alloc(SMEM_SMSM_SLEEP_DELAY, + sizeof(*msm_pm_sma.sleep_delay)); + if (msm_pm_sma.sleep_delay == NULL) { + printk(KERN_ERR "msm_pm_init: failed get SLEEP_DELAY\n"); + return -ENODEV; + } + + msm_pm_sma.limit_sleep = smem_alloc(SMEM_SMSM_LIMIT_SLEEP, + sizeof(*msm_pm_sma.limit_sleep)); + if (msm_pm_sma.limit_sleep == NULL) { + printk(KERN_ERR "msm_pm_init: failed get LIMIT_SLEEP\n"); + return -ENODEV; + } + + msm_pm_sma.int_info_ext = smem_alloc(SMEM_SMSM_INT_INFO, + sizeof(*msm_pm_sma.int_info_ext)); + + if (msm_pm_sma.int_info_ext) + msm_pm_sma.int_info = (struct smsm_interrupt_info *) + msm_pm_sma.int_info_ext; + else + msm_pm_sma.int_info = smem_alloc(SMEM_SMSM_INT_INFO, + sizeof(*msm_pm_sma.int_info)); + + if (msm_pm_sma.int_info == NULL) { + printk(KERN_ERR "msm_pm_init: failed get INT_INFO\n"); + return -ENODEV; + } + +#if defined(CONFIG_ARCH_MSM_SCORPION) + /* The bootloader is responsible for initializing many of Scorpion's + * coprocessor registers for things like cache timing. The state of + * these coprocessor registers is lost on reset, so part of the + * bootloader must be re-executed. Do not overwrite the reset vector + * or bootloader area. + */ + msm_pm_reset_vector = PAGE_OFFSET; +#else + msm_pm_reset_vector = ioremap(0, PAGE_SIZE); + if (msm_pm_reset_vector == NULL) { + printk(KERN_ERR "msm_pm_init: failed to map reset vector\n"); + return -ENODEV; + } +#endif /* CONFIG_ARCH_MSM_SCORPION */ + + BUG_ON(msm_pm_modes == NULL); + + atomic_set(&msm_pm_init_done, 1); + suspend_set_ops(&msm_pm_ops); + +#ifdef CONFIG_MSM_IDLE_STATS + d_entry = create_proc_entry("msm_pm_stats", + S_IRUGO | S_IWUSR | S_IWGRP, NULL); + if (d_entry) { + d_entry->read_proc = msm_pm_read_proc; + d_entry->write_proc = msm_pm_write_proc; + d_entry->data = NULL; + } +#endif + + return 0; +} + +void __init msm_pm_set_platform_data(struct msm_pm_platform_data *data) +{ + msm_pm_modes = data; +} + +late_initcall(msm_pm_init); diff --git a/arch/arm/mach-msm/pm.h b/arch/arm/mach-msm/pm.h new file mode 100644 index 000000000000..b6e60e9683ce --- /dev/null +++ b/arch/arm/mach-msm/pm.h @@ -0,0 +1,42 @@ +/* arch/arm/mach-msm/pm.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_PM_H +#define __ARCH_ARM_MACH_MSM_PM_H + +enum { + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE, + MSM_PM_SLEEP_MODE_APPS_SLEEP, + MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT, + MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT, + MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN, + MSM_PM_SLEEP_MODE_NR +}; + +struct msm_pm_platform_data { + u8 supported; + u8 suspend_enabled; /* enabled for suspend */ + u8 idle_enabled; /* enabled for idle low power */ + u32 latency; /* interrupt latency in microseconds when entering + and exiting the low power mode */ + u32 residency; /* time threshold in microseconds beyond which + staying in the low power mode saves power */ +}; + +void msm_pm_set_platform_data(struct msm_pm_platform_data *data); +#endif diff --git a/arch/arm/mach-msm/pm2.c b/arch/arm/mach-msm/pm2.c new file mode 100644 index 000000000000..dd5d690b76a5 --- /dev/null +++ b/arch/arm/mach-msm/pm2.c @@ -0,0 +1,1666 @@ +/* arch/arm/mach-msm/pm2.c + * + * MSM Power Management Routines + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/pm_qos_params.h> +#include <linux/proc_fs.h> +#include <linux/suspend.h> +#include <linux/reboot.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <mach/msm_iomap.h> +#include <mach/system.h> +#ifdef CONFIG_CACHE_L2X0 +#include <asm/hardware/cache-l2x0.h> +#endif +#ifdef CONFIG_VFP +#include <asm/vfp.h> +#endif + +#include "smd_private.h" +#include "acpuclock.h" +#include "clock.h" +#include "proc_comm.h" +#include "idle.h" +#include "irq.h" +#include "gpio.h" +#include "timer.h" +#include "pm.h" + +/****************************************************************************** + * Debug Definitions + *****************************************************************************/ + +enum { + MSM_PM_DEBUG_SUSPEND = 1U << 0, + MSM_PM_DEBUG_POWER_COLLAPSE = 1U << 1, + MSM_PM_DEBUG_STATE = 1U << 2, + MSM_PM_DEBUG_CLOCK = 1U << 3, + MSM_PM_DEBUG_RESET_VECTOR = 1U << 4, + MSM_PM_DEBUG_SMSM_STATE = 1U << 5, + MSM_PM_DEBUG_IDLE = 1U << 6, +}; + +static int msm_pm_debug_mask; +module_param_named( + debug_mask, msm_pm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP +); + +#define MSM_PM_DPRINTK(mask, level, message, ...) \ + do { \ + if ((mask) & msm_pm_debug_mask) \ + printk(level message, ## __VA_ARGS__); \ + } while (0) + +#define MSM_PM_DEBUG_PRINT_STATE(tag) \ + do { \ + MSM_PM_DPRINTK(MSM_PM_DEBUG_STATE, \ + KERN_INFO, "%s: " \ + "APPS_CLK_SLEEP_EN %x, APPS_PWRDOWN %x, " \ + "SMSM_POWER_MASTER_DEM %x, SMSM_MODEM_STATE %x, " \ + "SMSM_APPS_DEM %x\n", \ + tag, \ + readl(APPS_CLK_SLEEP_EN), readl(APPS_PWRDOWN), \ + smsm_get_state(SMSM_POWER_MASTER_DEM), \ + smsm_get_state(SMSM_MODEM_STATE), \ + smsm_get_state(SMSM_APPS_DEM)); \ + } while (0) + +#define MSM_PM_DEBUG_PRINT_SLEEP_INFO() \ + do { \ + if (msm_pm_debug_mask & MSM_PM_DEBUG_SMSM_STATE) \ + smsm_print_sleep_info(msm_pm_smem_data->sleep_time, \ + msm_pm_smem_data->resources_used, \ + msm_pm_smem_data->irq_mask, \ + msm_pm_smem_data->wakeup_reason, \ + msm_pm_smem_data->pending_irqs); \ + } while (0) + + +/****************************************************************************** + * Sleep Modes and Parameters + *****************************************************************************/ + +static int msm_pm_sleep_mode = CONFIG_MSM7X00A_SLEEP_MODE; +module_param_named( + sleep_mode, msm_pm_sleep_mode, + int, S_IRUGO | S_IWUSR | S_IWGRP +); + +static int msm_pm_idle_sleep_mode = CONFIG_MSM7X00A_IDLE_SLEEP_MODE; +module_param_named( + idle_sleep_mode, msm_pm_idle_sleep_mode, + int, S_IRUGO | S_IWUSR | S_IWGRP +); + +static int msm_pm_idle_sleep_min_time = CONFIG_MSM7X00A_IDLE_SLEEP_MIN_TIME; +module_param_named( + idle_sleep_min_time, msm_pm_idle_sleep_min_time, + int, S_IRUGO | S_IWUSR | S_IWGRP +); + +#define MSM_PM_MODE_ATTR_SUSPEND_ENABLED "suspend_enabled" +#define MSM_PM_MODE_ATTR_IDLE_ENABLED "idle_enabled" +#define MSM_PM_MODE_ATTR_LATENCY "latency" +#define MSM_PM_MODE_ATTR_RESIDENCY "residency" +#define MSM_PM_MODE_ATTR_NR (4) + +static char *msm_pm_sleep_mode_labels[MSM_PM_SLEEP_MODE_NR] = { + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND] = " ", + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = "power_collapse", + [MSM_PM_SLEEP_MODE_APPS_SLEEP] = "apps_sleep", + [MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = + "ramp_down_and_wfi", + [MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = "wfi", + [MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = + "power_collapse_no_xo_shutdown", +}; + +static struct msm_pm_platform_data *msm_pm_modes; + +static struct kobject *msm_pm_mode_kobjs[MSM_PM_SLEEP_MODE_NR]; +static struct attribute_group *msm_pm_mode_attr_group[MSM_PM_SLEEP_MODE_NR]; +static struct attribute **msm_pm_mode_attrs[MSM_PM_SLEEP_MODE_NR]; +static struct kobj_attribute *msm_pm_mode_kobj_attrs[MSM_PM_SLEEP_MODE_NR]; + +/* + * Write out the attribute. + */ +static ssize_t msm_pm_mode_attr_show( + struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct kernel_param kp; + + if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i])) + continue; + + if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_SUSPEND_ENABLED)) { + u32 arg = msm_pm_modes[i].suspend_enabled; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_IDLE_ENABLED)) { + u32 arg = msm_pm_modes[i].idle_enabled; + kp.arg = &arg; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_LATENCY)) { + kp.arg = &msm_pm_modes[i].latency; + ret = param_get_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_RESIDENCY)) { + kp.arg = &msm_pm_modes[i].residency; + ret = param_get_ulong(buf, &kp); + } + + break; + } + + if (ret > 0) { + strcat(buf, "\n"); + ret++; + } + + return ret; +} + +/* + * Read in the new attribute value. + */ +static ssize_t msm_pm_mode_attr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) { + struct kernel_param kp; + + if (strcmp(kobj->name, msm_pm_sleep_mode_labels[i])) + continue; + + if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_SUSPEND_ENABLED)) { + kp.arg = &msm_pm_modes[i].suspend_enabled; + ret = param_set_byte(buf, &kp); + } else if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_IDLE_ENABLED)) { + kp.arg = &msm_pm_modes[i].idle_enabled; + ret = param_set_byte(buf, &kp); + } else if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_LATENCY)) { + kp.arg = &msm_pm_modes[i].latency; + ret = param_set_ulong(buf, &kp); + } else if (!strcmp(attr->attr.name, + MSM_PM_MODE_ATTR_RESIDENCY)) { + kp.arg = &msm_pm_modes[i].residency; + ret = param_set_ulong(buf, &kp); + } + + break; + } + + return ret ? ret : count; +} + +/* + * Add sysfs entries for the sleep modes. + */ +static int __init msm_pm_mode_sysfs_add(void) +{ + struct kobject *module_kobj = NULL; + struct kobject *modes_kobj = NULL; + + struct kobject *kobj; + struct attribute_group *attr_group; + struct attribute **attrs; + struct kobj_attribute *kobj_attrs; + + int i, k; + int ret; + + module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); + if (!module_kobj) { + printk(KERN_ERR "%s: cannot find kobject for module %s\n", + __func__, KBUILD_MODNAME); + ret = -ENOENT; + goto mode_sysfs_add_cleanup; + } + + modes_kobj = kobject_create_and_add("modes", module_kobj); + if (!modes_kobj) { + printk(KERN_ERR "%s: cannot create modes kobject\n", __func__); + ret = -ENOMEM; + goto mode_sysfs_add_cleanup; + } + + for (i = 0; i < ARRAY_SIZE(msm_pm_mode_kobjs); i++) { + if (!msm_pm_modes[i].supported) + continue; + + kobj = kobject_create_and_add( + msm_pm_sleep_mode_labels[i], modes_kobj); + attr_group = kzalloc(sizeof(*attr_group), GFP_KERNEL); + attrs = kzalloc(sizeof(*attrs) * (MSM_PM_MODE_ATTR_NR + 1), + GFP_KERNEL); + kobj_attrs = kzalloc(sizeof(*kobj_attrs) * MSM_PM_MODE_ATTR_NR, + GFP_KERNEL); + + if (!kobj || !attr_group || !attrs || !kobj_attrs) { + printk(KERN_ERR + "%s: cannot create kobject or attributes\n", + __func__); + ret = -ENOMEM; + goto mode_sysfs_add_abort; + } + + kobj_attrs[0].attr.name = MSM_PM_MODE_ATTR_SUSPEND_ENABLED; + kobj_attrs[1].attr.name = MSM_PM_MODE_ATTR_IDLE_ENABLED; + kobj_attrs[2].attr.name = MSM_PM_MODE_ATTR_LATENCY; + kobj_attrs[3].attr.name = MSM_PM_MODE_ATTR_RESIDENCY; + + for (k = 0; k < MSM_PM_MODE_ATTR_NR; k++) { + kobj_attrs[k].attr.mode = 0644; + kobj_attrs[k].show = msm_pm_mode_attr_show; + kobj_attrs[k].store = msm_pm_mode_attr_store; + + attrs[k] = &kobj_attrs[k].attr; + } + attrs[MSM_PM_MODE_ATTR_NR] = NULL; + + attr_group->attrs = attrs; + ret = sysfs_create_group(kobj, attr_group); + if (ret) { + printk(KERN_ERR + "%s: cannot create kobject attribute group\n", + __func__); + goto mode_sysfs_add_abort; + } + + msm_pm_mode_kobjs[i] = kobj; + msm_pm_mode_attr_group[i] = attr_group; + msm_pm_mode_attrs[i] = attrs; + msm_pm_mode_kobj_attrs[i] = kobj_attrs; + } + + return 0; + +mode_sysfs_add_abort: + kfree(kobj_attrs); + kfree(attrs); + kfree(attr_group); + kobject_put(kobj); + +mode_sysfs_add_cleanup: + for (i = ARRAY_SIZE(msm_pm_mode_kobjs) - 1; i >= 0; i--) { + if (!msm_pm_mode_kobjs[i]) + continue; + + sysfs_remove_group( + msm_pm_mode_kobjs[i], msm_pm_mode_attr_group[i]); + + kfree(msm_pm_mode_kobj_attrs[i]); + kfree(msm_pm_mode_attrs[i]); + kfree(msm_pm_mode_attr_group[i]); + kobject_put(msm_pm_mode_kobjs[i]); + } + + return ret; +} + +void __init msm_pm_set_platform_data(struct msm_pm_platform_data *data) +{ + msm_pm_modes = data; +} + + +/****************************************************************************** + * Sleep Limitations + *****************************************************************************/ +enum { + SLEEP_LIMIT_NONE = 0, + SLEEP_LIMIT_NO_TCXO_SHUTDOWN = 2 +}; + + +/****************************************************************************** + * Configure Hardware for Power Down/Up + *****************************************************************************/ + +#define APPS_CLK_SLEEP_EN (MSM_CSR_BASE + 0x11c) +#define APPS_PWRDOWN (MSM_CSR_BASE + 0x440) +#define APPS_STANDBY_CTL (MSM_CSR_BASE + 0x108) + +/* + * Configure hardware registers in preparation for Apps power down. + */ +static void msm_pm_config_hw_before_power_down(void) +{ + writel(0x1f, APPS_CLK_SLEEP_EN); + writel(1, APPS_PWRDOWN); + writel(0, APPS_STANDBY_CTL); +} + +/* + * Clear hardware registers after Apps powers up. + */ +static void msm_pm_config_hw_after_power_up(void) +{ + writel(0, APPS_PWRDOWN); + writel(0, APPS_CLK_SLEEP_EN); +} + +/* + * Configure hardware registers in preparation for SWFI. + */ +static void msm_pm_config_hw_before_swfi(void) +{ +#ifdef CONFIG_ARCH_MSM_SCORPION + writel(0x1f, APPS_CLK_SLEEP_EN); +#else + writel(0x0f, APPS_CLK_SLEEP_EN); +#endif +} + +/* + * Respond to timing out waiting for Modem + * + * NOTE: The function never returns. + */ +static void msm_pm_timeout(void) +{ +#if defined(CONFIG_MSM_PM_TIMEOUT_RESET_CHIP) + printk(KERN_EMERG "%s(): resetting chip\n", __func__); + msm_proc_comm(PCOM_RESET_CHIP_IMM, NULL, NULL); +#elif defined(CONFIG_MSM_PM_TIMEOUT_RESET_MODEM) + printk(KERN_EMERG "%s(): resetting modem\n", __func__); + msm_proc_comm_reset_modem_now(); +#elif defined(CONFIG_MSM_PM_TIMEOUT_HALT) + printk(KERN_EMERG "%s(): halting\n", __func__); +#endif + for (;;) + ; +} + + +/****************************************************************************** + * State Polling Definitions + *****************************************************************************/ + +struct msm_pm_polled_group { + uint32_t group_id; + + uint32_t bits_all_set; + uint32_t bits_all_clear; + uint32_t bits_any_set; + uint32_t bits_any_clear; + + uint32_t value_read; +}; + +/* + * Return true if all bits indicated by flag are set in source. + */ +static inline bool msm_pm_all_set(uint32_t source, uint32_t flag) +{ + return (source & flag) == flag; +} + +/* + * Return true if any bit indicated by flag are set in source. + */ +static inline bool msm_pm_any_set(uint32_t source, uint32_t flag) +{ + return !flag || (source & flag); +} + +/* + * Return true if all bits indicated by flag are cleared in source. + */ +static inline bool msm_pm_all_clear(uint32_t source, uint32_t flag) +{ + return (~source & flag) == flag; +} + +/* + * Return true if any bit indicated by flag are cleared in source. + */ +static inline bool msm_pm_any_clear(uint32_t source, uint32_t flag) +{ + return !flag || (~source & flag); +} + +/* + * Poll the shared memory states as indicated by the poll groups. + * + * nr_grps: number of groups in the array + * grps: array of groups + * + * The function returns when conditions specified by any of the poll + * groups become true. The conditions specified by a poll group are + * deemed true when 1) at least one bit from bits_any_set is set OR one + * bit from bits_any_clear is cleared; and 2) all bits in bits_all_set + * are set; and 3) all bits in bits_all_clear are cleared. + * + * Return value: + * >=0: index of the poll group whose conditions have become true + * -ETIMEDOUT: timed out + */ +static int msm_pm_poll_state(int nr_grps, struct msm_pm_polled_group *grps) +{ + int i, k; + + for (i = 0; i < 500000; i++) + for (k = 0; k < nr_grps; k++) { + bool all_set, all_clear; + bool any_set, any_clear; + + grps[k].value_read = smsm_get_state(grps[k].group_id); + + all_set = msm_pm_all_set(grps[k].value_read, + grps[k].bits_all_set); + all_clear = msm_pm_all_clear(grps[k].value_read, + grps[k].bits_all_clear); + any_set = msm_pm_any_set(grps[k].value_read, + grps[k].bits_any_set); + any_clear = msm_pm_any_clear(grps[k].value_read, + grps[k].bits_any_clear); + + if (all_set && all_clear && (any_set || any_clear)) + return k; + } + + printk(KERN_ERR "%s failed:\n", __func__); + for (k = 0; k < nr_grps; k++) + printk(KERN_ERR "(%x, %x, %x, %x) %x\n", + grps[k].bits_all_set, grps[k].bits_all_clear, + grps[k].bits_any_set, grps[k].bits_any_clear, + grps[k].value_read); + + return -ETIMEDOUT; +} + + +/****************************************************************************** + * Suspend Max Sleep Time + *****************************************************************************/ + +#define SCLK_HZ (32768) +#define MSM_PM_SLEEP_TICK_LIMIT (0x6DDD000) + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE +static int msm_pm_sleep_time_override; +module_param_named(sleep_time_override, + msm_pm_sleep_time_override, int, S_IRUGO | S_IWUSR | S_IWGRP); +#endif + +static uint32_t msm_pm_max_sleep_time; + +/* + * Convert time from nanoseconds to slow clock ticks, then cap it to the + * specified limit + */ +static int64_t msm_pm_convert_and_cap_time(int64_t time_ns, int64_t limit) +{ + do_div(time_ns, NSEC_PER_SEC / SCLK_HZ); + return (time_ns > limit) ? limit : time_ns; +} + +/* + * Set the sleep time for suspend. 0 means infinite sleep time. + */ +void msm_pm_set_max_sleep_time(int64_t max_sleep_time_ns) +{ + unsigned long flags; + + local_irq_save(flags); + if (max_sleep_time_ns == 0) { + msm_pm_max_sleep_time = 0; + } else { + msm_pm_max_sleep_time = (uint32_t)msm_pm_convert_and_cap_time( + max_sleep_time_ns, MSM_PM_SLEEP_TICK_LIMIT); + + if (msm_pm_max_sleep_time == 0) + msm_pm_max_sleep_time = 1; + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, + "%s(): Requested %lld ns Giving %u sclk ticks\n", __func__, + max_sleep_time_ns, msm_pm_max_sleep_time); + local_irq_restore(flags); +} +EXPORT_SYMBOL(msm_pm_set_max_sleep_time); + + +/****************************************************************************** + * CONFIG_MSM_IDLE_STATS + *****************************************************************************/ + +#ifdef CONFIG_MSM_IDLE_STATS +enum msm_pm_time_stats_id { + MSM_PM_STAT_REQUESTED_IDLE, + MSM_PM_STAT_IDLE_SPIN, + MSM_PM_STAT_IDLE_WFI, + MSM_PM_STAT_IDLE_SLEEP, + MSM_PM_STAT_IDLE_FAILED_SLEEP, + MSM_PM_STAT_IDLE_POWER_COLLAPSE, + MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE, + MSM_PM_STAT_SUSPEND, + MSM_PM_STAT_FAILED_SUSPEND, + MSM_PM_STAT_NOT_IDLE, + MSM_PM_STAT_COUNT +}; + +static struct msm_pm_time_stats { + const char *name; + int64_t first_bucket_time; + int bucket[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t min_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT]; + int count; + int64_t total_time; +} msm_pm_stats[MSM_PM_STAT_COUNT] = { + [MSM_PM_STAT_REQUESTED_IDLE].name = "idle-request", + [MSM_PM_STAT_REQUESTED_IDLE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_SPIN].name = "idle-spin", + [MSM_PM_STAT_IDLE_SPIN].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_WFI].name = "idle-wfi", + [MSM_PM_STAT_IDLE_WFI].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_SLEEP].name = "idle-sleep", + [MSM_PM_STAT_IDLE_SLEEP].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_FAILED_SLEEP].name = "idle-failed-sleep", + [MSM_PM_STAT_IDLE_FAILED_SLEEP].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_POWER_COLLAPSE].name = "idle-power-collapse", + [MSM_PM_STAT_IDLE_POWER_COLLAPSE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].name = + "idle-failed-power-collapse", + [MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_SUSPEND].name = "suspend", + [MSM_PM_STAT_SUSPEND].first_bucket_time = + CONFIG_MSM_SUSPEND_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_FAILED_SUSPEND].name = "failed-suspend", + [MSM_PM_STAT_FAILED_SUSPEND].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, + + [MSM_PM_STAT_NOT_IDLE].name = "not-idle", + [MSM_PM_STAT_NOT_IDLE].first_bucket_time = + CONFIG_MSM_IDLE_STATS_FIRST_BUCKET, +}; + +static uint32_t msm_pm_sleep_limit = SLEEP_LIMIT_NONE; +static DECLARE_BITMAP(msm_pm_clocks_no_tcxo_shutdown, NR_CLKS); + +/* + * Add the given time data to the statistics collection. + */ +static void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) +{ + int i; + int64_t bt; + + msm_pm_stats[id].total_time += t; + msm_pm_stats[id].count++; + + bt = t; + do_div(bt, msm_pm_stats[id].first_bucket_time); + + if (bt < 1ULL << (CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT * + (CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1))) + i = DIV_ROUND_UP(fls((uint32_t)bt), + CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT); + else + i = CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; + + msm_pm_stats[id].bucket[i]++; + + if (t < msm_pm_stats[id].min_time[i] || !msm_pm_stats[id].max_time[i]) + msm_pm_stats[id].min_time[i] = t; + if (t > msm_pm_stats[id].max_time[i]) + msm_pm_stats[id].max_time[i] = t; +} + +/* + * Helper function of snprintf where buf is auto-incremented, size is auto- + * decremented, and there is no return value. + * + * NOTE: buf and size must be l-values (e.g. variables) + */ +#define SNPRINTF(buf, size, format, ...) \ + do { \ + if (size > 0) { \ + int ret; \ + ret = snprintf(buf, size, format, ## __VA_ARGS__); \ + if (ret > size) { \ + buf += size; \ + size = 0; \ + } else { \ + buf += ret; \ + size -= ret; \ + } \ + } \ + } while (0) + +/* + * Write out the power management statistics. + */ +static int msm_pm_read_proc + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int i; + char *p = page; + char clk_name[16]; + + if (count < 1024) { + *start = (char *) 0; + *eof = 0; + return 0; + } + + if (!off) { + SNPRINTF(p, count, "Clocks against last TCXO shutdown:\n"); + for_each_bit(i, msm_pm_clocks_no_tcxo_shutdown, NR_CLKS) { + clk_name[0] = '\0'; + msm_clock_get_name(i, clk_name, sizeof(clk_name)); + SNPRINTF(p, count, " %s (id=%d)\n", clk_name, i); + } + + SNPRINTF(p, count, "Last power collapse voted "); + if (msm_pm_sleep_limit == SLEEP_LIMIT_NONE) + SNPRINTF(p, count, "for TCXO shutdown\n\n"); + else + SNPRINTF(p, count, "against TCXO shutdown\n\n"); + + *start = (char *) 1; + *eof = 0; + } else if (--off < ARRAY_SIZE(msm_pm_stats)) { + int64_t bucket_time; + int64_t s; + uint32_t ns; + + s = msm_pm_stats[off].total_time; + ns = do_div(s, NSEC_PER_SEC); + SNPRINTF(p, count, + "%s:\n" + " count: %7d\n" + " total_time: %lld.%09u\n", + msm_pm_stats[off].name, + msm_pm_stats[off].count, + s, ns); + + bucket_time = msm_pm_stats[off].first_bucket_time; + for (i = 0; i < CONFIG_MSM_IDLE_STATS_BUCKET_COUNT - 1; i++) { + s = bucket_time; + ns = do_div(s, NSEC_PER_SEC); + SNPRINTF(p, count, + " <%6lld.%09u: %7d (%lld-%lld)\n", + s, ns, msm_pm_stats[off].bucket[i], + msm_pm_stats[off].min_time[i], + msm_pm_stats[off].max_time[i]); + + bucket_time <<= CONFIG_MSM_IDLE_STATS_BUCKET_SHIFT; + } + + SNPRINTF(p, count, " >=%6lld.%09u: %7d (%lld-%lld)\n", + s, ns, msm_pm_stats[off].bucket[i], + msm_pm_stats[off].min_time[i], + msm_pm_stats[off].max_time[i]); + + *start = (char *) 1; + *eof = (off + 1 >= ARRAY_SIZE(msm_pm_stats)); + } + + return p - page; +} +#undef SNPRINTF + +#define MSM_PM_STATS_RESET "reset" + +/* + * Reset the power management statistics values. + */ +static int msm_pm_write_proc(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + char buf[sizeof(MSM_PM_STATS_RESET)]; + int ret; + unsigned long flags; + int i; + + if (count < strlen(MSM_PM_STATS_RESET)) { + ret = -EINVAL; + goto write_proc_failed; + } + + if (copy_from_user(buf, buffer, strlen(MSM_PM_STATS_RESET))) { + ret = -EFAULT; + goto write_proc_failed; + } + + if (memcmp(buf, MSM_PM_STATS_RESET, strlen(MSM_PM_STATS_RESET))) { + ret = -EINVAL; + goto write_proc_failed; + } + + local_irq_save(flags); + for (i = 0; i < ARRAY_SIZE(msm_pm_stats); i++) { + memset(msm_pm_stats[i].bucket, + 0, sizeof(msm_pm_stats[i].bucket)); + memset(msm_pm_stats[i].min_time, + 0, sizeof(msm_pm_stats[i].min_time)); + memset(msm_pm_stats[i].max_time, + 0, sizeof(msm_pm_stats[i].max_time)); + msm_pm_stats[i].count = 0; + msm_pm_stats[i].total_time = 0; + } + + msm_pm_sleep_limit = SLEEP_LIMIT_NONE; + bitmap_zero(msm_pm_clocks_no_tcxo_shutdown, NR_CLKS); + local_irq_restore(flags); + + return count; + +write_proc_failed: + return ret; +} +#undef MSM_PM_STATS_RESET +#endif /* CONFIG_MSM_IDLE_STATS */ + + +/****************************************************************************** + * Shared Memory Bits + *****************************************************************************/ + +#define DEM_MASTER_BITS_PER_CPU 6 + +/* Power Master State Bits - Per CPU */ +#define DEM_MASTER_SMSM_RUN \ + (0x01UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_RSA \ + (0x02UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_PWRC_EARLY_EXIT \ + (0x04UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_SLEEP_EXIT \ + (0x08UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_READY \ + (0x10UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) +#define DEM_MASTER_SMSM_SLEEP \ + (0x20UL << (DEM_MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) + +/* Power Slave State Bits */ +#define DEM_SLAVE_SMSM_RUN (0x0001) +#define DEM_SLAVE_SMSM_PWRC (0x0002) +#define DEM_SLAVE_SMSM_PWRC_DELAY (0x0004) +#define DEM_SLAVE_SMSM_PWRC_EARLY_EXIT (0x0008) +#define DEM_SLAVE_SMSM_WFPI (0x0010) +#define DEM_SLAVE_SMSM_SLEEP (0x0020) +#define DEM_SLAVE_SMSM_SLEEP_EXIT (0x0040) +#define DEM_SLAVE_SMSM_MSGS_REDUCED (0x0080) +#define DEM_SLAVE_SMSM_RESET (0x0100) +#define DEM_SLAVE_SMSM_PWRC_SUSPEND (0x0200) + + +/****************************************************************************** + * Shared Memory Data + *****************************************************************************/ + +#define DEM_MAX_PORT_NAME_LEN (20) + +struct msm_pm_smem_t { + uint32_t sleep_time; + uint32_t irq_mask; + uint32_t resources_used; + uint32_t reserved1; + + uint32_t wakeup_reason; + uint32_t pending_irqs; + uint32_t rpc_prog; + uint32_t rpc_proc; + char smd_port_name[DEM_MAX_PORT_NAME_LEN]; + uint32_t reserved2; +}; + + +/****************************************************************************** + * + *****************************************************************************/ +static struct msm_pm_smem_t *msm_pm_smem_data; +static uint32_t *msm_pm_reset_vector; +static atomic_t msm_pm_init_done = ATOMIC_INIT(0); + +/* + * Power collapse the Apps processor. This function executes the handshake + * protocol with Modem. + * + * Return value: + * -EAGAIN: modem reset occurred or early exit from power collapse + * -EBUSY: modem not ready for our power collapse -- no power loss + * -ETIMEDOUT: timed out waiting for modem's handshake -- no power loss + * 0: success + */ +static int msm_pm_power_collapse + (bool from_idle, uint32_t sleep_delay, uint32_t sleep_limit) +{ + struct msm_pm_polled_group state_grps[2]; + unsigned long saved_acpuclk_rate; + uint32_t saved_vector[2]; + int collapsed = 0; + int ret; +#ifdef CONFIG_MSM_ADM_OFF_AT_POWER_COLLAPSE + unsigned id; +#endif + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, "%s(): idle %d, delay %u, limit %u\n", __func__, + (int)from_idle, sleep_delay, sleep_limit); + + if (!(smsm_get_state(SMSM_POWER_MASTER_DEM) & DEM_MASTER_SMSM_READY)) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, "%s(): master not ready\n", __func__); + ret = -EBUSY; + goto power_collapse_bail; + } + + memset(msm_pm_smem_data, 0, sizeof(*msm_pm_smem_data)); + + msm_irq_enter_sleep1(true, from_idle, &msm_pm_smem_data->irq_mask); + msm_gpio_enter_sleep(from_idle); + + msm_pm_smem_data->sleep_time = sleep_delay; + msm_pm_smem_data->resources_used = sleep_limit; + + /* Enter PWRC/PWRC_SUSPEND */ + + if (from_idle) + smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN, + DEM_SLAVE_SMSM_PWRC); + else + smsm_change_state(SMSM_APPS_DEM, DEM_SLAVE_SMSM_RUN, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC"); + MSM_PM_DEBUG_PRINT_SLEEP_INFO(); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_all_set = DEM_MASTER_SMSM_RSA; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse entry " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + goto power_collapse_early_exit; + } + + /* DEM Master in RSA */ + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): PWRC RSA"); + + ret = msm_irq_enter_sleep2(true, from_idle); + if (ret < 0) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_irq_enter_sleep2 aborted, %d\n", __func__, + ret); + goto power_collapse_early_exit; + } + +#ifdef CONFIG_MSM_ADM_OFF_AT_POWER_COLLAPSE + /* XXX: Temp workaround that needs to be removed soon. The + * right fix will probably involve the DMA driver taking + * ownership of the ADM clock. */ + /* id is set to denote ADM clock. */ + id = 1; + msm_proc_comm(PCOM_CLKCTL_RPC_DISABLE, &id, NULL); +#endif + + msm_pm_config_hw_before_power_down(); + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): pre power down"); + + saved_acpuclk_rate = acpuclk_power_collapse(); + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): change clock rate (old rate = %lu)\n", __func__, + saved_acpuclk_rate); + + if (saved_acpuclk_rate == 0) { + msm_pm_config_hw_after_power_up(); + goto power_collapse_early_exit; + } + + saved_vector[0] = msm_pm_reset_vector[0]; + saved_vector[1] = msm_pm_reset_vector[1]; + msm_pm_reset_vector[0] = 0xE51FF004; /* ldr pc, 4 */ + msm_pm_reset_vector[1] = virt_to_phys(msm_pm_collapse_exit); + + MSM_PM_DPRINTK(MSM_PM_DEBUG_RESET_VECTOR, KERN_INFO, + "%s(): vector %x %x -> %x %x\n", __func__, + saved_vector[0], saved_vector[1], + msm_pm_reset_vector[0], msm_pm_reset_vector[1]); + +#ifdef CONFIG_VFP + if (from_idle) + vfp_flush_context(); +#endif + +#ifdef CONFIG_CACHE_L2X0 + l2x0_suspend(); +#endif + + collapsed = msm_pm_collapse(); + +#ifdef CONFIG_CACHE_L2X0 + l2x0_resume(collapsed); +#endif + + msm_pm_reset_vector[0] = saved_vector[0]; + msm_pm_reset_vector[1] = saved_vector[1]; + + if (collapsed) { +#ifdef CONFIG_VFP + if (from_idle) + vfp_reinit(); +#endif + cpu_init(); + local_fiq_enable(); + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND | MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_collapse returned %d\n", __func__, collapsed); + + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): restore clock rate to %lu\n", __func__, + saved_acpuclk_rate); + if (acpuclk_set_rate(saved_acpuclk_rate, SETRATE_PC) < 0) + printk(KERN_ERR "%s(): failed to restore clock rate(%lu)\n", + __func__, saved_acpuclk_rate); + +#ifdef CONFIG_MSM_ADM_OFF_AT_POWER_COLLAPSE + /* id is set to denote ADM clock. */ + id = 1; + if (msm_proc_comm(PCOM_CLKCTL_RPC_ENABLE, &id, NULL) < 0 || id < 0) + printk(KERN_ERR + "%s(): failed to turn on ADM clock\n", __func__); +#endif + + msm_irq_exit_sleep1(msm_pm_smem_data->irq_mask, + msm_pm_smem_data->wakeup_reason, + msm_pm_smem_data->pending_irqs); + + msm_pm_config_hw_after_power_up(); + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): post power up"); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_any_set = + DEM_MASTER_SMSM_RSA | DEM_MASTER_SMSM_PWRC_EARLY_EXIT; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse exit " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + goto power_collapse_early_exit; + } + + /* Sanity check */ + if (collapsed) { + BUG_ON(!(state_grps[0].value_read & DEM_MASTER_SMSM_RSA)); + } else { + BUG_ON(!(state_grps[0].value_read & + DEM_MASTER_SMSM_PWRC_EARLY_EXIT)); + goto power_collapse_early_exit; + } + + /* Enter WFPI */ + + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND, + DEM_SLAVE_SMSM_WFPI); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI"); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_all_set = DEM_MASTER_SMSM_RUN; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse WFPI " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + ret = -EAGAIN; + goto power_collapse_restore_gpio_bail; + } + + /* DEM Master == RUN */ + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): WFPI RUN"); + MSM_PM_DEBUG_PRINT_SLEEP_INFO(); + + msm_irq_exit_sleep2(msm_pm_smem_data->irq_mask, + msm_pm_smem_data->wakeup_reason, + msm_pm_smem_data->pending_irqs); + msm_irq_exit_sleep3(msm_pm_smem_data->irq_mask, + msm_pm_smem_data->wakeup_reason, + msm_pm_smem_data->pending_irqs); + msm_gpio_exit_sleep(); + + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_WFPI, DEM_SLAVE_SMSM_RUN); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN"); + + smd_sleep_exit(); + return 0; + +power_collapse_early_exit: + /* Enter PWRC_EARLY_EXIT */ + + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND, + DEM_SLAVE_SMSM_PWRC_EARLY_EXIT); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT"); + + memset(state_grps, 0, sizeof(state_grps)); + state_grps[0].group_id = SMSM_POWER_MASTER_DEM; + state_grps[0].bits_all_set = DEM_MASTER_SMSM_PWRC_EARLY_EXIT; + state_grps[1].group_id = SMSM_MODEM_STATE; + state_grps[1].bits_all_set = SMSM_RESET; + + ret = msm_pm_poll_state(ARRAY_SIZE(state_grps), state_grps); + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): EARLY_EXIT EE"); + + if (ret < 0) { + printk(KERN_EMERG "%s(): power collapse EARLY_EXIT " + "timed out waiting for Modem's response\n", __func__); + msm_pm_timeout(); + } + + if (ret == 1) { + MSM_PM_DPRINTK( + MSM_PM_DEBUG_SUSPEND|MSM_PM_DEBUG_POWER_COLLAPSE, + KERN_INFO, + "%s(): msm_pm_poll_state detected Modem reset\n", + __func__); + } + + /* DEM Master == RESET or PWRC_EARLY_EXIT */ + + ret = -EAGAIN; + +power_collapse_restore_gpio_bail: + msm_gpio_exit_sleep(); + + /* Enter RUN */ + smsm_change_state(SMSM_APPS_DEM, + DEM_SLAVE_SMSM_PWRC | DEM_SLAVE_SMSM_PWRC_SUSPEND | + DEM_SLAVE_SMSM_PWRC_EARLY_EXIT, DEM_SLAVE_SMSM_RUN); + + MSM_PM_DEBUG_PRINT_STATE("msm_pm_power_collapse(): RUN"); + + if (collapsed) + smd_sleep_exit(); + +power_collapse_bail: + return ret; +} + +/* + * Apps-sleep the Apps processor. This function execute the handshake + * protocol with Modem. + * + * Return value: + * -ENOSYS: function not implemented yet + */ +static int msm_pm_apps_sleep(uint32_t sleep_delay, uint32_t sleep_limit) +{ + return -ENOSYS; +} + +/* + * Bring the Apps processor to SWFI. + * + * Return value: + * -EIO: could not ramp Apps processor clock + * 0: success + */ +static int msm_pm_swfi(bool ramp_acpu) +{ + unsigned long saved_acpuclk_rate = 0; + + if (ramp_acpu) { + saved_acpuclk_rate = acpuclk_wait_for_irq(); + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): change clock rate (old rate = %lu)\n", __func__, + saved_acpuclk_rate); + + if (!saved_acpuclk_rate) + return -EIO; + } + + msm_pm_config_hw_before_swfi(); + msm_arch_idle(); + + if (ramp_acpu) { + MSM_PM_DPRINTK(MSM_PM_DEBUG_CLOCK, KERN_INFO, + "%s(): restore clock rate to %lu\n", __func__, + saved_acpuclk_rate); + if (acpuclk_set_rate(saved_acpuclk_rate, SETRATE_SWFI) < 0) + printk(KERN_ERR + "%s(): failed to restore clock rate(%lu)\n", + __func__, saved_acpuclk_rate); + } + + return 0; +} + + +/****************************************************************************** + * External Idle/Suspend Functions + *****************************************************************************/ + +/* + * Put CPU in low power mode. + */ +void arch_idle(void) +{ + bool allow[MSM_PM_SLEEP_MODE_NR]; + uint32_t sleep_limit = SLEEP_LIMIT_NONE; + + int latency_qos; + int64_t timer_expiration; + + int low_power; + int ret; + int i; + +#ifdef CONFIG_MSM_IDLE_STATS + DECLARE_BITMAP(clk_ids, NR_CLKS); + int64_t t1; + static int64_t t2; + int exit_stat; +#endif /* CONFIG_MSM_IDLE_STATS */ + + if (!atomic_read(&msm_pm_init_done)) + return; + + latency_qos = pm_qos_requirement(PM_QOS_CPU_DMA_LATENCY); + timer_expiration = msm_timer_enter_idle(); + +#ifdef CONFIG_MSM_IDLE_STATS + t1 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(MSM_PM_STAT_NOT_IDLE, t1 - t2); + msm_pm_add_stat(MSM_PM_STAT_REQUESTED_IDLE, timer_expiration); +#endif /* CONFIG_MSM_IDLE_STATS */ + + for (i = 0; i < ARRAY_SIZE(allow); i++) + allow[i] = true; + + switch (msm_pm_idle_sleep_mode) { + case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT: + allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = + false; + /* fall through */ + case MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT: + allow[MSM_PM_SLEEP_MODE_APPS_SLEEP] = false; + /* fall through */ + case MSM_PM_SLEEP_MODE_APPS_SLEEP: + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false; + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; + /* fall through */ + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + break; + default: + printk(KERN_ERR "idle sleep mode is invalid: %d\n", + msm_pm_idle_sleep_mode); +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif /* CONFIG_MSM_IDLE_STATS */ + low_power = 0; + goto arch_idle_exit; + } + + if ((timer_expiration < msm_pm_idle_sleep_min_time) || + !msm_irq_idle_sleep_allowed()) { + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false; + allow[MSM_PM_SLEEP_MODE_APPS_SLEEP] = false; + } + + for (i = 0; i < ARRAY_SIZE(allow); i++) { + struct msm_pm_platform_data *mode = &msm_pm_modes[i]; + if (!mode->supported || !mode->idle_enabled || + mode->latency >= latency_qos || + mode->residency * 1000ULL >= timer_expiration) + allow[i] = false; + } + +#ifdef CONFIG_MSM_IDLE_STATS + ret = msm_clock_require_tcxo(clk_ids, NR_CLKS); +#elif defined(CONFIG_CLOCK_BASED_SLEEP_LIMIT) + ret = msm_clock_require_tcxo(NULL, 0); +#endif /* CONFIG_MSM_IDLE_STATS */ + +#ifdef CONFIG_CLOCK_BASED_SLEEP_LIMIT + if (ret) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; +#endif + + MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO, + "%s(): latency qos %d, next timer %lld, sleep limit %u\n", + __func__, latency_qos, timer_expiration, sleep_limit); + + for (i = 0; i < ARRAY_SIZE(allow); i++) + MSM_PM_DPRINTK(MSM_PM_DEBUG_IDLE, KERN_INFO, + "%s(): allow %s: %d\n", __func__, + msm_pm_sleep_mode_labels[i], (int)allow[i]); + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { + uint32_t sleep_delay; + + sleep_delay = (uint32_t) msm_pm_convert_and_cap_time( + timer_expiration, MSM_PM_SLEEP_TICK_LIMIT); + if (sleep_delay == 0) /* 0 would mean infinite time */ + sleep_delay = 1; + + if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; + + ret = msm_pm_power_collapse(true, sleep_delay, sleep_limit); + low_power = (ret != -EBUSY && ret != -ETIMEDOUT); + +#ifdef CONFIG_MSM_IDLE_STATS + if (ret) + exit_stat = MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE; + else { + exit_stat = MSM_PM_STAT_IDLE_POWER_COLLAPSE; + msm_pm_sleep_limit = sleep_limit; + bitmap_copy(msm_pm_clocks_no_tcxo_shutdown, clk_ids, + NR_CLKS); + } +#endif /* CONFIG_MSM_IDLE_STATS */ + } else if (allow[MSM_PM_SLEEP_MODE_APPS_SLEEP]) { + uint32_t sleep_delay; + + sleep_delay = (uint32_t) msm_pm_convert_and_cap_time( + timer_expiration, MSM_PM_SLEEP_TICK_LIMIT); + if (sleep_delay == 0) /* 0 would mean infinite time */ + sleep_delay = 1; + + ret = msm_pm_apps_sleep(sleep_delay, sleep_limit); + low_power = 0; + +#ifdef CONFIG_MSM_IDLE_STATS + if (ret) + exit_stat = MSM_PM_STAT_IDLE_FAILED_SLEEP; + else + exit_stat = MSM_PM_STAT_IDLE_SLEEP; +#endif /* CONFIG_MSM_IDLE_STATS */ + } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) { + ret = msm_pm_swfi(true); + if (ret) + while (!msm_irq_pending()) + udelay(1); + low_power = 0; +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = ret ? MSM_PM_STAT_IDLE_SPIN : MSM_PM_STAT_IDLE_WFI; +#endif /* CONFIG_MSM_IDLE_STATS */ + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + msm_pm_swfi(false); + low_power = 0; +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_WFI; +#endif /* CONFIG_MSM_IDLE_STATS */ + } else { + while (!msm_irq_pending()) + udelay(1); + low_power = 0; +#ifdef CONFIG_MSM_IDLE_STATS + exit_stat = MSM_PM_STAT_IDLE_SPIN; +#endif /* CONFIG_MSM_IDLE_STATS */ + } + +arch_idle_exit: + msm_timer_exit_idle(low_power); + +#ifdef CONFIG_MSM_IDLE_STATS + t2 = ktime_to_ns(ktime_get()); + msm_pm_add_stat(exit_stat, t2 - t1); +#endif /* CONFIG_MSM_IDLE_STATS */ +} + +/* + * Suspend the Apps processor. + * + * Return value: + * -EAGAIN: modem reset occurred or early exit from suspend + * -EBUSY: modem not ready for our suspend + * -EINVAL: invalid sleep mode + * -EIO: could not ramp Apps processor clock + * -ETIMEDOUT: timed out waiting for modem's handshake + * 0: success + */ +static int msm_pm_enter(suspend_state_t state) +{ + bool allow[MSM_PM_SLEEP_MODE_NR]; + uint32_t sleep_limit = SLEEP_LIMIT_NONE; + int ret; + int i; + +#ifdef CONFIG_MSM_IDLE_STATS + DECLARE_BITMAP(clk_ids, NR_CLKS); + int64_t period = 0; + int64_t time = 0; + + time = msm_timer_get_sclk_time(&period); + ret = msm_clock_require_tcxo(clk_ids, NR_CLKS); +#elif defined(CONFIG_CLOCK_BASED_SLEEP_LIMIT) + ret = msm_clock_require_tcxo(NULL, 0); +#endif /* CONFIG_MSM_IDLE_STATS */ + +#ifdef CONFIG_CLOCK_BASED_SLEEP_LIMIT + if (ret) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; +#endif + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, + "%s(): sleep limit %u\n", __func__, sleep_limit); + + for (i = 0; i < ARRAY_SIZE(allow); i++) + allow[i] = true; + + switch (msm_pm_sleep_mode) { + case MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT: + allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT] = + false; + /* fall through */ + case MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT: + allow[MSM_PM_SLEEP_MODE_APPS_SLEEP] = false; + /* fall through */ + case MSM_PM_SLEEP_MODE_APPS_SLEEP: + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN] = false; + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = false; + /* fall through */ + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND: + case MSM_PM_SLEEP_MODE_POWER_COLLAPSE: + break; + default: + printk(KERN_ERR "suspend sleep mode is invalid: %d\n", + msm_pm_sleep_mode); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(allow); i++) { + struct msm_pm_platform_data *mode = &msm_pm_modes[i]; + if (!mode->supported || !mode->suspend_enabled) + allow[i] = false; + } + + ret = 0; + + if (allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] || + allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_NO_XO_SHUTDOWN]) { +#ifdef CONFIG_MSM_IDLE_STATS + enum msm_pm_time_stats_id id; + int64_t end_time; +#endif + +#ifdef CONFIG_MSM_SLEEP_TIME_OVERRIDE + if (msm_pm_sleep_time_override > 0) { + int64_t ns; + ns = NSEC_PER_SEC * (int64_t)msm_pm_sleep_time_override; + msm_pm_set_max_sleep_time(ns); + msm_pm_sleep_time_override = 0; + } +#endif + if (!allow[MSM_PM_SLEEP_MODE_POWER_COLLAPSE]) + sleep_limit = SLEEP_LIMIT_NO_TCXO_SHUTDOWN; + + ret = msm_pm_power_collapse( + false, msm_pm_max_sleep_time, sleep_limit); + +#ifdef CONFIG_MSM_IDLE_STATS + if (ret) + id = MSM_PM_STAT_FAILED_SUSPEND; + else { + id = MSM_PM_STAT_SUSPEND; + msm_pm_sleep_limit = sleep_limit; + bitmap_copy(msm_pm_clocks_no_tcxo_shutdown, clk_ids, + NR_CLKS); + } + + if (time != 0) { + end_time = msm_timer_get_sclk_time(NULL); + if (end_time != 0) { + time = end_time - time; + if (time < 0) + time += period; + } else + time = 0; + } + + msm_pm_add_stat(id, time); +#endif + } else if (allow[MSM_PM_SLEEP_MODE_RAMP_DOWN_AND_WAIT_FOR_INTERRUPT]) { + ret = msm_pm_swfi(true); + if (ret) + while (!msm_irq_pending()) + udelay(1); + } else if (allow[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT]) { + msm_pm_swfi(false); + } + + MSM_PM_DPRINTK(MSM_PM_DEBUG_SUSPEND, KERN_INFO, + "%s(): return %d\n", __func__, ret); + + return ret; +} + +static struct platform_suspend_ops msm_pm_ops = { + .enter = msm_pm_enter, + .valid = suspend_valid_only_mem, +}; + + +/****************************************************************************** + * Restart Definitions + *****************************************************************************/ + +static uint32_t restart_reason = 0x776655AA; + +static void msm_pm_power_off(void) +{ + msm_proc_comm(PCOM_POWER_DOWN, 0, 0); + for (;;) + ; +} + +static void msm_pm_restart(char str, const char *cmd ) +{ + msm_proc_comm(PCOM_RESET_CHIP, &restart_reason, 0); + + for (;;) + ; +} + +static int msm_reboot_call + (struct notifier_block *this, unsigned long code, void *_cmd) +{ + if ((code == SYS_RESTART) && _cmd) { + char *cmd = _cmd; + if (!strcmp(cmd, "bootloader")) { + restart_reason = 0x77665500; + } else if (!strcmp(cmd, "recovery")) { + restart_reason = 0x77665502; + } else if (!strcmp(cmd, "eraseflash")) { + restart_reason = 0x776655EF; + } else if (!strncmp(cmd, "oem-", 4)) { + unsigned code = simple_strtoul(cmd + 4, 0, 16) & 0xff; + restart_reason = 0x6f656d00 | code; + } else { + restart_reason = 0x77665501; + } + } + return NOTIFY_DONE; +} + +static struct notifier_block msm_reboot_notifier = { + .notifier_call = msm_reboot_call, +}; + + +/****************************************************************************** + * + *****************************************************************************/ + +/* + * Initialize the power management subsystem. + * + * Return value: + * -ENODEV: initialization failed + * 0: success + */ +static int __init msm_pm_init(void) +{ +#ifdef CONFIG_MSM_IDLE_STATS + struct proc_dir_entry *d_entry; +#endif + int ret; + + pm_power_off = msm_pm_power_off; + arm_pm_restart = msm_pm_restart; + register_reboot_notifier(&msm_reboot_notifier); + + msm_pm_smem_data = smem_alloc(SMEM_APPS_DEM_SLAVE_DATA, + sizeof(*msm_pm_smem_data)); + if (msm_pm_smem_data == NULL) { + printk(KERN_ERR "%s: failed to get smsm_data\n", __func__); + return -ENODEV; + } + +#ifdef CONFIG_ARCH_MSM_SCORPION + /* The bootloader is responsible for initializing many of Scorpion's + * coprocessor registers for things like cache timing. The state of + * these coprocessor registers is lost on reset, so part of the + * bootloader must be re-executed. Do not overwrite the reset vector + * or bootloader area. + */ + msm_pm_reset_vector = (uint32_t *) PAGE_OFFSET; +#else + msm_pm_reset_vector = ioremap(0, PAGE_SIZE); + if (msm_pm_reset_vector == NULL) { + printk(KERN_ERR "%s: failed to map reset vector\n", __func__); + return -ENODEV; + } +#endif /* CONFIG_ARCH_MSM_SCORPION */ + + ret = msm_timer_init_time_sync(); + if (ret) + return ret; + + ret = smsm_change_intr_mask(SMSM_POWER_MASTER_DEM, 0xFFFFFFFF, 0); + if (ret) { + printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n", + __func__, ret); + return ret; + } + + BUG_ON(msm_pm_modes == NULL); + + atomic_set(&msm_pm_init_done, 1); + suspend_set_ops(&msm_pm_ops); + + msm_pm_mode_sysfs_add(); +#ifdef CONFIG_MSM_IDLE_STATS + d_entry = create_proc_entry("msm_pm_stats", + S_IRUGO | S_IWUSR | S_IWGRP, NULL); + if (d_entry) { + d_entry->read_proc = msm_pm_read_proc; + d_entry->write_proc = msm_pm_write_proc; + d_entry->data = NULL; + } +#endif + + return 0; +} + +late_initcall(msm_pm_init); diff --git a/arch/arm/mach-msm/pmic.c b/arch/arm/mach-msm/pmic.c new file mode 100644 index 000000000000..3c481a6bc180 --- /dev/null +++ b/arch/arm/mach-msm/pmic.c @@ -0,0 +1,1095 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/uaccess.h> +#include <linux/mutex.h> + +#include <mach/pmic.h> + +#include "smd_rpcrouter.h" + +#define TRACE_PMIC 0 + +#if TRACE_PMIC +#define PMIC(x...) printk(KERN_INFO "[PMIC] " x) +#else +#define PMIC(x...) do {} while (0) +#endif + + +#define LIB_NULL_PROC 0 +#define LIB_RPC_GLUE_CODE_INFO_REMOTE_PROC 1 +#define LP_MODE_CONTROL_PROC 2 +#define VREG_SET_LEVEL_PROC 3 +#define VREG_PULL_DOWN_SWITCH_PROC 4 +#define SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC 5 +#define SECURE_MPP_CONFIG_I_SINK_PROC 6 +#define RTC_START_PROC 7 +#define RTC_STOP_PROC 8 +#define RTC_GET_TIME_PROC 9 +#define RTC_ENABLE_ALARM_PROC 10 +#define RTC_DISABLE_ALARM_PROC 11 +#define RTC_GET_ALARM_TIME_PROC 12 +#define RTC_GET_ALARM_STATUS_PROC 13 +#define RTC_SET_TIME_ADJUST_PROC 14 +#define RTC_GET_TIME_ADJUST_PROC 15 +#define SET_LED_INTENSITY_PROC 16 +#define FLASH_LED_SET_CURRENT_PROC 17 +#define FLASH_LED_SET_MODE_PROC 18 +#define FLASH_LED_SET_POLARITY_PROC 19 +#define SPEAKER_CMD_PROC 20 +#define SET_SPEAKER_GAIN_PROC 21 +#define VIB_MOT_SET_VOLT_PROC 22 +#define VIB_MOT_SET_MODE_PROC 23 +#define VIB_MOT_SET_POLARITY_PROC 24 +#define VID_EN_PROC 25 +#define VID_IS_EN_PROC 26 +#define VID_LOAD_DETECT_EN_PROC 27 +#define MIC_EN_PROC 28 +#define MIC_IS_EN_PROC 29 +#define MIC_SET_VOLT_PROC 30 +#define MIC_GET_VOLT_PROC 31 +#define SPKR_EN_RIGHT_CHAN_PROC 32 +#define SPKR_IS_RIGHT_CHAN_EN_PROC 33 +#define SPKR_EN_LEFT_CHAN_PROC 34 +#define SPKR_IS_LEFT_CHAN_EN_PROC 35 +#define SET_SPKR_CONFIGURATION_PROC 36 +#define GET_SPKR_CONFIGURATION_PROC 37 +#define SPKR_GET_GAIN_PROC 38 +#define SPKR_IS_EN_PROC 39 +#define SPKR_EN_MUTE_PROC 40 +#define SPKR_IS_MUTE_EN_PROC 41 +#define SPKR_SET_DELAY_PROC 42 +#define SPKR_GET_DELAY_PROC 43 +#define SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC 44 +#define SET_SPEAKER_DELAY_PROC 45 +#define SPEAKER_1K6_ZIN_ENABLE_PROC 46 +#define SPKR_SET_MUX_HPF_CORNER_FREQ_PROC 47 +#define SPKR_GET_MUX_HPF_CORNER_FREQ_PROC 48 +#define SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC 49 +#define SPKR_EN_STEREO_PROC 50 +#define SPKR_IS_STEREO_EN_PROC 51 +#define SPKR_SELECT_USB_WITH_HPF_20HZ_PROC 52 +#define SPKR_IS_USB_WITH_HPF_20HZ_PROC 53 +#define SPKR_BYPASS_MUX_PROC 54 +#define SPKR_IS_MUX_BYPASSED_PROC 55 +#define SPKR_EN_HPF_PROC 56 +#define SPKR_IS_HPF_EN_PROC 57 +#define SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC 58 +#define SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC 59 +#define SPKR_ADD_RIGHT_LEFT_CHAN_PROC 60 +#define SPKR_SET_GAIN_PROC 61 +#define SPKR_EN_PROC 62 +#define HSED_SET_PERIOD_PROC 63 +#define HSED_SET_HYSTERESIS_PROC 64 +#define HSED_SET_CURRENT_THRESHOLD_PROC 65 +#define HSED_ENABLE_PROC 66 + + +/* rpc related */ +#define PMIC_RPC_TIMEOUT (5*HZ) + +#define PMIC_PDEV_NAME "rs00010001:00000000" +#define PMIC_RPC_PROG 0x30000061 +#define PMIC_RPC_VER_1_1 0x00010001 +#define PMIC_RPC_VER_2_1 0x00020001 + +/* error bit flags defined by modem side */ +#define PM_ERR_FLAG__PAR1_OUT_OF_RANGE (0x0001) +#define PM_ERR_FLAG__PAR2_OUT_OF_RANGE (0x0002) +#define PM_ERR_FLAG__PAR3_OUT_OF_RANGE (0x0004) +#define PM_ERR_FLAG__PAR4_OUT_OF_RANGE (0x0008) +#define PM_ERR_FLAG__PAR5_OUT_OF_RANGE (0x0010) + +#define PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE (0x001F) /* all 5 previous */ + +#define PM_ERR_FLAG__SBI_OPT_ERR (0x0080) +#define PM_ERR_FLAG__FEATURE_NOT_SUPPORTED (0x0100) + +#define PMIC_BUFF_SIZE 256 + +struct pmic_buf { + char *start; /* buffer start addr */ + char *end; /* buffer end addr */ + int size; /* buffer size */ + char *data; /* payload begin addr */ + int len; /* payload len */ +}; + +static DEFINE_MUTEX(pmic_mtx); + +struct pmic_ctrl { + int inited; + struct pmic_buf tbuf; + struct pmic_buf rbuf; + struct msm_rpc_endpoint *endpoint; +}; + +static struct pmic_ctrl pmic_ctrl = { + .inited = -1, +}; + +static int pmic_rpc_req_reply(struct pmic_buf *tbuf, + struct pmic_buf *rbuf, int proc); +static int pmic_rpc_set_only(uint data0, uint data1, uint data2, + uint data3, int num, int proc); +static int pmic_rpc_set_struct(int, uint, uint *data, uint size, int proc); +static int pmic_rpc_set_get(uint setdata, uint *getdata, int size, int proc); +static int pmic_rpc_get_only(uint *getdata, int size, int proc); + +static int pmic_buf_init(void) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + + memset(&pmic_ctrl, 0, sizeof(pmic_ctrl)); + + pm->tbuf.start = kmalloc(PMIC_BUFF_SIZE, GFP_KERNEL); + if (pm->tbuf.start == NULL) { + printk(KERN_ERR "%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + + pm->tbuf.data = pm->tbuf.start; + pm->tbuf.size = PMIC_BUFF_SIZE; + pm->tbuf.end = pm->tbuf.start + PMIC_BUFF_SIZE; + pm->tbuf.len = 0; + + pm->rbuf.start = kmalloc(PMIC_BUFF_SIZE, GFP_KERNEL); + if (pm->rbuf.start == NULL) { + kfree(pm->tbuf.start); + printk(KERN_ERR "%s:%u\n", __func__, __LINE__); + return -ENOMEM; + } + pm->rbuf.data = pm->rbuf.start; + pm->rbuf.size = PMIC_BUFF_SIZE; + pm->rbuf.end = pm->rbuf.start + PMIC_BUFF_SIZE; + pm->rbuf.len = 0; + + pm->inited = 1; + + return 0; +} + +static inline void pmic_buf_reserve(struct pmic_buf *bp, int len) +{ + bp->data += len; +} + +static inline void pmic_buf_reset(struct pmic_buf *bp) +{ + bp->data = bp->start; + bp->len = 0; +} + +static int modem_to_linux_err(uint err) +{ + if (err == 0) + return 0; + + if (err & PM_ERR_FLAG__ALL_PARMS_OUT_OF_RANGE) + return -EINVAL; /* PM_ERR_FLAG__PAR[1..5]_OUT_OF_RANGE */ + + if (err & PM_ERR_FLAG__SBI_OPT_ERR) + return -EIO; + + if (err & PM_ERR_FLAG__FEATURE_NOT_SUPPORTED) + return -ENOSYS; + + return -EPERM; +} + +static int pmic_put_tx_data(struct pmic_buf *tp, uint datav) +{ + uint *lp; + + if ((tp->size - tp->len) < sizeof(datav)) { + printk(KERN_ERR "%s: OVERFLOW size=%d len=%d\n", + __func__, tp->size, tp->len); + return -1; + } + + lp = (uint *)tp->data; + *lp = cpu_to_be32(datav); + tp->data += sizeof(datav); + tp->len += sizeof(datav); + + return sizeof(datav); +} + +static int pmic_pull_rx_data(struct pmic_buf *rp, uint *datap) +{ + uint *lp; + + if (rp->len < sizeof(*datap)) { + printk(KERN_ERR "%s: UNDERRUN len=%d\n", __func__, rp->len); + return -1; + } + lp = (uint *)rp->data; + *datap = be32_to_cpu(*lp); + rp->data += sizeof(*datap); + rp->len -= sizeof(*datap); + + return sizeof(*datap); +} + + +/* + * + * +-------------------+ + * | PROC cmd layer | + * +-------------------+ + * | RPC layer | + * +-------------------+ + * + * 1) network byte order + * 2) RPC request header(40 bytes) and RPC reply header (24 bytes) + * 3) each transaction consists of a request and reply + * 3) PROC (comamnd) layer has its own sub-protocol defined + * 4) sub-protocol can be grouped to follwoing 7 cases: + * a) set one argument, no get + * b) set two argument, no get + * c) set three argument, no get + * d) set a struct, no get + * e) set a argument followed by a struct, no get + * f) set a argument, get a argument + * g) no set, get either a argument or a struct + */ + +/** + * pmic_rpc_req_reply() - send request and wait for reply + * @tbuf: buffer contains arguments + * @rbuf: buffer to be filled with arguments at reply + * @proc: command/request id + * + * This function send request to modem and wait until reply received + */ +static int pmic_rpc_req_reply(struct pmic_buf *tbuf, struct pmic_buf *rbuf, + int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + int ans, len; + + + if ((pm->endpoint == NULL) || IS_ERR(pm->endpoint)) { + pm->endpoint = msm_rpc_connect_compatible(PMIC_RPC_PROG, + PMIC_RPC_VER_2_1, 0); + if (IS_ERR(pm->endpoint)) { + pm->endpoint = msm_rpc_connect_compatible(PMIC_RPC_PROG, + PMIC_RPC_VER_1_1, 0); + } + + if (IS_ERR(pm->endpoint)) { + ans = PTR_ERR(pm->endpoint); + printk(KERN_ERR "%s: init rpc failed! ans = %d\n", + __func__, ans); + return ans; + } + } + + /* + * data is point to next available space at this moment, + * move it back to beginning of request header and increase + * the length + */ + tbuf->data = tbuf->start; + tbuf->len += sizeof(struct rpc_request_hdr); + + len = msm_rpc_call_reply(pm->endpoint, proc, + tbuf->data, tbuf->len, + rbuf->data, rbuf->size, + PMIC_RPC_TIMEOUT); + + if (len <= 0) { + printk(KERN_ERR "%s: rpc failed! len = %d\n", __func__, len); + pm->endpoint = NULL; /* re-connect later ? */ + return len; + } + + rbuf->len = len; + /* strip off rpc_reply_hdr */ + rbuf->data += sizeof(struct rpc_reply_hdr); + rbuf->len -= sizeof(struct rpc_reply_hdr); + + return rbuf->len; +} + +/** + * pmic_rpc_set_only() - set arguments and no get + * @data0: first argumrnt + * @data1: second argument + * @data2: third argument + * @data3: fourth argument + * @num: number of argument + * @proc: command/request id + * + * This function covers case a, b, and c + */ +static int pmic_rpc_set_only(uint data0, uint data1, uint data2, uint data3, + int num, int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + int stat; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + if (num > 0) + pmic_put_tx_data(tp, data0); + + if (num > 1) + pmic_put_tx_data(tp, data1); + + if (num > 2) + pmic_put_tx_data(tp, data2); + + if (num > 3) + pmic_put_tx_data(tp, data3); + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + +/** + * pmic_rpc_set_struct() - set the whole struct + * @xflag: indicates an extra argument + * @xdata: the extra argument + * @*data: starting address of struct + * @size: size of struct + * @proc: command/request id + * + * This fucntion covers case d and e + */ +static int pmic_rpc_set_struct(int xflag, uint xdata, uint *data, uint size, + int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + int i, stat, more_data; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + if (xflag) + pmic_put_tx_data(tp, xdata); + + more_data = 1; /* tell server there have more data followed */ + pmic_put_tx_data(tp, more_data); + + size >>= 2; + for (i = 0; i < size; i++) { + pmic_put_tx_data(tp, *data); + data++; + } + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + +/** + * pmic_rpc_set_get() - set one argument and get one argument + * @setdata: set argument + * @*getdata: memory to store argumnet + * @size: size of memory + * @proc: command/request id + * + * This function covers case f + */ +static int pmic_rpc_set_get(uint setdata, uint *getdata, int size, int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + unsigned int *lp; + int i, stat, more_data; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + pmic_put_tx_data(tp, setdata); + + /* + * more_data = TRUE to ask server reply with requested datum + * otherwise, server will reply without datum + */ + more_data = (getdata != NULL); + pmic_put_tx_data(tp, more_data); + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + pmic_pull_rx_data(rp, &more_data); + + if (more_data) { /* more data followed */ + size >>= 2; + lp = getdata; + for (i = 0; i < size; i++) { + if (pmic_pull_rx_data(rp, lp++) < 0) + break; /* not supposed to happen */ + } + } + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + +/** + * pmic_rpc_get_only() - get one or more than one arguments + * @*getdata: memory to store arguments + * @size: size of mmory + * @proc: command/request id + * + * This function covers case g + */ +static int pmic_rpc_get_only(uint *getdata, int size, int proc) +{ + struct pmic_ctrl *pm = &pmic_ctrl; + struct pmic_buf *tp; + struct pmic_buf *rp; + unsigned int *lp; + int i, stat, more_data; + + + if (mutex_lock_interruptible(&pmic_mtx)) + return -ERESTARTSYS; + + if (pm->inited <= 0) { + stat = pmic_buf_init(); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + } + + tp = &pm->tbuf; + rp = &pm->rbuf; + + pmic_buf_reset(tp); + pmic_buf_reserve(tp, sizeof(struct rpc_request_hdr)); + pmic_buf_reset(rp); + + /* + * more_data = TRUE to ask server reply with requested datum + * otherwise, server will reply without datum + */ + more_data = (getdata != NULL); + pmic_put_tx_data(tp, more_data); + + stat = pmic_rpc_req_reply(tp, rp, proc); + if (stat < 0) { + mutex_unlock(&pmic_mtx); + return stat; + } + + pmic_pull_rx_data(rp, &stat); /* result from server */ + pmic_pull_rx_data(rp, &more_data); + + if (more_data) { /* more data followed */ + size >>= 2; + lp = getdata; + for (i = 0; i < size; i++) { + if (pmic_pull_rx_data(rp, lp++) < 0) + break; /* not supposed to happen */ + } + } + + mutex_unlock(&pmic_mtx); + + return modem_to_linux_err(stat); +} + + +int pmic_lp_mode_control(enum switch_cmd cmd, enum vreg_lp_id id) +{ + return pmic_rpc_set_only(cmd, id, 0, 0, 2, LP_MODE_CONTROL_PROC); +} +EXPORT_SYMBOL(pmic_lp_mode_control); + +int pmic_vreg_set_level(enum vreg_id vreg, int level) +{ + return pmic_rpc_set_only(vreg, level, 0, 0, 2, VREG_SET_LEVEL_PROC); +} +EXPORT_SYMBOL(pmic_vreg_set_level); + +int pmic_vreg_pull_down_switch(enum switch_cmd cmd, enum vreg_pdown_id id) +{ + return pmic_rpc_set_only(cmd, id, 0, 0, 2, VREG_PULL_DOWN_SWITCH_PROC); +} +EXPORT_SYMBOL(pmic_vreg_pull_down_switch); + +int pmic_secure_mpp_control_digital_output(enum mpp_which which, + enum mpp_dlogic_level level, + enum mpp_dlogic_out_ctrl out) +{ + return pmic_rpc_set_only(which, level, out, 0, 3, + SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_control_digital_output); + +int pmic_secure_mpp_config_i_sink(enum mpp_which which, + enum mpp_i_sink_level level, + enum mpp_i_sink_switch onoff) +{ + return pmic_rpc_set_only(which, level, onoff, 0, 3, + SECURE_MPP_CONFIG_I_SINK_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_config_i_sink); + +int pmic_secure_mpp_config_digital_input(enum mpp_which which, + enum mpp_dlogic_level level, + enum mpp_dlogic_in_dbus dbus) +{ + return pmic_rpc_set_only(which, level, dbus, 0, 3, + SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC); +} +EXPORT_SYMBOL(pmic_secure_mpp_config_digital_input); + +int pmic_rtc_start(struct rtc_time *time) +{ + return pmic_rpc_set_struct(0, 0, (uint *)time, sizeof(*time), + RTC_START_PROC); +} +EXPORT_SYMBOL(pmic_rtc_start); + +int pmic_rtc_stop(void) +{ + return pmic_rpc_set_only(0, 0, 0, 0, 0, RTC_STOP_PROC); +} +EXPORT_SYMBOL(pmic_rtc_stop); + +int pmic_rtc_get_time(struct rtc_time *time) +{ + return pmic_rpc_get_only((uint *)time, sizeof(*time), + RTC_GET_TIME_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_time); + +int pmic_rtc_enable_alarm(enum rtc_alarm alarm, + struct rtc_time *time) +{ + return pmic_rpc_set_struct(1, alarm, (uint *)time, sizeof(*time), + RTC_ENABLE_ALARM_PROC); +} +EXPORT_SYMBOL(pmic_rtc_enable_alarm); + +int pmic_rtc_disable_alarm(enum rtc_alarm alarm) +{ + return pmic_rpc_set_only(alarm, 0, 0, 0, 1, RTC_DISABLE_ALARM_PROC); +} +EXPORT_SYMBOL(pmic_rtc_disable_alarm); + +int pmic_rtc_get_alarm_time(enum rtc_alarm alarm, + struct rtc_time *time) +{ + return pmic_rpc_set_get(alarm, (uint *)time, sizeof(*time), + RTC_GET_ALARM_TIME_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_alarm_time); + +int pmic_rtc_get_alarm_status(uint *status) +{ + return pmic_rpc_get_only(status, sizeof(*status), + RTC_GET_ALARM_STATUS_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_alarm_status); + +int pmic_rtc_set_time_adjust(uint adjust) +{ + return pmic_rpc_set_only(adjust, 0, 0, 0, 1, + RTC_SET_TIME_ADJUST_PROC); +} +EXPORT_SYMBOL(pmic_rtc_set_time_adjust); + +int pmic_rtc_get_time_adjust(uint *adjust) +{ + return pmic_rpc_get_only(adjust, sizeof(*adjust), + RTC_GET_TIME_ADJUST_PROC); +} +EXPORT_SYMBOL(pmic_rtc_get_time_adjust); + +/* + * generic speaker + */ +int pmic_speaker_cmd(const enum spkr_cmd cmd) +{ + return pmic_rpc_set_only(cmd, 0, 0, 0, 1, SPEAKER_CMD_PROC); +} +EXPORT_SYMBOL(pmic_speaker_cmd); + +int pmic_set_spkr_configuration(struct spkr_config_mode *cfg) +{ + return pmic_rpc_set_struct(0, 0, (uint *)cfg, sizeof(*cfg), + SET_SPKR_CONFIGURATION_PROC); +} +EXPORT_SYMBOL(pmic_set_spkr_configuration); + +int pmic_get_spkr_configuration(struct spkr_config_mode *cfg) +{ + return pmic_rpc_get_only((uint *)cfg, sizeof(*cfg), + GET_SPKR_CONFIGURATION_PROC); +} +EXPORT_SYMBOL(pmic_get_spkr_configuration); + +int pmic_spkr_en_right_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_RIGHT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_right_chan); + +int pmic_spkr_is_right_chan_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_RIGHT_CHAN_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_right_chan_en); + +int pmic_spkr_en_left_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_LEFT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_left_chan); + +int pmic_spkr_is_left_chan_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_LEFT_CHAN_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_left_chan_en); + +int pmic_set_speaker_gain(enum spkr_gain gain) +{ + return pmic_rpc_set_only(gain, 0, 0, 0, 1, SET_SPEAKER_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_set_speaker_gain); + +int pmic_set_speaker_delay(enum spkr_dly delay) +{ + return pmic_rpc_set_only(delay, 0, 0, 0, 1, SET_SPEAKER_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_set_speaker_delay); + +int pmic_speaker_1k6_zin_enable(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPEAKER_1K6_ZIN_ENABLE_PROC); +} +EXPORT_SYMBOL(pmic_speaker_1k6_zin_enable); + +int pmic_spkr_set_mux_hpf_corner_freq(enum spkr_hpf_corner_freq freq) +{ + return pmic_rpc_set_only(freq, 0, 0, 0, 1, + SPKR_SET_MUX_HPF_CORNER_FREQ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_mux_hpf_corner_freq); + +int pmic_spkr_get_mux_hpf_corner_freq(enum spkr_hpf_corner_freq *freq) +{ + return pmic_rpc_get_only(freq, sizeof(*freq), + SPKR_GET_MUX_HPF_CORNER_FREQ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_get_mux_hpf_corner_freq); + +int pmic_spkr_select_usb_with_hpf_20hz(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_SELECT_USB_WITH_HPF_20HZ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_select_usb_with_hpf_20hz); + +int pmic_spkr_is_usb_with_hpf_20hz(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_USB_WITH_HPF_20HZ_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_usb_with_hpf_20hz); + +int pmic_spkr_bypass_mux(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_BYPASS_MUX_PROC); +} +EXPORT_SYMBOL(pmic_spkr_bypass_mux); + +int pmic_spkr_is_mux_bypassed(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_MUX_BYPASSED_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_mux_bypassed); + +int pmic_spkr_en_hpf(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_HPF_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_hpf); + +int pmic_spkr_is_hpf_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_HPF_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_hpf_en); + +int pmic_spkr_en_sink_curr_from_ref_volt_cir(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_sink_curr_from_ref_volt_cir); + +int pmic_spkr_is_sink_curr_from_ref_volt_cir_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_sink_curr_from_ref_volt_cir_en); + +/* + * speaker indexed by left_right + */ +int pmic_spkr_en(enum spkr_left_right left_right, uint enable) +{ + return pmic_rpc_set_only(left_right, enable, 0, 0, 2, SPKR_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en); + +int pmic_spkr_is_en(enum spkr_left_right left_right, uint *enabled) +{ + return pmic_rpc_set_get(left_right, enabled, sizeof(*enabled), + SPKR_IS_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_en); + +int pmic_spkr_set_gain(enum spkr_left_right left_right, enum spkr_gain gain) +{ + return pmic_rpc_set_only(left_right, gain, 0, 0, 2, SPKR_SET_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_gain); + +int pmic_spkr_get_gain(enum spkr_left_right left_right, enum spkr_gain *gain) +{ + return pmic_rpc_set_get(left_right, gain, sizeof(*gain), + SPKR_GET_GAIN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_get_gain); + +int pmic_spkr_set_delay(enum spkr_left_right left_right, enum spkr_dly delay) +{ + return pmic_rpc_set_only(left_right, delay, 0, 0, 2, + SPKR_SET_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_spkr_set_delay); + +int pmic_spkr_get_delay(enum spkr_left_right left_right, enum spkr_dly *delay) +{ + return pmic_rpc_set_get(left_right, delay, sizeof(*delay), + SPKR_GET_DELAY_PROC); +} +EXPORT_SYMBOL(pmic_spkr_get_delay); + +int pmic_spkr_en_mute(enum spkr_left_right left_right, uint enabled) +{ + return pmic_rpc_set_only(left_right, enabled, 0, 0, 2, + SPKR_EN_MUTE_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_mute); + +int pmic_spkr_is_mute_en(enum spkr_left_right left_right, uint *enabled) +{ + return pmic_rpc_set_get(left_right, enabled, sizeof(*enabled), + SPKR_IS_MUTE_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_mute_en); + +/* + * mic + */ +int pmic_mic_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, MIC_EN_PROC); +} +EXPORT_SYMBOL(pmic_mic_en); + +int pmic_mic_is_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), MIC_IS_EN_PROC); +} +EXPORT_SYMBOL(pmic_mic_is_en); + +int pmic_mic_set_volt(enum mic_volt vol) +{ + return pmic_rpc_set_only(vol, 0, 0, 0, 1, MIC_SET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_mic_set_volt); + +int pmic_mic_get_volt(enum mic_volt *voltage) +{ + return pmic_rpc_get_only(voltage, sizeof(*voltage), MIC_GET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_mic_get_volt); + +int pmic_vib_mot_set_volt(uint vol) +{ + return pmic_rpc_set_only(vol, 0, 0, 0, 1, VIB_MOT_SET_VOLT_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_volt); + +int pmic_vib_mot_set_mode(enum pm_vib_mot_mode mode) +{ + return pmic_rpc_set_only(mode, 0, 0, 0, 1, VIB_MOT_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_mode); + +int pmic_vib_mot_set_polarity(enum pm_vib_mot_pol pol) +{ + return pmic_rpc_set_only(pol, 0, 0, 0, 1, VIB_MOT_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_vib_mot_set_polarity); + +int pmic_vid_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_en); + +int pmic_vid_is_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), VID_IS_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_is_en); + +int pmic_vid_load_detect_en(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, VID_LOAD_DETECT_EN_PROC); +} +EXPORT_SYMBOL(pmic_vid_load_detect_en); + +int pmic_set_led_intensity(enum ledtype type, int level) +{ + return pmic_rpc_set_only(type, level, 0, 0, 2, SET_LED_INTENSITY_PROC); +} +EXPORT_SYMBOL(pmic_set_led_intensity); + +int pmic_flash_led_set_current(const uint16_t milliamps) +{ + return pmic_rpc_set_only(milliamps, 0, 0, 0, 1, + FLASH_LED_SET_CURRENT_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_current); + +int pmic_flash_led_set_mode(enum flash_led_mode mode) +{ + return pmic_rpc_set_only((int)mode, 0, 0, 0, 1, + FLASH_LED_SET_MODE_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_mode); + +int pmic_flash_led_set_polarity(enum flash_led_pol pol) +{ + return pmic_rpc_set_only((int)pol, 0, 0, 0, 1, + FLASH_LED_SET_POLARITY_PROC); +} +EXPORT_SYMBOL(pmic_flash_led_set_polarity); + +int pmic_spkr_add_right_left_chan(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, + SPKR_ADD_RIGHT_LEFT_CHAN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_add_right_left_chan); + +int pmic_spkr_is_right_left_chan_added(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_right_left_chan_added); + +int pmic_spkr_en_stereo(uint enable) +{ + return pmic_rpc_set_only(enable, 0, 0, 0, 1, SPKR_EN_STEREO_PROC); +} +EXPORT_SYMBOL(pmic_spkr_en_stereo); + +int pmic_spkr_is_stereo_en(uint *enabled) +{ + return pmic_rpc_get_only(enabled, sizeof(*enabled), + SPKR_IS_STEREO_EN_PROC); +} +EXPORT_SYMBOL(pmic_spkr_is_stereo_en); + +int pmic_hsed_set_period( + enum hsed_controller controller, + enum hsed_period_pre_div period_pre_div, + enum hsed_period_time period_time +) +{ + return pmic_rpc_set_only(controller, period_pre_div, period_time, 0, + 3, + HSED_SET_PERIOD_PROC); +} +EXPORT_SYMBOL(pmic_hsed_set_period); + +int pmic_hsed_set_hysteresis( + enum hsed_controller controller, + enum hsed_hyst_pre_div hyst_pre_div, + enum hsed_hyst_time hyst_time +) +{ + return pmic_rpc_set_only(controller, hyst_pre_div, hyst_time, 0, + 3, + HSED_SET_HYSTERESIS_PROC); +} +EXPORT_SYMBOL(pmic_hsed_set_hysteresis); + +int pmic_hsed_set_current_threshold( + enum hsed_controller controller, + enum hsed_switch switch_hsed, + uint32_t current_threshold +) +{ + return pmic_rpc_set_only(controller, switch_hsed, current_threshold, 0, + 3, + HSED_SET_CURRENT_THRESHOLD_PROC); +} +EXPORT_SYMBOL(pmic_hsed_set_current_threshold); + +int pmic_hsed_enable( + enum hsed_controller controller, + enum hsed_enable enable_hsed +) +{ + return pmic_rpc_set_only(controller, enable_hsed, 0, 0, + 2, + HSED_ENABLE_PROC); +} +EXPORT_SYMBOL(pmic_hsed_enable); diff --git a/arch/arm/mach-msm/pmic8058-gpio.c b/arch/arm/mach-msm/pmic8058-gpio.c new file mode 100644 index 000000000000..09a465d028d9 --- /dev/null +++ b/arch/arm/mach-msm/pmic8058-gpio.c @@ -0,0 +1,150 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Qualcomm PMIC8058 GPIO driver + * + */ + +#include <linux/gpio.h> +#include <linux/mfd/pmic8058.h> +#include "gpio_chip.h" + +#define PM8058_GPIO_TO_INT(n) (PMIC8058_IRQ_BASE + (n)) + +static int pm8058_gpio_configure(struct gpio_chip *chip, + unsigned int gpio, + unsigned long flags) +{ + int rc = 0, direction; + + gpio -= chip->start; + + if (flags & (GPIOF_INPUT | GPIOF_DRIVE_OUTPUT)) { + direction = 0; + if (flags & GPIOF_INPUT) + direction |= PM_GPIO_DIR_IN; + if (flags & GPIOF_DRIVE_OUTPUT) + direction |= PM_GPIO_DIR_OUT; + + if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) { + if (flags & GPIOF_OUTPUT_HIGH) + rc = pm8058_gpio_set(gpio, 1); + else + rc = pm8058_gpio_set(gpio, 0); + + if (rc) { + pr_err("%s: FAIL pm8058_gpio_set(): rc=%d.\n", + __func__, rc); + goto bail_out; + } + } + + rc = pm8058_gpio_set_direction(gpio, direction); + if (rc) + pr_err("%s: FAIL pm8058_gpio_config(): rc=%d.\n", + __func__, rc); + } + +bail_out: + return rc; +} + +static int pm8058_gpio_get_irq_num(struct gpio_chip *chip, + unsigned int gpio, + unsigned int *irqp, + unsigned long *irqnumflagsp) +{ + gpio -= chip->start; + *irqp = PM8058_GPIO_TO_INT(gpio); + if (irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + +static int pm8058_gpio_read(struct gpio_chip *chip, unsigned n) +{ + n -= chip->start; + return pm8058_gpio_get(n); +} + +static int pm8058_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on) +{ + n -= chip->start; + return pm8058_gpio_set(n, on); +} + +struct msm_gpio_chip pm8058_gpio_chip = { + .chip = { + .start = NR_GPIO_IRQS, + .end = NR_GPIO_IRQS + NR_PMIC8058_GPIO_IRQS - 1, + .configure = pm8058_gpio_configure, + .get_irq_num = pm8058_gpio_get_irq_num, + .read = pm8058_gpio_read, + .write = pm8058_gpio_write, + } +}; + +static int __init pm8058_gpio_init(void) +{ + int rc; + + rc = register_gpio_chip(&pm8058_gpio_chip.chip); + pr_info("%s: register_gpio_chip(): rc=%d\n", __func__, rc); + + return rc; +} +device_initcall(pm8058_gpio_init); diff --git a/arch/arm/mach-msm/pmic8058-mpp.c b/arch/arm/mach-msm/pmic8058-mpp.c new file mode 100644 index 000000000000..5c4f396c544b --- /dev/null +++ b/arch/arm/mach-msm/pmic8058-mpp.c @@ -0,0 +1,105 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Qualcomm PMIC8058 MPP driver + * + */ + +#include <linux/gpio.h> +#include <linux/mfd/pmic8058.h> +#include "gpio_chip.h" + +#define PM8058_MPP_TO_INT(n) (PMIC8058_IRQ_BASE + NR_PMIC8058_GPIO_IRQS + (n)) + +static int pm8058_mpp_get_irq_num(struct gpio_chip *chip, + unsigned int gpio, + unsigned int *irqp, + unsigned long *irqnumflagsp) +{ + gpio -= chip->start; + *irqp = PM8058_MPP_TO_INT(gpio); + if (irqnumflagsp) + *irqnumflagsp = 0; + return 0; +} + +static int pm8058_mpp_read(struct gpio_chip *chip, unsigned n) +{ + n -= chip->start; + return pm8058_mpp_get(n); +} + +struct msm_gpio_chip pm8058_mpp_chip = { + .chip = { + .start = NR_GPIO_IRQS + NR_PMIC8058_GPIO_IRQS, + .end = NR_GPIO_IRQS + NR_PMIC8058_GPIO_IRQS + + NR_PMIC8058_MPP_IRQS - 1, + .get_irq_num = pm8058_mpp_get_irq_num, + .read = pm8058_mpp_read, + } +}; + +static int __init pm8058_mpp_init(void) +{ + int rc; + + rc = register_gpio_chip(&pm8058_mpp_chip.chip); + pr_info("%s: register_gpio_chip(): rc=%d\n", __func__, rc); + + return rc; +} +device_initcall(pm8058_mpp_init); diff --git a/arch/arm/mach-msm/pmic_debugfs.c b/arch/arm/mach-msm/pmic_debugfs.c new file mode 100644 index 000000000000..3614c3bcde0e --- /dev/null +++ b/arch/arm/mach-msm/pmic_debugfs.c @@ -0,0 +1,1200 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/uaccess.h> + +#include <mach/pmic.h> + + +static int debug_lp_mode_control(char *buf, int size) +{ + enum switch_cmd cmd; + enum vreg_lp_id id; + int cnt; + + + cnt = sscanf(buf, "%u %u", &cmd, &id); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d", __func__, cnt); + return -EINVAL; + } + + if (pmic_lp_mode_control(cmd, id) < 0) + return -EFAULT; + + return size; +} + +static int debug_vreg_set_level(char *buf, int size) +{ + enum vreg_id vreg; + int level; + int cnt; + + cnt = sscanf(buf, "%u %u", &vreg, &level); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d", __func__, cnt); + return -EINVAL; + } + if (pmic_vreg_set_level(vreg, level) < 0) + return -EFAULT; + + return size; +} + +static int debug_vreg_pull_down_switch(char *buf, int size) +{ + enum switch_cmd cmd; + enum vreg_pdown_id id; + int cnt; + + cnt = sscanf(buf, "%u %u", &cmd, &id); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d", __func__, cnt); + return -EINVAL; + } + if (pmic_vreg_pull_down_switch(cmd, id) < 0) + return -EFAULT; + + return size; +} + +static int debug_secure_mpp_control_digital_output(char *buf, int size) +{ + enum mpp_which which; + enum mpp_dlogic_level level; + enum mpp_dlogic_out_ctrl out; + int cnt; + + cnt = sscanf(buf, "%u %u %u", &which, &level, &out); + if (cnt < 3) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + + if (pmic_secure_mpp_control_digital_output(which, level, out) < 0) + return -EFAULT; + + return size; +} + +static int debug_secure_mpp_config_i_sink(char *buf, int size) +{ + enum mpp_which which; + enum mpp_i_sink_level level; + enum mpp_i_sink_switch onoff; + int cnt; + + cnt = sscanf(buf, "%u %u %u", &which, &level, &onoff); + if (cnt < 3) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + + if (pmic_secure_mpp_config_i_sink(which, level, onoff) < 0) + return -EFAULT; + + return size; +} + +static int debug_secure_mpp_config_digital_input(char *buf, int size) +{ + enum mpp_which which; + enum mpp_dlogic_level level; + enum mpp_dlogic_in_dbus dbus; + int cnt; + + cnt = sscanf(buf, "%u %u %u", &which, &level, &dbus); + if (cnt < 3) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_secure_mpp_config_digital_input(which, level, dbus) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_start(char *buf, int size) +{ + uint time; + struct rtc_time *hal; + int cnt; + + cnt = sscanf(buf, "%d", &time); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + hal = (struct rtc_time *)&time; + if (pmic_rtc_start(hal) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_stop(char *buf, int size) +{ + if (pmic_rtc_stop() < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_get_time(char *buf, int size) +{ + uint time; + struct rtc_time *hal; + + hal = (struct rtc_time *)&time; + if (pmic_rtc_get_time(hal) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", time); +} + +static int debug_rtc_alarm_ndx; + +int debug_rtc_enable_alarm(char *buf, int size) +{ + enum rtc_alarm alarm; + struct rtc_time *hal; + uint time; + int cnt; + + + cnt = sscanf(buf, "%u %u", &alarm, &time); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + hal = (struct rtc_time *)&time; + + if (pmic_rtc_enable_alarm(alarm, hal) < 0) + return -EFAULT; + + debug_rtc_alarm_ndx = alarm; + return size; +} + +static int debug_rtc_disable_alarm(char *buf, int size) +{ + + enum rtc_alarm alarm; + int cnt; + + cnt = sscanf(buf, "%u", &alarm); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_rtc_disable_alarm(alarm) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_get_alarm_time(char *buf, int size) +{ + uint time; + struct rtc_time *hal; + + hal = (struct rtc_time *)&time; + if (pmic_rtc_get_alarm_time(debug_rtc_alarm_ndx, hal) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", time); +} +static int debug_rtc_get_alarm_status(char *buf, int size) +{ + int status;; + + if (pmic_rtc_get_alarm_status(&status) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", status); + +} + +static int debug_rtc_set_time_adjust(char *buf, int size) +{ + uint adjust; + int cnt; + + cnt = sscanf(buf, "%d", &adjust); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_rtc_set_time_adjust(adjust) < 0) + return -EFAULT; + + return size; +} + +static int debug_rtc_get_time_adjust(char *buf, int size) +{ + int adjust;; + + if (pmic_rtc_get_time_adjust(&adjust) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", adjust); +} + +static int debug_set_led_intensity(char *buf, int size) +{ + enum ledtype type; + int level; + int cnt; + + cnt = sscanf(buf, "%u %d", &type, &level); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_set_led_intensity(type, level) < 0) + return -EFAULT; + + return size; +} + +static int debug_flash_led_set_current(char *buf, int size) +{ + int milliamps; + int cnt; + + cnt = sscanf(buf, "%d", &milliamps); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_flash_led_set_current(milliamps) < 0) + return -EFAULT; + + return size; +} +static int debug_flash_led_set_mode(char *buf, int size) +{ + + uint mode; + int cnt; + + cnt = sscanf(buf, "%d", &mode); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_flash_led_set_mode(mode) < 0) + return -EFAULT; + + return size; +} + +static int debug_flash_led_set_polarity(char *buf, int size) +{ + int pol; + int cnt; + + cnt = sscanf(buf, "%d", &pol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_flash_led_set_polarity(pol) < 0) + return -EFAULT; + + return size; +} + +static int debug_speaker_cmd(char *buf, int size) +{ + int cmd; + int cnt; + + cnt = sscanf(buf, "%d", &cmd); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_speaker_cmd(cmd) < 0) + return -EFAULT; + + return size; +} +static int debug_set_speaker_gain(char *buf, int size) +{ + int gain; + int cnt; + + cnt = sscanf(buf, "%d", &gain); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_set_speaker_gain(gain) < 0) + return -EFAULT; + + return size; +} + +static int debug_mic_en(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_mic_en(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_mic_is_en(char *buf, int size) +{ + int enabled; + + if (pmic_mic_is_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_mic_set_volt(char *buf, int size) +{ + int vol; + int cnt; + + cnt = sscanf(buf, "%d", &vol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_mic_set_volt(vol) < 0) + return -EFAULT; + + return size; +} + +static int debug_mic_get_volt(char *buf, int size) +{ + uint vol; + + if (pmic_mic_get_volt(&vol) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", vol); +} + +static int debug_spkr_en_right_chan(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_right_chan(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_right_chan_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_right_chan_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} +static int debug_spkr_en_left_chan(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_left_chan(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_left_chan_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_left_chan_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_set_spkr_configuration(char *buf, int size) +{ + + struct spkr_config_mode cfg; + int cnt; + + cnt = sscanf(buf, "%d %d %d %d %d %d %d %d", + &cfg.is_right_chan_en, + &cfg.is_left_chan_en, + &cfg.is_right_left_chan_added, + &cfg.is_stereo_en, + &cfg.is_usb_with_hpf_20hz, + &cfg.is_mux_bypassed, + &cfg.is_hpf_en, + &cfg.is_sink_curr_from_ref_volt_cir_en); + + if (cnt < 8) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + + if (pmic_set_spkr_configuration(&cfg) < 0) + return -EFAULT; + + return size; +} + +static int debug_get_spkr_configuration(char *buf, int size) +{ + struct spkr_config_mode cfg; + + if (pmic_get_spkr_configuration(&cfg) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d %d %d %d %d %d %d %d\n", + cfg.is_right_chan_en, + cfg.is_left_chan_en, + cfg.is_right_left_chan_added, + cfg.is_stereo_en, + cfg.is_usb_with_hpf_20hz, + cfg.is_mux_bypassed, + cfg.is_hpf_en, + cfg.is_sink_curr_from_ref_volt_cir_en); + +} + +static int debug_set_speaker_delay(char *buf, int size) +{ + int delay; + int cnt; + + cnt = sscanf(buf, "%d", &delay); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_set_speaker_delay(delay) < 0) + return -EFAULT; + + return size; +} + +static int debug_speaker_1k6_zin_enable(char *buf, int size) +{ + uint enable; + int cnt; + + cnt = sscanf(buf, "%u", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_speaker_1k6_zin_enable(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_set_mux_hpf_corner_freq(char *buf, int size) +{ + int freq; + int cnt; + + cnt = sscanf(buf, "%d", &freq); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_set_mux_hpf_corner_freq(freq) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_get_mux_hpf_corner_freq(char *buf, int size) +{ + uint freq; + + if (pmic_spkr_get_mux_hpf_corner_freq(&freq) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", freq); +} + +static int debug_spkr_add_right_left_chan(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_add_right_left_chan(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_right_left_chan_added(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_right_left_chan_added(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_en_stereo(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_stereo(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_stereo_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_stereo_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_select_usb_with_hpf_20hz(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_select_usb_with_hpf_20hz(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_usb_with_hpf_20hz(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_usb_with_hpf_20hz(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_bypass_mux(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_bypass_mux(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_mux_bypassed(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_mux_bypassed(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_en_hpf(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_hpf(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_spkr_is_hpf_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_hpf_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_en_sink_curr_from_ref_volt_cir(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_sink_curr_from_ref_volt_cir(enable) < 0) + return -EFAULT; + + return size; +} + +static int debug_spkr_is_sink_curr_from_ref_volt_cir_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_sink_curr_from_ref_volt_cir_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_vib_mot_set_volt(char *buf, int size) +{ + int vol; + int cnt; + + cnt = sscanf(buf, "%d", &vol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vib_mot_set_volt(vol) < 0) + return -EFAULT; + + return size; +} +static int debug_vib_mot_set_mode(char *buf, int size) +{ + int mode; + int cnt; + + cnt = sscanf(buf, "%d", &mode); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vib_mot_set_mode(mode) < 0) + return -EFAULT; + + return size; +} + +static int debug_vib_mot_set_polarity(char *buf, int size) +{ + int pol; + int cnt; + + cnt = sscanf(buf, "%d", &pol); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vib_mot_set_polarity(pol) < 0) + return -EFAULT; + + return size; +} +static int debug_vid_en(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vid_en(enable) < 0) + return -EFAULT; + + return size; +} +static int debug_vid_is_en(char *buf, int size) +{ + int enabled; + + if (pmic_vid_is_en(&enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_vid_load_detect_en(char *buf, int size) +{ + int enable; + int cnt; + + cnt = sscanf(buf, "%d", &enable); + if (cnt < 1) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_vid_load_detect_en(enable) < 0) + return -EFAULT; + + return size; +} + +/************************************************** + * speaker indexed by left_right +**************************************************/ +static enum spkr_left_right debug_spkr_left_right = LEFT_SPKR; + +static int debug_spkr_en(char *buf, int size) +{ + int left_right; + int enable; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &enable); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en(left_right, enable) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_is_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_en(debug_spkr_left_right, &enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +static int debug_spkr_set_gain(char *buf, int size) +{ + int left_right; + int enable; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &enable); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_set_gain(left_right, enable) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_get_gain(char *buf, int size) +{ + uint gain; + + if (pmic_spkr_get_gain(debug_spkr_left_right, &gain) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", gain); +} +static int debug_spkr_set_delay(char *buf, int size) +{ + int left_right; + int delay; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &delay); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_set_delay(left_right, delay) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_get_delay(char *buf, int size) +{ + uint delay; + + if (pmic_spkr_get_delay(debug_spkr_left_right, &delay) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", delay); +} + +static int debug_spkr_en_mute(char *buf, int size) +{ + int left_right; + int enable; + int cnt; + + cnt = sscanf(buf, "%d %d", &left_right, &enable); + if (cnt < 2) { + printk(KERN_ERR "%s: sscanf failed cnt=%d" , __func__, cnt); + return -EINVAL; + } + if (pmic_spkr_en_mute(left_right, enable) >= 0) { + debug_spkr_left_right = left_right; + return size; + } + return -EFAULT; +} + +static int debug_spkr_is_mute_en(char *buf, int size) +{ + int enabled; + + if (pmic_spkr_is_mute_en(debug_spkr_left_right, &enabled) < 0) + return -EFAULT; + + return snprintf(buf, size, "%d\n", enabled); +} + +/******************************************************************* + * debug function table +*******************************************************************/ + +struct pmic_debug_desc { + int (*get) (char *, int); + int (*set) (char *, int); +}; + +struct pmic_debug_desc pmic_debug[] = { + {NULL, NULL}, /*LIB_NULL_PROC */ + {NULL, NULL}, /* LIB_RPC_GLUE_CODE_INFO_REMOTE_PROC */ + {NULL, debug_lp_mode_control}, /* LP_MODE_CONTROL_PROC */ + {NULL, debug_vreg_set_level}, /*VREG_SET_LEVEL_PROC */ + {NULL, debug_vreg_pull_down_switch}, /*VREG_PULL_DOWN_SWITCH_PROC */ + {NULL, debug_secure_mpp_control_digital_output}, + /* SECURE_MPP_CONFIG_DIGITAL_OUTPUT_PROC */ + /*SECURE_MPP_CONFIG_I_SINK_PROC */ + {NULL, debug_secure_mpp_config_i_sink}, + {NULL, debug_rtc_start}, /*RTC_START_PROC */ + {NULL, debug_rtc_stop}, /* RTC_STOP_PROC */ + {debug_rtc_get_time, NULL}, /* RTC_GET_TIME_PROC */ + {NULL, debug_rtc_enable_alarm}, /* RTC_ENABLE_ALARM_PROC */ + {NULL , debug_rtc_disable_alarm}, /*RTC_DISABLE_ALARM_PROC */ + {debug_rtc_get_alarm_time, NULL}, /* RTC_GET_ALARM_TIME_PROC */ + {debug_rtc_get_alarm_status, NULL}, /* RTC_GET_ALARM_STATUS_PROC */ + {NULL, debug_rtc_set_time_adjust}, /* RTC_SET_TIME_ADJUST_PROC */ + {debug_rtc_get_time_adjust, NULL}, /* RTC_GET_TIME_ADJUST_PROC */ + {NULL, debug_set_led_intensity}, /* SET_LED_INTENSITY_PROC */ + {NULL, debug_flash_led_set_current}, /* FLASH_LED_SET_CURRENT_PROC */ + {NULL, debug_flash_led_set_mode}, /* FLASH_LED_SET_MODE_PROC */ + {NULL, debug_flash_led_set_polarity}, /* FLASH_LED_SET_POLARITY_PROC */ + {NULL, debug_speaker_cmd}, /* SPEAKER_CMD_PROC */ + {NULL, debug_set_speaker_gain}, /* SET_SPEAKER_GAIN_PROC */ + {NULL, debug_vib_mot_set_volt}, /* VIB_MOT_SET_VOLT_PROC */ + {NULL, debug_vib_mot_set_mode}, /* VIB_MOT_SET_MODE_PROC */ + {NULL, debug_vib_mot_set_polarity}, /* VIB_MOT_SET_POLARITY_PROC */ + {NULL, debug_vid_en}, /* VID_EN_PROC */ + {debug_vid_is_en, NULL}, /* VID_IS_EN_PROC */ + {NULL, debug_vid_load_detect_en}, /* VID_LOAD_DETECT_EN_PROC */ + {NULL, debug_mic_en}, /* MIC_EN_PROC */ + {debug_mic_is_en, NULL}, /* MIC_IS_EN_PROC */ + {NULL, debug_mic_set_volt}, /* MIC_SET_VOLT_PROC */ + {debug_mic_get_volt, NULL}, /* MIC_GET_VOLT_PROC */ + {NULL, debug_spkr_en_right_chan}, /* SPKR_EN_RIGHT_CHAN_PROC */ + {debug_spkr_is_right_chan_en, NULL}, /* SPKR_IS_RIGHT_CHAN_EN_PROC */ + {NULL, debug_spkr_en_left_chan}, /* SPKR_EN_LEFT_CHAN_PROC */ + {debug_spkr_is_left_chan_en, NULL}, /* SPKR_IS_LEFT_CHAN_EN_PROC */ + {NULL, debug_set_spkr_configuration}, /* SET_SPKR_CONFIGURATION_PROC */ + {debug_get_spkr_configuration, NULL}, /* GET_SPKR_CONFIGURATION_PROC */ + {debug_spkr_get_gain, NULL}, /* SPKR_GET_GAIN_PROC */ + {debug_spkr_is_en, NULL}, /* SPKR_IS_EN_PROC */ + {NULL, debug_spkr_en_mute}, /* SPKR_EN_MUTE_PROC */ + {debug_spkr_is_mute_en, NULL}, /* SPKR_IS_MUTE_EN_PROC */ + {NULL, debug_spkr_set_delay}, /* SPKR_SET_DELAY_PROC */ + {debug_spkr_get_delay, NULL}, /* SPKR_GET_DELAY_PROC */ + /* SECURE_MPP_CONFIG_DIGITAL_INPUT_PROC */ + {NULL, debug_secure_mpp_config_digital_input}, + {NULL, debug_set_speaker_delay}, /* SET_SPEAKER_DELAY_PROC */ + {NULL, debug_speaker_1k6_zin_enable}, /* SPEAKER_1K6_ZIN_ENABLE_PROC */ + /* SPKR_SET_MUX_HPF_CORNER_FREQ_PROC */ + {NULL, debug_spkr_set_mux_hpf_corner_freq}, + /* SPKR_GET_MUX_HPF_CORNER_FREQ_PROC */ + {debug_spkr_get_mux_hpf_corner_freq, NULL}, + /* SPKR_IS_RIGHT_LEFT_CHAN_ADDED_PROC */ + {debug_spkr_is_right_left_chan_added, NULL}, + {NULL, debug_spkr_en_stereo}, /* SPKR_EN_STEREO_PROC */ + {debug_spkr_is_stereo_en, NULL}, /* SPKR_IS_STEREO_EN_PROC */ + /* SPKR_SELECT_USB_WITH_HPF_20HZ_PROC */ + {NULL, debug_spkr_select_usb_with_hpf_20hz}, + /* SPKR_IS_USB_WITH_HPF_20HZ_PROC */ + {debug_spkr_is_usb_with_hpf_20hz, NULL}, + {NULL, debug_spkr_bypass_mux}, /* SPKR_BYPASS_MUX_PROC */ + {debug_spkr_is_mux_bypassed, NULL}, /* SPKR_IS_MUX_BYPASSED_PROC */ + {NULL, debug_spkr_en_hpf}, /* SPKR_EN_HPF_PROC */ + { debug_spkr_is_hpf_en, NULL}, /* SPKR_IS_HPF_EN_PROC */ + /* SPKR_EN_SINK_CURR_FROM_REF_VOLT_CIR_PROC */ + {NULL, debug_spkr_en_sink_curr_from_ref_volt_cir}, + /* SPKR_IS_SINK_CURR_FROM_REF_VOLT_CIR_EN_PROC */ + {debug_spkr_is_sink_curr_from_ref_volt_cir_en, NULL}, + /* SPKR_ADD_RIGHT_LEFT_CHAN_PROC */ + {NULL, debug_spkr_add_right_left_chan}, + {NULL, debug_spkr_set_gain}, /* SPKR_SET_GAIN_PROC */ + {NULL , debug_spkr_en}, /* SPKR_EN_PROC */ +}; + +/***********************************************************************/ + +#define PROC_END (sizeof(pmic_debug)/sizeof(struct pmic_debug_desc)) + + +#define PMIC_DEBUG_BUF 512 + +static int debug_proc; /* PROC's index */ + +static char debug_buf[PMIC_DEBUG_BUF]; + +static int proc_index_set(void *data, u64 val) +{ + int ndx; + + ndx = (int)val; + + if (ndx >= 0 && ndx <= PROC_END) + debug_proc = ndx; + + return 0; +} + +static int proc_index_get(void *data, u64 *val) +{ + *val = (u64)debug_proc; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE( + proc_index_fops, + proc_index_get, + proc_index_set, + "%llu\n"); + + +static int pmic_debugfs_open(struct inode *inode, struct file *file) +{ + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + return 0; +} + +static int pmic_debugfs_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t pmic_debugfs_write( + struct file *file, + const char __user *buff, + size_t count, + loff_t *ppos) +{ + struct pmic_debug_desc *pd; + int len = 0; + + printk(KERN_INFO "%s: proc=%d count=%d *ppos=%d\n", + __func__, debug_proc, count, (uint)*ppos); + + if (count > sizeof(debug_buf)) + return -EFAULT; + + if (copy_from_user(debug_buf, buff, count)) + return -EFAULT; + + + debug_buf[count] = 0; /* end of string */ + + pd = &pmic_debug[debug_proc]; + + if (pd->set) { + len = pd->set(debug_buf, count); + printk(KERN_INFO "%s: len=%d\n", __func__, len); + return len; + } + + return 0; +} + +static ssize_t pmic_debugfs_read( + struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct pmic_debug_desc *pd; + int len = 0; + + printk(KERN_INFO "%s: proc=%d count=%d *ppos=%d\n", + __func__, debug_proc, count, (uint)*ppos); + + pd = &pmic_debug[debug_proc]; + + if (*ppos) + return 0; /* the end */ + + if (pd->get) { + len = pd->get(debug_buf, sizeof(debug_buf)); + if (len > 0) { + if (len > count) + len = count; + if (copy_to_user(buff, debug_buf, len)) + return -EFAULT; + } + } + + printk(KERN_INFO "%s: len=%d\n", __func__, len); + + if (len < 0) + return 0; + + *ppos += len; /* increase offset */ + + return len; +} + +static const struct file_operations pmic_debugfs_fops = { + .open = pmic_debugfs_open, + .release = pmic_debugfs_release, + .read = pmic_debugfs_read, + .write = pmic_debugfs_write, +}; + +static int __init pmic_debugfs_init(void) +{ + struct dentry *dent = debugfs_create_dir("pmic", NULL); + + if (IS_ERR(dent)) { + printk(KERN_ERR "%s(%d): debugfs_create_dir fail, error %ld\n", + __FILE__, __LINE__, PTR_ERR(dent)); + return -1; + } + + if (debugfs_create_file("index", 0644, dent, 0, &proc_index_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: index fail\n", + __FILE__, __LINE__); + return -1; + } + + if (debugfs_create_file("debug", 0644, dent, 0, &pmic_debugfs_fops) + == NULL) { + printk(KERN_ERR "%s(%d): debugfs_create_file: debug fail\n", + __FILE__, __LINE__); + return -1; + } + + debug_proc = 0; + debug_rtc_alarm_ndx = 0; + + return 0; +} + +late_initcall(pmic_debugfs_init); diff --git a/arch/arm/mach-msm/proc_comm.c b/arch/arm/mach-msm/proc_comm.c index 915ee704ed3c..937e59b46dfe 100644 --- a/arch/arm/mach-msm/proc_comm.c +++ b/arch/arm/mach-msm/proc_comm.c @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/proc_comm.c * * Copyright (C) 2007-2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -18,16 +19,21 @@ #include <linux/errno.h> #include <linux/io.h> #include <linux/spinlock.h> +#include <linux/module.h> #include <mach/msm_iomap.h> #include <mach/system.h> #include "proc_comm.h" -#define MSM_A2M_INT(n) (MSM_CSR_BASE + 0x400 + (n) * 4) +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_TRIG_A2M_INT(n) (writel(1 << n, MSM_GCC_BASE + 0x8)) +#else +#define MSM_TRIG_A2M_INT(n) (writel(1, MSM_CSR_BASE + 0x400 + (n) * 4)) +#endif static inline void notify_other_proc_comm(void) { - writel(1, MSM_A2M_INT(6)); + MSM_TRIG_A2M_INT(6); } #define APP_COMMAND 0x00 @@ -43,68 +49,89 @@ static inline void notify_other_proc_comm(void) static DEFINE_SPINLOCK(proc_comm_lock); /* The higher level SMD support will install this to - * provide a way to check for and handle modem restart. + * provide a way to check for and handle modem restart? */ int (*msm_check_for_modem_crash)(void); /* Poll for a state change, checking for possible * modem crashes along the way (so we don't wait - * forever while the ARM9 is blowing up). + * forever while the ARM9 is blowing up. * * Return an error in the event of a modem crash and * restart so the msm_proc_comm() routine can restart * the operation from the beginning. */ -static int proc_comm_wait_for(void __iomem *addr, unsigned value) +static int proc_comm_wait_for(unsigned addr, unsigned value) { - for (;;) { + while (1) { if (readl(addr) == value) return 0; if (msm_check_for_modem_crash) if (msm_check_for_modem_crash()) return -EAGAIN; + + udelay(5); } } +void msm_proc_comm_reset_modem_now(void) +{ + unsigned base = (unsigned)MSM_SHARED_RAM_BASE; + unsigned long flags; + + spin_lock_irqsave(&proc_comm_lock, flags); + +again: + if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) + goto again; + + writel(PCOM_RESET_MODEM, base + APP_COMMAND); + writel(0, base + APP_DATA1); + writel(0, base + APP_DATA2); + + spin_unlock_irqrestore(&proc_comm_lock, flags); + + notify_other_proc_comm(); + + return; +} +EXPORT_SYMBOL(msm_proc_comm_reset_modem_now); + int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2) { - void __iomem *base = MSM_SHARED_RAM_BASE; + unsigned base = (unsigned)MSM_SHARED_RAM_BASE; unsigned long flags; int ret; spin_lock_irqsave(&proc_comm_lock, flags); - for (;;) { - if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) - continue; - - writel(cmd, base + APP_COMMAND); - writel(data1 ? *data1 : 0, base + APP_DATA1); - writel(data2 ? *data2 : 0, base + APP_DATA2); - - notify_other_proc_comm(); - - if (proc_comm_wait_for(base + APP_COMMAND, PCOM_CMD_DONE)) - continue; - - if (readl(base + APP_STATUS) != PCOM_CMD_FAIL) { - if (data1) - *data1 = readl(base + APP_DATA1); - if (data2) - *data2 = readl(base + APP_DATA2); - ret = 0; - } else { - ret = -EIO; - } - break; +again: + if (proc_comm_wait_for(base + MDM_STATUS, PCOM_READY)) + goto again; + + writel(cmd, base + APP_COMMAND); + writel(data1 ? *data1 : 0, base + APP_DATA1); + writel(data2 ? *data2 : 0, base + APP_DATA2); + + notify_other_proc_comm(); + + if (proc_comm_wait_for(base + APP_COMMAND, PCOM_CMD_DONE)) + goto again; + + if (readl(base + APP_STATUS) == PCOM_CMD_SUCCESS) { + if (data1) + *data1 = readl(base + APP_DATA1); + if (data2) + *data2 = readl(base + APP_DATA2); + ret = 0; + } else { + ret = -EIO; } writel(PCOM_CMD_IDLE, base + APP_COMMAND); spin_unlock_irqrestore(&proc_comm_lock, flags); - return ret; } - - +EXPORT_SYMBOL(msm_proc_comm); diff --git a/arch/arm/mach-msm/proc_comm.h b/arch/arm/mach-msm/proc_comm.h index 834760f25692..5e08f050d07a 100644 --- a/arch/arm/mach-msm/proc_comm.h +++ b/arch/arm/mach-msm/proc_comm.h @@ -1,6 +1,6 @@ /* arch/arm/mach-msm/proc_comm.h * - * Copyright (c) 2007 QUALCOMM Incorporated + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -13,8 +13,8 @@ * */ -#ifndef _ARCH_ARM_MACH_MSM_PROC_COMM_H_ -#define _ARCH_ARM_MACH_MSM_PROC_COMM_H_ +#ifndef _ARCH_ARM_MACH_MSM_MSM_PROC_COMM_H_ +#define _ARCH_ARM_MACH_MSM_MSM_PROC_COMM_H_ enum { PCOM_CMD_IDLE = 0x0, @@ -62,104 +62,110 @@ enum { PCOM_RESET_CHIP_IMM, PCOM_PM_VID_EN, PCOM_VREG_PULLDOWN, - PCOM_NUM_CMDS, + PCOM_GET_MODEM_VERSION, + PCOM_CLK_REGIME_SEC_RESET, + PCOM_CLK_REGIME_SEC_RESET_ASSERT, + PCOM_CLK_REGIME_SEC_RESET_DEASSERT, + PCOM_CLK_REGIME_SEC_PLL_REQUEST_WRP, + PCOM_CLK_REGIME_SEC_ENABLE, + PCOM_CLK_REGIME_SEC_DISABLE, + PCOM_CLK_REGIME_SEC_IS_ON, + PCOM_CLK_REGIME_SEC_SEL_CLK_INV, + PCOM_CLK_REGIME_SEC_SEL_CLK_SRC, + PCOM_CLK_REGIME_SEC_SEL_CLK_DIV, + PCOM_CLK_REGIME_SEC_ICODEC_CLK_ENABLE, + PCOM_CLK_REGIME_SEC_ICODEC_CLK_DISABLE, + PCOM_CLK_REGIME_SEC_SEL_SPEED, + PCOM_CLK_REGIME_SEC_CONFIG_GP_CLK_WRP, + PCOM_CLK_REGIME_SEC_CONFIG_MDH_CLK_WRP, + PCOM_CLK_REGIME_SEC_USB_XTAL_ON, + PCOM_CLK_REGIME_SEC_USB_XTAL_OFF, + PCOM_CLK_REGIME_SEC_SET_QDSP_DME_MODE, + PCOM_CLK_REGIME_SEC_SWITCH_ADSP_CLK, + PCOM_CLK_REGIME_SEC_GET_MAX_ADSP_CLK_KHZ, + PCOM_CLK_REGIME_SEC_GET_I2C_CLK_KHZ, + PCOM_CLK_REGIME_SEC_MSM_GET_CLK_FREQ_KHZ, + PCOM_CLK_REGIME_SEC_SEL_VFE_SRC, + PCOM_CLK_REGIME_SEC_MSM_SEL_CAMCLK, + PCOM_CLK_REGIME_SEC_MSM_SEL_LCDCLK, + PCOM_CLK_REGIME_SEC_VFE_RAIL_OFF, + PCOM_CLK_REGIME_SEC_VFE_RAIL_ON, + PCOM_CLK_REGIME_SEC_GRP_RAIL_OFF, + PCOM_CLK_REGIME_SEC_GRP_RAIL_ON, + PCOM_CLK_REGIME_SEC_VDC_RAIL_OFF, + PCOM_CLK_REGIME_SEC_VDC_RAIL_ON, + PCOM_CLK_REGIME_SEC_LCD_CTRL, + PCOM_CLK_REGIME_SEC_REGISTER_FOR_CPU_RESOURCE, + PCOM_CLK_REGIME_SEC_DEREGISTER_FOR_CPU_RESOURCE, + PCOM_CLK_REGIME_SEC_RESOURCE_REQUEST_WRP, + PCOM_CLK_REGIME_MSM_SEC_SEL_CLK_OWNER, + PCOM_CLK_REGIME_SEC_DEVMAN_REQUEST_WRP, + PCOM_GPIO_CONFIG, + PCOM_GPIO_CONFIGURE_GROUP, + PCOM_GPIO_TLMM_SET_PORT, + PCOM_GPIO_TLMM_CONFIG_EX, + PCOM_SET_FTM_BOOT_COUNT, + PCOM_RESERVED0, + PCOM_RESERVED1, + PCOM_CUSTOMER_CMD1, + PCOM_CUSTOMER_CMD2, + PCOM_CUSTOMER_CMD3, + PCOM_CLK_REGIME_ENTER_APPSBL_CHG_MODE, + PCOM_CLK_REGIME_EXIT_APPSBL_CHG_MODE, + PCOM_CLK_REGIME_SEC_RAIL_DISABLE, + PCOM_CLK_REGIME_SEC_RAIL_ENABLE, + PCOM_CLK_REGIME_SEC_RAIL_CONTROL, + PCOM_SET_SW_WATCHDOG_STATE, + PCOM_PM_MPP_CONFIG_DIGITAL_INPUT, + PCOM_PM_MPP_CONFIG_I_SINK, + PCOM_RESERVED_101, + PCOM_MSM_HSUSB_PHY_RESET, + PCOM_GET_BATT_MV_LEVEL, + PCOM_CHG_USB_IS_PC_CONNECTED, + PCOM_CHG_USB_IS_CHARGER_CONNECTED, + PCOM_CHG_USB_IS_DISCONNECTED, + PCOM_CHG_USB_IS_AVAILABLE, + PCOM_CLK_REGIME_SEC_MSM_SEL_FREQ, + PCOM_CLK_REGIME_SEC_SET_PCLK_AXI_POLICY, + PCOM_CLKCTL_RPC_RESET_ASSERT, + PCOM_CLKCTL_RPC_RESET_DEASSERT, + PCOM_CLKCTL_RPC_RAIL_ON, + PCOM_CLKCTL_RPC_RAIL_OFF, + PCOM_CLKCTL_RPC_RAIL_ENABLE, + PCOM_CLKCTL_RPC_RAIL_DISABLE, + PCOM_CLKCTL_RPC_RAIL_CONTROL, + PCOM_CLKCTL_RPC_MIN_MSMC1, }; enum { - PCOM_INVALID_STATUS = 0x0, - PCOM_READY, - PCOM_CMD_RUNNING, - PCOM_CMD_SUCCESS, - PCOM_CMD_FAIL, -}; - -/* List of VREGs that support the Pull Down Resistor setting. */ -enum { - PM_VREG_PDOWN_MSMA_ID, - PM_VREG_PDOWN_MSMP_ID, - PM_VREG_PDOWN_MSME1_ID, /* Not supported in Panoramix */ - PM_VREG_PDOWN_MSMC1_ID, /* Not supported in PM6620 */ - PM_VREG_PDOWN_MSMC2_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP3_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_MSME2_ID, /* Supported in PM7500 and Panoramix only */ - PM_VREG_PDOWN_GP4_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP1_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_TCXO_ID, - PM_VREG_PDOWN_PA_ID, - PM_VREG_PDOWN_RFTX_ID, - PM_VREG_PDOWN_RFRX1_ID, - PM_VREG_PDOWN_RFRX2_ID, - PM_VREG_PDOWN_SYNT_ID, - PM_VREG_PDOWN_WLAN_ID, - PM_VREG_PDOWN_USB_ID, - PM_VREG_PDOWN_MMC_ID, - PM_VREG_PDOWN_RUIM_ID, - PM_VREG_PDOWN_MSMC0_ID, /* Supported in PM6610 only */ - PM_VREG_PDOWN_GP2_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP5_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_GP6_ID, /* Supported in PM7500 only */ - PM_VREG_PDOWN_RF_ID, - PM_VREG_PDOWN_RF_VCO_ID, - PM_VREG_PDOWN_MPLL_ID, - PM_VREG_PDOWN_S2_ID, - PM_VREG_PDOWN_S3_ID, - PM_VREG_PDOWN_RFUBM_ID, + PCOM_OEM_FIRST_CMD = 0x10000000, + PCOM_OEM_TEST_CMD = PCOM_OEM_FIRST_CMD, - /* new for HAN */ - PM_VREG_PDOWN_RF1_ID, - PM_VREG_PDOWN_RF2_ID, - PM_VREG_PDOWN_RFA_ID, - PM_VREG_PDOWN_CDC2_ID, - PM_VREG_PDOWN_RFTX2_ID, - PM_VREG_PDOWN_USIM_ID, - PM_VREG_PDOWN_USB2P6_ID, - PM_VREG_PDOWN_USB3P3_ID, - PM_VREG_PDOWN_INVALID_ID, + /* add OEM PROC COMM commands here */ - /* backward compatible enums only */ - PM_VREG_PDOWN_CAM_ID = PM_VREG_PDOWN_GP1_ID, - PM_VREG_PDOWN_MDDI_ID = PM_VREG_PDOWN_GP2_ID, - PM_VREG_PDOWN_RUIM2_ID = PM_VREG_PDOWN_GP3_ID, - PM_VREG_PDOWN_AUX_ID = PM_VREG_PDOWN_GP4_ID, - PM_VREG_PDOWN_AUX2_ID = PM_VREG_PDOWN_GP5_ID, - PM_VREG_PDOWN_BT_ID = PM_VREG_PDOWN_GP6_ID, - - PM_VREG_PDOWN_MSME_ID = PM_VREG_PDOWN_MSME1_ID, - PM_VREG_PDOWN_MSMC_ID = PM_VREG_PDOWN_MSMC1_ID, - PM_VREG_PDOWN_RFA1_ID = PM_VREG_PDOWN_RFRX2_ID, - PM_VREG_PDOWN_RFA2_ID = PM_VREG_PDOWN_RFTX2_ID, - PM_VREG_PDOWN_XO_ID = PM_VREG_PDOWN_TCXO_ID + PCOM_OEM_LAST = PCOM_OEM_TEST_CMD, }; -/* gpio info for PCOM_RPC_GPIO_TLMM_CONFIG_EX */ - -#define GPIO_ENABLE 0 -#define GPIO_DISABLE 1 - -#define GPIO_INPUT 0 -#define GPIO_OUTPUT 1 - -#define GPIO_NO_PULL 0 -#define GPIO_PULL_DOWN 1 -#define GPIO_KEEPER 2 -#define GPIO_PULL_UP 3 - -#define GPIO_2MA 0 -#define GPIO_4MA 1 -#define GPIO_6MA 2 -#define GPIO_8MA 3 -#define GPIO_10MA 4 -#define GPIO_12MA 5 -#define GPIO_14MA 6 -#define GPIO_16MA 7 - -#define PCOM_GPIO_CFG(gpio, func, dir, pull, drvstr) \ - ((((gpio) & 0x3FF) << 4) | \ - ((func) & 0xf) | \ - (((dir) & 0x1) << 14) | \ - (((pull) & 0x3) << 15) | \ - (((drvstr) & 0xF) << 17)) +enum { + PCOM_INVALID_STATUS = 0x0, + PCOM_READY, + PCOM_CMD_RUNNING, + PCOM_CMD_SUCCESS, + PCOM_CMD_FAIL, + PCOM_CMD_FAIL_FALSE_RETURNED, + PCOM_CMD_FAIL_CMD_OUT_OF_BOUNDS_SERVER, + PCOM_CMD_FAIL_CMD_OUT_OF_BOUNDS_CLIENT, + PCOM_CMD_FAIL_CMD_UNREGISTERED, + PCOM_CMD_FAIL_CMD_LOCKED, + PCOM_CMD_FAIL_SERVER_NOT_YET_READY, + PCOM_CMD_FAIL_BAD_DESTINATION, + PCOM_CMD_FAIL_SERVER_RESET, + PCOM_CMD_FAIL_SMSM_NOT_INIT, + PCOM_CMD_FAIL_PROC_COMM_BUSY, + PCOM_CMD_FAIL_PROC_COMM_NOT_INIT, +}; +void msm_proc_comm_reset_modem_now(void); int msm_proc_comm(unsigned cmd, unsigned *data1, unsigned *data2); #endif diff --git a/arch/arm/mach-msm/proc_comm_test.c b/arch/arm/mach-msm/proc_comm_test.c new file mode 100644 index 000000000000..e38b8d651d27 --- /dev/null +++ b/arch/arm/mach-msm/proc_comm_test.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * PROC COMM TEST Driver source file + */ + +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/debugfs.h> +#include <linux/module.h> +#include "proc_comm.h" + +static struct dentry *dent; +static int proc_comm_test_res; + +static int proc_comm_reverse_test(void) +{ + uint32_t data1, data2; + int rc; + + data1 = 10; + data2 = 20; + + rc = msm_proc_comm(PCOM_OEM_TEST_CMD, &data1, &data2); + if (rc) + return rc; + + if ((data1 != 20) || (data2 != 10)) + return -1; + + return 0; +} + +static ssize_t debug_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char _buf[16]; + + snprintf(_buf, sizeof(_buf), "%i\n", proc_comm_test_res); + + return simple_read_from_buffer(buf, count, pos, _buf, strlen(_buf)); +} + +static ssize_t debug_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + + unsigned char cmd[64]; + int len; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "reverse_test", 64)) + proc_comm_test_res = proc_comm_reverse_test(); + else + proc_comm_test_res = -EINVAL; + + if (proc_comm_test_res) + pr_err("proc comm test fail %d\n", + proc_comm_test_res); + else + pr_info("proc comm test passed\n"); + + return count; +} + +static int debug_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static int debug_open(struct inode *ip, struct file *fp) +{ + return 0; +} + +static const struct file_operations debug_ops = { + .owner = THIS_MODULE, + .open = debug_open, + .release = debug_release, + .read = debug_read, + .write = debug_write, +}; + +static void __exit proc_comm_test_mod_exit(void) +{ + debugfs_remove(dent); +} + +static int __init proc_comm_test_mod_init(void) +{ + dent = debugfs_create_file("proc_comm", 0444, 0, NULL, &debug_ops); + proc_comm_test_res = -1; + return 0; +} + +module_init(proc_comm_test_mod_init); +module_exit(proc_comm_test_mod_exit); + +MODULE_DESCRIPTION("PROC COMM TEST Driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/arch/arm/mach-msm/remote_spinlock.c b/arch/arm/mach-msm/remote_spinlock.c new file mode 100644 index 000000000000..6e488865930e --- /dev/null +++ b/arch/arm/mach-msm/remote_spinlock.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/err.h> +#include <linux/kernel.h> + +#include <asm/system.h> + +#include <mach/remote_spinlock.h> +#include "smd_private.h" + +#define SMEM_SPINLOCK_COUNT 8 +#define SMEM_SPINLOCK_ARRAY_SIZE (SMEM_SPINLOCK_COUNT * sizeof(uint32_t)) + +int _remote_spin_lock_init(remote_spin_lock_id_t id, _remote_spinlock_t *lock) +{ + _remote_spinlock_t spinlock_start; + + if (id >= SMEM_SPINLOCK_COUNT) + return -EINVAL; + + spinlock_start = smem_alloc(SMEM_SPINLOCK_ARRAY, + SMEM_SPINLOCK_ARRAY_SIZE); + if (spinlock_start == NULL) + return -ENXIO; + + *lock = spinlock_start + id; + + return 0; +} + diff --git a/arch/arm/mach-msm/reset_modem.c b/arch/arm/mach-msm/reset_modem.c new file mode 100644 index 000000000000..5d5b0f9a0620 --- /dev/null +++ b/arch/arm/mach-msm/reset_modem.c @@ -0,0 +1,226 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * MSM architecture driver to reset the modem + */ +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> + +#include "smd_private.h" +#include "proc_comm.h" + +#define DEBUG +/* #undef DEBUG */ +#ifdef DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +static ssize_t reset_modem_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + return 0; +} + +static ssize_t reset_modem_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + unsigned char cmd[64]; + int len; + int time; + int zero = 0; + int r; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "wait", 4)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: WAIT\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem(SMSM_MODEM_WAIT); + } else if (!strncmp(cmd, "continue", 8)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: CONTINUE\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem_cont(); + } else if (!strncmp(cmd, "download", 8)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: DOWNLOAD\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem(SMSM_SYSTEM_DOWNLOAD); + } else if (sscanf(cmd, "deferred reset %i", &time) == 1) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: DEFERRED RESET %ims\n", + __FILE__, + __LINE__, + __func__, + time); + if (time == 0) { + r = 0; + msm_proc_comm_reset_modem_now(); + } else { + r = msm_proc_comm(PCOM_RESET_MODEM, &time, &zero); + } + if (r < 0) + return r; + } else if (!strncmp(cmd, "deferred reset", 14)) { + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: DEFERRED RESET 0ms\n", + __FILE__, + __LINE__, + __func__); + r = 0; + msm_proc_comm_reset_modem_now(); + if (r < 0) + return r; + } else if (!strncmp(cmd, "reset chip now", 14)) { + uint param1 = 0x0; + uint param2 = 0x0; + + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: CHIP RESET IMMEDIATE\n", + __FILE__, + __LINE__, + __func__); + + r = msm_proc_comm(PCOM_RESET_CHIP_IMM, ¶m1, ¶m2); + + if (r < 0) + return r; + } else if (!strncmp(cmd, "reset chip", 10)) { + + uint param1 = 0x0; + uint param2 = 0x0; + + D(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: CHIP RESET \n", + __FILE__, + __LINE__, + __func__); + + r = msm_proc_comm(PCOM_RESET_CHIP, ¶m1, ¶m2); + + if (r < 0) + return r; + } else { /* if (!strncmp(cmd, "reset", 5)) */ + printk(KERN_ERR "INFO:%s:%i:%s: " + "MODEM RESTART: RESET\n", + __FILE__, + __LINE__, + __func__); + smsm_reset_modem(SMSM_RESET); + } + + return count; +} + +static int reset_modem_open(struct inode *ip, struct file *fp) +{ + return 0; +} + +static int reset_modem_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static const struct file_operations reset_modem_fops = { + .owner = THIS_MODULE, + .read = reset_modem_read, + .write = reset_modem_write, + .open = reset_modem_open, + .release = reset_modem_release, +}; + +static struct miscdevice reset_modem_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "reset_modem", + .fops = &reset_modem_fops, +}; + +static int __init reset_modem_init(void) +{ + return misc_register(&reset_modem_dev); +} + +module_init(reset_modem_init); + +MODULE_DESCRIPTION("Reset Modem"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/rpc_hsusb.c b/arch/arm/mach-msm/rpc_hsusb.c new file mode 100644 index 000000000000..22dee1c3322b --- /dev/null +++ b/arch/arm/mach-msm/rpc_hsusb.c @@ -0,0 +1,574 @@ +/* linux/arch/arm/mach-msm/rpc_hsusb.c + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include <linux/err.h> +#include <mach/rpc_hsusb.h> +#include <asm/mach-types.h> + +static struct msm_rpc_endpoint *usb_ep; +static struct msm_rpc_endpoint *chg_ep; + +struct msm_chg_rpc_ids { + unsigned long prog; + unsigned long vers_comp; + unsigned chg_usb_charger_connected_proc; + unsigned chg_usb_charger_disconnected_proc; + unsigned chg_usb_i_is_available_proc; + unsigned chg_usb_i_is_not_available_proc; +}; + +struct msm_hsusb_rpc_ids { + unsigned long prog; + unsigned long vers_comp; + unsigned long init_phy; + unsigned long vbus_pwr_up; + unsigned long vbus_pwr_down; + unsigned long update_product_id; + unsigned long update_serial_num; + unsigned long update_is_serial_num_null; + unsigned long reset_rework_installed; + unsigned long enable_pmic_ulpi_data0; + unsigned long disable_pmic_ulpi_data0; +}; + +static struct msm_hsusb_rpc_ids usb_rpc_ids; +static struct msm_chg_rpc_ids chg_rpc_ids; + +static int msm_hsusb_init_rpc_ids(unsigned long vers) +{ + if (vers == 0x00010001) { + usb_rpc_ids.prog = 0x30000064; + usb_rpc_ids.vers_comp = 0x00010001; + usb_rpc_ids.init_phy = 2; + usb_rpc_ids.vbus_pwr_up = 6; + usb_rpc_ids.vbus_pwr_down = 7; + usb_rpc_ids.update_product_id = 8; + usb_rpc_ids.update_serial_num = 9; + usb_rpc_ids.update_is_serial_num_null = 10; + usb_rpc_ids.reset_rework_installed = 17; + usb_rpc_ids.enable_pmic_ulpi_data0 = 18; + usb_rpc_ids.disable_pmic_ulpi_data0 = 19; + return 0; + } else if (vers == 0x00010002) { + usb_rpc_ids.prog = 0x30000064; + usb_rpc_ids.vers_comp = 0x00010002; + usb_rpc_ids.init_phy = 2; + usb_rpc_ids.vbus_pwr_up = 6; + usb_rpc_ids.vbus_pwr_down = 7; + usb_rpc_ids.update_product_id = 8; + usb_rpc_ids.update_serial_num = 9; + usb_rpc_ids.update_is_serial_num_null = 10; + usb_rpc_ids.reset_rework_installed = 17; + usb_rpc_ids.enable_pmic_ulpi_data0 = 18; + usb_rpc_ids.disable_pmic_ulpi_data0 = 19; + return 0; + } else { + printk(KERN_INFO "%s: no matches found for version\n", + __func__); + return -ENODATA; + } +} + +static int msm_chg_init_rpc_ids(unsigned long vers) +{ + if (vers == 0x00010001) { + chg_rpc_ids.prog = 0x3000001a; + chg_rpc_ids.vers_comp = 0x00010001; + chg_rpc_ids.chg_usb_charger_connected_proc = 7; + chg_rpc_ids.chg_usb_charger_disconnected_proc = 8; + chg_rpc_ids.chg_usb_i_is_available_proc = 9; + chg_rpc_ids.chg_usb_i_is_not_available_proc = 10; + return 0; + } else { + printk(KERN_INFO "%s: no matches found for version\n", + __func__); + return -ENODATA; + } +} +EXPORT_SYMBOL(msm_chg_init_rpc_ids); + +/* rpc connect for hsusb */ +int msm_hsusb_rpc_connect(void) +{ + + if (usb_ep && !IS_ERR(usb_ep)) { + printk(KERN_INFO "%s: usb_ep already connected\n", __func__); + return 0; + } + + /* Initialize rpc ids */ + if (msm_hsusb_init_rpc_ids(0x00010001)) { + printk(KERN_ERR "%s: rpc ids initialization failed\n" + , __func__); + return -ENODATA; + } + + usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog, + usb_rpc_ids.vers_comp, 0); + + if (IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: connect compatible failed vers = %lx\n", + __func__, usb_rpc_ids.vers_comp); + + /* Initialize rpc ids */ + if (msm_hsusb_init_rpc_ids(0x00010002)) { + printk(KERN_ERR "%s: rpc ids initialization failed\n", + __func__); + return -ENODATA; + } + usb_ep = msm_rpc_connect_compatible(usb_rpc_ids.prog, + usb_rpc_ids.vers_comp, 0); + } + + if (IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: connect compatible failed vers = %lx\n", + __func__, usb_rpc_ids.vers_comp); + return -EAGAIN; + } else + printk(KERN_INFO "%s: rpc connect success vers = %lx\n", + __func__, usb_rpc_ids.vers_comp); + + return 0; +} +EXPORT_SYMBOL(msm_hsusb_rpc_connect); + +/* rpc connect for charging */ +int msm_chg_rpc_connect(void) +{ + + if (machine_is_msm7201a_surf() || machine_is_msm7x27_surf() || + machine_is_qsd8x50_surf()) + return -ENOTSUPP; + + if (chg_ep && !IS_ERR(chg_ep)) { + printk(KERN_INFO "%s: chg_ep already connected\n", __func__); + return 0; + } + + /* Initialize rpc ids */ + if (msm_chg_init_rpc_ids(0x00010001)) { + printk(KERN_ERR "%s: rpc ids initialization failed\n" + , __func__); + return -ENODATA; + } + + chg_ep = msm_rpc_connect_compatible(chg_rpc_ids.prog, + chg_rpc_ids.vers_comp, 0); + + if (IS_ERR(chg_ep)) { + printk(KERN_ERR "%s: connect compatible failed vers = %lx\n", + __func__, chg_rpc_ids.vers_comp); + return -EAGAIN; + } else + printk(KERN_INFO "%s: rpc connect success vers = %lx\n", + __func__, chg_rpc_ids.vers_comp); + + return 0; +} +EXPORT_SYMBOL(msm_chg_rpc_connect); + +/* rpc call for phy_reset */ +int msm_hsusb_phy_reset(void) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: phy_reset rpc failed before call," + "rc = %ld\n", __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call(usb_ep, usb_rpc_ids.init_phy, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: phy_reset rpc failed! rc = %d\n", + __func__, rc); + } else + printk(KERN_INFO "msm_hsusb_phy_reset\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_phy_reset); + +/* rpc call for vbus powerup */ +int msm_hsusb_vbus_powerup(void) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: vbus_powerup rpc failed before call," + "rc = %ld\n", __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_up, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: vbus_powerup failed! rc = %d\n", + __func__, rc); + } else + printk(KERN_INFO "msm_hsusb_vbus_powerup\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_vbus_powerup); + +/* rpc call for vbus shutdown */ +int msm_hsusb_vbus_shutdown(void) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: vbus_shutdown rpc failed before call," + "rc = %ld\n", __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call(usb_ep, usb_rpc_ids.vbus_pwr_down, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: vbus_shutdown failed! rc = %d\n", + __func__, rc); + } else + printk(KERN_INFO "msm_hsusb_vbus_shutdown\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_vbus_shutdown); + +int msm_hsusb_send_productID(uint32_t product_id) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + uint32_t product_id; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: rpc connect failed: rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + req.product_id = cpu_to_be32(product_id); + rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_product_id, + &req, sizeof(req), + 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: rpc call failed! error: %d\n", + __func__, rc); + else + printk(KERN_ERR "%s: rpc call success\n" , + __func__); + return rc; +} +EXPORT_SYMBOL(msm_hsusb_send_productID); + +int msm_hsusb_send_serial_number(char *serial_number) +{ + int rc = 0, serial_len; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + uint32_t length; + char serial_num[20]; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: rpc connect failed: rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + serial_len = strlen(serial_number)+1; + strncpy(req.serial_num, serial_number, 20); + req.length = cpu_to_be32(serial_len); + rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_serial_num, + &req, sizeof(req), + 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: rpc call failed! error: %d\n", + __func__, rc); + else + printk(KERN_ERR "%s: rpc call success\n" , + __func__); + return rc; +} +EXPORT_SYMBOL(msm_hsusb_send_serial_number); + +int msm_hsusb_is_serial_num_null(uint32_t val) +{ + int rc = 0; + struct hsusb_phy_start_req { + struct rpc_request_hdr hdr; + uint32_t value; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: rpc connect failed: rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + if (!usb_rpc_ids.update_is_serial_num_null) { + printk(KERN_ERR "%s: proc id not supported \n", __func__); + return -ENODATA; + } + + req.value = cpu_to_be32(val); + rc = msm_rpc_call(usb_ep, usb_rpc_ids.update_is_serial_num_null, + &req, sizeof(req), + 5 * HZ); + if (rc < 0) + printk(KERN_ERR "%s: rpc call failed! error: %d\n" , + __func__, rc); + else + printk(KERN_ERR "%s: rpc call success\n" , + __func__); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_is_serial_num_null); + +int msm_chg_usb_charger_connected(uint32_t device) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + uint32_t otg_dev; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + req.otg_dev = cpu_to_be32(device); + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_connected_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: charger_connected failed! rc = %d\n", + __func__, rc); + } else + printk(KERN_INFO "msm_chg_usb_charger_connected\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_charger_connected); + +int msm_chg_usb_i_is_available(uint32_t sample) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + uint32_t i_ma; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + req.i_ma = cpu_to_be32(sample); + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_available_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: charger_i_available failed! rc = %d\n", + __func__, rc); + } else + printk(KERN_INFO "msm_chg_usb_i_is_available\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_i_is_available); + +int msm_chg_usb_i_is_not_available(void) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_i_is_not_available_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: charger_i_not_available failed! rc =" + "%d \n", __func__, rc); + } else + printk(KERN_INFO "msm_chg_usb_i_is_not_available\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_i_is_not_available); + +int msm_chg_usb_charger_disconnected(void) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!chg_ep || IS_ERR(chg_ep)) + return -EAGAIN; + rc = msm_rpc_call(chg_ep, chg_rpc_ids.chg_usb_charger_disconnected_proc, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) { + printk(KERN_ERR "%s: charger_disconnected failed! rc = %d\n", + __func__, rc); + } else + printk(KERN_INFO "msm_chg_usb_charger_disconnected\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_usb_charger_disconnected); + +/* rpc call to close connection */ +int msm_hsusb_rpc_close(void) +{ + int rc = 0; + + if (IS_ERR(usb_ep)) { + printk(KERN_ERR "%s: rpc_close failed before call, rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_close(usb_ep); + usb_ep = NULL; + + if (rc < 0) { + printk(KERN_ERR "%s: close rpc failed! rc = %d\n", + __func__, rc); + return -EAGAIN; + } else + printk(KERN_INFO "rpc close success\n"); + + return rc; +} +EXPORT_SYMBOL(msm_hsusb_rpc_close); + +/* rpc call to close charging connection */ +int msm_chg_rpc_close(void) +{ + int rc = 0; + + if (IS_ERR(chg_ep)) { + printk(KERN_ERR "%s: rpc_close failed before call, rc = %ld\n", + __func__, PTR_ERR(chg_ep)); + return -EAGAIN; + } + + rc = msm_rpc_close(chg_ep); + chg_ep = NULL; + + if (rc < 0) { + printk(KERN_ERR "%s: close rpc failed! rc = %d\n", + __func__, rc); + return -EAGAIN; + } else + printk(KERN_INFO "rpc close success\n"); + + return rc; +} +EXPORT_SYMBOL(msm_chg_rpc_close); + +int msm_hsusb_reset_rework_installed(void) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + struct hsusb_rpc_rep { + struct rpc_reply_hdr hdr; + uint32_t rework; + } rep; + + memset(&rep, 0, sizeof(rep)); + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + rc = msm_rpc_call_reply(usb_ep, usb_rpc_ids.reset_rework_installed, + &req, sizeof(req), + &rep, sizeof(rep), 5 * HZ); + + if (rc < 0) { + pr_err("%s: rpc call failed! error: (%d)" + "proc id: (%lx)\n", + __func__, rc, + usb_rpc_ids.reset_rework_installed); + return rc; + } + + pr_info("%s: rework: (%d)\n", __func__, rep.rework); + return be32_to_cpu(rep.rework); +} +EXPORT_SYMBOL(msm_hsusb_reset_rework_installed); + +static int msm_hsusb_pmic_ulpidata0_config(int enable) +{ + int rc = 0; + struct hsusb_start_req { + struct rpc_request_hdr hdr; + } req; + + if (!usb_ep || IS_ERR(usb_ep)) { + pr_err("%s: hsusb rpc connection not initialized, rc = %ld\n", + __func__, PTR_ERR(usb_ep)); + return -EAGAIN; + } + + if (enable) + rc = msm_rpc_call(usb_ep, usb_rpc_ids.enable_pmic_ulpi_data0, + &req, sizeof(req), 5 * HZ); + else + rc = msm_rpc_call(usb_ep, usb_rpc_ids.disable_pmic_ulpi_data0, + &req, sizeof(req), 5 * HZ); + + if (rc < 0) + pr_err("%s: rpc call failed! error: %d\n", + __func__, rc); + return rc; +} + +int msm_hsusb_enable_pmic_ulpidata0(void) +{ + return msm_hsusb_pmic_ulpidata0_config(1); +} +EXPORT_SYMBOL(msm_hsusb_enable_pmic_ulpidata0); + +int msm_hsusb_disable_pmic_ulpidata0(void) +{ + return msm_hsusb_pmic_ulpidata0_config(0); +} +EXPORT_SYMBOL(msm_hsusb_disable_pmic_ulpidata0); diff --git a/arch/arm/mach-msm/rpc_server_dog_keepalive.c b/arch/arm/mach-msm/rpc_server_dog_keepalive.c new file mode 100644 index 000000000000..5e0f46da379d --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_dog_keepalive.c @@ -0,0 +1,77 @@ +/* arch/arm/mach-msm/rpc_server_dog_keepalive.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev <ibm@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <mach/msm_rpcrouter.h> + +/* dog_keepalive server definitions */ + +#define DOG_KEEPALIVE_PROG 0x30000015 +#if CONFIG_MSM_AMSS_VERSION==6210 +#define DOG_KEEPALIVE_VERS 0 +#define RPC_DOG_KEEPALIVE_BEACON 1 +#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) +#define DOG_KEEPALIVE_VERS 0x731fa727 +#define RPC_DOG_KEEPALIVE_BEACON 2 +#else +#error "Unsupported AMSS version" +#endif +#define DOG_KEEPALIVE_VERS_COMP 0x00010001 +#define RPC_DOG_KEEPALIVE_NULL 0 + + +/* TODO: Remove server registration with _VERS when modem is upated with _COMP*/ + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_DOG_KEEPALIVE_NULL: + return 0; + case RPC_DOG_KEEPALIVE_BEACON: + return 0; + default: + return -ENODEV; + } +} + +static struct msm_rpc_server rpc_server[] = { + { + .prog = DOG_KEEPALIVE_PROG, + .vers = DOG_KEEPALIVE_VERS, + .rpc_call = handle_rpc_call, + }, + { + .prog = DOG_KEEPALIVE_PROG, + .vers = DOG_KEEPALIVE_VERS_COMP, + .rpc_call = handle_rpc_call, + }, +}; + +static int __init rpc_server_init(void) +{ + /* Dual server registration to support backwards compatibility vers */ + int ret; + ret = msm_rpc_create_server(&rpc_server[1]); + if (ret < 0) + return ret; + return msm_rpc_create_server(&rpc_server[0]); +} + + +module_init(rpc_server_init); diff --git a/arch/arm/mach-msm/rpc_server_handset.c b/arch/arm/mach-msm/rpc_server_handset.c new file mode 100644 index 000000000000..f2df4aca231b --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_handset.c @@ -0,0 +1,380 @@ +/* arch/arm/mach-msm/rpc_server_handset.c + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org. + */ + +#include <linux/module.h> +#include <linux/kernel.h> + +#include <asm/mach-types.h> + +#include <mach/msm_handset.h> +#include <mach/msm_rpcrouter.h> +#include <mach/board.h> + +#include "keypad-surf-ffa.h" + +#define HS_SERVER_PROG 0x30000062 +#define HS_SERVER_VERS 0x00010001 + +#define HS_RPC_PROG 0x30000091 +#define HS_RPC_VERS 0x00010001 + +#define HS_RPC_CB_PROG 0x31000091 +#define HS_RPC_CB_VERS 0x00010001 + +#define HS_SUBSCRIBE_SRVC_PROC 0x03 +#define HS_EVENT_CB_PROC 1 + +#define RPC_KEYPAD_NULL_PROC 0 +#define RPC_KEYPAD_PASS_KEY_CODE_PROC 2 +#define RPC_KEYPAD_SET_PWR_KEY_STATE_PROC 3 + +#define HS_PWR_K 0x6F /* Power key */ +#define HS_END_K 0x51 /* End key or Power key */ +#define HS_STEREO_HEADSET_K 0x82 +#define HS_HEADSET_SWITCH_K 0x84 +#define HS_REL_K 0xFF /* key release */ + +#define KEY(hs_key, input_key) ((hs_key << 24) | input_key) + +struct hs_key_data { + uint32_t ver; /* Version number to track sturcture changes */ + uint32_t code; /* which key? */ + uint32_t parm; /* key status. Up/down or pressed/released */ +}; + +enum hs_subs_srvc { + HS_SUBS_SEND_CMD = 0, /* Subscribe to send commands to HS */ + HS_SUBS_RCV_EVNT, /* Subscribe to receive Events from HS */ + HS_SUBS_SRVC_MAX +}; + +enum hs_subs_req { + HS_SUBS_REGISTER, /* Subscribe */ + HS_SUBS_CANCEL, /* Unsubscribe */ + HS_SUB_STATUS_MAX +}; + +enum hs_event_class { + HS_EVNT_CLASS_ALL = 0, /* All HS events */ + HS_EVNT_CLASS_LAST, /* Should always be the last class type */ + HS_EVNT_CLASS_MAX +}; + +enum hs_cmd_class { + HS_CMD_CLASS_LCD = 0, /* Send LCD related commands */ + HS_CMD_CLASS_KPD, /* Send KPD related commands */ + HS_CMD_CLASS_LAST, /* Should always be the last class type */ + HS_CMD_CLASS_MAX +}; + +/* + * Receive events or send command + */ +union hs_subs_class { + enum hs_event_class evnt; + enum hs_cmd_class cmd; +}; + +struct hs_subs { + uint32_t ver; + enum hs_subs_srvc srvc; /* commands or events */ + enum hs_subs_req req; /* subscribe or unsubscribe */ + uint32_t host_os; + enum hs_subs_req disc; /* discriminator */ + union hs_subs_class id; +}; + +struct hs_event_cb_recv { + uint32_t cb_id; + uint32_t hs_key_data_ptr; + struct hs_key_data key; +}; + +static const uint32_t hs_key_map[] = { + KEY(HS_PWR_K, KEY_POWER), + KEY(HS_END_K, KEY_END), + KEY(HS_STEREO_HEADSET_K, SW_HEADPHONE_INSERT), + KEY(HS_HEADSET_SWITCH_K, KEY_MEDIA), + 0 +}; + +static struct input_dev *kpdev; +static struct input_dev *hsdev; +static struct msm_rpc_client *rpc_client; + +static int hs_find_key(uint32_t hscode) +{ + int i, key; + + key = KEY(hscode, 0); + + for (i = 0; hs_key_map[i] != 0; i++) { + if ((hs_key_map[i] & 0xff000000) == key) + return hs_key_map[i] & 0x00ffffff; + } + return -1; +} + +static void +report_headset_switch(struct input_dev *dev, int key, int value) +{ + struct msm_handset *hs = input_get_drvdata(dev); + + input_report_switch(dev, key, value); + input_sync(dev); +} + +/* + * tuple format: (key_code, key_param) + * + * old-architecture: + * key-press = (key_code, 0) + * key-release = (0xff, key_code) + * + * new-architecutre: + * key-press = (key_code, 0) + * key-release = (key_code, 0xff) + */ +static void report_hs_key(uint32_t key_code, uint32_t key_parm) +{ + int key, temp_key_code; + + if (key_code == HS_REL_K) + key = hs_find_key(key_parm); + else + key = hs_find_key(key_code); + + temp_key_code = key_code; + + if (key_parm == HS_REL_K) + key_code = key_parm; + + kpdev = msm_keypad_get_input_dev(); + hsdev = msm_get_handset_input_dev(); + + switch (key) { + case KEY_POWER: + case KEY_END: + if (!kpdev) { + printk(KERN_ERR "%s: No input device for reporting " + "pwr/end key press\n", __func__); + return; + } + input_report_key(kpdev, key, (key_code != HS_REL_K)); + input_sync(kpdev); + break; + case SW_HEADPHONE_INSERT: + if (!hsdev) { + printk(KERN_ERR "%s: No input device for reporting " + "handset events\n", __func__); + return; + } + report_headset_switch(hsdev, key, (key_code != HS_REL_K)); + break; + case KEY_MEDIA: + if (!hsdev) { + printk(KERN_ERR "%s: No input device for reporting " + "handset events\n", __func__); + return; + } + input_report_key(hsdev, key, (key_code != HS_REL_K)); + input_sync(hsdev); + break; + case -1: + printk(KERN_ERR "%s: No mapping for remote handset event %d\n", + __func__, temp_key_code); + break; + default: + printk(KERN_ERR "%s: Unhandled handset key %d\n", __func__, + key); + } +} + +static int handle_hs_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + struct rpc_keypad_pass_key_code_args { + uint32_t key_code; + uint32_t key_parm; + }; + + switch (req->procedure) { + case RPC_KEYPAD_NULL_PROC: + return 0; + + case RPC_KEYPAD_PASS_KEY_CODE_PROC: { + struct rpc_keypad_pass_key_code_args *args; + + args = (struct rpc_keypad_pass_key_code_args *)(req + 1); + args->key_code = be32_to_cpu(args->key_code); + args->key_parm = be32_to_cpu(args->key_parm); + + report_hs_key(args->key_code, args->key_parm); + + return 0; + } + + case RPC_KEYPAD_SET_PWR_KEY_STATE_PROC: + /* This RPC function must be available for the ARM9 + * to function properly. This function is redundant + * when RPC_KEYPAD_PASS_KEY_CODE_PROC is handled. So + * input_report_key is not needed. + */ + return 0; + default: + return -ENODEV; + } +} + +static struct msm_rpc_server hs_rpc_server = { + .prog = HS_SERVER_PROG, + .vers = HS_SERVER_VERS, + .rpc_call = handle_hs_rpc_call, +}; + +static int process_subs_srvc_callback(struct hs_event_cb_recv *recv) +{ + if (!recv) + return -ENODATA; + + report_hs_key(be32_to_cpu(recv->key.code), be32_to_cpu(recv->key.parm)); + + return 0; +} + +static void process_hs_rpc_request(uint32_t proc, void *data) +{ + if (proc == HS_EVENT_CB_PROC) + process_subs_srvc_callback(data); + else + pr_err("%s: unknown rpc proc %d\n", __func__, proc); +} + +static int hs_rpc_register_subs_arg(struct msm_rpc_client *client, + void *buffer, void *data) +{ + struct hs_subs_rpc_req { + uint32_t hs_subs_ptr; + struct hs_subs hs_subs; + uint32_t hs_cb_id; + uint32_t hs_handle_ptr; + uint32_t hs_handle_data; + }; + + struct hs_subs_rpc_req *req = buffer; + + req->hs_subs_ptr = cpu_to_be32(0x1); + req->hs_subs.ver = cpu_to_be32(0x1); + req->hs_subs.srvc = cpu_to_be32(HS_SUBS_RCV_EVNT); + req->hs_subs.req = cpu_to_be32(HS_SUBS_REGISTER); + req->hs_subs.host_os = cpu_to_be32(0x4); /* linux */ + req->hs_subs.disc = cpu_to_be32(HS_SUBS_RCV_EVNT); + req->hs_subs.id.evnt = cpu_to_be32(HS_EVNT_CLASS_ALL); + + req->hs_cb_id = cpu_to_be32(0x1); + + req->hs_handle_ptr = cpu_to_be32(0x1); + req->hs_handle_data = cpu_to_be32(0x0); + + return sizeof(*req); +} + +static int hs_rpc_register_subs_res(struct msm_rpc_client *client, + void *buffer, void *data) +{ + uint32_t result; + + result = be32_to_cpu(*((uint32_t *)buffer)); + pr_debug("%s: request completed: 0x%x\n", __func__, result); + + return 0; +} + +static int hs_cb_func(struct msm_rpc_client *client, void *buffer, int in_size) +{ + int rc = -1; + + struct rpc_request_hdr *hdr = buffer; + + hdr->type = be32_to_cpu(hdr->type); + hdr->xid = be32_to_cpu(hdr->xid); + hdr->rpc_vers = be32_to_cpu(hdr->rpc_vers); + hdr->prog = be32_to_cpu(hdr->prog); + hdr->vers = be32_to_cpu(hdr->vers); + hdr->procedure = be32_to_cpu(hdr->procedure); + + if (hdr->type != 0) + return rc; + if (hdr->rpc_vers != 2) + return rc; + if (hdr->prog != HS_RPC_CB_PROG) + return rc; + if (!msm_rpc_is_compatible_version(HS_RPC_CB_VERS, + hdr->vers)) + return rc; + + process_hs_rpc_request(hdr->procedure, + (void *) (hdr + 1)); + + msm_rpc_start_accepted_reply(client, hdr->xid, + RPC_ACCEPTSTAT_SUCCESS); + rc = msm_rpc_send_accepted_reply(client, 0); + if (rc) { + pr_err("%s: sending reply failed: %d\n", __func__, rc); + return rc; + } + + return 0; +} + +static int __init hs_rpc_cb_init(void) +{ + int rc = 0; + + rpc_client = msm_rpc_register_client("hs", + HS_RPC_PROG, HS_RPC_VERS, 0, hs_cb_func); + + if (IS_ERR(rpc_client)) { + pr_err("%s: couldn't open rpc client err %ld\n", __func__, + PTR_ERR(rpc_client)); + return PTR_ERR(rpc_client); + } + + rc = msm_rpc_client_req(rpc_client, HS_SUBSCRIBE_SRVC_PROC, + hs_rpc_register_subs_arg, NULL, + hs_rpc_register_subs_res, NULL, -1); + if (rc) { + pr_err("%s: couldn't send rpc client request\n", __func__); + msm_rpc_unregister_client(rpc_client); + } + + return rc; +} + +static int __init hs_rpc_init(void) +{ + int rc; + + if (machine_is_msm7x27_surf() || machine_is_msm7x27_ffa()) { + rc = hs_rpc_cb_init(); + if (rc) + pr_err("%s: failed to initialize\n", __func__); + } + + return msm_rpc_create_server(&hs_rpc_server); +} +module_init(hs_rpc_init); diff --git a/arch/arm/mach-msm/rpc_server_time_remote.c b/arch/arm/mach-msm/rpc_server_time_remote.c new file mode 100644 index 000000000000..5e9719a085cf --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_time_remote.c @@ -0,0 +1,90 @@ +/* arch/arm/mach-msm/rpc_server_time_remote.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev <ibm@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <mach/msm_rpcrouter.h> +#include "rpc_server_time_remote.h" + +/* time_remote_mtoa server definitions. */ + +#define TIME_REMOTE_MTOA_PROG 0x3000005d +#if CONFIG_MSM_AMSS_VERSION==6210 +#define TIME_REMOTE_MTOA_VERS 0 +#elif (CONFIG_MSM_AMSS_VERSION==6220) || (CONFIG_MSM_AMSS_VERSION==6225) +#define TIME_REMOTE_MTOA_VERS 0x9202a8e4 +#else +#error "Unknown AMSS version" +#endif +#define TIME_REMOTE_MTOA_VERS_COMP 0x00010001 +#define RPC_TIME_REMOTE_MTOA_NULL 0 +#define RPC_TIME_TOD_SET_APPS_BASES 2 + +struct rpc_time_tod_set_apps_bases_args { + uint32_t tick; + uint64_t stamp; +}; + +static int handle_rpc_call(struct msm_rpc_server *server, + struct rpc_request_hdr *req, unsigned len) +{ + switch (req->procedure) { + case RPC_TIME_REMOTE_MTOA_NULL: + return 0; + + case RPC_TIME_TOD_SET_APPS_BASES: { + struct rpc_time_tod_set_apps_bases_args *args; + args = (struct rpc_time_tod_set_apps_bases_args *)(req + 1); + args->tick = be32_to_cpu(args->tick); + args->stamp = be64_to_cpu(args->stamp); + printk(KERN_INFO "RPC_TIME_TOD_SET_APPS_BASES:\n" + "\ttick = %d\n" + "\tstamp = %lld\n", + args->tick, args->stamp); + rtc_hctosys(); + return 0; + } + default: + return -ENODEV; + } +} + +static struct msm_rpc_server rpc_server[] = { + { + .prog = TIME_REMOTE_MTOA_PROG, + .vers = TIME_REMOTE_MTOA_VERS, + .rpc_call = handle_rpc_call, + }, + { + .prog = TIME_REMOTE_MTOA_PROG, + .vers = TIME_REMOTE_MTOA_VERS_COMP, + .rpc_call = handle_rpc_call, + }, +}; + +static int __init rpc_server_init(void) +{ + /* Dual server registration to support backwards compatibility vers */ + int ret; + ret = msm_rpc_create_server(&rpc_server[1]); + if (ret < 0) + return ret; + return msm_rpc_create_server(&rpc_server[0]); +} + + +module_init(rpc_server_init); diff --git a/arch/arm/mach-msm/rpc_server_time_remote.h b/arch/arm/mach-msm/rpc_server_time_remote.h new file mode 100644 index 000000000000..056666f50013 --- /dev/null +++ b/arch/arm/mach-msm/rpc_server_time_remote.h @@ -0,0 +1,21 @@ +/* arch/arm/mach-msm/rpc_server_time_remote.h + * + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ARCH_ARM_MACH_MSM_RPC_SERVER_TIME_REMOTE_H +#define __ARCH_ARM_MACH_MSM_RPC_SERVER_TIME_REMOTE_H + +int rtc_hctosys(void); + +#endif diff --git a/arch/arm/mach-msm/sirc.c b/arch/arm/mach-msm/sirc.c new file mode 100644 index 000000000000..5a64aa44ddbe --- /dev/null +++ b/arch/arm/mach-msm/sirc.c @@ -0,0 +1,239 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <asm/irq.h> + +static void sirc_irq_mask(unsigned int irq); +static void sirc_irq_unmask(unsigned int irq); +static void sirc_irq_ack(unsigned int irq); +static int sirc_irq_set_wake(unsigned int irq, unsigned int on); +static int sirc_irq_set_type(unsigned int irq, unsigned int flow_type); +static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc); + +static unsigned int int_enable; +static unsigned int wake_enable; + +static struct sirc_regs_t sirc_regs = { + .int_enable = SPSS_SIRC_INT_ENABLE, + .int_enable_clear = SPSS_SIRC_INT_ENABLE_CLEAR, + .int_enable_set = SPSS_SIRC_INT_ENABLE_SET, + .int_type = SPSS_SIRC_INT_TYPE, + .int_polarity = SPSS_SIRC_INT_POLARITY, + .int_clear = SPSS_SIRC_INT_CLEAR, +}; + +static struct sirc_cascade_regs sirc_reg_table[] = { + { + .int_status = SPSS_SIRC_IRQ_STATUS, + .cascade_irq = INT_SIRC_0, + } +}; + +static unsigned int save_type; +static unsigned int save_polarity; + +/* Mask off the given interrupt. Keep the int_enable mask in sync with + the enable reg, so it can be restored after power collapse. */ +static void sirc_irq_mask(unsigned int irq) +{ + unsigned int mask; + + + mask = 1 << (irq - FIRST_SIRC_IRQ); + writel(mask, sirc_regs.int_enable_clear); + int_enable &= ~mask; + return; +} + +/* Unmask the given interrupt. Keep the int_enable mask in sync with + the enable reg, so it can be restored after power collapse. */ +static void sirc_irq_unmask(unsigned int irq) +{ + unsigned int mask; + + mask = 1 << (irq - FIRST_SIRC_IRQ); + writel(mask, sirc_regs.int_enable_set); + int_enable |= mask; + return; +} + +static void sirc_irq_ack(unsigned int irq) +{ + unsigned int mask; + + mask = 1 << (irq - FIRST_SIRC_IRQ); + writel(mask, sirc_regs.int_clear); + return; +} + +static int sirc_irq_set_wake(unsigned int irq, unsigned int on) +{ + unsigned int mask; + + /* Used to set the interrupt enable mask during power collapse. */ + mask = 1 << (irq - FIRST_SIRC_IRQ); + if (on) + wake_enable |= mask; + else + wake_enable &= ~mask; + + return 0; +} + +static int sirc_irq_set_type(unsigned int irq, unsigned int flow_type) +{ + unsigned int mask; + unsigned int val; + + mask = 1 << (irq - FIRST_SIRC_IRQ); + val = readl(sirc_regs.int_polarity); + + if (flow_type & (IRQF_TRIGGER_LOW | IRQF_TRIGGER_FALLING)) + val |= mask; + else + val &= ~mask; + + writel(val, sirc_regs.int_polarity); + + val = readl(sirc_regs.int_type); + if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { + val |= mask; + irq_desc[irq].handle_irq = handle_edge_irq; + } else { + val &= ~mask; + irq_desc[irq].handle_irq = handle_level_irq; + } + + writel(val, sirc_regs.int_type); + + return 0; +} + +/* Finds the pending interrupt on the passed cascade irq and redrives it */ +static void sirc_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + unsigned int reg = 0; + unsigned int sirq; + unsigned int status; + + while ((reg < ARRAY_SIZE(sirc_reg_table)) && + (sirc_reg_table[reg].cascade_irq != irq)) + reg++; + + status = readl(sirc_reg_table[reg].int_status); + status &= SIRC_MASK; + if (status == 0) + return; + + for (sirq = 0; + (sirq < NR_SIRC_IRQS) && ((status & (1U << sirq)) == 0); + sirq++) + ; + generic_handle_irq(sirq+FIRST_SIRC_IRQ); + + desc->chip->ack(irq); +} + +void msm_sirc_enter_sleep(void) +{ + save_type = readl(sirc_regs.int_type); + save_polarity = readl(sirc_regs.int_polarity); + writel(wake_enable, sirc_regs.int_enable); + return; +} + +void msm_sirc_exit_sleep(void) +{ + writel(save_type, sirc_regs.int_type); + writel(save_polarity, sirc_regs.int_polarity); + writel(int_enable, sirc_regs.int_enable); + return; +} + +static struct irq_chip sirc_irq_chip = { + .name = "sirc", + .ack = sirc_irq_ack, + .mask = sirc_irq_mask, + .unmask = sirc_irq_unmask, + .set_wake = sirc_irq_set_wake, + .set_type = sirc_irq_set_type, +}; + +void __init msm_init_sirc(void) +{ + int i; + + int_enable = 0; + wake_enable = 0; + + for (i = FIRST_SIRC_IRQ; i < LAST_SIRC_IRQ; i++) { + set_irq_chip(i, &sirc_irq_chip); + set_irq_handler(i, handle_edge_irq); + set_irq_flags(i, IRQF_VALID); + } + + for (i = 0; i < ARRAY_SIZE(sirc_reg_table); i++) { + set_irq_chained_handler(sirc_reg_table[i].cascade_irq, + sirc_irq_handler); + set_irq_wake(sirc_reg_table[i].cascade_irq, 1); + } + return; +} + diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c new file mode 100644 index 000000000000..82596828c7e0 --- /dev/null +++ b/arch/arm/mach-msm/smd.c @@ -0,0 +1,1960 @@ +/* arch/arm/mach-msm/smd.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/termios.h> +#include <linux/ctype.h> +#include <mach/msm_smd.h> +#include <mach/msm_iomap.h> +#include <mach/system.h> +#include <linux/io.h> + +#include "smd_private.h" +#include "proc_comm.h" +#include "modem_notifier.h" + +#define MODULE_NAME "msm_smd" +#define SMEM_VERSION 0x000B +#define SMD_VERSION 0x00020000 + +enum { + MSM_SMD_DEBUG = 1U << 0, + MSM_SMSM_DEBUG = 1U << 1, + MSM_SMD_INFO = 1U << 2, + MSM_SMSM_INFO = 1U << 3, +}; + +enum { + SMEM_APPS_Q6_SMSM = 3, + SMEM_Q6_APPS_SMSM = 5, + SMSM_NUM_INTR_MUX = 8, +}; + +/* Internal definitions which are not exported in some targets */ +enum { + SMSM_Q6_I = 2, +}; + +enum { + SMSM_APPS_DEM_I = 3, +}; + +enum { + SMD_APPS_QDSP_I = 1, + SMD_MODEM_QDSP_I = 2 +}; + +static int msm_smd_debug_mask; +module_param_named(debug_mask, msm_smd_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +void *smem_find(unsigned id, unsigned size); +void smd_diag(void); + +static unsigned last_heap_free = 0xffffffff; + +#if defined(CONFIG_MSM_SMD_DEBUG) +#define SMD_DBG(x...) do { \ + if (msm_smd_debug_mask & MSM_SMD_DEBUG) \ + printk(KERN_DEBUG x); \ + } while (0) + +#define SMSM_DBG(x...) do { \ + if (msm_smd_debug_mask & MSM_SMSM_DEBUG) \ + printk(KERN_DEBUG x); \ + } while (0) + +#define SMD_INFO(x...) do { \ + if (msm_smd_debug_mask & MSM_SMD_INFO) \ + printk(KERN_INFO x); \ + } while (0) + +#define SMSM_INFO(x...) do { \ + if (msm_smd_debug_mask & MSM_SMSM_INFO) \ + printk(KERN_INFO x); \ + } while (0) +#else +#define SMD_DBG(x...) do { } while (0) +#define SMSM_DBG(x...) do { } while (0) +#define SMD_INFO(x...) do { } while (0) +#define SMSM_INFO(x...) do { } while (0) +#endif + +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_TRIG_A2M_INT(n) (writel(1 << n, MSM_GCC_BASE + 0x8)) +#else +#define MSM_TRIG_A2M_INT(n) (writel(1, MSM_CSR_BASE + 0x400 + (n) * 4)) +#endif + +static void notify_other_smsm(uint32_t smsm_entry, + uint32_t old_val, uint32_t new_val) +{ + uint32_t *smsm_intr_mask; + uint32_t *smsm_intr_mux; + + smsm_intr_mask = smem_alloc(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * + sizeof(uint32_t)); + + /* older protocol don't use smsm_intr_mask, + but still communicates with modem */ + if (!smsm_intr_mask || + (smsm_intr_mask[smsm_entry * SMSM_NUM_HOSTS + SMSM_MODEM] & + (old_val ^ new_val))) + MSM_TRIG_A2M_INT(5); + + if (smsm_intr_mask && + (smsm_intr_mask[smsm_entry * SMSM_NUM_HOSTS + SMSM_Q6_I] & + (old_val ^ new_val))) { + smsm_intr_mux = smem_alloc(SMEM_SMD_SMSM_INTR_MUX, + SMSM_NUM_INTR_MUX * + sizeof(uint32_t)); + if (smsm_intr_mux) + smsm_intr_mux[SMEM_APPS_Q6_SMSM]++; + + MSM_TRIG_A2M_INT(8); + } +} + +static inline void notify_other_smd(uint32_t ch_type) +{ + if (ch_type == SMD_APPS_MODEM) + MSM_TRIG_A2M_INT(0); + else if (ch_type == SMD_APPS_QDSP_I) + MSM_TRIG_A2M_INT(8); +} + +void smd_diag(void) +{ + char *x; + int size; + + x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); + if (x != 0) { + x[SZ_DIAG_ERR_MSG - 1] = 0; + SMD_INFO("smem: DIAG '%s'\n", x); + } + + x = smem_get_entry(SMEM_ERR_CRASH_LOG, &size); + if (x != 0) { + x[size - 1] = 0; + printk(KERN_ERR "smem: CRASH LOG\n'%s'\n", x); + } +} + +extern int (*msm_check_for_modem_crash)(void); + +static int check_for_modem_crash(void) +{ + uint32_t *smsm; + + smsm = smem_find(ID_SHARED_STATE, SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + /* if the modem's not ready yet, we have to hope for the best */ + if (!smsm) + return 0; + + if (smsm[SMSM_MODEM_STATE] & SMSM_RESET) { + pr_err("proc_comm: ARM9 has crashed\n"); + smd_diag(); + } else { + return 0; + } + + /* hard reboot if possible FIXME + if (msm_reset_hook) + msm_reset_hook(0); + */ + + for (;;) + ; +} + +#define SMD_SS_CLOSED 0x00000000 +#define SMD_SS_OPENING 0x00000001 +#define SMD_SS_OPENED 0x00000002 +#define SMD_SS_FLUSHING 0x00000003 +#define SMD_SS_CLOSING 0x00000004 +#define SMD_SS_RESET 0x00000005 +#define SMD_SS_RESET_OPENING 0x00000006 + +#define SMD_BUF_SIZE 8192 +#define SMD_CHANNELS 64 + +#define SMD_HEADER_SIZE 20 + + +/* the spinlock is used to synchronize between the +** irq handler and code that mutates the channel +** list or fiddles with channel state +*/ +static DEFINE_SPINLOCK(smd_lock); +static DEFINE_SPINLOCK(smem_lock); + +/* the mutex is used during open() and close() +** operations to avoid races while creating or +** destroying smd_channel structures +*/ +static DEFINE_MUTEX(smd_creation_mutex); + +static int smd_initialized; + +/* 'type' field of smd_alloc_elm structure + * has the following breakup + * bits 0-7 -> channel type + * bits 8-11 -> xfer type + * bits 12-31 -> reserved + */ +struct smd_alloc_elm { + char name[20]; + uint32_t cid; + uint32_t type; + uint32_t ref_count; +}; + +#define SMD_CHANNEL_TYPE(x) ((x) & 0x000000FF) +#define SMD_XFER_TYPE(x) (((x) & 0x00000F00) >> 8) + +struct smd_half_channel { + unsigned state; + unsigned char fDSR; + unsigned char fCTS; + unsigned char fCD; + unsigned char fRI; + unsigned char fHEAD; + unsigned char fTAIL; + unsigned char fSTATE; + unsigned char fUNUSED; + unsigned tail; + unsigned head; +}; + +struct smd_channel { + volatile struct smd_half_channel *send; + volatile struct smd_half_channel *recv; + unsigned char *send_buf; + unsigned char *recv_buf; + unsigned buf_size; + struct list_head ch_list; + + unsigned current_packet; + unsigned n; + void *priv; + void (*notify)(void *priv, unsigned flags); + + int (*read)(smd_channel_t *ch, void *data, int len); + int (*write)(smd_channel_t *ch, const void *data, int len); + int (*read_avail)(smd_channel_t *ch); + int (*write_avail)(smd_channel_t *ch); + int (*read_from_cb)(smd_channel_t *ch, void *data, int len); + + void (*update_state)(smd_channel_t *ch); + unsigned last_state; + + char name[20]; + struct platform_device pdev; + unsigned type; +}; + +static LIST_HEAD(smd_ch_closed_list); +static LIST_HEAD(smd_ch_list); + +static unsigned char smd_ch_allocated[64]; +static struct work_struct probe_work; + +static void smd_alloc_channel(struct smd_alloc_elm *alloc_elm); +static void *_smem_find(unsigned id, unsigned *size); + +static void smd_channel_probe_worker(struct work_struct *work) +{ + struct smd_alloc_elm *shared; + unsigned n; + + shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); + + BUG_ON(!shared); + + for (n = 0; n < 64; n++) { + if (smd_ch_allocated[n]) + continue; + + /* channel should be allocated only if APPS + processor is involved */ + if (SMD_CHANNEL_TYPE(shared[n].type) == SMD_MODEM_QDSP_I) + continue; + if (!shared[n].ref_count) + continue; + if (!shared[n].name[0]) + continue; + + smd_alloc_channel(&shared[n]); + smd_ch_allocated[n] = 1; + } +} + +static char *chstate(unsigned n) +{ + switch (n) { + case SMD_SS_CLOSED: return "CLOSED"; + case SMD_SS_OPENING: return "OPENING"; + case SMD_SS_OPENED: return "OPENED"; + case SMD_SS_FLUSHING: return "FLUSHING"; + case SMD_SS_CLOSING: return "CLOSING"; + case SMD_SS_RESET: return "RESET"; + case SMD_SS_RESET_OPENING: return "ROPENING"; + default: return "UNKNOWN"; + } +} + +/* how many bytes are available for reading */ +static int smd_stream_read_avail(struct smd_channel *ch) +{ + return (ch->recv->head - ch->recv->tail) & (ch->buf_size - 1); +} + +/* how many bytes we are free to write */ +static int smd_stream_write_avail(struct smd_channel *ch) +{ + return (ch->buf_size - 1) - + ((ch->send->head - ch->send->tail) & (ch->buf_size - 1)); +} + +static int smd_packet_read_avail(struct smd_channel *ch) +{ + if (ch->current_packet) { + int n = smd_stream_read_avail(ch); + if (n > ch->current_packet) + n = ch->current_packet; + return n; + } else { + return 0; + } +} + +static int smd_packet_write_avail(struct smd_channel *ch) +{ + int n = smd_stream_write_avail(ch); + return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0; +} + +static int ch_is_open(struct smd_channel *ch) +{ + return (ch->recv->state == SMD_SS_OPENED || + ch->recv->state == SMD_SS_FLUSHING) + && (ch->send->state == SMD_SS_OPENED); +} + +/* provide a pointer and length to readable data in the fifo */ +static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr) +{ + unsigned head = ch->recv->head; + unsigned tail = ch->recv->tail; + *ptr = (void *) (ch->recv_buf + tail); + + if (tail <= head) + return head - tail; + else + return ch->buf_size - tail; +} + +/* advance the fifo read pointer after data from ch_read_buffer is consumed */ +static void ch_read_done(struct smd_channel *ch, unsigned count) +{ + BUG_ON(count > smd_stream_read_avail(ch)); + ch->recv->tail = (ch->recv->tail + count) & (ch->buf_size - 1); + ch->send->fTAIL = 1; +} + +/* basic read interface to ch_read_{buffer,done} used +** by smd_*_read() and update_packet_state() +** will read-and-discard if the _data pointer is null +*/ +static int ch_read(struct smd_channel *ch, void *_data, int len) +{ + void *ptr; + unsigned n; + unsigned char *data = _data; + int orig_len = len; + + while (len > 0) { + n = ch_read_buffer(ch, &ptr); + if (n == 0) + break; + + if (n > len) + n = len; + if (_data) + memcpy(data, ptr, n); + + data += n; + len -= n; + ch_read_done(ch, n); + } + + return orig_len - len; +} + +static void update_stream_state(struct smd_channel *ch) +{ + /* streams have no special state requiring updating */ +} + +static void update_packet_state(struct smd_channel *ch) +{ + unsigned hdr[5]; + int r; + + /* can't do anything if we're in the middle of a packet */ + while (ch->current_packet == 0) { + /* discard 0 length packets if any */ + + /* don't bother unless we can get the full header */ + if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE) + return; + + r = ch_read(ch, hdr, SMD_HEADER_SIZE); + BUG_ON(r != SMD_HEADER_SIZE); + + ch->current_packet = hdr[0]; + } +} + +/* provide a pointer and length to next free space in the fifo */ +static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr) +{ + unsigned head = ch->send->head; + unsigned tail = ch->send->tail; + *ptr = (void *) (ch->send_buf + head); + + if (head < tail) { + return tail - head - 1; + } else { + if (tail == 0) + return ch->buf_size - head - 1; + else + return ch->buf_size - head; + } +} + +/* advace the fifo write pointer after freespace from ch_write_buffer is filled */ +static void ch_write_done(struct smd_channel *ch, unsigned count) +{ + BUG_ON(count > smd_stream_write_avail(ch)); + ch->send->head = (ch->send->head + count) & (ch->buf_size - 1); + ch->send->fHEAD = 1; +} + +static void ch_set_state(struct smd_channel *ch, unsigned n) +{ + if (n == SMD_SS_OPENED) { + ch->send->fDSR = 1; + ch->send->fCTS = 1; + ch->send->fCD = 1; + } else { + ch->send->fDSR = 0; + ch->send->fCTS = 0; + ch->send->fCD = 0; + } + ch->send->state = n; + ch->send->fSTATE = 1; + notify_other_smd(ch->type); +} + +static void do_smd_probe(void) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + if (shared->heap_info.free_offset != last_heap_free) { + last_heap_free = shared->heap_info.free_offset; + schedule_work(&probe_work); + } +} + +static void smd_state_change(struct smd_channel *ch, + unsigned last, unsigned next) +{ + ch->last_state = next; + + SMD_INFO("SMD: ch %d %s -> %s\n", ch->n, + chstate(last), chstate(next)); + + switch (next) { + case SMD_SS_OPENING: + if (ch->send->state == SMD_SS_CLOSING || + ch->send->state == SMD_SS_CLOSED) { + ch->recv->tail = 0; + ch->send->head = 0; + ch_set_state(ch, SMD_SS_OPENING); + } + break; + case SMD_SS_OPENED: + if (ch->send->state == SMD_SS_OPENING) { + ch_set_state(ch, SMD_SS_OPENED); + ch->notify(ch->priv, SMD_EVENT_OPEN); + } + break; + case SMD_SS_FLUSHING: + case SMD_SS_RESET: + /* we should force them to close? */ + break; + case SMD_SS_CLOSED: + if (ch->send->state == SMD_SS_OPENED) { + ch_set_state(ch, SMD_SS_CLOSING); + ch->notify(ch->priv, SMD_EVENT_CLOSE); + } + break; + } +} + +static irqreturn_t smd_irq_handler(int irq, void *data) +{ + unsigned long flags; + struct smd_channel *ch; + int do_notify_modem = 0; + int do_notify_qdsp = 0; + unsigned ch_flags; + unsigned tmp; + + spin_lock_irqsave(&smd_lock, flags); + list_for_each_entry(ch, &smd_ch_list, ch_list) { + ch_flags = 0; + if (ch_is_open(ch)) { + if (ch->recv->fHEAD) { + ch->recv->fHEAD = 0; + ch_flags |= 1; + if (ch->type == SMD_APPS_MODEM) + do_notify_modem |= 1; + else if (ch->type == SMD_APPS_QDSP_I) + do_notify_qdsp |= 1; + } + if (ch->recv->fTAIL) { + ch->recv->fTAIL = 0; + ch_flags |= 2; + if (ch->type == SMD_APPS_MODEM) + do_notify_modem |= 1; + else if (ch->type == SMD_APPS_QDSP_I) + do_notify_qdsp |= 1; + } + if (ch->recv->fSTATE) { + ch->recv->fSTATE = 0; + ch_flags |= 4; + if (ch->type == SMD_APPS_MODEM) + do_notify_modem |= 1; + else if (ch->type == SMD_APPS_QDSP_I) + do_notify_qdsp |= 1; + } + } + tmp = ch->recv->state; + if (tmp != ch->last_state) + smd_state_change(ch, ch->last_state, tmp); + if (ch_flags) { + ch->update_state(ch); + ch->notify(ch->priv, SMD_EVENT_DATA); + } + } + if (do_notify_modem) + notify_other_smd(SMD_APPS_MODEM); + + if (do_notify_qdsp) + notify_other_smd(SMD_APPS_QDSP_I); + + spin_unlock_irqrestore(&smd_lock, flags); + do_smd_probe(); + return IRQ_HANDLED; +} + +static void smd_fake_irq_handler(unsigned long arg) +{ + smd_irq_handler(0, NULL); +} + +static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0); + +void smd_sleep_exit(void) +{ + unsigned long flags; + struct smd_channel *ch; + unsigned tmp; + int need_int = 0; + + spin_lock_irqsave(&smd_lock, flags); + list_for_each_entry(ch, &smd_ch_list, ch_list) { + if (ch_is_open(ch)) { + if (ch->recv->fHEAD) { + SMD_DBG("smd_sleep_exit ch %d fHEAD " + "%x %x %x\n", + ch->n, + ch->recv->fHEAD, + ch->recv->head, ch->recv->tail); + need_int = 1; + break; + } + if (ch->recv->fTAIL) { + SMD_DBG("smd_sleep_exit ch %d fTAIL " + "%x %x %x\n", + ch->n, + ch->recv->fTAIL, + ch->send->head, ch->send->tail); + need_int = 1; + break; + } + if (ch->recv->fSTATE) { + SMD_DBG("smd_sleep_exit ch %d fSTATE %x" + "\n", ch->n, + ch->recv->fSTATE); + need_int = 1; + break; + } + tmp = ch->recv->state; + if (tmp != ch->last_state) { + SMD_DBG("smd_sleep_exit ch %d " + "state %x != %x\n", + ch->n, tmp, + ch->last_state); + need_int = 1; + break; + } + } + } + spin_unlock_irqrestore(&smd_lock, flags); + do_smd_probe(); + if (need_int) { + SMD_DBG("smd_sleep_exit need interrupt\n"); + tasklet_schedule(&smd_fake_irq_tasklet); + } +} + +static int smd_is_packet(struct smd_alloc_elm *alloc_elm) +{ + if (SMD_XFER_TYPE(alloc_elm->type) == 1) + return 0; + else if (SMD_XFER_TYPE(alloc_elm->type) == 2) + return 1; + + /* for cases where xfer type is 0 */ + if (!strncmp(alloc_elm->name, "DAL", 3)) + return 0; + + if (alloc_elm->cid > 4 || alloc_elm->cid == 1) + return 1; + else + return 0; +} + +static int smd_stream_write(smd_channel_t *ch, const void *_data, int len) +{ + void *ptr; + const unsigned char *buf = _data; + unsigned xfer; + int orig_len = len; + + SMD_DBG("smd_stream_write() %d -> ch%d\n", len, ch->n); + if (len < 0) + return -EINVAL; + else if (len == 0) + return 0; + + while ((xfer = ch_write_buffer(ch, &ptr)) != 0) { + if (!ch_is_open(ch)) + break; + if (xfer > len) + xfer = len; + memcpy(ptr, buf, xfer); + ch_write_done(ch, xfer); + len -= xfer; + buf += xfer; + if (len == 0) + break; + } + + if (orig_len - len) + notify_other_smd(ch->type); + + return orig_len - len; +} + +static int smd_packet_write(smd_channel_t *ch, const void *_data, int len) +{ + int ret; + unsigned hdr[5]; + + SMD_DBG("smd_packet_write() %d -> ch%d\n", len, ch->n); + if (len < 0) + return -EINVAL; + else if (len == 0) + return 0; + + if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE)) + return -ENOMEM; + + hdr[0] = len; + hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0; + + + ret = smd_stream_write(ch, hdr, sizeof(hdr)); + if (ret < 0 || ret != sizeof(hdr)) { + SMD_DBG("%s failed to write pkt header: " + "%d returned\n", __func__, ret); + return -1; + } + + + ret = smd_stream_write(ch, _data, len); + if (ret < 0 || ret != len) { + SMD_DBG("%s failed to write pkt data: " + "%d returned\n", __func__, ret); + return ret; + } + + return len; +} + +static int smd_stream_read(smd_channel_t *ch, void *data, int len) +{ + int r; + + if (len < 0) + return -EINVAL; + + r = ch_read(ch, data, len); + if (r > 0) + notify_other_smd(ch->type); + + return r; +} + +static int smd_packet_read(smd_channel_t *ch, void *data, int len) +{ + unsigned long flags; + int r; + + if (len < 0) + return -EINVAL; + + if (len > ch->current_packet) + len = ch->current_packet; + + r = ch_read(ch, data, len); + if (r > 0) + notify_other_smd(ch->type); + + spin_lock_irqsave(&smd_lock, flags); + ch->current_packet -= r; + update_packet_state(ch); + spin_unlock_irqrestore(&smd_lock, flags); + + return r; +} + +static int smd_packet_read_from_cb(smd_channel_t *ch, void *data, int len) +{ + int r; + + if (len < 0) + return -EINVAL; + + if (len > ch->current_packet) + len = ch->current_packet; + + r = ch_read(ch, data, len); + if (r > 0) + notify_other_smd(ch->type); + + ch->current_packet -= r; + update_packet_state(ch); + + return r; +} + +static struct smd_channel *_smd_alloc_channel_v1(uint32_t cid) +{ + struct smd_channel *ch; + void *shared; + + shared = smem_alloc(ID_SMD_CHANNELS + cid, + 2 * (sizeof(struct smd_half_channel) + + SMD_BUF_SIZE)); + if (!shared) { + pr_err("smd_alloc_channel: cid %d does not exist\n", cid); + return NULL; + } + + ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL); + if (ch) { + ch->send = shared; + ch->send_buf = shared + sizeof(struct smd_half_channel); + ch->recv = (struct smd_half_channel *) + (ch->send_buf + SMD_BUF_SIZE); + ch->recv_buf = (unsigned char *)ch->recv + + sizeof(struct smd_half_channel); + ch->buf_size = SMD_BUF_SIZE; + ch->n = cid; + } else + pr_err("smd_alloc_channel: out of memory\n"); + + return ch; +} + +static struct smd_channel *_smd_alloc_channel_v2(uint32_t cid) +{ + struct smd_channel *ch; + void *shared, *shared_fifo; + unsigned size; + + shared = smem_alloc(ID_SMD_CHANNELS + cid, + 2 * sizeof(struct smd_half_channel)); + if (!shared) { + pr_err("smd_alloc_channel: cid %d does not exist\n", cid); + return NULL; + } + + shared_fifo = _smem_find(SMEM_SMD_FIFO_BASE_ID + cid, &size); + if (!shared_fifo) { + pr_err("smd_alloc_channel: cid %d fifo do not exist\n", cid); + return NULL; + } + SMD_INFO("smd_alloc_channel: cid %d fifo found; size = %d\n", + cid, (size / 2)); + + ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL); + if (ch) { + ch->send = shared; + ch->recv = shared + sizeof(struct smd_half_channel); + ch->send_buf = shared_fifo; + ch->recv_buf = shared_fifo + (size / 2); + ch->buf_size = size / 2; + ch->n = cid; + } else + pr_err("smd_alloc_channel() out of memory\n"); + + return ch; +} + +static void smd_alloc_channel(struct smd_alloc_elm *alloc_elm) +{ + struct smd_channel *ch; + uint32_t *smd_ver; + + smd_ver = smem_alloc(SMEM_VERSION_SMD, 32 * sizeof(uint32_t)); + + if (smd_ver && ((smd_ver[VERSION_MODEM] >> 16) >= 1)) + ch = _smd_alloc_channel_v2(alloc_elm->cid); + else + ch = _smd_alloc_channel_v1(alloc_elm->cid); + + if (ch == 0) + return; + + ch->type = SMD_CHANNEL_TYPE(alloc_elm->type); + memcpy(ch->name, alloc_elm->name, 20); + ch->name[19] = 0; + + if (smd_is_packet(alloc_elm)) { + ch->read = smd_packet_read; + ch->write = smd_packet_write; + ch->read_avail = smd_packet_read_avail; + ch->write_avail = smd_packet_write_avail; + ch->update_state = update_packet_state; + ch->read_from_cb = smd_packet_read_from_cb; + } else { + ch->read = smd_stream_read; + ch->write = smd_stream_write; + ch->read_avail = smd_stream_read_avail; + ch->write_avail = smd_stream_write_avail; + ch->update_state = update_stream_state; + ch->read_from_cb = smd_stream_read; + } + + ch->pdev.name = ch->name; + ch->pdev.id = ch->type; + + SMD_INFO("smd_alloc_channel() '%s' cid=%d\n", + ch->name, ch->n); + + mutex_lock(&smd_creation_mutex); + list_add(&ch->ch_list, &smd_ch_closed_list); + mutex_unlock(&smd_creation_mutex); + + platform_device_register(&ch->pdev); +} + +static void do_nothing_notify(void *priv, unsigned flags) +{ +} + +struct smd_channel *smd_get_channel(const char *name, uint32_t type) +{ + struct smd_channel *ch; + + mutex_lock(&smd_creation_mutex); + list_for_each_entry(ch, &smd_ch_closed_list, ch_list) { + if (!strcmp(name, ch->name) && + (type == ch->type)) { + list_del(&ch->ch_list); + mutex_unlock(&smd_creation_mutex); + return ch; + } + } + mutex_unlock(&smd_creation_mutex); + + return NULL; +} + +int smd_named_open_on_edge(const char *name, uint32_t edge, + smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)) +{ + struct smd_channel *ch; + unsigned long flags; + + if (smd_initialized == 0) { + SMD_INFO("smd_open() before smd_init()\n"); + return -ENODEV; + } + + SMD_DBG("smd_open('%s', %p, %p)\n", name, priv, notify); + + ch = smd_get_channel(name, edge); + if (!ch) + return -ENODEV; + + if (notify == 0) + notify = do_nothing_notify; + + ch->notify = notify; + ch->current_packet = 0; + ch->last_state = SMD_SS_CLOSED; + ch->priv = priv; + + *_ch = ch; + + SMD_DBG("smd_open: opening '%s'\n", ch->name); + + spin_lock_irqsave(&smd_lock, flags); + list_add(&ch->ch_list, &smd_ch_list); + SMD_DBG("%s: opening ch %d\n", __func__, ch->n); + + smd_state_change(ch, ch->last_state, SMD_SS_OPENING); + + spin_unlock_irqrestore(&smd_lock, flags); + + return 0; +} +EXPORT_SYMBOL(smd_named_open_on_edge); + + +int smd_open(const char *name, smd_channel_t **_ch, + void *priv, void (*notify)(void *, unsigned)) +{ + return smd_named_open_on_edge(name, SMD_APPS_MODEM, _ch, priv, + notify); +} +EXPORT_SYMBOL(smd_open); + +int smd_close(smd_channel_t *ch) +{ + unsigned long flags; + + SMD_INFO("smd_close(%p)\n", ch); + + if (ch == 0) + return -1; + + spin_lock_irqsave(&smd_lock, flags); + ch->notify = do_nothing_notify; + list_del(&ch->ch_list); + ch_set_state(ch, SMD_SS_CLOSED); + spin_unlock_irqrestore(&smd_lock, flags); + + mutex_lock(&smd_creation_mutex); + list_add(&ch->ch_list, &smd_ch_closed_list); + mutex_unlock(&smd_creation_mutex); + + return 0; +} +EXPORT_SYMBOL(smd_close); + +int smd_read(smd_channel_t *ch, void *data, int len) +{ + return ch->read(ch, data, len); +} +EXPORT_SYMBOL(smd_read); + +int smd_read_from_cb(smd_channel_t *ch, void *data, int len) +{ + return ch->read_from_cb(ch, data, len); +} +EXPORT_SYMBOL(smd_read_from_cb); + +int smd_write(smd_channel_t *ch, const void *data, int len) +{ + return ch->write(ch, data, len); +} +EXPORT_SYMBOL(smd_write); + +int smd_read_avail(smd_channel_t *ch) +{ + return ch->read_avail(ch); +} +EXPORT_SYMBOL(smd_read_avail); + +int smd_write_avail(smd_channel_t *ch) +{ + return ch->write_avail(ch); +} +EXPORT_SYMBOL(smd_write_avail); + +int smd_wait_until_readable(smd_channel_t *ch, int bytes) +{ + return -1; +} + +int smd_wait_until_writable(smd_channel_t *ch, int bytes) +{ + return -1; +} + +int smd_cur_packet_size(smd_channel_t *ch) +{ + return ch->current_packet; +} + +int smd_tiocmget(smd_channel_t *ch) +{ + return (ch->recv->fDSR ? TIOCM_DSR : 0) | + (ch->recv->fCTS ? TIOCM_CTS : 0) | + (ch->recv->fCD ? TIOCM_CD : 0) | + (ch->recv->fRI ? TIOCM_RI : 0) | + (ch->send->fCTS ? TIOCM_RTS : 0) | + (ch->send->fDSR ? TIOCM_DTR : 0); +} + +int smd_tiocmset(smd_channel_t *ch, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + spin_lock_irqsave(&smd_lock, flags); + if (set & TIOCM_DTR) + ch->send->fDSR = 1; + + if (set & TIOCM_RTS) + ch->send->fCTS = 1; + + if (clear & TIOCM_DTR) + ch->send->fDSR = 0; + + if (clear & TIOCM_RTS) + ch->send->fCTS = 0; + + ch->send->fSTATE = 1; + barrier(); + notify_other_smd(ch->type); + spin_unlock_irqrestore(&smd_lock, flags); + + return 0; +} + + +/* -------------------------------------------------------------------------- */ + +void *smem_alloc(unsigned id, unsigned size) +{ + return smem_find(id, size); +} + +void *smem_get_entry(unsigned id, unsigned *size) +{ + return _smem_find(id, size); +} + +static void *_smem_find(unsigned id, unsigned *size) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + struct smem_heap_entry *toc = shared->heap_toc; + + if (id >= SMEM_NUM_ITEMS) + return 0; + + if (toc[id].allocated) { + *size = toc[id].size; + return (void *) (MSM_SHARED_RAM_BASE + toc[id].offset); + } + + return 0; +} + +void *smem_find(unsigned id, unsigned size_in) +{ + unsigned size; + void *ptr; + + ptr = _smem_find(id, &size); + if (!ptr) + return 0; + + size_in = ALIGN(size_in, 8); + if (size_in != size) { + pr_err("smem_find(%d, %d): wrong size %d\n", + id, size_in, size); + return 0; + } + + return ptr; +} + +static int smem_init(void) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + uint32_t *smsm, i; + + smsm = smem_alloc(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + if (smsm) { + smsm[SMSM_APPS_STATE] = 0; + if ((shared->version[VERSION_MODEM] >> 16) >= 0xB) + smsm[SMSM_APPS_DEM_I] = 0; + } + + smsm = smem_alloc(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t)); + + if (smsm) + for (i = 0; i < SMSM_NUM_ENTRIES; i++) + smsm[i * SMSM_NUM_HOSTS + SMSM_APPS] = 0xffffffff; + + return 0; +} + +void smsm_reset_modem(unsigned mode) +{ + if (mode == SMSM_SYSTEM_DOWNLOAD) { + mode = SMSM_RESET | SMSM_SYSTEM_DOWNLOAD; + } else if (mode == SMSM_MODEM_WAIT) { + mode = SMSM_RESET | SMSM_MODEM_WAIT; + } else { /* reset_mode is SMSM_RESET or default */ + mode = SMSM_RESET; + } + + smsm_change_state(SMSM_APPS_STATE, mode, mode); +} +EXPORT_SYMBOL(smsm_reset_modem); + +void smsm_reset_modem_cont(void) +{ + unsigned long flags; + uint32_t *smsm; + + spin_lock_irqsave(&smem_lock, flags); + smsm = smem_alloc(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + smsm[SMSM_APPS_STATE] &= ~SMSM_MODEM_WAIT; + spin_unlock_irqrestore(&smem_lock, flags); +} +EXPORT_SYMBOL(smsm_reset_modem_cont); + +static irqreturn_t smsm_irq_handler(int irq, void *data) +{ + unsigned long flags; + uint32_t *smsm; + static uint32_t prev_smem_q6_apps_smsm; + + if (irq == INT_ADSP_A11) { + smsm = smem_alloc(SMEM_SMD_SMSM_INTR_MUX, + SMSM_NUM_INTR_MUX * sizeof(uint32_t)); + if (!smsm || + (smsm[SMEM_Q6_APPS_SMSM] == prev_smem_q6_apps_smsm)) + return IRQ_HANDLED; + + prev_smem_q6_apps_smsm = smsm[SMEM_Q6_APPS_SMSM]; + } + + spin_lock_irqsave(&smem_lock, flags); + smsm = smem_alloc(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + if (smsm == 0) { + SMSM_INFO("<SM NO STATE>\n"); + } else { + unsigned old_apps, apps; + unsigned modm = smsm[SMSM_MODEM_STATE]; + + old_apps = apps = smsm[SMSM_APPS_STATE]; + + SMSM_DBG("<SM %08x %08x>\n", apps, modm); + if (apps & SMSM_RESET) { + /* If we get an interrupt and the apps SMSM_RESET + bit is already set, the modem is acking the + app's reset ack. */ + apps &= ~SMSM_RESET; + + /* Issue a fake irq to handle any + * smd state changes during reset + */ + smd_fake_irq_handler(0); + + /* queue modem restart notify chain */ + modem_queue_start_reset_notify(); + + } else if (modm & SMSM_RESET) { + apps |= SMSM_RESET; + } else { + apps |= SMSM_INIT; + if (modm & SMSM_SMDINIT) + apps |= SMSM_SMDINIT; + if (modm & SMSM_RPCINIT) + apps |= SMSM_RPCINIT; + if ((apps & (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT)) == + (SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT)) + apps |= SMSM_RUN; + } + + if (smsm[SMSM_APPS_STATE] != apps) { + SMSM_DBG("<SM %08x NOTIFY>\n", apps); + smsm[SMSM_APPS_STATE] = apps; + do_smd_probe(); + notify_other_smsm(SMSM_APPS_STATE, old_apps, apps); + } + } + spin_unlock_irqrestore(&smem_lock, flags); + return IRQ_HANDLED; +} + +int smsm_change_state(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask) +{ + unsigned long flags; + uint32_t *smsm; + uint32_t old_state; + + if (smsm_entry >= SMSM_NUM_ENTRIES) { + printk(KERN_ERR "smsm_change_state: Invalid entry %d", + smsm_entry); + return -EINVAL; + } + + spin_lock_irqsave(&smem_lock, flags); + + smsm = smem_alloc(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + if (smsm) { + old_state = smsm[smsm_entry]; + smsm[smsm_entry] = (smsm[smsm_entry] & ~clear_mask) | set_mask; + SMSM_DBG("smsm_change_state %x\n", smsm[smsm_entry]); + notify_other_smsm(SMSM_APPS_STATE, old_state, smsm[smsm_entry]); + } + + spin_unlock_irqrestore(&smem_lock, flags); + + if (smsm == NULL) { + printk(KERN_ERR "smsm_change_state <SM NO STATE>\n"); + return -EIO; + } + return 0; +} + +int smsm_change_intr_mask(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask) +{ + uint32_t *smsm; + + if (smsm_entry >= SMSM_NUM_ENTRIES) { + printk(KERN_ERR "smsm_change_state: Invalid entry %d\n", + smsm_entry); + return -EINVAL; + } + + smsm = smem_alloc(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t)); + + if (smsm) { + smsm[smsm_entry * SMSM_NUM_HOSTS + SMSM_APPS] = + (smsm[smsm_entry * SMSM_NUM_HOSTS + SMSM_APPS] & + ~clear_mask) | set_mask; + SMSM_INFO("smsm_entry %d, new intr_mask %x\n", smsm_entry, + smsm[smsm_entry * SMSM_NUM_HOSTS + SMSM_APPS]); + } else { + printk(KERN_ERR "smsm_change_intr_mask <SM NO INTR_MASK>\n"); + return -EIO; + } + + return 0; +} + +int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask) +{ + uint32_t *smsm; + + if ((smsm_entry >= SMSM_NUM_ENTRIES) || (!intr_mask)) { + printk(KERN_ERR "smsm_change_state: Invalid input " + "entry %d, mask 0x%x\n", + smsm_entry, (unsigned int)intr_mask); + return -EINVAL; + } + + smsm = smem_alloc(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t)); + + if (smsm) { + *intr_mask = smsm[smsm_entry * SMSM_NUM_HOSTS + SMSM_APPS]; + } else { + printk(KERN_ERR "smsm_change_intr_mask <SM NO INTR_MASK>\n"); + return -EIO; + } + + return 0; +} + +uint32_t smsm_get_state(uint32_t smsm_entry) +{ + unsigned long flags; + uint32_t *smsm; + uint32_t rv; + + if (smsm_entry >= SMSM_NUM_ENTRIES) { + printk(KERN_ERR "smsm_change_state: Invalid entry %d", + smsm_entry); + return -EINVAL; + } + + spin_lock_irqsave(&smem_lock, flags); + + smsm = smem_alloc(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + if (smsm) + rv = smsm[smsm_entry]; + else + rv = 0; + + spin_unlock_irqrestore(&smem_lock, flags); + + if (smsm == NULL) + printk(KERN_ERR "smsm_get_state <SM NO STATE>\n"); + return rv; + +} + +#define MAX_NUM_SLEEP_CLIENTS 64 +#define MAX_SLEEP_NAME_LEN 8 + +#define NUM_GPIO_INT_REGISTERS 6 +#define GPIO_SMEM_NUM_GROUPS 2 +#define GPIO_SMEM_MAX_PC_INTERRUPTS 8 +struct tramp_gpio_save { + unsigned int enable; + unsigned int detect; + unsigned int polarity; +}; + +struct tramp_gpio_smem { + uint16_t num_fired[GPIO_SMEM_NUM_GROUPS]; + uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS]; + uint32_t enabled[NUM_GPIO_INT_REGISTERS]; + uint32_t detection[NUM_GPIO_INT_REGISTERS]; + uint32_t polarity[NUM_GPIO_INT_REGISTERS]; +}; + +/* + * Print debug information on shared memory sleep variables + */ +void smsm_print_sleep_info(uint32_t sleep_delay, uint32_t sleep_limit, + uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs) +{ + unsigned long flags; + uint32_t *ptr; + struct tramp_gpio_smem *gpio; + + spin_lock_irqsave(&smem_lock, flags); + + printk(KERN_ERR "SMEM_SMSM_SLEEP_DELAY: %x\n", sleep_delay); + printk(KERN_ERR "SMEM_SMSM_LIMIT_SLEEP: %x\n", sleep_limit); + + ptr = smem_alloc(SMEM_SLEEP_POWER_COLLAPSE_DISABLED, sizeof(*ptr)); + if (ptr) + printk(KERN_ERR "SMEM_SLEEP_POWER_COLLAPSE_DISABLED: %x\n", *ptr); + else + printk(KERN_ERR "SMEM_SLEEP_POWER_COLLAPSE_DISABLED: missing\n"); + + printk(KERN_ERR "SMEM_SMSM_INT_INFO %x %x %x\n", + irq_mask, pending_irqs, wakeup_reason); + + gpio = smem_alloc(SMEM_GPIO_INT, sizeof(*gpio)); + if (gpio) { + int i; + for (i = 0; i < NUM_GPIO_INT_REGISTERS; i++) { + printk(KERN_ERR "SMEM_GPIO_INT: %d: e %x d %x p %x\n", + i, gpio->enabled[i], gpio->detection[i], + gpio->polarity[i]); + } + for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) { + printk(KERN_ERR "SMEM_GPIO_INT: %d: f %d: %d %d...\n", + i, gpio->num_fired[i], gpio->fired[i][0], + gpio->fired[i][1]); + } + } else + printk(KERN_ERR "SMEM_GPIO_INT: missing\n"); + +#if 0 + ptr = smem_alloc(SMEM_SLEEP_STATIC, + 2 * MAX_NUM_SLEEP_CLIENTS * (MAX_SLEEP_NAME_LEN + 1)); + if (ptr) + printk(KERN_ERR "SMEM_SLEEP_STATIC: %x %x %x %x\n", + ptr[0], ptr[1], ptr[2], ptr[3]); + else + printk(KERN_ERR "SMEM_SLEEP_STATIC: missing\n"); +#endif + + spin_unlock_irqrestore(&smem_lock, flags); +} + +int smd_core_init(void) +{ + int r; + SMD_INFO("smd_core_init()\n"); + + r = request_irq(INT_A9_M2A_0, smd_irq_handler, + IRQF_TRIGGER_RISING, "smd_dev", 0); + if (r < 0) + return r; + r = enable_irq_wake(INT_A9_M2A_0); + if (r < 0) + printk(KERN_ERR "smd_core_init: " + "enable_irq_wake failed for INT_A9_M2A_0\n"); + + r = request_irq(INT_A9_M2A_5, smsm_irq_handler, + IRQF_TRIGGER_RISING, "smsm_dev", 0); + if (r < 0) { + free_irq(INT_A9_M2A_0, 0); + return r; + } + + r = enable_irq_wake(INT_A9_M2A_5); + if (r < 0) + printk(KERN_ERR "smd_core_init: " + "enable_irq_wake failed for INT_A9_M2A_5\n"); + + r = request_irq(INT_ADSP_A11, smd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_SHARED, "smd_dev", + smd_irq_handler); + if (r < 0) + printk(KERN_ERR "smd_core_init: " + "request_irq failed for INT_ADSP_A11\n"); + + r = request_irq(INT_ADSP_A11, smsm_irq_handler, + IRQF_TRIGGER_RISING | IRQF_SHARED, "smsm_dev", + smsm_irq_handler); + if (r < 0) + printk(KERN_ERR "smd_core_init: " + "request_irq failed for INT_ADSP_A11\n"); + + r = enable_irq_wake(INT_ADSP_A11); + if (r < 0) + printk(KERN_ERR "smd_core_init: " + "enable_irq_wake failed for INT_ADSP_A11\n"); + + /* we may have missed a signal while booting -- fake + * an interrupt to make sure we process any existing + * state + */ + smsm_irq_handler(0, 0); + + SMD_INFO("smd_core_init() done\n"); + + return 0; +} + +#if defined(CONFIG_DEBUG_FS) + +static int debug_f3(char *buf, int max) +{ + char *x; + int size; + int i = 0, j = 0; + unsigned cols = 0; + char str[4*sizeof(unsigned)+1] = {0}; + + i += scnprintf(buf + i, max - i, + "Printing to log\n"); + + x = smem_get_entry(SMEM_ERR_F3_TRACE_LOG, &size); + if (x != 0) { + printk(KERN_ERR "smem: F3 TRACE LOG\n"); + while (size > 0) { + if (size >= sizeof(unsigned)) { + printk(KERN_ERR "%08x", *((unsigned *) x)); + for (j = 0; j < sizeof(unsigned); ++j) + if (isprint(*(x+j))) + str[cols*sizeof(unsigned) + j] + = *(x+j); + else + str[cols*sizeof(unsigned) + j] + = '-'; + x += sizeof(unsigned); + size -= sizeof(unsigned); + } else { + while (size-- > 0) + printk(KERN_ERR "%02x", + (unsigned) *x++); + break; + } + if (cols == 3) { + cols = 0; + str[4*sizeof(unsigned)] = 0; + printk(KERN_ERR " %s\n", str); + str[0] = 0; + } else { + cols++; + printk(KERN_ERR " "); + } + } + printk(KERN_ERR "\n"); + } + + return max; +} + +static int debug_diag(char *buf, int max) +{ + int i = 0; + + i += scnprintf(buf + i, max - i, + "Printing to log\n"); + smd_diag(); + + return i; +} + +static int debug_modem_err_f3(char *buf, int max) +{ + char *x; + int size; + int i = 0, j = 0; + unsigned cols = 0; + char str[4*sizeof(unsigned)+1] = {0}; + + x = smem_get_entry(SMEM_ERR_F3_TRACE_LOG, &size); + if (x != 0) { + printk(KERN_ERR "smem: F3 TRACE LOG\n"); + while (size > 0 && max - i) { + if (size >= sizeof(unsigned)) { + i += scnprintf(buf + i, max - i, "%08x", + *((unsigned *) x)); + for (j = 0; j < sizeof(unsigned); ++j) + if (isprint(*(x+j))) + str[cols*sizeof(unsigned) + j] + = *(x+j); + else + str[cols*sizeof(unsigned) + j] + = '-'; + x += sizeof(unsigned); + size -= sizeof(unsigned); + } else { + while (size-- > 0 && max - i) + i += scnprintf(buf + i, max - i, + "%02x", + (unsigned) *x++); + break; + } + if (cols == 3) { + cols = 0; + str[4*sizeof(unsigned)] = 0; + i += scnprintf(buf + i, max - i, " %s\n", + str); + str[0] = 0; + } else { + cols++; + i += scnprintf(buf + i, max - i, " "); + } + } + i += scnprintf(buf + i, max - i, "\n"); + } + + return i; +} + +static int debug_modem_err(char *buf, int max) +{ + char *x; + int size; + int i = 0; + + x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); + if (x != 0) { + x[SZ_DIAG_ERR_MSG - 1] = 0; + i += scnprintf(buf + i, max - i, + "smem: DIAG '%s'\n", x); + } + + x = smem_get_entry(SMEM_ERR_CRASH_LOG, &size); + if (x != 0) { + x[size - 1] = 0; + i += scnprintf(buf + i, max - i, + "smem: CRASH LOG\n'%s'\n", x); + } + i += scnprintf(buf + i, max - i, "\n"); + + return i; +} + +static int dump_ch(char *buf, int max, int n, + struct smd_half_channel *s, + struct smd_half_channel *r) +{ + return scnprintf( + buf, max, + "ch%02d:" + " %8s(%04d/%04d) %c%c%c%c%c%c%c <->" + " %8s(%04d/%04d) %c%c%c%c%c%c%c\n", n, + chstate(s->state), s->tail, s->head, + s->fDSR ? 'D' : 'd', + s->fCTS ? 'C' : 'c', + s->fCD ? 'C' : 'c', + s->fRI ? 'I' : 'i', + s->fHEAD ? 'W' : 'w', + s->fTAIL ? 'R' : 'r', + s->fSTATE ? 'S' : 's', + chstate(r->state), r->tail, r->head, + r->fDSR ? 'D' : 'd', + r->fCTS ? 'R' : 'r', + r->fCD ? 'C' : 'c', + r->fRI ? 'I' : 'i', + r->fHEAD ? 'W' : 'w', + r->fTAIL ? 'R' : 'r', + r->fSTATE ? 'S' : 's' + ); +} + +static int debug_read_diag_msg(char *buf, int max) +{ + char *msg; + int i = 0; + + msg = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); + + if (msg) { + msg[SZ_DIAG_ERR_MSG - 1] = 0; + i += scnprintf(buf + i, max - i, "diag: '%s'\n", msg); + } + return i; +} + +static int debug_read_mem(char *buf, int max) +{ + unsigned n; + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + struct smem_heap_entry *toc = shared->heap_toc; + int i = 0; + + i += scnprintf(buf + i, max - i, + "heap: init=%d free=%d remain=%d\n", + shared->heap_info.initialized, + shared->heap_info.free_offset, + shared->heap_info.heap_remaining); + + for (n = 0; n < SMD_HEAP_SIZE; n++) { + if (toc[n].allocated == 0) + continue; + i += scnprintf(buf + i, max - i, + "%04d: offset %08x size %08x\n", + n, toc[n].offset, toc[n].size); + } + return i; +} + +static int debug_read_ch_v1(char *buf, int max) +{ + void *shared; + int n, i = 0; + + for (n = 0; n < SMD_CHANNELS; n++) { + shared = smem_find(ID_SMD_CHANNELS + n, + 2 * (sizeof(struct smd_half_channel) + + SMD_BUF_SIZE)); + + if (shared == 0) + continue; + i += dump_ch(buf + i, max - i, n, shared, + (shared + sizeof(struct smd_half_channel) + + SMD_BUF_SIZE)); + } + + return i; +} + +static int debug_read_ch_v2(char *buf, int max) +{ + void *shared; + int n, i = 0; + + for (n = 0; n < SMD_CHANNELS; n++) { + shared = smem_find(ID_SMD_CHANNELS + n, + 2 * sizeof(struct smd_half_channel)); + + if (shared == 0) + continue; + i += dump_ch(buf + i, max - i, n, shared, + (shared + sizeof(struct smd_half_channel))); + } + + return i; +} + +static int debug_read_smem_version(char *buf, int max) +{ + struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; + uint32_t n, version, i = 0; + + for (n = 0; n < 32; n++) { + version = shared->version[n]; + i += scnprintf(buf + i, max - i, + "entry %d: smem = %d proc_comm = %d\n", n, + version >> 16, + version & 0xffff); + } + + return i; +} + +static int debug_read_smd_version(char *buf, int max) +{ + uint32_t *smd_ver; + uint32_t n, version, i = 0; + + smd_ver = smem_alloc(SMEM_VERSION_SMD, 32 * sizeof(uint32_t)); + + if (smd_ver) + for (n = 0; n < 32; n++) { + version = smd_ver[n]; + i += scnprintf(buf + i, max - i, + "entry %d: %d.%d\n", n, + version >> 16, + version & 0xffff); + } + + return i; +} + +static int debug_read_alloc_tbl(char *buf, int max) +{ + struct smd_alloc_elm *shared; + int n, i = 0; + + shared = smem_find(ID_CH_ALLOC_TBL, sizeof(struct smd_alloc_elm[64])); + + BUG_ON(!shared); + + for (n = 0; n < 64; n++) { + i += scnprintf(buf + i, max - i, + "name=%s cid=%d ch type=%d " + "xfer type=%d ref_count=%d\n", + shared[n].name, + shared[n].cid, + SMD_CHANNEL_TYPE(shared[n].type), + SMD_XFER_TYPE(shared[n].type), + shared[n].ref_count); + } + + return i; +} + +static int debug_read_smsm_state(char *buf, int max) +{ + uint32_t *smsm; + int n, i = 0; + + smsm = smem_find(ID_SHARED_STATE, + SMSM_NUM_ENTRIES * sizeof(uint32_t)); + + if (smsm) + for (n = 0; n < SMSM_NUM_ENTRIES; n++) + i += scnprintf(buf + i, max - i, "entry %d: 0x%08x\n", + n, smsm[n]); + + return i; + +} + +static int debug_read_intr_mask(char *buf, int max) +{ + uint32_t *smsm; + int m, n, i = 0; + + smsm = smem_alloc(SMEM_SMSM_CPU_INTR_MASK, + SMSM_NUM_ENTRIES * SMSM_NUM_HOSTS * sizeof(uint32_t)); + + if (smsm) + for (m = 0; m < SMSM_NUM_ENTRIES; m++) { + i += scnprintf(buf + i, max - i, "entry %d:", m); + for (n = 0; n < SMSM_NUM_HOSTS; n++) + i += scnprintf(buf + i, max - i, + " host %d: 0x%08x", + n, smsm[m * SMSM_NUM_HOSTS + n]); + i += scnprintf(buf + i, max - i, "\n"); + } + + return i; +} + +static int debug_read_intr_mux(char *buf, int max) +{ + uint32_t *smsm; + int n, i = 0; + + smsm = smem_alloc(SMEM_SMD_SMSM_INTR_MUX, + SMSM_NUM_INTR_MUX * sizeof(uint32_t)); + + if (smsm) + for (n = 0; n < SMSM_NUM_INTR_MUX; n++) + i += scnprintf(buf + i, max - i, "entry %d: %d\n", + n, smsm[n]); + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +static void smd_debugfs_init(void) +{ + struct dentry *dent; + uint32_t *smd_ver; + + dent = debugfs_create_dir("smd", 0); + if (IS_ERR(dent)) + return; + + smd_ver = smem_alloc(SMEM_VERSION_SMD, 32 * sizeof(uint32_t)); + + if (smd_ver && ((smd_ver[VERSION_MODEM] >> 16) >= 1)) + debug_create("ch", 0444, dent, debug_read_ch_v2); + else + debug_create("ch", 0444, dent, debug_read_ch_v1); + + debug_create("diag", 0444, dent, debug_read_diag_msg); + debug_create("mem", 0444, dent, debug_read_mem); + debug_create("version", 0444, dent, debug_read_smd_version); + debug_create("tbl", 0444, dent, debug_read_alloc_tbl); + debug_create("modem_err", 0444, dent, debug_modem_err); + debug_create("modem_err_f3", 0444, dent, debug_modem_err_f3); + debug_create("print_diag", 0444, dent, debug_diag); + debug_create("print_f3", 0444, dent, debug_f3); +} + +static void smsm_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smsm", 0); + if (IS_ERR(dent)) + return; + + debug_create("state", 0444, dent, debug_read_smsm_state); + debug_create("intr_mask", 0444, dent, debug_read_intr_mask); + debug_create("intr_mux", 0444, dent, debug_read_intr_mux); + debug_create("version", 0444, dent, debug_read_smem_version); +} +#else +static void smd_debugfs_init(void) {} +static void smsm_debugfs_init(void) {} +#endif + +static int __init msm_smd_probe(struct platform_device *pdev) +{ + /* enable smd and smsm info messages */ + msm_smd_debug_mask = 0xc; + + SMD_INFO("smd probe\n"); + + INIT_WORK(&probe_work, smd_channel_probe_worker); + + if (smem_init()) { + printk(KERN_ERR "smem_init() failed\n"); + return -1; + } + + if (smd_core_init()) { + printk(KERN_ERR "smd_core_init() failed\n"); + return -1; + } + + do_smd_probe(); + + msm_check_for_modem_crash = check_for_modem_crash; + + smd_initialized = 1; + + smd_debugfs_init(); + smsm_debugfs_init(); + + return 0; +} + +static struct platform_driver msm_smd_driver = { + .probe = msm_smd_probe, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init msm_smd_init(void) +{ + return platform_driver_register(&msm_smd_driver); +} + +module_init(msm_smd_init); + +MODULE_DESCRIPTION("MSM Shared Memory Core"); +MODULE_AUTHOR("Brian Swetland <swetland@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/smd_ctl2.c b/arch/arm/mach-msm/smd_ctl2.c new file mode 100644 index 000000000000..8348f94dae68 --- /dev/null +++ b/arch/arm/mach-msm/smd_ctl2.c @@ -0,0 +1,677 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * SMD Control Driver -- Provides a binary SMD non-muxed control port + * interface. + */ + +#include <linux/cdev.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <linux/workqueue.h> +#include <asm/ioctls.h> + +#include <mach/msm_smd.h> + +#include "modem_notifier.h" + +#define NUM_SMD_CTL_PORTS 3 +#define DEVICE_NAME "smdcntl" +#define MAX_BUF_SIZE 2048 + +struct smd_ctl_dev { + struct cdev cdev; + char name[9]; + struct device *devicep; + + struct smd_channel *ctl_ch; + struct mutex ctl_ch_lock; + struct mutex rx_lock; + struct mutex is_open_lock; + struct workqueue_struct *ctl_wq; + struct work_struct ctl_work; + wait_queue_head_t ctl_wait_queue; + wait_queue_head_t ctl_opened_wait_queue; + + int i; + + unsigned char tx_buf[MAX_BUF_SIZE]; + unsigned char rx_buf[MAX_BUF_SIZE]; + int bytes_read; + int is_open; + + struct notifier_block nb; + int has_reset; + struct mutex has_reset_lock; + +} *smd_ctl_devp[NUM_SMD_CTL_PORTS]; + +struct class *smd_ctl_classp; +static dev_t smd_ctl_number; + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + int i; \ + printk(KERN_ERR "%s", prestr); \ + for (i = 0; i < cnt; i++) \ + printk(KERN_ERR "%.2x", buf[i]); \ + printk(KERN_ERR "\n"); \ +} while (0) +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) do {} while (0) +#endif + +#ifdef DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +static void clean_and_signal(struct smd_ctl_dev *smd_ctl_devp) +{ + flush_workqueue(smd_ctl_devp->ctl_wq); + + mutex_lock(&smd_ctl_devp->has_reset_lock); + smd_ctl_devp->has_reset = 1; + mutex_unlock(&smd_ctl_devp->has_reset_lock); + + mutex_lock(&smd_ctl_devp->rx_lock); + smd_ctl_devp->bytes_read = 0; + mutex_unlock(&smd_ctl_devp->rx_lock); + + mutex_lock(&smd_ctl_devp->is_open_lock); + smd_ctl_devp->is_open = 0; + mutex_unlock(&smd_ctl_devp->is_open_lock); + + wake_up_interruptible(&smd_ctl_devp->ctl_wait_queue); + wake_up_interruptible(&smd_ctl_devp->ctl_opened_wait_queue); +} + +static int modem_notifier(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + struct smd_ctl_dev *smd_ctl_devp = + container_of(this, + struct smd_ctl_dev, + nb); + + if (!smd_ctl_devp) + return NOTIFY_DONE; + + switch (code) { + case MODEM_NOTIFIER_START_RESET: + printk(KERN_ERR "Notify: start reset ch:%i\n", + smd_ctl_devp->i); + clean_and_signal(smd_ctl_devp); + break; + case MODEM_NOTIFIER_END_RESET: + printk(KERN_ERR "Notify: end reset\n"); + break; + default: + printk(KERN_ERR "Notify: general\n"); + break; + } + return NOTIFY_DONE; +} + +int smd_ctl_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret; + struct smd_ctl_dev *smd_ctl_devp; + + smd_ctl_devp = file->private_data; + + switch (cmd) { + case TIOCMGET: + ret = smd_tiocmget(smd_ctl_devp->ctl_ch); + break; + case TIOCMSET: + ret = smd_tiocmset(smd_ctl_devp->ctl_ch, arg, ~arg); + break; + default: + ret = -1; + } + + return ret; +} + +ssize_t smd_ctl_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int r; + int bytes_read; + struct smd_ctl_dev *smd_ctl_devp; + + D(KERN_ERR "%s: read %i bytes\n", + __func__, count); + + smd_ctl_devp = file->private_data; + + if (!smd_ctl_devp->ctl_ch) + return -EINVAL; + + r = wait_event_interruptible(smd_ctl_devp->ctl_wait_queue, + smd_ctl_devp->bytes_read | + smd_ctl_devp->has_reset); + + if (smd_ctl_devp->has_reset) + return -ENETRESET; + + if (r < 0) { + /* qualify error message */ + if (r != -ERESTARTSYS) { + /* we get this anytime a signal comes in */ + printk(KERN_ERR "ERROR:%s:%i:%s: " + "wait_event_interruptible ret %i\n", + __FILE__, + __LINE__, + __func__, + r + ); + } + return r; + } + + /* Here we have a whole packet waiting for us */ + + mutex_lock(&smd_ctl_devp->rx_lock); + bytes_read = smd_ctl_devp->bytes_read; + smd_ctl_devp->bytes_read = 0; + mutex_unlock(&smd_ctl_devp->rx_lock); + + D(KERN_ERR "%s: after wait_event_interruptible bytes_read = %i\n", + __func__, bytes_read); + + if (bytes_read > count) { + printk(KERN_ERR "packet size %i > buffer size %i, " + "dropping packet!", bytes_read, count); + smd_read(smd_ctl_devp->ctl_ch, 0, bytes_read); + return -EINVAL; + } + + /* smd_read and copy_to_user need to be merged to only do 1 copy */ + if (smd_read(smd_ctl_devp->ctl_ch, smd_ctl_devp->rx_buf, bytes_read) + != bytes_read) { + if (smd_ctl_devp->has_reset) + return -ENETRESET; + + printk(KERN_ERR "user read: not enough data?!\n"); + return -EINVAL; + } + D_DUMP_BUFFER("read: ", bytes_read, smd_ctl_devp->rx_buf); + r = copy_to_user(buf, smd_ctl_devp->rx_buf, bytes_read); + + if (r > 0) { + printk(KERN_ERR "ERROR:%s:%i:%s: " + "copy_to_user could not copy %i bytes.\n", + __FILE__, + __LINE__, + __func__, + r); + return r; + } + + D(KERN_ERR "%s: just read %i bytes\n", + __func__, bytes_read); + + /* Not all packet events get explictly handled, this doesn't + matter if a constant stream of packets is streaming in, but + eventually a packet will be received and we'll have missed + the event. Queuing one more work item will catch this if + its happened, but do nothing if it hasn't. + */ + queue_work(smd_ctl_devp->ctl_wq, &smd_ctl_devp->ctl_work); + + D(KERN_ERR "%s: just queued more work\n", __func__); + + return bytes_read; +} + +ssize_t smd_ctl_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + int r; + struct smd_ctl_dev *smd_ctl_devp; + + if (count > MAX_BUF_SIZE) + return -EINVAL; + + D(KERN_ERR "%s: writting %i bytes\n", + __func__, count); + + smd_ctl_devp = file->private_data; + + if (!smd_ctl_devp->ctl_ch) + return -EINVAL; + + r = wait_event_interruptible(smd_ctl_devp->ctl_opened_wait_queue, + smd_ctl_devp->is_open | + smd_ctl_devp->has_reset); + + if (smd_ctl_devp->has_reset) + return -ENETRESET; + + if (r < 0) { + /* qualify error message */ + if (r != -ERESTARTSYS) { + /* we get this anytime a signal comes in */ + printk(KERN_ERR "ERROR:%s:%i:%s: " + "wait_event_interruptible ret %i\n", + __FILE__, + __LINE__, + __func__, + r + ); + } + return r; + } + + D_DUMP_BUFFER("write: ", count, buf); + + r = copy_from_user(smd_ctl_devp->tx_buf, buf, count); + if (r > 0) { + printk(KERN_ERR "ERROR:%s:%i:%s: " + "copy_from_user could not copy %i bytes.\n", + __FILE__, + __LINE__, + __func__, + r); + return r; + } + + D(KERN_ERR "%s: after copy_from_user. count = %i\n", + __func__, count); + + r = smd_write(smd_ctl_devp->ctl_ch, smd_ctl_devp->tx_buf, count); + if (r != count) { + if (smd_ctl_devp->has_reset) + return -ENETRESET; + + printk(KERN_ERR "ERROR:%s:%i:%s: " + "smd_write(ch,buf,count = %i) ret %i.\n", + __FILE__, + __LINE__, + __func__, + count, + r); + return r; + } + + D(KERN_ERR "%s: just wrote %i bytes\n", + __func__, count); + + return count; +} + +static void ctl_work_func(struct work_struct *work) +{ + /* unsigned char buf[MAX_BUF_SIZE]; */ + int sz; + struct smd_ctl_dev *smd_ctl_devp = container_of(work, + struct smd_ctl_dev, + ctl_work); + + if (!smd_ctl_devp->ctl_ch) + return; + + for (;;) { + sz = smd_cur_packet_size(smd_ctl_devp->ctl_ch); + if (sz == 0) { + D(KERN_ERR "%s: packet size is 0\n", __func__); + break; + } + if (sz > smd_read_avail(smd_ctl_devp->ctl_ch)) { + D(KERN_ERR "%s: packet size is %i - " + "the whole packet isn't here\n", + __func__, sz); + break; + } + if (sz > MAX_BUF_SIZE) { + smd_read(smd_ctl_devp->ctl_ch, 0, sz); + D(KERN_ERR "%s: packet size is %i - " + "greater than max %i, dropping\n", + __func__, sz, MAX_BUF_SIZE); + continue; + } + + /* here we have a packet of size sz ready */ + + mutex_lock(&smd_ctl_devp->rx_lock); + smd_ctl_devp->bytes_read = sz; + mutex_unlock(&smd_ctl_devp->rx_lock); + wake_up_interruptible(&smd_ctl_devp->ctl_wait_queue); + D(KERN_ERR "%s: after wake_up\n", __func__); + break; + } +} + +static void ctl_notify(void *priv, unsigned event) +{ + struct smd_ctl_dev *smd_ctl_devp = priv; + + if (smd_ctl_devp->ctl_ch == 0) + return; + + switch (event) { + case SMD_EVENT_DATA: { + int sz; + D(KERN_ERR "%s: data\n", + __func__); + sz = smd_cur_packet_size(smd_ctl_devp->ctl_ch); + D(KERN_ERR "%s: data sz = %i\n", + __func__, sz); + D(KERN_ERR "%s: smd_read_avail = %i\n", + __func__, smd_read_avail(smd_ctl_devp->ctl_ch)); + if ((sz > 0) && (sz <= smd_read_avail(smd_ctl_devp->ctl_ch))) { + queue_work(smd_ctl_devp->ctl_wq, + &smd_ctl_devp->ctl_work); + D(KERN_ERR "%s: data just queued\n", + __func__); + } + D(KERN_ERR "%s: data after queueing\n", + __func__); + break; + } + case SMD_EVENT_OPEN: + D(KERN_ERR "%s: smd opened\n", + __func__); + smd_ctl_devp->is_open = 1; + wake_up_interruptible(&smd_ctl_devp->ctl_opened_wait_queue); + break; + case SMD_EVENT_CLOSE: + smd_ctl_devp->is_open = 0; + printk(KERN_ERR "%s: smd closed\n", + __func__); + break; + } +} + +static char *smd_ctl_name[] = { + "DATA5_CNTL", + "DATA6_CNTL", + "DATA7_CNTL", +}; + +int smd_ctl_open(struct inode *inode, struct file *file) +{ + int r = 0; + struct smd_ctl_dev *smd_ctl_devp; + + smd_ctl_devp = container_of(inode->i_cdev, struct smd_ctl_dev, cdev); + + if (!smd_ctl_devp) + return -EINVAL; + + file->private_data = smd_ctl_devp; + + mutex_lock(&smd_ctl_devp->ctl_ch_lock); + if (smd_ctl_devp->ctl_ch == 0) + r = smd_open(smd_ctl_name[smd_ctl_devp->i], + &smd_ctl_devp->ctl_ch, + smd_ctl_devp, + ctl_notify); + mutex_unlock(&smd_ctl_devp->ctl_ch_lock); + + return r; +} + +int smd_ctl_release(struct inode *inode, struct file *file) +{ + int r = 0; + struct smd_ctl_dev *smd_ctl_devp = file->private_data; + + if (!smd_ctl_devp) + return -EINVAL; + + clean_and_signal(smd_ctl_devp); + + mutex_lock(&smd_ctl_devp->ctl_ch_lock); + if (smd_ctl_devp->ctl_ch != 0) { + r = smd_close(smd_ctl_devp->ctl_ch); + smd_ctl_devp->ctl_ch = 0; + } + mutex_unlock(&smd_ctl_devp->ctl_ch_lock); + + mutex_lock(&smd_ctl_devp->has_reset_lock); + smd_ctl_devp->has_reset = 0; + mutex_unlock(&smd_ctl_devp->has_reset_lock); + + return r; +} + +static const struct file_operations smd_ctl_fops = { + .owner = THIS_MODULE, + .open = smd_ctl_open, + .release = smd_ctl_release, + .read = smd_ctl_read, + .write = smd_ctl_write, + .ioctl = smd_ctl_ioctl, +}; + +static int __init smd_ctl_init(void) +{ + int i; + int r; + unsigned char buf[32]; + + r = alloc_chrdev_region(&smd_ctl_number, + 0, + NUM_SMD_CTL_PORTS, + DEVICE_NAME); + if (IS_ERR_VALUE(r)) { + printk(KERN_ERR "ERROR:%s:%i:%s: " + "alloc_chrdev_region() ret %i.\n", + __FILE__, + __LINE__, + __func__, + r); + goto error0; + } + + smd_ctl_classp = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(smd_ctl_classp)) { + printk(KERN_ERR "ERROR:%s:%i:%s: " + "class_create() ENOMEM\n", + __FILE__, + __LINE__, + __func__); + r = -ENOMEM; + goto error1; + } + + for (i = 0; i < NUM_SMD_CTL_PORTS; ++i) { + smd_ctl_devp[i] = kzalloc(sizeof(struct smd_ctl_dev), + GFP_KERNEL); + if (IS_ERR(smd_ctl_devp[i])) { + printk(KERN_ERR "ERROR:%s:%i:%s kmalloc() ENOMEM\n", + __FILE__, + __LINE__, + __func__); + r = -ENOMEM; + goto error2; + } + + smd_ctl_devp[i]->i = i; + + scnprintf(buf, 32, "ctl%i", i); + smd_ctl_devp[i]->ctl_wq = create_singlethread_workqueue(buf); + if (&smd_ctl_devp[i]->ctl_wq == 0) { + printk(KERN_ERR + "%s:%i:%s: " + "create_singlethread_workqueue() ret 0\n", + __FILE__, + __LINE__, + __func__); + r = -ENOMEM; + goto error2; + } + + init_waitqueue_head(&smd_ctl_devp[i]->ctl_wait_queue); + smd_ctl_devp[i]->is_open = 0; + init_waitqueue_head(&smd_ctl_devp[i]->ctl_opened_wait_queue); + INIT_WORK(&smd_ctl_devp[i]->ctl_work, + ctl_work_func); + + mutex_init(&smd_ctl_devp[i]->ctl_ch_lock); + mutex_init(&smd_ctl_devp[i]->rx_lock); + mutex_init(&smd_ctl_devp[i]->is_open_lock); + + cdev_init(&smd_ctl_devp[i]->cdev, &smd_ctl_fops); + smd_ctl_devp[i]->cdev.owner = THIS_MODULE; + + r = cdev_add(&smd_ctl_devp[i]->cdev, + (smd_ctl_number + i), + 1); + + if (IS_ERR_VALUE(r)) { + printk(KERN_ERR "%s:%i:%s: cdev_add() ret %i\n", + __FILE__, + __LINE__, + __func__, + r); + destroy_workqueue(smd_ctl_devp[i]->ctl_wq); + kfree(smd_ctl_devp[i]); + goto error2; + } + + smd_ctl_devp[i]->devicep = + device_create(smd_ctl_classp, + NULL, + (smd_ctl_number + i), + NULL, + DEVICE_NAME "%d", + i); + + if (IS_ERR(smd_ctl_devp[i]->devicep)) { + printk(KERN_ERR "%s:%i:%s: " + "device_create() ENOMEM\n", + __FILE__, + __LINE__, + __func__); + r = -ENOMEM; + cdev_del(&smd_ctl_devp[i]->cdev); + destroy_workqueue(smd_ctl_devp[i]->ctl_wq); + kfree(smd_ctl_devp[i]); + goto error2; + } + + smd_ctl_devp[i]->nb.notifier_call = modem_notifier; + modem_register_notifier(&smd_ctl_devp[i]->nb); + mutex_init(&smd_ctl_devp[i]->has_reset_lock); + + } + + printk(KERN_INFO "SMD Control Port Driver Initialized.\n"); + return 0; + + error2: + if (i > 0) { + while (--i >= 0) { + cdev_del(&smd_ctl_devp[i]->cdev); + destroy_workqueue(smd_ctl_devp[i]->ctl_wq); + kfree(smd_ctl_devp[i]); + device_destroy(smd_ctl_classp, + MKDEV(MAJOR(smd_ctl_number), i)); + } + } + + class_destroy(smd_ctl_classp); + error1: + unregister_chrdev_region(MAJOR(smd_ctl_number), NUM_SMD_CTL_PORTS); + error0: + return r; +} + +static void __exit smd_ctl_cleanup(void) +{ + int i; + + for (i = 0; i < NUM_SMD_CTL_PORTS; ++i) { + modem_unregister_notifier(&smd_ctl_devp[i]->nb); + cdev_del(&smd_ctl_devp[i]->cdev); + kfree(smd_ctl_devp[i]); + device_destroy(smd_ctl_classp, + MKDEV(MAJOR(smd_ctl_number), i)); + } + + class_destroy(smd_ctl_classp); + + unregister_chrdev_region(MAJOR(smd_ctl_number), NUM_SMD_CTL_PORTS); +} + +module_init(smd_ctl_init); +module_exit(smd_ctl_cleanup); + +MODULE_DESCRIPTION("MSM Shared Memory Control Port"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/smd_nmea.c b/arch/arm/mach-msm/smd_nmea.c new file mode 100644 index 000000000000..35e1cbdca1e6 --- /dev/null +++ b/arch/arm/mach-msm/smd_nmea.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * SMD NMEA Driver -- Provides GPS NMEA device to SMD port interface. + * + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/miscdevice.h> +#include <linux/workqueue.h> +#include <linux/uaccess.h> + +#include <mach/msm_smd.h> + +#define MAX_BUF_SIZE 200 + +static DEFINE_MUTEX(nmea_ch_lock); +static DEFINE_MUTEX(nmea_rx_buf_lock); + +static DECLARE_WAIT_QUEUE_HEAD(nmea_wait_queue); + +struct nmea_device_t { + struct miscdevice misc; + + struct smd_channel *ch; + + unsigned char rx_buf[MAX_BUF_SIZE]; + unsigned int bytes_read; +}; + +struct nmea_device_t *nmea_devp; + +static void nmea_work_func(struct work_struct *ws) +{ + int sz; + + for (;;) { + sz = smd_cur_packet_size(nmea_devp->ch); + if (sz == 0) + break; + if (sz > smd_read_avail(nmea_devp->ch)) + break; + if (sz > MAX_BUF_SIZE) { + smd_read(nmea_devp->ch, 0, sz); + continue; + } + + mutex_lock(&nmea_rx_buf_lock); + if (smd_read(nmea_devp->ch, nmea_devp->rx_buf, sz) != sz) { + mutex_unlock(&nmea_rx_buf_lock); + printk(KERN_ERR "nmea: not enough data?!\n"); + continue; + } + nmea_devp->bytes_read = sz; + mutex_unlock(&nmea_rx_buf_lock); + wake_up_interruptible(&nmea_wait_queue); + } +} + +struct workqueue_struct *nmea_wq; +static DECLARE_WORK(nmea_work, nmea_work_func); + +static void nmea_notify(void *priv, unsigned event) +{ + switch (event) { + case SMD_EVENT_DATA: { + int sz; + sz = smd_cur_packet_size(nmea_devp->ch); + if ((sz > 0) && (sz <= smd_read_avail(nmea_devp->ch))) + queue_work(nmea_wq, &nmea_work); + break; + } + case SMD_EVENT_OPEN: + printk(KERN_INFO "nmea: smd opened\n"); + break; + case SMD_EVENT_CLOSE: + printk(KERN_INFO "nmea: smd closed\n"); + break; + } +} + +static ssize_t nmea_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + int r; + int bytes_read; + + r = wait_event_interruptible(nmea_wait_queue, + nmea_devp->bytes_read); + if (r < 0) { + /* qualify error message */ + if (r != -ERESTARTSYS) { + /* we get this anytime a signal comes in */ + printk(KERN_ERR "ERROR:%s:%i:%s: " + "wait_event_interruptible ret %i\n", + __FILE__, + __LINE__, + __func__, + r + ); + } + return r; + } + + mutex_lock(&nmea_rx_buf_lock); + bytes_read = nmea_devp->bytes_read; + nmea_devp->bytes_read = 0; + r = copy_to_user(buf, nmea_devp->rx_buf, bytes_read); + mutex_unlock(&nmea_rx_buf_lock); + + if (r > 0) { + printk(KERN_ERR "ERROR:%s:%i:%s: " + "copy_to_user could not copy %i bytes.\n", + __FILE__, + __LINE__, + __func__, + r); + return r; + } + + return bytes_read; +} + +static int nmea_open(struct inode *ip, struct file *fp) +{ + int r = 0; + + mutex_lock(&nmea_ch_lock); + if (nmea_devp->ch == 0) + r = smd_open("GPSNMEA", &nmea_devp->ch, nmea_devp, nmea_notify); + mutex_unlock(&nmea_ch_lock); + + return r; +} + +static int nmea_release(struct inode *ip, struct file *fp) +{ + int r = 0; + + mutex_lock(&nmea_ch_lock); + if (nmea_devp->ch != 0) { + r = smd_close(nmea_devp->ch); + nmea_devp->ch = 0; + } + mutex_unlock(&nmea_ch_lock); + + return r; +} + +static const struct file_operations nmea_fops = { + .owner = THIS_MODULE, + .read = nmea_read, + .open = nmea_open, + .release = nmea_release, +}; + +static struct nmea_device_t nmea_device = { + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "nmea", + .fops = &nmea_fops, + } +}; + +static void __exit nmea_exit(void) +{ + destroy_workqueue(nmea_wq); + misc_deregister(&nmea_device.misc); +} + +static int __init nmea_init(void) +{ + int ret; + + nmea_device.bytes_read = 0; + nmea_devp = &nmea_device; + + nmea_wq = create_singlethread_workqueue("nmea"); + if (nmea_wq == 0) + return -ENOMEM; + + ret = misc_register(&nmea_device.misc); + return ret; +} + +module_init(nmea_init); +module_exit(nmea_exit); + +MODULE_DESCRIPTION("MSM Shared Memory NMEA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/smd_private.h b/arch/arm/mach-msm/smd_private.h new file mode 100644 index 000000000000..3dec5d8ef794 --- /dev/null +++ b/arch/arm/mach-msm/smd_private.h @@ -0,0 +1,256 @@ +/* arch/arm/mach-msm/smd_private.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#ifndef _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_ +#define _ARCH_ARM_MACH_MSM_MSM_SMD_PRIVATE_H_ + +struct smem_heap_info +{ + unsigned initialized; + unsigned free_offset; + unsigned heap_remaining; + unsigned reserved; +}; + +struct smem_heap_entry +{ + unsigned allocated; + unsigned offset; + unsigned size; + unsigned reserved; +}; + +struct smem_proc_comm +{ + unsigned command; + unsigned status; + unsigned data1; + unsigned data2; +}; + +#define PC_APPS 0 +#define PC_MODEM 1 + +#define VERSION_QDSP6 4 +#define VERSION_APPS_SBL 6 +#define VERSION_MODEM_SBL 7 +#define VERSION_APPS 8 +#define VERSION_MODEM 9 + +#define SMD_HEAP_SIZE 512 + +struct smem_shared +{ + struct smem_proc_comm proc_comm[4]; + unsigned version[32]; + struct smem_heap_info heap_info; + struct smem_heap_entry heap_toc[SMD_HEAP_SIZE]; +}; + +#if defined(CONFIG_MSM_SMD_PKG4) +struct smsm_interrupt_info { + uint32_t aArm_en_mask; + uint32_t aArm_interrupts_pending; + uint32_t aArm_wakeup_reason; + uint32_t aArm_rpc_prog; + uint32_t aArm_rpc_proc; + char aArm_smd_port_name[20]; + uint32_t aArm_gpio_info; +}; +#elif defined(CONFIG_MSM_SMD_PKG3) +struct smsm_interrupt_info { + uint32_t aArm_en_mask; + uint32_t aArm_interrupts_pending; + uint32_t aArm_wakeup_reason; +}; +#else +#error No SMD Package Specified; aborting +#endif + +#if defined(CONFIG_MSM_N_WAY_SMSM) +enum { + SMSM_APPS_STATE, + SMSM_MODEM_STATE, + SMSM_Q6_STATE, + SMSM_APPS_DEM, + SMSM_MODEM_DEM, + SMSM_Q6_DEM, + SMSM_POWER_MASTER_DEM, + SMSM_TIME_MASTER_DEM, + SMSM_NUM_ENTRIES, +}; +#else +enum { + SMSM_APPS_STATE = 1, + SMSM_MODEM_STATE = 3, + SMSM_NUM_ENTRIES, +}; +#endif + +enum { + SMSM_APPS, + SMSM_MODEM, + SMSM_Q6, + SMSM_NUM_HOSTS, +}; + +#define SZ_DIAG_ERR_MSG 0xC8 +#define ID_DIAG_ERR_MSG SMEM_DIAG_ERR_MESSAGE +#define ID_SMD_CHANNELS SMEM_SMD_BASE_ID +#define ID_SHARED_STATE SMEM_SMSM_SHARED_STATE +#define ID_CH_ALLOC_TBL SMEM_CHANNEL_ALLOC_TBL + +#define SMSM_INIT 0x00000001 +#define SMSM_OSENTERED 0x00000002 +#define SMSM_SMDWAIT 0x00000004 +#define SMSM_SMDINIT 0x00000008 +#define SMSM_RPCWAIT 0x00000010 +#define SMSM_RPCINIT 0x00000020 +#define SMSM_RESET 0x00000040 +#define SMSM_RSA 0x00000080 +#define SMSM_RUN 0x00000100 +#define SMSM_PWRC 0x00000200 +#define SMSM_TIMEWAIT 0x00000400 +#define SMSM_TIMEINIT 0x00000800 +#define SMSM_PWRC_EARLY_EXIT 0x00001000 +#define SMSM_WFPI 0x00002000 +#define SMSM_SLEEP 0x00004000 +#define SMSM_SLEEPEXIT 0x00008000 +#define SMSM_OEMSBL_RELEASE 0x00010000 +#define SMSM_APPS_REBOOT 0x00020000 +#define SMSM_SYSTEM_POWER_DOWN 0x00040000 +#define SMSM_SYSTEM_REBOOT 0x00080000 +#define SMSM_SYSTEM_DOWNLOAD 0x00100000 +#define SMSM_PWRC_SUSPEND 0x00200000 +#define SMSM_APPS_SHUTDOWN 0x00400000 +#define SMSM_SMD_LOOPBACK 0x00800000 +#define SMSM_RUN_QUIET 0x01000000 +#define SMSM_MODEM_WAIT 0x02000000 +#define SMSM_MODEM_BREAK 0x04000000 +#define SMSM_MODEM_CONTINUE 0x08000000 +#define SMSM_UNKNOWN 0x80000000 + +#define SMSM_WKUP_REASON_RPC 0x00000001 +#define SMSM_WKUP_REASON_INT 0x00000002 +#define SMSM_WKUP_REASON_GPIO 0x00000004 +#define SMSM_WKUP_REASON_TIMER 0x00000008 +#define SMSM_WKUP_REASON_ALARM 0x00000010 +#define SMSM_WKUP_REASON_RESET 0x00000020 + +void *smem_alloc(unsigned id, unsigned size); +void *smem_get_entry(unsigned id, unsigned *size); +int smsm_change_state(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask); +int smsm_change_intr_mask(uint32_t smsm_entry, + uint32_t clear_mask, uint32_t set_mask); +int smsm_get_intr_mask(uint32_t smsm_entry, uint32_t *intr_mask); +uint32_t smsm_get_state(uint32_t smsm_entry); +void smsm_print_sleep_info(uint32_t sleep_delay, uint32_t sleep_limit, + uint32_t irq_mask, uint32_t wakeup_reason, uint32_t pending_irqs); +void smsm_reset_modem(unsigned mode); +void smsm_reset_modem_cont(void); +void smd_sleep_exit(void); + +#define SMEM_NUM_SMD_STREAM_CHANNELS 64 +#define SMEM_NUM_SMD_BLOCK_CHANNELS 64 + +enum { + /* fixed items */ + SMEM_PROC_COMM = 0, + SMEM_HEAP_INFO, + SMEM_ALLOCATION_TABLE, + SMEM_VERSION_INFO, + SMEM_HW_RESET_DETECT, + SMEM_AARM_WARM_BOOT, + SMEM_DIAG_ERR_MESSAGE, + SMEM_SPINLOCK_ARRAY, + SMEM_MEMORY_BARRIER_LOCATION, + + /* dynamic items */ + SMEM_AARM_PARTITION_TABLE, + SMEM_AARM_BAD_BLOCK_TABLE, + SMEM_RESERVE_BAD_BLOCKS, + SMEM_WM_UUID, + SMEM_CHANNEL_ALLOC_TBL, + SMEM_SMD_BASE_ID, + SMEM_SMEM_LOG_IDX = SMEM_SMD_BASE_ID + SMEM_NUM_SMD_STREAM_CHANNELS, + SMEM_SMEM_LOG_EVENTS, + SMEM_SMEM_STATIC_LOG_IDX, + SMEM_SMEM_STATIC_LOG_EVENTS, + SMEM_SMEM_SLOW_CLOCK_SYNC, + SMEM_SMEM_SLOW_CLOCK_VALUE, + SMEM_BIO_LED_BUF, + SMEM_SMSM_SHARED_STATE, + SMEM_SMSM_INT_INFO, + SMEM_SMSM_SLEEP_DELAY, + SMEM_SMSM_LIMIT_SLEEP, + SMEM_SLEEP_POWER_COLLAPSE_DISABLED, + SMEM_KEYPAD_KEYS_PRESSED, + SMEM_KEYPAD_STATE_UPDATED, + SMEM_KEYPAD_STATE_IDX, + SMEM_GPIO_INT, + SMEM_MDDI_LCD_IDX, + SMEM_MDDI_HOST_DRIVER_STATE, + SMEM_MDDI_LCD_DISP_STATE, + SMEM_LCD_CUR_PANEL, + SMEM_MARM_BOOT_SEGMENT_INFO, + SMEM_AARM_BOOT_SEGMENT_INFO, + SMEM_SLEEP_STATIC, + SMEM_SCORPION_FREQUENCY, + SMEM_SMD_PROFILES, + SMEM_TSSC_BUSY, + SMEM_HS_SUSPEND_FILTER_INFO, + SMEM_BATT_INFO, + SMEM_APPS_BOOT_MODE, + SMEM_VERSION_FIRST, + SMEM_VERSION_SMD = SMEM_VERSION_FIRST, + SMEM_VERSION_LAST = SMEM_VERSION_FIRST + 24, + SMEM_OSS_RRCASN1_BUF1, + SMEM_OSS_RRCASN1_BUF2, + SMEM_ID_VENDOR0, + SMEM_ID_VENDOR1, + SMEM_ID_VENDOR2, + SMEM_HW_SW_BUILD_ID, + SMEM_SMD_BLOCK_PORT_BASE_ID, + SMEM_SMD_BLOCK_PORT_PROC0_HEAP = SMEM_SMD_BLOCK_PORT_BASE_ID + + SMEM_NUM_SMD_BLOCK_CHANNELS, + SMEM_SMD_BLOCK_PORT_PROC1_HEAP = SMEM_SMD_BLOCK_PORT_PROC0_HEAP + + SMEM_NUM_SMD_BLOCK_CHANNELS, + SMEM_I2C_MUTEX = SMEM_SMD_BLOCK_PORT_PROC1_HEAP + + SMEM_NUM_SMD_BLOCK_CHANNELS, + SMEM_SCLK_CONVERSION, + SMEM_SMD_SMSM_INTR_MUX, + SMEM_SMSM_CPU_INTR_MASK, + SMEM_APPS_DEM_SLAVE_DATA, + SMEM_QDSP6_DEM_SLAVE_DATA, + SMEM_CLKREGIM_BSP, + SMEM_CLKREGIM_SOURCES, + SMEM_SMD_FIFO_BASE_ID, + SMEM_USABLE_RAM_PARTITION_TABLE = SMEM_SMD_FIFO_BASE_ID + + SMEM_NUM_SMD_STREAM_CHANNELS, + SMEM_POWER_ON_STATUS_INFO, + SMEM_DAL_AREA, + SMEM_SMEM_LOG_POWER_IDX, + SMEM_SMEM_LOG_POWER_WRAP, + SMEM_SMEM_LOG_POWER_EVENTS, + SMEM_ERR_CRASH_LOG, + SMEM_ERR_F3_TRACE_LOG, + SMEM_SMD_BRIDGE_ALLOC_TABLE, + SMEM_SMDLITE_TABLE, + SMEM_SD_IMG_UPGRADE_STATUS, + SMEM_NUM_ITEMS, +}; + +#endif diff --git a/arch/arm/mach-msm/smd_qmi.c b/arch/arm/mach-msm/smd_qmi.c new file mode 100644 index 000000000000..066d535809c1 --- /dev/null +++ b/arch/arm/mach-msm/smd_qmi.c @@ -0,0 +1,843 @@ +/* arch/arm/mach-msm/smd_qmi.c + * + * QMI Control Driver -- Manages network data connections. + * + * Copyright (C) 2007 Google, Inc. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/miscdevice.h> +#include <linux/workqueue.h> + +#include <asm/uaccess.h> +#include <mach/msm_smd.h> + +#define QMI_CTL 0x00 +#define QMI_WDS 0x01 +#define QMI_DMS 0x02 +#define QMI_NAS 0x03 + +#define QMI_RESULT_SUCCESS 0x0000 +#define QMI_RESULT_FAILURE 0x0001 + +struct qmi_msg { + unsigned char service; + unsigned char client_id; + unsigned short txn_id; + unsigned short type; + unsigned short size; + unsigned char *tlv; +}; + +#define qmi_ctl_client_id 0 + +#define STATE_OFFLINE 0 +#define STATE_QUERYING 1 +#define STATE_ONLINE 2 + +struct qmi_ctxt { + struct miscdevice misc; + + struct mutex lock; + + unsigned char ctl_txn_id; + unsigned char wds_client_id; + unsigned short wds_txn_id; + + unsigned wds_busy; + unsigned wds_handle; + unsigned state_dirty; + unsigned state; + + unsigned char addr[4]; + unsigned char mask[4]; + unsigned char gateway[4]; + unsigned char dns1[4]; + unsigned char dns2[4]; + + smd_channel_t *ch; + const char *ch_name; + + struct work_struct open_work; + struct work_struct read_work; +}; + +static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n); + +static void qmi_read_work(struct work_struct *ws); +static void qmi_open_work(struct work_struct *work); + +void qmi_ctxt_init(struct qmi_ctxt *ctxt, unsigned n) +{ + mutex_init(&ctxt->lock); + INIT_WORK(&ctxt->read_work, qmi_read_work); + INIT_WORK(&ctxt->open_work, qmi_open_work); + ctxt->ctl_txn_id = 1; + ctxt->wds_txn_id = 1; + ctxt->wds_busy = 1; + ctxt->state = STATE_OFFLINE; + +} + +static struct workqueue_struct *qmi_wq; + +static int verbose = 0; + +/* anyone waiting for a state change waits here */ +static DECLARE_WAIT_QUEUE_HEAD(qmi_wait_queue); + + +static void qmi_dump_msg(struct qmi_msg *msg, const char *prefix) +{ + unsigned sz, n; + unsigned char *x; + + if (!verbose) + return; + + printk(KERN_INFO + "qmi: %s: svc=%02x cid=%02x tid=%04x type=%04x size=%04x\n", + prefix, msg->service, msg->client_id, + msg->txn_id, msg->type, msg->size); + + x = msg->tlv; + sz = msg->size; + + while (sz >= 3) { + sz -= 3; + + n = x[1] | (x[2] << 8); + if (n > sz) + break; + + printk(KERN_INFO "qmi: %s: tlv: %02x %04x { ", + prefix, x[0], n); + x += 3; + sz -= n; + while (n-- > 0) + printk("%02x ", *x++); + printk("}\n"); + } +} + +int qmi_add_tlv(struct qmi_msg *msg, + unsigned type, unsigned size, const void *data) +{ + unsigned char *x = msg->tlv + msg->size; + + x[0] = type; + x[1] = size; + x[2] = size >> 8; + + memcpy(x + 3, data, size); + + msg->size += (size + 3); + + return 0; +} + +/* Extract a tagged item from a qmi message buffer, +** taking care not to overrun the buffer. +*/ +static int qmi_get_tlv(struct qmi_msg *msg, + unsigned type, unsigned size, void *data) +{ + unsigned char *x = msg->tlv; + unsigned len = msg->size; + unsigned n; + + while (len >= 3) { + len -= 3; + + /* size of this item */ + n = x[1] | (x[2] << 8); + if (n > len) + break; + + if (x[0] == type) { + if (n != size) + return -1; + memcpy(data, x + 3, size); + return 0; + } + + x += (n + 3); + len -= n; + } + + return -1; +} + +static unsigned qmi_get_status(struct qmi_msg *msg, unsigned *error) +{ + unsigned short status[2]; + if (qmi_get_tlv(msg, 0x02, sizeof(status), status)) { + *error = 0; + return QMI_RESULT_FAILURE; + } else { + *error = status[1]; + return status[0]; + } +} + +/* 0x01 <qmux-header> <payload> */ +#define QMUX_HEADER 13 + +/* should be >= HEADER + FOOTER */ +#define QMUX_OVERHEAD 16 + +static int qmi_send(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned char *data; + unsigned hlen; + unsigned len; + int r; + + qmi_dump_msg(msg, "send"); + + if (msg->service == QMI_CTL) { + hlen = QMUX_HEADER - 1; + } else { + hlen = QMUX_HEADER; + } + + /* QMUX length is total header + total payload - IFC selector */ + len = hlen + msg->size - 1; + if (len > 0xffff) + return -1; + + data = msg->tlv - hlen; + + /* prepend encap and qmux header */ + *data++ = 0x01; /* ifc selector */ + + /* qmux header */ + *data++ = len; + *data++ = len >> 8; + *data++ = 0x00; /* flags: client */ + *data++ = msg->service; + *data++ = msg->client_id; + + /* qmi header */ + *data++ = 0x00; /* flags: send */ + *data++ = msg->txn_id; + if (msg->service != QMI_CTL) + *data++ = msg->txn_id >> 8; + + *data++ = msg->type; + *data++ = msg->type >> 8; + *data++ = msg->size; + *data++ = msg->size >> 8; + + /* len + 1 takes the interface selector into account */ + r = smd_write(ctxt->ch, msg->tlv - hlen, len + 1); + + if (r != len) { + return -1; + } else { + return 0; + } +} + +static void qmi_process_ctl_msg(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned err; + if (msg->type == 0x0022) { + unsigned char n[2]; + if (qmi_get_status(msg, &err)) + return; + if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) + return; + if (n[0] == QMI_WDS) { + printk(KERN_INFO + "qmi: ctl: wds use client_id 0x%02x\n", n[1]); + ctxt->wds_client_id = n[1]; + ctxt->wds_busy = 0; + } + } +} + +static int qmi_network_get_profile(struct qmi_ctxt *ctxt); + +static void swapaddr(unsigned char *src, unsigned char *dst) +{ + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +static unsigned char zero[4]; +static void qmi_read_runtime_profile(struct qmi_ctxt *ctxt, struct qmi_msg *msg) +{ + unsigned char tmp[4]; + unsigned r; + + r = qmi_get_tlv(msg, 0x1e, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->addr); + r = qmi_get_tlv(msg, 0x21, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->mask); + r = qmi_get_tlv(msg, 0x20, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->gateway); + r = qmi_get_tlv(msg, 0x15, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->dns1); + r = qmi_get_tlv(msg, 0x16, 4, tmp); + swapaddr(r ? zero : tmp, ctxt->dns2); +} + +static void qmi_process_unicast_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + unsigned err; + switch (msg->type) { + case 0x0021: + if (qmi_get_status(msg, &err)) { + printk(KERN_ERR + "qmi: wds: network stop failed (%04x)\n", err); + } else { + printk(KERN_INFO + "qmi: wds: network stopped\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + } + break; + case 0x0020: + if (qmi_get_status(msg, &err)) { + printk(KERN_ERR + "qmi: wds: network start failed (%04x)\n", err); + } else if (qmi_get_tlv(msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle)) { + printk(KERN_INFO + "qmi: wds no handle?\n"); + } else { + printk(KERN_INFO + "qmi: wds: got handle 0x%08x\n", + ctxt->wds_handle); + } + break; + case 0x002D: + printk("qmi: got network profile\n"); + if (ctxt->state == STATE_QUERYING) { + qmi_read_runtime_profile(ctxt, msg); + ctxt->state = STATE_ONLINE; + ctxt->state_dirty = 1; + } + break; + default: + printk(KERN_ERR "qmi: unknown msg type 0x%04x\n", msg->type); + } + ctxt->wds_busy = 0; +} + +static void qmi_process_broadcast_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + if (msg->type == 0x0022) { + unsigned char n[2]; + if (qmi_get_tlv(msg, 0x01, sizeof(n), n)) + return; + switch (n[0]) { + case 1: + printk(KERN_INFO "qmi: wds: DISCONNECTED\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + break; + case 2: + printk(KERN_INFO "qmi: wds: CONNECTED\n"); + ctxt->state = STATE_QUERYING; + ctxt->state_dirty = 1; + qmi_network_get_profile(ctxt); + break; + case 3: + printk(KERN_INFO "qmi: wds: SUSPENDED\n"); + ctxt->state = STATE_OFFLINE; + ctxt->state_dirty = 1; + } + } else { + printk(KERN_ERR "qmi: unknown bcast msg type 0x%04x\n", msg->type); + } +} + +static void qmi_process_wds_msg(struct qmi_ctxt *ctxt, + struct qmi_msg *msg) +{ + printk("wds: %04x @ %02x\n", msg->type, msg->client_id); + if (msg->client_id == ctxt->wds_client_id) { + qmi_process_unicast_wds_msg(ctxt, msg); + } else if (msg->client_id == 0xff) { + qmi_process_broadcast_wds_msg(ctxt, msg); + } else { + printk(KERN_ERR + "qmi_process_wds_msg client id 0x%02x unknown\n", + msg->client_id); + } +} + +static void qmi_process_qmux(struct qmi_ctxt *ctxt, + unsigned char *buf, unsigned sz) +{ + struct qmi_msg msg; + + /* require a full header */ + if (sz < 5) + return; + + /* require a size that matches the buffer size */ + if (sz != (buf[0] | (buf[1] << 8))) + return; + + /* only messages from a service (bit7=1) are allowed */ + if (buf[2] != 0x80) + return; + + msg.service = buf[3]; + msg.client_id = buf[4]; + + /* annoyingly, CTL messages have a shorter TID */ + if (buf[3] == 0) { + if (sz < 7) + return; + msg.txn_id = buf[6]; + buf += 7; + sz -= 7; + } else { + if (sz < 8) + return; + msg.txn_id = buf[6] | (buf[7] << 8); + buf += 8; + sz -= 8; + } + + /* no type and size!? */ + if (sz < 4) + return; + sz -= 4; + + msg.type = buf[0] | (buf[1] << 8); + msg.size = buf[2] | (buf[3] << 8); + msg.tlv = buf + 4; + + if (sz != msg.size) + return; + + qmi_dump_msg(&msg, "recv"); + + mutex_lock(&ctxt->lock); + switch (msg.service) { + case QMI_CTL: + qmi_process_ctl_msg(ctxt, &msg); + break; + case QMI_WDS: + qmi_process_wds_msg(ctxt, &msg); + break; + default: + printk(KERN_ERR "qmi: msg from unknown svc 0x%02x\n", + msg.service); + break; + } + mutex_unlock(&ctxt->lock); + + wake_up(&qmi_wait_queue); +} + +#define QMI_MAX_PACKET (256 + QMUX_OVERHEAD) + +static void qmi_read_work(struct work_struct *ws) +{ + struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, read_work); + struct smd_channel *ch = ctxt->ch; + unsigned char buf[QMI_MAX_PACKET]; + int sz; + + for (;;) { + sz = smd_cur_packet_size(ch); + if (sz == 0) + break; + if (sz < smd_read_avail(ch)) + break; + if (sz > QMI_MAX_PACKET) { + smd_read(ch, 0, sz); + continue; + } + if (smd_read(ch, buf, sz) != sz) { + printk(KERN_ERR "qmi: not enough data?!\n"); + continue; + } + + /* interface selector must be 1 */ + if (buf[0] != 0x01) + continue; + + qmi_process_qmux(ctxt, buf + 1, sz - 1); + } +} + +static int qmi_request_wds_cid(struct qmi_ctxt *ctxt); + +static void qmi_open_work(struct work_struct *ws) +{ + struct qmi_ctxt *ctxt = container_of(ws, struct qmi_ctxt, open_work); + mutex_lock(&ctxt->lock); + qmi_request_wds_cid(ctxt); + mutex_unlock(&ctxt->lock); +} + +static void qmi_notify(void *priv, unsigned event) +{ + struct qmi_ctxt *ctxt = priv; + + switch (event) { + case SMD_EVENT_DATA: { + int sz; + sz = smd_cur_packet_size(ctxt->ch); + if ((sz > 0) && (sz <= smd_read_avail(ctxt->ch))) + queue_work(qmi_wq, &ctxt->read_work); + break; + } + case SMD_EVENT_OPEN: + printk(KERN_INFO "qmi: smd opened\n"); + queue_work(qmi_wq, &ctxt->open_work); + break; + case SMD_EVENT_CLOSE: + printk(KERN_INFO "qmi: smd closed\n"); + break; + } +} + +static int qmi_request_wds_cid(struct qmi_ctxt *ctxt) +{ + unsigned char data[64 + QMUX_OVERHEAD]; + struct qmi_msg msg; + unsigned char n; + + msg.service = QMI_CTL; + msg.client_id = qmi_ctl_client_id; + msg.txn_id = ctxt->ctl_txn_id; + msg.type = 0x0022; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->ctl_txn_id += 2; + + n = QMI_WDS; + qmi_add_tlv(&msg, 0x01, 0x01, &n); + + return qmi_send(ctxt, &msg); +} + +static int qmi_network_get_profile(struct qmi_ctxt *ctxt) +{ + unsigned char data[96 + QMUX_OVERHEAD]; + struct qmi_msg msg; + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x002D; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + return qmi_send(ctxt, &msg); +} + +static int qmi_network_up(struct qmi_ctxt *ctxt, char *apn) +{ + unsigned char data[96 + QMUX_OVERHEAD]; + struct qmi_msg msg; + char *user; + char *pass; + + for (user = apn; *user; user++) { + if (*user == ' ') { + *user++ = 0; + break; + } + } + for (pass = user; *pass; pass++) { + if (*pass == ' ') { + *pass++ = 0; + break; + } + } + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x0020; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + qmi_add_tlv(&msg, 0x14, strlen(apn), apn); + if (*user) { + unsigned char x; + x = 3; + qmi_add_tlv(&msg, 0x16, 1, &x); + qmi_add_tlv(&msg, 0x17, strlen(user), user); + if (*pass) + qmi_add_tlv(&msg, 0x18, strlen(pass), pass); + } + return qmi_send(ctxt, &msg); +} + +static int qmi_network_down(struct qmi_ctxt *ctxt) +{ + unsigned char data[16 + QMUX_OVERHEAD]; + struct qmi_msg msg; + + msg.service = QMI_WDS; + msg.client_id = ctxt->wds_client_id; + msg.txn_id = ctxt->wds_txn_id; + msg.type = 0x0021; + msg.size = 0; + msg.tlv = data + QMUX_HEADER; + + ctxt->wds_txn_id += 2; + + qmi_add_tlv(&msg, 0x01, sizeof(ctxt->wds_handle), &ctxt->wds_handle); + + return qmi_send(ctxt, &msg); +} + +static int qmi_print_state(struct qmi_ctxt *ctxt, char *buf, int max) +{ + int i; + char *statename; + + if (ctxt->state == STATE_ONLINE) { + statename = "up"; + } else if (ctxt->state == STATE_OFFLINE) { + statename = "down"; + } else { + statename = "busy"; + } + + i = scnprintf(buf, max, "STATE=%s\n", statename); + i += scnprintf(buf + i, max - i, "CID=%d\n",ctxt->wds_client_id); + + if (ctxt->state != STATE_ONLINE){ + return i; + } + + i += scnprintf(buf + i, max - i, "ADDR=%d.%d.%d.%d\n", + ctxt->addr[0], ctxt->addr[1], ctxt->addr[2], ctxt->addr[3]); + i += scnprintf(buf + i, max - i, "MASK=%d.%d.%d.%d\n", + ctxt->mask[0], ctxt->mask[1], ctxt->mask[2], ctxt->mask[3]); + i += scnprintf(buf + i, max - i, "GATEWAY=%d.%d.%d.%d\n", + ctxt->gateway[0], ctxt->gateway[1], ctxt->gateway[2], + ctxt->gateway[3]); + i += scnprintf(buf + i, max - i, "DNS1=%d.%d.%d.%d\n", + ctxt->dns1[0], ctxt->dns1[1], ctxt->dns1[2], ctxt->dns1[3]); + i += scnprintf(buf + i, max - i, "DNS2=%d.%d.%d.%d\n", + ctxt->dns2[0], ctxt->dns2[1], ctxt->dns2[2], ctxt->dns2[3]); + + return i; +} + +static ssize_t qmi_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + struct qmi_ctxt *ctxt = fp->private_data; + char msg[256]; + int len; + int r; + + mutex_lock(&ctxt->lock); + for (;;) { + if (ctxt->state_dirty) { + ctxt->state_dirty = 0; + len = qmi_print_state(ctxt, msg, 256); + break; + } + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, ctxt->state_dirty); + if (r < 0) + return r; + mutex_lock(&ctxt->lock); + } + mutex_unlock(&ctxt->lock); + + if (len > count) + len = count; + + if (copy_to_user(buf, msg, len)) + return -EFAULT; + + return len; +} + + +static ssize_t qmi_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + struct qmi_ctxt *ctxt = fp->private_data; + unsigned char cmd[64]; + int len; + int r; + + if (count < 1) + return 0; + + len = count > 63 ? 63 : count; + + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + + cmd[len] = 0; + + /* lazy */ + if (cmd[len-1] == '\n') { + cmd[len-1] = 0; + len--; + } + + if (!strncmp(cmd, "verbose", 7)) { + verbose = 1; + } else if (!strncmp(cmd, "terse", 5)) { + verbose = 0; + } else if (!strncmp(cmd, "poll", 4)) { + ctxt->state_dirty = 1; + wake_up(&qmi_wait_queue); + } else if (!strncmp(cmd, "down", 4)) { +retry_down: + mutex_lock(&ctxt->lock); + if (ctxt->wds_busy) { + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); + if (r < 0) + return r; + goto retry_down; + } + ctxt->wds_busy = 1; + qmi_network_down(ctxt); + mutex_unlock(&ctxt->lock); + } else if (!strncmp(cmd, "up:", 3)) { +retry_up: + mutex_lock(&ctxt->lock); + if (ctxt->wds_busy) { + mutex_unlock(&ctxt->lock); + r = wait_event_interruptible(qmi_wait_queue, !ctxt->wds_busy); + if (r < 0) + return r; + goto retry_up; + } + ctxt->wds_busy = 1; + qmi_network_up(ctxt, cmd+3); + mutex_unlock(&ctxt->lock); + } else { + return -EINVAL; + } + + return count; +} + +static int qmi_open(struct inode *ip, struct file *fp) +{ + struct qmi_ctxt *ctxt = qmi_minor_to_ctxt(MINOR(ip->i_rdev)); + int r = 0; + + if (!ctxt) { + printk(KERN_ERR "unknown qmi misc %d\n", MINOR(ip->i_rdev)); + return -ENODEV; + } + + fp->private_data = ctxt; + + mutex_lock(&ctxt->lock); + if (ctxt->ch == 0) + r = smd_open(ctxt->ch_name, &ctxt->ch, ctxt, qmi_notify); + if (r == 0) + wake_up(&qmi_wait_queue); + mutex_unlock(&ctxt->lock); + + return r; +} + +static int qmi_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static struct file_operations qmi_fops = { + .owner = THIS_MODULE, + .read = qmi_read, + .write = qmi_write, + .open = qmi_open, + .release = qmi_release, +}; + +static struct qmi_ctxt qmi_device0 = { + .ch_name = "SMD_DATA5_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi0", + .fops = &qmi_fops, + } +}; +static struct qmi_ctxt qmi_device1 = { + .ch_name = "SMD_DATA6_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi1", + .fops = &qmi_fops, + } +}; +static struct qmi_ctxt qmi_device2 = { + .ch_name = "SMD_DATA7_CNTL", + .misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "qmi2", + .fops = &qmi_fops, + } +}; + +static struct qmi_ctxt *qmi_minor_to_ctxt(unsigned n) +{ + if (n == qmi_device0.misc.minor) + return &qmi_device0; + if (n == qmi_device1.misc.minor) + return &qmi_device1; + if (n == qmi_device2.misc.minor) + return &qmi_device2; + return 0; +} + +static int __init qmi_init(void) +{ + int ret; + + qmi_wq = create_singlethread_workqueue("qmi"); + if (qmi_wq == 0) + return -ENOMEM; + + qmi_ctxt_init(&qmi_device0, 0); + qmi_ctxt_init(&qmi_device1, 1); + qmi_ctxt_init(&qmi_device2, 2); + + ret = misc_register(&qmi_device0.misc); + if (ret == 0) + ret = misc_register(&qmi_device1.misc); + if (ret == 0) + ret = misc_register(&qmi_device2.misc); + return ret; +} + +module_init(qmi_init); diff --git a/arch/arm/mach-msm/smd_rpcrouter.c b/arch/arm/mach-msm/smd_rpcrouter.c new file mode 100644 index 000000000000..eb69670e2b24 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.c @@ -0,0 +1,2197 @@ +/* arch/arm/mach-msm/smd_rpcrouter.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* TODO: handle cases where smd_write() will tempfail due to full fifo */ +/* TODO: thread priority? schedule a work to bump it? */ +/* TODO: maybe make server_list_lock a mutex */ +/* TODO: pool fragments to avoid kmalloc/kfree churn */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/cdev.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/poll.h> +#include <asm/uaccess.h> +#include <asm/byteorder.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/debugfs.h> + +#include <asm/byteorder.h> + +#include <mach/msm_smd.h> +#include <mach/smem_log.h> +#include "smd_rpcrouter.h" +#include "modem_notifier.h" + +enum { + SMEM_LOG = 1U << 0, + RTR_DBG = 1U << 1, + R2R_MSG = 1U << 2, + R2R_RAW = 1U << 3, + RPC_MSG = 1U << 4, + NTFY_MSG = 1U << 5, + RAW_PMR = 1U << 6, + RAW_PMW = 1U << 7, + R2R_RAW_HDR = 1U << 8, +}; +static int smd_rpcrouter_debug_mask; +module_param_named(debug_mask, smd_rpcrouter_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define DIAG(x...) printk(KERN_ERR "[RR] ERROR " x) + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) +#define D(x...) do { \ +if (smd_rpcrouter_debug_mask & RTR_DBG) \ + printk(KERN_ERR x); \ +} while (0) + +#define RR(x...) do { \ +if (smd_rpcrouter_debug_mask & R2R_MSG) \ + printk(KERN_ERR "[RR] "x); \ +} while (0) + +#define RAW(x...) do { \ +if (smd_rpcrouter_debug_mask & R2R_RAW) \ + printk(KERN_ERR "[RAW] "x); \ +} while (0) + +#define RAW_HDR(x...) do { \ +if (smd_rpcrouter_debug_mask & R2R_RAW_HDR) \ + printk(KERN_ERR "[HDR] "x); \ +} while (0) + +#define RAW_PMR(x...) do { \ +if (smd_rpcrouter_debug_mask & RAW_PMR) \ + printk(KERN_ERR "[PMR] "x); \ +} while (0) + +#define RAW_PMR_NOMASK(x...) do { \ + printk(KERN_ERR "[PMR] "x); \ +} while (0) + +#define RAW_PMW(x...) do { \ +if (smd_rpcrouter_debug_mask & RAW_PMW) \ + printk(KERN_ERR "[PMW] "x); \ +} while (0) + +#define RAW_PMW_NOMASK(x...) do { \ + printk(KERN_ERR "[PMW] "x); \ +} while (0) + +#define IO(x...) do { \ +if (smd_rpcrouter_debug_mask & RPC_MSG) \ + printk(KERN_ERR "[RPC] "x); \ +} while (0) + +#define NTFY(x...) do { \ +if (smd_rpcrouter_debug_mask & NTFY_MSG) \ + printk(KERN_ERR "[NOTIFY] "x); \ +} while (0) +#else +#define D(x...) do { } while (0) +#define RR(x...) do { } while (0) +#define RAW(x...) do { } while (0) +#define RAW_HDR(x...) do { } while (0) +#define RAW_PMR(x...) do { } while (0) +#define RAW_PMR_NO_MASK(x...) do { } while (0) +#define RAW_PMW(x...) do { } while (0) +#define RAW_PMW_NO_MASK(x...) do { } while (0) +#define IO(x...) do { } while (0) +#define NTFY(x...) do { } while (0) +#endif + + +static LIST_HEAD(local_endpoints); +static LIST_HEAD(remote_endpoints); + +static LIST_HEAD(server_list); + +static smd_channel_t *smd_channel; +static int initialized; +static wait_queue_head_t newserver_wait; +static wait_queue_head_t smd_wait; + +static DEFINE_SPINLOCK(local_endpoints_lock); +static DEFINE_SPINLOCK(remote_endpoints_lock); +static DEFINE_SPINLOCK(server_list_lock); +static DEFINE_SPINLOCK(smd_lock); + +static struct workqueue_struct *rpcrouter_workqueue; + +static atomic_t next_xid = ATOMIC_INIT(1); +static atomic_t pm_mid = ATOMIC_INIT(1); + +static void do_read_data(struct work_struct *work); +static void do_create_pdevs(struct work_struct *work); +static void do_create_rpcrouter_pdev(struct work_struct *work); + +static DECLARE_WORK(work_read_data, do_read_data); +static DECLARE_WORK(work_create_pdevs, do_create_pdevs); +static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev); + +#define RR_STATE_IDLE 0 +#define RR_STATE_HEADER 1 +#define RR_STATE_BODY 2 +#define RR_STATE_ERROR 3 + +/* After restart notification, local ep keep + * state for server restart and for ep notify. + * Server restart cleared by R-R new svr msg. + * NTFY cleared by calling msm_rpc_clear_netreset +*/ + +#define RESTART_NORMAL 0 +#define RESTART_PEND_SVR 1 +#define RESTART_PEND_NTFY 2 +#define RESTART_PEND_NTFY_SVR 3 + +/* State for remote ep following restart */ +#define RESTART_QUOTA_ABORT 1 + +struct rr_context { + struct rr_packet *pkt; + uint8_t *ptr; + uint32_t state; /* current assembly state */ + uint32_t count; /* bytes needed in this state */ +}; + +struct rr_context the_rr_context; + +static struct platform_device rpcrouter_pdev = { + .name = "oncrpc_router", + .id = -1, +}; + + +static int rpcrouter_send_control_msg(union rr_control_msg *msg) +{ + struct rr_header hdr; + unsigned long flags; + int need; + + if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !initialized) { + printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, " + "router not initialized\n"); + return -EINVAL; + } + + hdr.version = RPCROUTER_VERSION; + hdr.type = msg->cmd; + hdr.src_pid = RPCROUTER_PID_LOCAL; + hdr.src_cid = RPCROUTER_ROUTER_ADDRESS; + hdr.confirm_rx = 0; + hdr.size = sizeof(*msg); + hdr.dst_pid = 0; + hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS; + + /* TODO: what if channel is full? */ + + need = sizeof(hdr) + hdr.size; + spin_lock_irqsave(&smd_lock, flags); + while (smd_write_avail(smd_channel) < need) { + spin_unlock_irqrestore(&smd_lock, flags); + msleep(250); + spin_lock_irqsave(&smd_lock, flags); + } + smd_write(smd_channel, &hdr, sizeof(hdr)); + smd_write(smd_channel, msg, hdr.size); + spin_unlock_irqrestore(&smd_lock, flags); + return 0; +} + +static void modem_reset_start_cleanup(void) +{ + struct msm_rpc_endpoint *ept; + struct rr_remote_endpoint *r_ept; + struct rr_packet *pkt, *tmp_pkt; + struct rr_fragment *frag, *next; + struct msm_rpc_reply *reply, *reply_tmp; + unsigned long flags; + + spin_lock_irqsave(&local_endpoints_lock, flags); + /* remove all partial packets received */ + list_for_each_entry(ept, &local_endpoints, list) { + RR("modem_reset_start_clenup PID %x, remotepid:%d \n", + ept->dst_pid, RPCROUTER_PID_REMOTE); + /* remove replies */ + spin_lock(&ept->reply_q_lock); + list_for_each_entry_safe(reply, reply_tmp, + &ept->reply_pend_q, list) { + list_del(&reply->list); + kfree(reply); + } + list_for_each_entry_safe(reply, reply_tmp, + &ept->reply_avail_q, list) { + list_del(&reply->list); + kfree(reply); + } + spin_unlock(&ept->reply_q_lock); + if (ept->dst_pid == RPCROUTER_PID_REMOTE) { + spin_lock(&ept->incomplete_lock); + list_for_each_entry_safe(pkt, tmp_pkt, + &ept->incomplete, list) { + list_del(&pkt->list); + frag = pkt->first; + while (frag != NULL) { + next = frag->next; + kfree(frag); + frag = next; + } + kfree(pkt); + } + spin_unlock(&ept->incomplete_lock); + /* remove all completed packets waiting to be read*/ + spin_lock(&ept->read_q_lock); + list_for_each_entry_safe(pkt, tmp_pkt, &ept->read_q, + list) { + list_del(&pkt->list); + frag = pkt->first; + while (frag != NULL) { + next = frag->next; + kfree(frag); + frag = next; + } + kfree(pkt); + } + spin_unlock(&ept->read_q_lock); + /* Set restart state for local ep */ + RR("EPT:0x%p, State %d RESTART_PEND_NTFY_SVR " + "PROG:0x%08x VERS:0x%08x \n", + ept, ept->restart_state, be32_to_cpu(ept->dst_prog), + be32_to_cpu(ept->dst_vers)); + spin_lock(&ept->restart_lock); + ept->restart_state = RESTART_PEND_NTFY_SVR; + spin_unlock(&ept->restart_lock); + wake_up(&ept->wait_q); + } + } + + spin_unlock_irqrestore(&local_endpoints_lock, flags); + + /* Unblock endpoints waiting for quota ack*/ + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(r_ept, &remote_endpoints, list) { + spin_lock(&r_ept->quota_lock); + r_ept->quota_restart_state = RESTART_QUOTA_ABORT; + RR("Set STATE_PENDING PID:0x%08x CID:0x%08x \n", r_ept->pid, + r_ept->cid); + spin_unlock(&r_ept->quota_lock); + wake_up(&r_ept->quota_wait); + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + +} + + +static struct rr_server *rpcrouter_create_server(uint32_t pid, + uint32_t cid, + uint32_t prog, + uint32_t ver) +{ + struct rr_server *server; + unsigned long flags; + int rc; + + server = kmalloc(sizeof(struct rr_server), GFP_KERNEL); + if (!server) + return ERR_PTR(-ENOMEM); + + memset(server, 0, sizeof(struct rr_server)); + server->pid = pid; + server->cid = cid; + server->prog = prog; + server->vers = ver; + + spin_lock_irqsave(&server_list_lock, flags); + list_add_tail(&server->list, &server_list); + spin_unlock_irqrestore(&server_list_lock, flags); + + if (pid == RPCROUTER_PID_REMOTE) { + rc = msm_rpcrouter_create_server_cdev(server); + if (rc < 0) + goto out_fail; + } + return server; +out_fail: + spin_lock_irqsave(&server_list_lock, flags); + list_del(&server->list); + spin_unlock_irqrestore(&server_list_lock, flags); + kfree(server); + return ERR_PTR(rc); +} + +static void rpcrouter_destroy_server(struct rr_server *server) +{ + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_del(&server->list); + spin_unlock_irqrestore(&server_list_lock, flags); + device_destroy(msm_rpcrouter_class, server->device_number); + kfree(server); +} + +static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver) +{ + struct rr_server *server; + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->prog == prog + && server->vers == ver) { + spin_unlock_irqrestore(&server_list_lock, flags); + return server; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + +static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev) +{ + struct rr_server *server; + unsigned long flags; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->device_number == dev) { + spin_unlock_irqrestore(&server_list_lock, flags); + return server; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return NULL; +} + +struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) +{ + struct msm_rpc_endpoint *ept; + unsigned long flags; + + ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL); + if (!ept) + return NULL; + memset(ept, 0, sizeof(struct msm_rpc_endpoint)); + ept->cid = (uint32_t) ept; + ept->pid = RPCROUTER_PID_LOCAL; + ept->dev = dev; + + if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) { + struct rr_server *srv; + /* + * This is a userspace client which opened + * a program/ver devicenode. Bind the client + * to that destination + */ + srv = rpcrouter_lookup_server_by_dev(dev); + /* TODO: bug? really? */ + BUG_ON(!srv); + + ept->dst_pid = srv->pid; + ept->dst_cid = srv->cid; + ept->dst_prog = cpu_to_be32(srv->prog); + ept->dst_vers = cpu_to_be32(srv->vers); + } else { + /* mark not connected */ + ept->dst_pid = 0xffffffff; + } + + init_waitqueue_head(&ept->wait_q); + INIT_LIST_HEAD(&ept->read_q); + spin_lock_init(&ept->read_q_lock); + INIT_LIST_HEAD(&ept->reply_avail_q); + INIT_LIST_HEAD(&ept->reply_pend_q); + spin_lock_init(&ept->reply_q_lock); + spin_lock_init(&ept->restart_lock); + init_waitqueue_head(&ept->restart_wait); + ept->restart_state = RESTART_NORMAL; + INIT_LIST_HEAD(&ept->incomplete); + spin_lock_init(&ept->incomplete_lock); + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_add_tail(&ept->list, &local_endpoints); + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return ept; +} + +int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept) +{ + int rc; + union rr_control_msg msg; + struct msm_rpc_reply *reply, *reply_tmp; + unsigned long flags; + + msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT; + msg.cli.pid = ept->pid; + msg.cli.cid = ept->cid; + + RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid); + rc = rpcrouter_send_control_msg(&msg); + if (rc < 0) + return rc; + + /* Free replies */ + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_for_each_entry_safe(reply, reply_tmp, &ept->reply_pend_q, list) { + list_del(&reply->list); + kfree(reply); + } + list_for_each_entry_safe(reply, reply_tmp, &ept->reply_avail_q, list) { + list_del(&reply->list); + kfree(reply); + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + + list_del(&ept->list); + kfree(ept); + return 0; +} + +static int rpcrouter_create_remote_endpoint(uint32_t cid) +{ + struct rr_remote_endpoint *new_c; + unsigned long flags; + + new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL); + if (!new_c) + return -ENOMEM; + memset(new_c, 0, sizeof(struct rr_remote_endpoint)); + + new_c->cid = cid; + new_c->pid = RPCROUTER_PID_REMOTE; + init_waitqueue_head(&new_c->quota_wait); + spin_lock_init(&new_c->quota_lock); + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_add_tail(&new_c->list, &remote_endpoints); + new_c->quota_restart_state = RESTART_NORMAL; + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return 0; +} + +static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid) +{ + struct msm_rpc_endpoint *ept; + unsigned long flags; + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_for_each_entry(ept, &local_endpoints, list) { + if (ept->cid == cid) { + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return ept; + } + } + spin_unlock_irqrestore(&local_endpoints_lock, flags); + return NULL; +} + +static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t cid) +{ + struct rr_remote_endpoint *ept; + unsigned long flags; + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(ept, &remote_endpoints, list) { + if (ept->cid == cid) { + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return ept; + } + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + return NULL; +} + +static void handle_server_restart(struct rr_server *server, uint32_t cid, + uint32_t prog, uint32_t vers) +{ + struct rr_remote_endpoint *r_ept; + struct msm_rpc_endpoint *ept; + unsigned long flags; + r_ept = rpcrouter_lookup_remote_endpoint(cid); + if (r_ept && (r_ept->quota_restart_state != + RESTART_NORMAL)) { + spin_lock_irqsave(&r_ept->quota_lock, flags); + r_ept->tx_quota_cntr = 0; + r_ept->quota_restart_state = + RESTART_NORMAL; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + printk(KERN_INFO "rpcrouter: Remote EP %0x Reset\n", + (unsigned int)r_ept); + wake_up(&r_ept->quota_wait); + } + spin_lock_irqsave(&local_endpoints_lock, flags); + list_for_each_entry(ept, &local_endpoints, list) { + if ((be32_to_cpu(ept->dst_prog) == prog) && + (be32_to_cpu(ept->dst_vers) == vers) && + (ept->restart_state & RESTART_PEND_SVR)) { + spin_lock(&ept->restart_lock); + ept->restart_state &= ~RESTART_PEND_SVR; + spin_unlock(&ept->restart_lock); + D("rpcrouter: Local EPT Reset %08x:%08x \n", + prog, vers); + wake_up(&ept->restart_wait); + wake_up(&ept->wait_q); + } + } + spin_unlock_irqrestore(&local_endpoints_lock, flags); +} + +static int process_control_msg(union rr_control_msg *msg, int len) +{ + union rr_control_msg ctl; + struct rr_server *server; + struct rr_remote_endpoint *r_ept; + int rc = 0; + unsigned long flags; + static int first = 1; + + if (len != sizeof(*msg)) { + printk(KERN_ERR "rpcrouter: r2r msg size %d != %d\n", + len, sizeof(*msg)); + return -EINVAL; + } + + switch (msg->cmd) { + case RPCROUTER_CTRL_CMD_HELLO: + RR("o HELLO\n"); + + RR("x HELLO\n"); + memset(&ctl, 0, sizeof(ctl)); + ctl.cmd = RPCROUTER_CTRL_CMD_HELLO; + rpcrouter_send_control_msg(&ctl); + + initialized = 1; + + /* Send list of servers one at a time */ + ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; + + /* TODO: long time to hold a spinlock... */ + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->pid != RPCROUTER_PID_LOCAL) + continue; + ctl.srv.pid = server->pid; + ctl.srv.cid = server->cid; + ctl.srv.prog = server->prog; + ctl.srv.vers = server->vers; + + RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", + server->pid, server->cid, + server->prog, server->vers); + + rpcrouter_send_control_msg(&ctl); + } + spin_unlock_irqrestore(&server_list_lock, flags); + + if (first) { + first = 0; + queue_work(rpcrouter_workqueue, + &work_create_rpcrouter_pdev); + } + break; + + case RPCROUTER_CTRL_CMD_RESUME_TX: + RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid); + + r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid); + if (!r_ept) { + printk(KERN_ERR + "rpcrouter: Unable to resume client\n"); + break; + } + spin_lock_irqsave(&r_ept->quota_lock, flags); + r_ept->tx_quota_cntr = 0; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + wake_up(&r_ept->quota_wait); + break; + + case RPCROUTER_CTRL_CMD_NEW_SERVER: + if (msg->srv.vers == 0) { + pr_err( + "rpcrouter: Server create rejected, version = 0, " + "program = %08x\n", msg->srv.prog); + break; + } + + RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n", + msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); + + server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); + + if (!server) { + server = rpcrouter_create_server( + msg->srv.pid, msg->srv.cid, + msg->srv.prog, msg->srv.vers); + if (!server) + return -ENOMEM; + /* + * XXX: Verify that its okay to add the + * client to our remote client list + * if we get a NEW_SERVER notification + */ + if (!rpcrouter_lookup_remote_endpoint(msg->srv.cid)) { + rc = rpcrouter_create_remote_endpoint( + msg->srv.cid); + if (rc < 0) + printk(KERN_ERR + "rpcrouter:Client create" + "error (%d)\n", rc); + } + schedule_work(&work_create_pdevs); + wake_up(&newserver_wait); + } else { + if ((server->pid == msg->srv.pid) && + (server->cid == msg->srv.cid)) { + handle_server_restart(server, msg->srv.cid, + msg->srv.prog, + msg->srv.vers); + } else { + server->pid = msg->srv.pid; + server->cid = msg->srv.cid; + } + } + break; + + case RPCROUTER_CTRL_CMD_REMOVE_SERVER: + RR("o REMOVE_SERVER prog=%08x:%d\n", + msg->srv.prog, msg->srv.vers); + server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); + if (server) + rpcrouter_destroy_server(server); + break; + + case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: + RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid); + if (msg->cli.pid != RPCROUTER_PID_REMOTE) { + printk(KERN_ERR + "rpcrouter: Denying remote removal of " + "local client\n"); + break; + } + r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.cid); + if (r_ept) { + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_del(&r_ept->list); + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + kfree(r_ept); + } + + /* Notify local clients of this event */ + printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n"); + rc = -ENOSYS; + + break; + case RPCROUTER_CTRL_CMD_PING: + /* No action needed for ping messages received */ + RR("o PING\n"); + break; + default: + RR("o UNKNOWN(%08x)\n", msg->cmd); + rc = -ENOSYS; + } + + return rc; +} + +static void do_create_rpcrouter_pdev(struct work_struct *work) +{ + platform_device_register(&rpcrouter_pdev); +} + +static void do_create_pdevs(struct work_struct *work) +{ + unsigned long flags; + struct rr_server *server; + + /* TODO: race if destroyed while being registered */ + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if (server->pid == RPCROUTER_PID_REMOTE) { + if (server->pdev_name[0] == 0) { + spin_unlock_irqrestore(&server_list_lock, + flags); + msm_rpcrouter_create_server_pdev(server); + schedule_work(&work_create_pdevs); + return; + } + } + } + spin_unlock_irqrestore(&server_list_lock, flags); +} + +static void rpcrouter_smdnotify(void *_dev, unsigned event) +{ + if (event != SMD_EVENT_DATA) + return; + + wake_up(&smd_wait); +} + +static void *rr_malloc(unsigned sz) +{ + void *ptr = kmalloc(sz, GFP_KERNEL); + if (ptr) + return ptr; + + printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz); + do { + ptr = kmalloc(sz, GFP_KERNEL); + } while (!ptr); + + return ptr; +} + +/* TODO: deal with channel teardown / restore */ +static int rr_read(void *data, int len) +{ + int rc; + unsigned long flags; +// printk("rr_read() %d\n", len); + for(;;) { + spin_lock_irqsave(&smd_lock, flags); + if (smd_read_avail(smd_channel) >= len) { + rc = smd_read(smd_channel, data, len); + spin_unlock_irqrestore(&smd_lock, flags); + if (rc == len) + return 0; + else + return -EIO; + } + spin_unlock_irqrestore(&smd_lock, flags); + +// printk("rr_read: waiting (%d)\n", len); + wait_event(smd_wait, smd_read_avail(smd_channel) >= len); + } + return 0; +} + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) +static char *type_to_str(int i) +{ + switch (i) { + case RPCROUTER_CTRL_CMD_DATA: + return "data "; + case RPCROUTER_CTRL_CMD_HELLO: + return "hello "; + case RPCROUTER_CTRL_CMD_BYE: + return "bye "; + case RPCROUTER_CTRL_CMD_NEW_SERVER: + return "new_srvr"; + case RPCROUTER_CTRL_CMD_REMOVE_SERVER: + return "rmv_srvr"; + case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: + return "rmv_clnt"; + case RPCROUTER_CTRL_CMD_RESUME_TX: + return "resum_tx"; + case RPCROUTER_CTRL_CMD_EXIT: + return "cmd_exit"; + default: + return "invalid"; + } +} +#endif + +static uint32_t r2r_buf[RPCROUTER_MSGSIZE_MAX]; + +static void do_read_data(struct work_struct *work) +{ + struct rr_header hdr; + struct rr_packet *pkt; + struct rr_fragment *frag; + struct msm_rpc_endpoint *ept; +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + struct rpc_request_hdr *rq; +#endif + uint32_t pm, mid; + unsigned long flags; + + if (rr_read(&hdr, sizeof(hdr))) + goto fail_io; + + RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n", + hdr.version, hdr.type, hdr.src_pid, hdr.src_cid, + hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); + RAW_HDR("[r rr_h] " + "ver=%i,type=%s,src_pid=%08x,src_cid=%08x," + "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", + hdr.version, type_to_str(hdr.type), hdr.src_pid, hdr.src_cid, + hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); + + if (hdr.version != RPCROUTER_VERSION) { + DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION); + goto fail_data; + } + if (hdr.size > RPCROUTER_MSGSIZE_MAX) { + DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX); + goto fail_data; + } + + if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) { + if (rr_read(r2r_buf, hdr.size)) + goto fail_io; + process_control_msg((void*) r2r_buf, hdr.size); + goto done; + } + + if (hdr.size < sizeof(pm)) { + DIAG("runt packet (no pacmark)\n"); + goto fail_data; + } + if (rr_read(&pm, sizeof(pm))) + goto fail_io; + + hdr.size -= sizeof(pm); + + frag = rr_malloc(hdr.size + sizeof(*frag)); + frag->next = NULL; + frag->length = hdr.size; + if (rr_read(frag->data, hdr.size)) + goto fail_io; + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if ((smd_rpcrouter_debug_mask & RAW_PMR) && + ((pm >> 30 & 0x1) || (pm >> 31 & 0x1))) { + uint32_t xid = 0; + if (pm >> 30 & 0x1) { + rq = (struct rpc_request_hdr *) frag->data; + xid = ntohl(rq->xid); + } + if ((pm >> 31 & 0x1) || (pm >> 30 & 0x1)) + RAW_PMR_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i," + "len=%3i,dst_cid=%08x\n", + xid, + pm >> 30 & 0x1, + pm >> 31 & 0x1, + pm >> 16 & 0xFF, + pm & 0xFFFF, hdr.dst_cid); + } + + if (smd_rpcrouter_debug_mask & SMEM_LOG) { + rq = (struct rpc_request_hdr *) frag->data; + if (rq->xid == 0) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MID_READ, + PACMARK_MID(pm), + hdr.dst_cid, + hdr.src_cid); + else + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_READ, + ntohl(rq->xid), + hdr.dst_cid, + hdr.src_cid); + } +#endif + + ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); + if (!ept) { + DIAG("no local ept for cid %08x\n", hdr.dst_cid); + kfree(frag); + goto done; + } + + /* See if there is already a partial packet that matches our mid + * and if so, append this fragment to that packet. + */ + mid = PACMARK_MID(pm); + spin_lock_irqsave(&ept->incomplete_lock, flags); + list_for_each_entry(pkt, &ept->incomplete, list) { + if (pkt->mid == mid) { + pkt->last->next = frag; + pkt->last = frag; + pkt->length += frag->length; + if (PACMARK_LAST(pm)) { + list_del(&pkt->list); + spin_unlock_irqrestore(&ept->incomplete_lock, + flags); + goto packet_complete; + } + spin_unlock_irqrestore(&ept->incomplete_lock, flags); + goto done; + } + } + spin_unlock_irqrestore(&ept->incomplete_lock, flags); + /* This mid is new -- create a packet for it, and put it on + * the incomplete list if this fragment is not a last fragment, + * otherwise put it on the read queue. + */ + pkt = rr_malloc(sizeof(struct rr_packet)); + pkt->first = frag; + pkt->last = frag; + memcpy(&pkt->hdr, &hdr, sizeof(hdr)); + pkt->mid = mid; + pkt->length = frag->length; + if (!PACMARK_LAST(pm)) { + list_add_tail(&pkt->list, &ept->incomplete); + goto done; + } + +packet_complete: + spin_lock_irqsave(&ept->read_q_lock, flags); + list_add_tail(&pkt->list, &ept->read_q); + wake_up(&ept->wait_q); + spin_unlock_irqrestore(&ept->read_q_lock, flags); +done: + + if (hdr.confirm_rx) { + union rr_control_msg msg; + + msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX; + msg.cli.pid = hdr.dst_pid; + msg.cli.cid = hdr.dst_cid; + + RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid); + rpcrouter_send_control_msg(&msg); + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if (smd_rpcrouter_debug_mask & SMEM_LOG) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT, + RPCROUTER_PID_LOCAL, + hdr.dst_cid, + hdr.src_cid); +#endif + + } + + queue_work(rpcrouter_workqueue, &work_read_data); + return; + +fail_io: +fail_data: + printk(KERN_ERR "rpc_router has died\n"); +} + +void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog, + uint32_t vers, uint32_t proc) +{ + memset(hdr, 0, sizeof(struct rpc_request_hdr)); + hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); + hdr->rpc_vers = cpu_to_be32(2); + hdr->prog = cpu_to_be32(prog); + hdr->vers = cpu_to_be32(vers); + hdr->procedure = cpu_to_be32(proc); +} +EXPORT_SYMBOL(msm_rpc_setup_req); + +struct msm_rpc_endpoint *msm_rpc_open(void) +{ + struct msm_rpc_endpoint *ept; + + ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0)); + if (ept == NULL) + return ERR_PTR(-ENOMEM); + + return ept; +} + +int msm_rpc_close(struct msm_rpc_endpoint *ept) +{ + return msm_rpcrouter_destroy_local_endpoint(ept); +} +EXPORT_SYMBOL(msm_rpc_close); + +static int msm_rpc_write_pkt( + struct rr_header *hdr, + struct msm_rpc_endpoint *ept, + struct rr_remote_endpoint *r_ept, + void *buffer, + int count, + int first, + int last, + uint32_t mid + ) +{ +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + struct rpc_request_hdr *rq = buffer; +#endif + uint32_t pacmark; + unsigned long flags; + int needed; + + DEFINE_WAIT(__wait); + + /* Create routing header */ + hdr->type = RPCROUTER_CTRL_CMD_DATA; + hdr->version = RPCROUTER_VERSION; + hdr->src_pid = ept->pid; + hdr->src_cid = ept->cid; + hdr->confirm_rx = 0; + hdr->size = count + sizeof(uint32_t); + + for (;;) { + prepare_to_wait(&ept->restart_wait, &__wait, + TASK_INTERRUPTIBLE); + spin_lock_irqsave(&ept->restart_lock, flags); + if (ept->restart_state == RESTART_NORMAL) { + spin_unlock_irqrestore(&ept->restart_lock, flags); + break; + } + if (signal_pending(current) && + ((!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))) { + spin_unlock_irqrestore(&ept->restart_lock, flags); + break; + } + spin_unlock_irqrestore(&ept->restart_lock, flags); + schedule(); + } + finish_wait(&ept->restart_wait, &__wait); + + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { + return -ERESTARTSYS; + } + + for (;;) { + prepare_to_wait(&r_ept->quota_wait, &__wait, + TASK_INTERRUPTIBLE); + spin_lock_irqsave(&r_ept->quota_lock, flags); + if ((r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA) || + (r_ept->quota_restart_state != RESTART_NORMAL)) + break; + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) + break; + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + schedule(); + } + finish_wait(&r_ept->quota_wait, &__wait); + + if (r_ept->quota_restart_state != RESTART_NORMAL) { + spin_lock(&ept->restart_lock); + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + return -ENETRESET; + } + + if (signal_pending(current) && + (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + return -ERESTARTSYS; + } + r_ept->tx_quota_cntr++; + if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) { + hdr->confirm_rx = 1; + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if (smd_rpcrouter_debug_mask & SMEM_LOG) { + if (rq->xid == 0) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MID_CFM_REQ, + hdr->dst_pid, + hdr->dst_cid, + hdr->src_cid); + else + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ, + hdr->dst_pid, + hdr->dst_cid, + hdr->src_cid); + } +#endif + + } + pacmark = PACMARK(count, mid, first, last); + + spin_unlock_irqrestore(&r_ept->quota_lock, flags); + + spin_lock_irqsave(&smd_lock, flags); + spin_lock(&ept->restart_lock); + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&smd_lock, flags); + return -ENETRESET; + } + + needed = sizeof(*hdr) + hdr->size; + while ((ept->restart_state == RESTART_NORMAL) && + (smd_write_avail(smd_channel) < needed)) { + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&smd_lock, flags); + msleep(250); + spin_lock_irqsave(&smd_lock, flags); + spin_lock(&ept->restart_lock); + } + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&smd_lock, flags); + return -ENETRESET; + } + + /* TODO: deal with full fifo */ + smd_write(smd_channel, hdr, sizeof(*hdr)); + RAW_HDR("[w rr_h] " + "ver=%i,type=%s,src_pid=%08x,src_cid=%08x," + "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", + hdr->version, type_to_str(hdr->type), hdr->src_pid, hdr->src_cid, + hdr->confirm_rx, hdr->size, hdr->dst_pid, hdr->dst_cid); + smd_write(smd_channel, &pacmark, sizeof(pacmark)); + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if ((smd_rpcrouter_debug_mask & RAW_PMW) && + ((pacmark >> 30 & 0x1) || (pacmark >> 31 & 0x1))) { + uint32_t xid = 0; + if (pacmark >> 30 & 0x1) + xid = ntohl(rq->xid); + if ((pacmark >> 31 & 0x1) || (pacmark >> 30 & 0x1)) + RAW_PMW_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i," + "len=%3i,src_cid=%x\n", + xid, + pacmark >> 30 & 0x1, + pacmark >> 31 & 0x1, + pacmark >> 16 & 0xFF, + pacmark & 0xFFFF, hdr->src_cid); + } +#endif + + smd_write(smd_channel, buffer, count); + spin_unlock(&ept->restart_lock); + spin_unlock_irqrestore(&smd_lock, flags); + +#if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) + if (smd_rpcrouter_debug_mask & SMEM_LOG) { + if (rq->xid == 0) + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MID_WRITTEN, + PACMARK_MID(pacmark), + hdr->dst_cid, + hdr->src_cid); + else + smem_log_event(SMEM_LOG_PROC_ID_APPS | + RPC_ROUTER_LOG_EVENT_MSG_WRITTEN, + ntohl(rq->xid), + hdr->dst_cid, + hdr->src_cid); + } +#endif + + return needed; +} + +static struct msm_rpc_reply *get_pend_reply(struct msm_rpc_endpoint *ept, + uint32_t xid) +{ + unsigned long flags; + struct msm_rpc_reply *reply; + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_for_each_entry(reply, &ept->reply_pend_q, list) { + if (reply->xid == xid) { + list_del(&reply->list); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return reply; + } + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return NULL; +} + +void get_requesting_client(struct msm_rpc_endpoint *ept, uint32_t xid, + struct msm_rpc_client_info *clnt_info) +{ + unsigned long flags; + struct msm_rpc_reply *reply; + + if (!clnt_info) + return; + + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_for_each_entry(reply, &ept->reply_pend_q, list) { + if (reply->xid == xid) { + clnt_info->pid = reply->pid; + clnt_info->cid = reply->cid; + clnt_info->prog = reply->prog; + clnt_info->vers = reply->vers; + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return; + } + } + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + return; +} + +static void set_avail_reply(struct msm_rpc_endpoint *ept, + struct msm_rpc_reply *reply) +{ + unsigned long flags; + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_add_tail(&reply->list, &ept->reply_avail_q); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); +} + +static struct msm_rpc_reply *get_avail_reply(struct msm_rpc_endpoint *ept) +{ + struct msm_rpc_reply *reply; + unsigned long flags; + if (list_empty(&ept->reply_avail_q)) { + if (ept->reply_cnt >= RPCROUTER_PEND_REPLIES_MAX) { + printk(KERN_ERR + "exceeding max replies of %d \n", + RPCROUTER_PEND_REPLIES_MAX); + return 0; + } + reply = kmalloc(sizeof(struct msm_rpc_reply), GFP_KERNEL); + if (!reply) + return 0; + D("Adding reply 0x%08x \n", (unsigned int)reply); + memset(reply, 0, sizeof(struct msm_rpc_reply)); + spin_lock_irqsave(&ept->reply_q_lock, flags); + ept->reply_cnt++; + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + } else { + spin_lock_irqsave(&ept->reply_q_lock, flags); + reply = list_first_entry(&ept->reply_avail_q, + struct msm_rpc_reply, + list); + list_del(&reply->list); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); + } + return reply; +} + +static void set_pend_reply(struct msm_rpc_endpoint *ept, + struct msm_rpc_reply *reply) +{ + unsigned long flags; + spin_lock_irqsave(&ept->reply_q_lock, flags); + list_add_tail(&reply->list, &ept->reply_pend_q); + spin_unlock_irqrestore(&ept->reply_q_lock, flags); +} + +int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) +{ + struct rr_header hdr; + struct rpc_request_hdr *rq = buffer; + struct rr_remote_endpoint *r_ept; + struct msm_rpc_reply *reply; + int max_tx; + int tx_cnt; + char *tx_buf; + int rc; + int first_pkt = 1; + uint32_t mid; + + /* snoop the RPC packet and enforce permissions */ + + /* has to have at least the xid and type fields */ + if (count < (sizeof(uint32_t) * 2)) { + printk(KERN_ERR "rr_write: rejecting runt packet\n"); + return -EINVAL; + } + + if (rq->type == 0) { + /* RPC CALL */ + if (count < (sizeof(uint32_t) * 6)) { + printk(KERN_ERR + "rr_write: rejecting runt call packet\n"); + return -EINVAL; + } + if (ept->dst_pid == 0xffffffff) { + printk(KERN_ERR "rr_write: not connected\n"); + return -ENOTCONN; + } + if ((ept->dst_prog != rq->prog) || + ((be32_to_cpu(ept->dst_vers) & 0x0fff0000) != + (be32_to_cpu(rq->vers) & 0x0fff0000))) { + printk(KERN_ERR + "rr_write: cannot write to %08x:%08x " + "(bound to %08x:%08x)\n", + be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + be32_to_cpu(ept->dst_prog), + be32_to_cpu(ept->dst_vers)); + return -EINVAL; + } + hdr.dst_pid = ept->dst_pid; + hdr.dst_cid = ept->dst_cid; + IO("CALL to %08x:%d @ %d:%08x (%d bytes)\n", + be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), + ept->dst_pid, ept->dst_cid, count); + } else { + /* RPC REPLY */ + reply = get_pend_reply(ept, rq->xid); + if (!reply) { + printk(KERN_ERR + "rr_write: rejecting, reply not found \n"); + return -EINVAL; + } + hdr.dst_pid = reply->pid; + hdr.dst_cid = reply->cid; + set_avail_reply(ept, reply); + IO("REPLY to xid=%d @ %d:%08x (%d bytes)\n", + be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); + } + + r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_cid); + + if (!r_ept) { + printk(KERN_ERR + "msm_rpc_write(): No route to ept " + "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid); + return -EHOSTUNREACH; + } + + tx_cnt = count; + tx_buf = buffer; + mid = atomic_add_return(1, &pm_mid) & 0xFF; + /* The modem's router can only take 500 bytes of data. The + first 8 bytes it uses on the modem side for addressing, + the next 4 bytes are for the pacmark header. */ + max_tx = RPCROUTER_MSGSIZE_MAX - 8 - sizeof(uint32_t); + IO("Writing %d bytes, max pkt size is %d\n", + tx_cnt, max_tx); + while (tx_cnt > 0) { + if (tx_cnt > max_tx) { + rc = msm_rpc_write_pkt(&hdr, ept, r_ept, + tx_buf, max_tx, + first_pkt, 0, mid); + if (rc < 0) + return rc; + IO("Wrote %d bytes First %d, Last 0 mid %d\n", + rc, first_pkt, mid); + tx_cnt -= max_tx; + tx_buf += max_tx; + } else { + rc = msm_rpc_write_pkt(&hdr, ept, r_ept, + tx_buf, tx_cnt, + first_pkt, 1, mid); + if (rc < 0) + return rc; + IO("Wrote %d bytes First %d Last 1 mid %d\n", + rc, first_pkt, mid); + break; + } + first_pkt = 0; + } + + return count; +} +EXPORT_SYMBOL(msm_rpc_write); + +/* + * NOTE: It is the responsibility of the caller to kfree buffer + */ +int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer, + unsigned user_len, long timeout) +{ + struct rr_fragment *frag, *next; + char *buf; + int rc; + + rc = __msm_rpc_read(ept, &frag, user_len, timeout); + if (rc <= 0) + return rc; + + /* single-fragment messages conveniently can be + * returned as-is (the buffer is at the front) + */ + if (frag->next == 0) { + *buffer = (void*) frag; + return rc; + } + + /* multi-fragment messages, we have to do it the + * hard way, which is rather disgusting right now + */ + buf = rr_malloc(rc); + *buffer = buf; + + while (frag != NULL) { + memcpy(buf, frag->data, frag->length); + next = frag->next; + buf += frag->length; + kfree(frag); + frag = next; + } + + return rc; +} +EXPORT_SYMBOL(msm_rpc_read); + +int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, + void *_request, int request_size, + long timeout) +{ + return msm_rpc_call_reply(ept, proc, + _request, request_size, + NULL, 0, timeout); +} +EXPORT_SYMBOL(msm_rpc_call); + +int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, + void *_request, int request_size, + void *_reply, int reply_size, + long timeout) +{ + struct rpc_request_hdr *req = _request; + struct rpc_reply_hdr *reply; + int rc; + + if (request_size < sizeof(*req)) + return -ETOOSMALL; + + if (ept->dst_pid == 0xffffffff) + return -ENOTCONN; + + memset(req, 0, sizeof(*req)); + req->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); + req->rpc_vers = cpu_to_be32(2); + req->prog = ept->dst_prog; + req->vers = ept->dst_vers; + req->procedure = cpu_to_be32(proc); + + rc = msm_rpc_write(ept, req, request_size); + if (rc < 0) + return rc; + + for (;;) { + rc = msm_rpc_read(ept, (void*) &reply, -1, timeout); + if (rc < 0) + return rc; + if (rc < (3 * sizeof(uint32_t))) { + rc = -EIO; + break; + } + /* we should not get CALL packets -- ignore them */ + if (reply->type == 0) { + kfree(reply); + continue; + } + /* If an earlier call timed out, we could get the (no + * longer wanted) reply for it. Ignore replies that + * we don't expect + */ + if (reply->xid != req->xid) { + kfree(reply); + continue; + } + if (reply->reply_stat != 0) { + rc = -EPERM; + break; + } + if (reply->data.acc_hdr.accept_stat != 0) { + rc = -EINVAL; + break; + } + if (_reply == NULL) { + rc = 0; + break; + } + if (rc > reply_size) { + rc = -ENOMEM; + } else { + memcpy(_reply, reply, rc); + } + break; + } + kfree(reply); + return rc; +} +EXPORT_SYMBOL(msm_rpc_call_reply); + + +static inline int ept_packet_available(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + int ret; + spin_lock_irqsave(&ept->read_q_lock, flags); + ret = !list_empty(&ept->read_q); + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return ret; +} + +int __msm_rpc_read(struct msm_rpc_endpoint *ept, + struct rr_fragment **frag_ret, + unsigned len, long timeout) +{ + struct rr_packet *pkt; + struct rpc_request_hdr *rq; + struct msm_rpc_reply *reply; + DEFINE_WAIT(__wait); + unsigned long flags; + int rc; + + IO("READ on ept %p\n", ept); + spin_lock_irqsave(&ept->restart_lock, flags); + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + spin_unlock_irqrestore(&ept->restart_lock, flags); + return -ENETRESET; + } + spin_unlock_irqrestore(&ept->restart_lock, flags); + + if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) { + if (timeout < 0) { + wait_event(ept->wait_q, ept_packet_available(ept)); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + } else { + rc = wait_event_timeout( + ept->wait_q, ept_packet_available(ept), + timeout); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + if (rc == 0) + return -ETIMEDOUT; + } + } else { + if (timeout < 0) { + rc = wait_event_interruptible( + ept->wait_q, ept_packet_available(ept)); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + if (rc < 0) + return rc; + } else { + rc = wait_event_interruptible_timeout( + ept->wait_q, ept_packet_available(ept), + timeout); + if (!msm_rpc_clear_netreset(ept)) + return -ENETRESET; + if (rc == 0) + return -ETIMEDOUT; + } + } + + spin_lock_irqsave(&ept->read_q_lock, flags); + if (list_empty(&ept->read_q)) { + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return -EAGAIN; + } + pkt = list_first_entry(&ept->read_q, struct rr_packet, list); + if (pkt->length > len) { + spin_unlock_irqrestore(&ept->read_q_lock, flags); + return -ETOOSMALL; + } + list_del(&pkt->list); + spin_unlock_irqrestore(&ept->read_q_lock, flags); + + rc = pkt->length; + + *frag_ret = pkt->first; + rq = (void*) pkt->first->data; + if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) { + /* RPC CALL */ + reply = get_avail_reply(ept); + if (!reply) + return -ENOMEM; + reply->cid = pkt->hdr.src_cid; + reply->pid = pkt->hdr.src_pid; + reply->xid = rq->xid; + reply->prog = rq->prog; + reply->vers = rq->vers; + set_pend_reply(ept, reply); + } + + kfree(pkt); + + IO("READ on ept %p (%d bytes)\n", ept, rc); + return rc; +} + +int msm_rpc_is_compatible_version(uint32_t server_version, + uint32_t client_version) +{ + + if ((server_version & RPC_VERSION_MODE_MASK) != + (client_version & RPC_VERSION_MODE_MASK)) + return 0; + + if (server_version & RPC_VERSION_MODE_MASK) + return server_version == client_version; + + return ((server_version & RPC_VERSION_MAJOR_MASK) == + (client_version & RPC_VERSION_MAJOR_MASK)) && + ((server_version & RPC_VERSION_MINOR_MASK) >= + (client_version & RPC_VERSION_MINOR_MASK)); +} +EXPORT_SYMBOL(msm_rpc_is_compatible_version); + +int msm_rpc_get_compatible_server(uint32_t prog, + uint32_t ver, + uint32_t *found_vers) +{ + struct rr_server *server; + unsigned long flags; + uint32_t found = -1; + if (found_vers == NULL) + return 0; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(server, &server_list, list) { + if ((server->prog == prog) && + msm_rpc_is_compatible_version(server->vers, ver)) { + *found_vers = server->vers; + spin_unlock_irqrestore(&server_list_lock, flags); + return 0; + } + } + spin_unlock_irqrestore(&server_list_lock, flags); + return found; +} +EXPORT_SYMBOL(msm_rpc_get_compatible_server); + +struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog, + uint32_t vers, unsigned flags) +{ + uint32_t found_vers; + int ret; + ret = msm_rpc_get_compatible_server(prog, vers, &found_vers); + if (ret < 0) + return ERR_PTR(-EHOSTUNREACH); + if (found_vers != vers) { + D("RPC Using new version 0x%08x(0x%08x) prog 0x%08x", + vers, found_vers, prog); + D(" ... Continuing\n"); + } + return msm_rpc_connect(prog, found_vers, flags); +} +EXPORT_SYMBOL(msm_rpc_connect_compatible); + +struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags) +{ + struct msm_rpc_endpoint *ept; + struct rr_server *server; + + server = rpcrouter_lookup_server(prog, vers); + if (!server) + return ERR_PTR(-EHOSTUNREACH); + + ept = msm_rpc_open(); + if (IS_ERR(ept)) + return ept; + + ept->flags = flags; + ept->dst_pid = server->pid; + ept->dst_cid = server->cid; + ept->dst_prog = cpu_to_be32(prog); + ept->dst_vers = cpu_to_be32(vers); + + return ept; +} +EXPORT_SYMBOL(msm_rpc_connect); + +/* TODO: permission check? */ +int msm_rpc_register_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers) +{ + int rc; + union rr_control_msg msg; + struct rr_server *server; + + server = rpcrouter_create_server(ept->pid, ept->cid, + prog, vers); + if (!server) + return -ENODEV; + + msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; + msg.srv.pid = ept->pid; + msg.srv.cid = ept->cid; + msg.srv.prog = prog; + msg.srv.vers = vers; + + RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", + ept->pid, ept->cid, prog, vers); + + rc = rpcrouter_send_control_msg(&msg); + if (rc < 0) + return rc; + + return 0; +} + +int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept) +{ + unsigned long flags; + int rc = 1; + RR("RESET RESTART FLAG for EPT:%08x \n", (unsigned int)ept); + spin_lock_irqsave(&ept->restart_lock, flags); + if (ept->restart_state != RESTART_NORMAL) { + ept->restart_state &= ~RESTART_PEND_NTFY; + rc = 0; + } + spin_unlock_irqrestore(&ept->restart_lock, flags); + return rc; +} + +/* TODO: permission check -- disallow unreg of somebody else's server */ +int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, + uint32_t prog, uint32_t vers) +{ + struct rr_server *server; + server = rpcrouter_lookup_server(prog, vers); + + if (!server) + return -ENOENT; + rpcrouter_destroy_server(server); + return 0; +} + +static int msm_rpcrouter_modem_notify(struct notifier_block *this, + unsigned long code, + void *_cmd) +{ + switch (code) { + case MODEM_NOTIFIER_START_RESET: + NTFY("%s: MODEM_NOTIFIER_START_RESET", __func__); + modem_reset_start_cleanup(); + break; + case MODEM_NOTIFIER_END_RESET: + NTFY("%s: MODEM_NOTIFIER_END_RESET", __func__); + break; + default: + NTFY("%s: default", __func__); + break; + } + return NOTIFY_DONE; +} + +static struct notifier_block msm_rpcrouter_nb = { + .notifier_call = msm_rpcrouter_modem_notify, +}; + +static int msm_rpcrouter_probe(struct platform_device *pdev) +{ + int rc; + + /* Initialize what we need to start processing */ + INIT_LIST_HEAD(&local_endpoints); + INIT_LIST_HEAD(&remote_endpoints); + + init_waitqueue_head(&newserver_wait); + init_waitqueue_head(&smd_wait); + + rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter"); + if (!rpcrouter_workqueue) + return -ENOMEM; + + rc = msm_rpcrouter_init_devices(); + if (rc < 0) + goto fail_destroy_workqueue; + + rc = modem_register_notifier(&msm_rpcrouter_nb); + if (rc < 0) + goto fail_remove_devices; + + /* Open up SMD channel 2 */ + initialized = 0; + rc = smd_open("RPCCALL", &smd_channel, NULL, rpcrouter_smdnotify); + if (rc < 0) + goto fail_remove_reset_notifier; + + queue_work(rpcrouter_workqueue, &work_read_data); + return 0; + + fail_remove_reset_notifier: + modem_unregister_notifier(&msm_rpcrouter_nb); + fail_remove_devices: + msm_rpcrouter_exit_devices(); + fail_destroy_workqueue: + destroy_workqueue(rpcrouter_workqueue); + return rc; +} + +static struct platform_driver msm_smd_channel2_driver = { + .probe = msm_rpcrouter_probe, + .driver = { + .name = "RPCCALL", + .owner = THIS_MODULE, + }, +}; + +#if defined(CONFIG_DEBUG_FS) +#define HSIZE 13 + +struct sym { + uint32_t val; + char *str; + struct hlist_node node; +}; + +static struct sym oncrpc_syms[] = { + { 0x30000000, "CM" }, + { 0x30000001, "DB" }, + { 0x30000002, "SND" }, + { 0x30000003, "WMS" }, + { 0x30000004, "PDSM" }, + { 0x30000005, "MISC_MODEM_APIS" }, + { 0x30000006, "MISC_APPS_APIS" }, + { 0x30000007, "JOYST" }, + { 0x30000008, "VJOY" }, + { 0x30000009, "JOYSTC" }, + { 0x3000000a, "ADSPRTOSATOM" }, + { 0x3000000b, "ADSPRTOSMTOA" }, + { 0x3000000c, "I2C" }, + { 0x3000000d, "TIME_REMOTE" }, + { 0x3000000e, "NV" }, + { 0x3000000f, "CLKRGM_SEC" }, + { 0x30000010, "RDEVMAP" }, + { 0x30000011, "FS_RAPI" }, + { 0x30000012, "PBMLIB" }, + { 0x30000013, "AUDMGR" }, + { 0x30000014, "MVS" }, + { 0x30000015, "DOG_KEEPALIVE" }, + { 0x30000016, "GSDI_EXP" }, + { 0x30000017, "AUTH" }, + { 0x30000018, "NVRUIMI" }, + { 0x30000019, "MMGSDILIB" }, + { 0x3000001a, "CHARGER" }, + { 0x3000001b, "UIM" }, + { 0x3000001C, "ONCRPCTEST" }, + { 0x3000001d, "PDSM_ATL" }, + { 0x3000001e, "FS_XMOUNT" }, + { 0x3000001f, "SECUTIL " }, + { 0x30000020, "MCCMEID" }, + { 0x30000021, "PM_STROBE_FLASH" }, + { 0x30000022, "DS707_EXTIF" }, + { 0x30000023, "SMD BRIDGE_MODEM" }, + { 0x30000024, "SMD PORT_MGR" }, + { 0x30000025, "BUS_PERF" }, + { 0x30000026, "BUS_MON" }, + { 0x30000027, "MC" }, + { 0x30000028, "MCCAP" }, + { 0x30000029, "MCCDMA" }, + { 0x3000002a, "MCCDS" }, + { 0x3000002b, "MCCSCH" }, + { 0x3000002c, "MCCSRID" }, + { 0x3000002d, "SNM" }, + { 0x3000002e, "MCCSYOBJ" }, + { 0x3000002f, "DS707_APIS" }, + { 0x30000030, "DS_MP_SHIM_APPS_ASYNC" }, + { 0x30000031, "DSRLP_APIS" }, + { 0x30000032, "RLP_APIS" }, + { 0x30000033, "DS_MP_SHIM_MODEM" }, + { 0x30000034, "DSHDR_APIS" }, + { 0x30000035, "DSHDR_MDM_APIS" }, + { 0x30000036, "DS_MP_SHIM_APPS" }, + { 0x30000037, "HDRMC_APIS" }, + { 0x30000038, "SMD_BRIDGE_MTOA" }, + { 0x30000039, "SMD_BRIDGE_ATOM" }, + { 0x3000003a, "DPMAPP_OTG" }, + { 0x3000003b, "DIAG" }, + { 0x3000003c, "GSTK_EXP" }, + { 0x3000003d, "DSBC_MDM_APIS" }, + { 0x3000003e, "HDRMRLP_MDM_APIS" }, + { 0x3000003f, "HDRMRLP_APPS_APIS" }, + { 0x30000040, "HDRMC_MRLP_APIS" }, + { 0x30000041, "PDCOMM_APP_API" }, + { 0x30000042, "DSAT_APIS" }, + { 0x30000043, "MISC_RF_APIS" }, + { 0x30000044, "CMIPAPP" }, + { 0x30000045, "DSMP_UMTS_MODEM_APIS" }, + { 0x30000046, "DSMP_UMTS_APPS_APIS" }, + { 0x30000047, "DSUCSDMPSHIM" }, + { 0x30000048, "TIME_REMOTE_ATOM" }, + { 0x3000004a, "SD" }, + { 0x3000004b, "MMOC" }, + { 0x3000004c, "WLAN_ADP_FTM" }, + { 0x3000004d, "WLAN_CP_CM" }, + { 0x3000004e, "FTM_WLAN" }, + { 0x3000004f, "SDCC_CPRM" }, + { 0x30000050, "CPRMINTERFACE" }, + { 0x30000051, "DATA_ON_MODEM_MTOA_APIS" }, + { 0x30000052, "DATA_ON_APPS_ATOM_APIS" }, + { 0x30000053, "MISC_MODEM_APIS_NONWINMOB" }, + { 0x30000054, "MISC_APPS_APIS_NONWINMOB" }, + { 0x30000055, "PMEM_REMOTE" }, + { 0x30000056, "TCXOMGR" }, + { 0x30000057, "DSUCSDAPPIF_APIS" }, + { 0x30000058, "BT" }, + { 0x30000059, "PD_COMMS_API" }, + { 0x3000005a, "PD_COMMS_CLIENT_API" }, + { 0x3000005b, "PDAPI" }, + { 0x3000005c, "LSA_SUPL_DSM" }, + { 0x3000005d, "TIME_REMOTE_MTOA" }, + { 0x3000005e, "FTM_BT" }, + { 0X3000005f, "DSUCSDAPPIF_APIS" }, + { 0X30000060, "PMAPP_GEN" }, + { 0X30000061, "PM_LIB" }, + { 0X30000062, "KEYPAD" }, + { 0X30000063, "HSU_APP_APIS" }, + { 0X30000064, "HSU_MDM_APIS" }, + { 0X30000065, "ADIE_ADC_REMOTE_ATOM " }, + { 0X30000066, "TLMM_REMOTE_ATOM" }, + { 0X30000067, "UI_CALLCTRL" }, + { 0X30000068, "UIUTILS" }, + { 0X30000069, "PRL" }, + { 0X3000006a, "HW" }, + { 0X3000006b, "OEM_RAPI" }, + { 0X3000006c, "WMSPM" }, + { 0X3000006d, "BTPF" }, + { 0X3000006e, "CLKRGM_SYNC_EVENT" }, + { 0X3000006f, "USB_APPS_RPC" }, + { 0X30000070, "USB_MODEM_RPC" }, + { 0X30000071, "ADC" }, + { 0X30000072, "CAMERAREMOTED" }, + { 0X30000073, "SECAPIREMOTED" }, + { 0X30000074, "DSATAPI" }, + { 0X30000075, "CLKCTL_RPC" }, + { 0X30000076, "BREWAPPCOORD" }, + { 0X30000077, "ALTENVSHELL" }, + { 0X30000078, "WLAN_TRP_UTILS" }, + { 0X30000079, "GPIO_RPC" }, + { 0X3000007a, "PING_RPC" }, + { 0X3000007b, "DSC_DCM_API" }, + { 0X3000007c, "L1_DS" }, + { 0X3000007d, "QCHATPK_APIS" }, + { 0X3000007e, "GPS_API" }, + { 0X3000007f, "OSS_RRCASN_REMOTE" }, + { 0X30000080, "PMAPP_OTG_REMOTE" }, + { 0X30000081, "PING_MDM_RPC" }, + { 0X30000082, "PING_KERNEL_RPC" }, + { 0X30000083, "TIMETICK" }, + { 0X30000084, "WM_BTHCI_FTM " }, + { 0X30000085, "WM_BT_PF" }, + { 0X30000086, "IPA_IPC_APIS" }, + { 0X30000087, "UKCC_IPC_APIS" }, + { 0X30000088, "CMIPSMS " }, + { 0X30000089, "VBATT_REMOTE" }, + { 0X3000008a, "MFPAL" }, + { 0X3000008b, "DSUMTSPDPREG" }, + { 0X3000fe00, "RESTART_DAEMON NUMBER 0" }, + { 0X3000fe01, "RESTART_DAEMON NUMBER 1" }, + { 0X3000feff, "RESTART_DAEMON NUMBER 255" }, + { 0X3000fffe, "BACKWARDS_COMPATIBILITY_IN_RPC_CLNT_LOOKUP" }, + { 0X3000ffff, "RPC_ROUTER_SERVER_PROGRAM" }, +}; + +#define ONCRPC_SYM 0 + +static struct sym_tbl { + struct sym *data; + int size; + struct hlist_head hlist[HSIZE]; +} tbl[] = { + { oncrpc_syms, ARRAY_SIZE(oncrpc_syms) }, +}; + +#define hash(val) (val % HSIZE) + +static void init_syms(void) +{ + int i; + int j; + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < HSIZE; ++j) + INIT_HLIST_HEAD(&tbl[i].hlist[j]); + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < tbl[i].size; ++j) { + INIT_HLIST_NODE(&tbl[i].data[j].node); + hlist_add_head(&tbl[i].data[j].node, + &tbl[i].hlist[hash(tbl[i].data[j].val)]); + } +} + +static char *find_sym(uint32_t id, uint32_t val) +{ + struct hlist_node *n; + struct sym *s; + + hlist_for_each(n, &tbl[id].hlist[hash(val)]) { + s = hlist_entry(n, struct sym, node); + if (s->val == val) + return s->str; + } + + return 0; +} + +static int dump_servers(char *buf, int max) +{ + int i = 0; + unsigned long flags; + struct rr_server *svr; + char *sym; + + spin_lock_irqsave(&server_list_lock, flags); + list_for_each_entry(svr, &server_list, list) { + i += scnprintf(buf + i, max - i, "pdev_name: %s\n", + svr->pdev_name); + i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", svr->pid); + i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", svr->cid); + i += scnprintf(buf + i, max - i, "prog: 0x%08x", svr->prog); + sym = find_sym(ONCRPC_SYM, svr->prog); + if (sym) + i += scnprintf(buf + i, max - i, " (%s)\n", sym); + else + i += scnprintf(buf + i, max - i, "\n"); + i += scnprintf(buf + i, max - i, "vers: 0x%08x\n", svr->vers); + i += scnprintf(buf + i, max - i, "\n"); + } + spin_unlock_irqrestore(&server_list_lock, flags); + + return i; +} + +static int dump_remote_endpoints(char *buf, int max) +{ + int i = 0; + unsigned long flags; + struct rr_remote_endpoint *ept; + + spin_lock_irqsave(&remote_endpoints_lock, flags); + list_for_each_entry(ept, &remote_endpoints, list) { + i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid); + i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid); + i += scnprintf(buf + i, max - i, "tx_quota_cntr: %i\n", + ept->tx_quota_cntr); + i += scnprintf(buf + i, max - i, "quota_restart_state: %i\n", + ept->quota_restart_state); + i += scnprintf(buf + i, max - i, "\n"); + } + spin_unlock_irqrestore(&remote_endpoints_lock, flags); + + return i; +} + +static int dump_msm_rpc_endpoint(char *buf, int max) +{ + int i = 0; + unsigned long flags; + struct msm_rpc_reply *reply; + struct msm_rpc_endpoint *ept; + struct rr_packet *pkt; + char *sym; + + spin_lock_irqsave(&local_endpoints_lock, flags); + list_for_each_entry(ept, &local_endpoints, list) { + i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid); + i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid); + i += scnprintf(buf + i, max - i, "dst_pid: 0x%08x\n", + ept->dst_pid); + i += scnprintf(buf + i, max - i, "dst_cid: 0x%08x\n", + ept->dst_cid); + i += scnprintf(buf + i, max - i, "dst_prog: 0x%08x", + be32_to_cpu(ept->dst_prog)); + sym = find_sym(ONCRPC_SYM, be32_to_cpu(ept->dst_prog)); + if (sym) + i += scnprintf(buf + i, max - i, " (%s)\n", sym); + else + i += scnprintf(buf + i, max - i, "\n"); + i += scnprintf(buf + i, max - i, "dst_vers: 0x%08x\n", + be32_to_cpu(ept->dst_vers)); + i += scnprintf(buf + i, max - i, "reply_cnt: %i\n", + ept->reply_cnt); + i += scnprintf(buf + i, max - i, "restart_state: %i\n", + ept->restart_state); + + i += scnprintf(buf + i, max - i, "outstanding xids:\n"); + spin_lock(&ept->reply_q_lock); + list_for_each_entry(reply, &ept->reply_pend_q, list) + i += scnprintf(buf + i, max - i, " xid = %u\n", + ntohl(reply->xid)); + spin_unlock(&ept->reply_q_lock); + + i += scnprintf(buf + i, max - i, "complete unread packets:\n"); + spin_lock(&ept->read_q_lock); + list_for_each_entry(pkt, &ept->read_q, list) { + i += scnprintf(buf + i, max - i, " mid = %i\n", + pkt->mid); + i += scnprintf(buf + i, max - i, " length = %i\n", + pkt->length); + } + spin_unlock(&ept->read_q_lock); + i += scnprintf(buf + i, max - i, "\n"); + } + spin_unlock_irqrestore(&local_endpoints_lock, flags); + + return i; +} + +#define DEBUG_BUFMAX 4096 +static char debug_buffer[DEBUG_BUFMAX]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, DEBUG_BUFMAX); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +static void debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smd_rpcrouter", 0); + if (IS_ERR(dent)) + return; + + debug_create("dump_msm_rpc_endpoints", 0444, dent, + dump_msm_rpc_endpoint); + debug_create("dump_remote_endpoints", 0444, dent, + dump_remote_endpoints); + debug_create("dump_servers", 0444, dent, + dump_servers); + + init_syms(); +} + +#else +static void debugfs_init(void) {} +#endif + + +static int __init rpcrouter_init(void) +{ + int ret; + + ret = platform_driver_register(&msm_smd_channel2_driver); + if (ret) + return ret; + + debugfs_init(); + + return ret; +} + +module_init(rpcrouter_init); +MODULE_DESCRIPTION("MSM RPC Router"); +MODULE_AUTHOR("San Mehat <san@android.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/smd_rpcrouter.h b/arch/arm/mach-msm/smd_rpcrouter.h new file mode 100644 index 000000000000..fa5bc84267f7 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter.h @@ -0,0 +1,210 @@ +/** arch/arm/mach-msm/smd_rpcrouter.h + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H +#define _ARCH_ARM_MACH_MSM_SMD_RPCROUTER_H + +#include <linux/types.h> +#include <linux/list.h> +#include <linux/cdev.h> +#include <linux/platform_device.h> + +#include <mach/msm_smd.h> +#include <mach/msm_rpcrouter.h> + +/* definitions for the R2R wire protcol */ + +#define RPCROUTER_VERSION 1 +#define RPCROUTER_PROCESSORS_MAX 4 +#define RPCROUTER_MSGSIZE_MAX 512 +#define RPCROUTER_PEND_REPLIES_MAX 32 + +#define RPCROUTER_CLIENT_BCAST_ID 0xffffffff +#define RPCROUTER_ROUTER_ADDRESS 0xfffffffe + +#define RPCROUTER_PID_LOCAL 1 +#define RPCROUTER_PID_REMOTE 0 + +#define RPCROUTER_CTRL_CMD_DATA 1 +#define RPCROUTER_CTRL_CMD_HELLO 2 +#define RPCROUTER_CTRL_CMD_BYE 3 +#define RPCROUTER_CTRL_CMD_NEW_SERVER 4 +#define RPCROUTER_CTRL_CMD_REMOVE_SERVER 5 +#define RPCROUTER_CTRL_CMD_REMOVE_CLIENT 6 +#define RPCROUTER_CTRL_CMD_RESUME_TX 7 +#define RPCROUTER_CTRL_CMD_EXIT 8 +#define RPCROUTER_CTRL_CMD_PING 9 + +#define RPCROUTER_DEFAULT_RX_QUOTA 5 + +union rr_control_msg { + uint32_t cmd; + struct { + uint32_t cmd; + uint32_t prog; + uint32_t vers; + uint32_t pid; + uint32_t cid; + } srv; + struct { + uint32_t cmd; + uint32_t pid; + uint32_t cid; + } cli; +}; + +struct rr_header { + uint32_t version; + uint32_t type; + uint32_t src_pid; + uint32_t src_cid; + uint32_t confirm_rx; + uint32_t size; + uint32_t dst_pid; + uint32_t dst_cid; +}; + +/* internals */ + +#define RPCROUTER_MAX_REMOTE_SERVERS 100 + +struct rr_fragment { + unsigned char data[RPCROUTER_MSGSIZE_MAX]; + uint32_t length; + struct rr_fragment *next; +}; + +struct rr_packet { + struct list_head list; + struct rr_fragment *first; + struct rr_fragment *last; + struct rr_header hdr; + uint32_t mid; + uint32_t length; +}; + +#define PACMARK_LAST(n) ((n) & 0x80000000) +#define PACMARK_MID(n) (((n) >> 16) & 0xFF) +#define PACMARK_LEN(n) ((n) & 0xFFFF) + +static inline uint32_t PACMARK(uint32_t len, uint32_t mid, uint32_t first, + uint32_t last) +{ + return (len & 0xFFFF) | + ((mid & 0xFF) << 16) | + ((!!first) << 30) | + ((!!last) << 31); +} + +struct rr_server { + struct list_head list; + + uint32_t pid; + uint32_t cid; + uint32_t prog; + uint32_t vers; + + dev_t device_number; + struct cdev cdev; + struct device *device; + struct rpcsvr_platform_device p_device; + char pdev_name[32]; +}; + +struct rr_remote_endpoint { + uint32_t pid; + uint32_t cid; + + int tx_quota_cntr; + int quota_restart_state; + spinlock_t quota_lock; + wait_queue_head_t quota_wait; + + struct list_head list; +}; + +struct msm_rpc_reply { + struct list_head list; + uint32_t pid; + uint32_t cid; + uint32_t prog; /* be32 */ + uint32_t vers; /* be32 */ + uint32_t xid; /* be32 */ +}; + +struct msm_rpc_endpoint { + struct list_head list; + + /* incomplete packets waiting for assembly */ + struct list_head incomplete; + spinlock_t incomplete_lock; + + /* complete packets waiting to be read */ + struct list_head read_q; + spinlock_t read_q_lock; + wait_queue_head_t wait_q; + unsigned flags; + + /* restart handling */ + int restart_state; + spinlock_t restart_lock; + wait_queue_head_t restart_wait; + + /* endpoint address */ + uint32_t pid; + uint32_t cid; + + /* bound remote address + * if not connected (dst_pid == 0xffffffff) RPC_CALL writes fail + * RPC_CALLs must be to the prog/vers below or they will fail + */ + uint32_t dst_pid; + uint32_t dst_cid; + uint32_t dst_prog; /* be32 */ + uint32_t dst_vers; /* be32 */ + + /* reply queue for inbound messages */ + struct list_head reply_pend_q; + struct list_head reply_avail_q; + spinlock_t reply_q_lock; + uint32_t reply_cnt; + + /* device node if this endpoint is accessed via userspace */ + dev_t dev; +}; + +/* shared between smd_rpcrouter*.c */ + +int __msm_rpc_read(struct msm_rpc_endpoint *ept, + struct rr_fragment **frag, + unsigned len, long timeout); + +struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev); +int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept); + +int msm_rpcrouter_create_server_cdev(struct rr_server *server); +int msm_rpcrouter_create_server_pdev(struct rr_server *server); + +int msm_rpcrouter_init_devices(void); +void msm_rpcrouter_exit_devices(void); + +void get_requesting_client(struct msm_rpc_endpoint *ept, uint32_t xid, + struct msm_rpc_client_info *clnt_info); + +extern dev_t msm_rpcrouter_devno; +extern struct class *msm_rpcrouter_class; +#endif diff --git a/arch/arm/mach-msm/smd_rpcrouter_clients.c b/arch/arm/mach-msm/smd_rpcrouter_clients.c new file mode 100644 index 000000000000..5baf79a7ae4b --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_clients.c @@ -0,0 +1,607 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * SMD RPCROUTER CLIENTS module. + */ + +#include <linux/kernel.h> +#include <linux/kthread.h> + +#include <mach/msm_rpcrouter.h> +#include "smd_rpcrouter.h" + +struct msm_rpc_client_cb_item { + struct list_head list; + + void *buf; + int size; +}; + +struct msm_rpc_cb_table_item { + struct list_head list; + + uint32_t cb_id; + void *cb_func; +}; + +static int rpc_clients_cb_thread(void *data) +{ + struct msm_rpc_client_cb_item *cb_item; + struct msm_rpc_client *client; + struct rpc_request_hdr *req; + + client = data; + for (;;) { + wait_event(client->cb_wait, client->cb_avail); + if (client->exit_flag) + break; + + client->cb_avail = 0; + mutex_lock(&client->cb_item_list_lock); + while (!list_empty(&client->cb_item_list)) { + cb_item = list_first_entry( + &client->cb_item_list, + struct msm_rpc_client_cb_item, + list); + list_del(&cb_item->list); + mutex_unlock(&client->cb_item_list_lock); + req = (struct rpc_request_hdr *)cb_item->buf; + + if (be32_to_cpu(req->type) != 0) + goto bad_rpc; + if (be32_to_cpu(req->rpc_vers) != 2) + goto bad_rpc; + if (be32_to_cpu(req->prog) != + (client->prog | 0x01000000)) + goto bad_rpc; + + client->cb_func(client, + cb_item->buf, cb_item->size); + bad_rpc: + kfree(cb_item->buf); + kfree(cb_item); + mutex_lock(&client->cb_item_list_lock); + } + mutex_unlock(&client->cb_item_list_lock); + } + complete_and_exit(&client->cb_complete, 0); +} + +static int rpc_clients_thread(void *data) +{ + void *buffer; + uint32_t type; + struct msm_rpc_client *client; + int rc = 0; + struct msm_rpc_client_cb_item *cb_item; + struct rpc_request_hdr *req; + + client = data; + for (;;) { + rc = msm_rpc_read(client->ept, &buffer, -1, HZ); + if (client->exit_flag) + break; + if (rc < ((int)(sizeof(uint32_t) * 2))) + continue; + + type = be32_to_cpu(*((uint32_t *)buffer + 1)); + if (type == 1) { + client->buf = buffer; + client->read_avail = 1; + wake_up(&client->reply_wait); + } else if (type == 0) { + cb_item = kmalloc(sizeof(*cb_item), GFP_KERNEL); + if (!cb_item) { + pr_err("%s: no memory for cb item\n", + __func__); + continue; + } + + if (client->cb_thread == NULL) { + req = (struct rpc_request_hdr *)buffer; + + if ((be32_to_cpu(req->rpc_vers) == 2) && + (be32_to_cpu(req->prog) == + (client->prog | 0x01000000))) + client->cb_func(client, buffer, rc); + kfree(buffer); + } else { + INIT_LIST_HEAD(&cb_item->list); + cb_item->buf = buffer; + cb_item->size = rc; + mutex_lock(&client->cb_item_list_lock); + list_add_tail(&cb_item->list, + &client->cb_item_list); + mutex_unlock(&client->cb_item_list_lock); + client->cb_avail = 1; + wake_up(&client->cb_wait); + } + } + } + complete_and_exit(&client->complete, 0); +} + +static struct msm_rpc_client *msm_rpc_create_client(void) +{ + struct msm_rpc_client *client; + + client = kmalloc(sizeof(struct msm_rpc_client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->req = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!client->req) { + kfree(client); + return ERR_PTR(-ENOMEM); + } + + client->reply = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!client->reply) { + kfree(client->req); + kfree(client); + return ERR_PTR(-ENOMEM); + } + + init_waitqueue_head(&client->reply_wait); + mutex_init(&client->req_lock); + mutex_init(&client->reply_lock); + client->buf = NULL; + client->read_avail = 0; + client->cb_buf = NULL; + client->cb_size = 0; + client->exit_flag = 0; + init_completion(&client->complete); + init_completion(&client->cb_complete); + INIT_LIST_HEAD(&client->cb_item_list); + mutex_init(&client->cb_item_list_lock); + client->cb_avail = 0; + init_waitqueue_head(&client->cb_wait); + INIT_LIST_HEAD(&client->cb_list); + mutex_init(&client->cb_list_lock); + atomic_set(&client->next_cb_id, 1); + + return client; +} + +void msm_rpc_remove_all_cb_func(struct msm_rpc_client *client) +{ + struct msm_rpc_cb_table_item *cb_item, *tmp_cb_item; + + mutex_lock(&client->cb_list_lock); + list_for_each_entry_safe(cb_item, tmp_cb_item, + &client->cb_list, list) { + list_del(&cb_item->list); + kfree(cb_item); + } + mutex_unlock(&client->cb_list_lock); +} + +/* + * Interface to be used to register the client. + * + * name: string representing the client + * + * prog: program number of the client + * + * ver: version number of the client + * + * create_cb_thread: if set calls the callback function from a seprate thread + * which helps the client requests to be processed without + * getting loaded by callback handling. + * + * cb_func: function to be called if callback request is received. + * unmarshaling should be handled by the user in callback function + * + * Return Value: + * Pointer to initialized client data sturcture + * Or, the error code if registration fails. + * + */ +struct msm_rpc_client *msm_rpc_register_client( + const char *name, + uint32_t prog, uint32_t ver, + uint32_t create_cb_thread, + int (*cb_func)(struct msm_rpc_client *, void *, int)) +{ + struct msm_rpc_client *client; + struct msm_rpc_endpoint *ept; + int rc; + + client = msm_rpc_create_client(); + if (IS_ERR(client)) + return client; + + ept = msm_rpc_connect_compatible(prog, ver, MSM_RPC_UNINTERRUPTIBLE); + if (IS_ERR(ept)) { + kfree(client); + return (struct msm_rpc_client *)ept; + } + + client->prog = prog; + client->ver = ver; + client->ept = ept; + client->cb_func = cb_func; + + /* start the read thread */ + client->read_thread = kthread_run(rpc_clients_thread, client, + "k%sclntd", name); + if (IS_ERR(client->read_thread)) { + rc = PTR_ERR(client->read_thread); + msm_rpc_close(client->ept); + kfree(client); + return ERR_PTR(rc); + } + + if (!create_cb_thread || (cb_func == NULL)) { + client->cb_thread = NULL; + return client; + } + + /* start the callback thread */ + client->cb_thread = kthread_run(rpc_clients_cb_thread, client, + "k%sclntcbd", name); + if (IS_ERR(client->cb_thread)) { + rc = PTR_ERR(client->cb_thread); + client->exit_flag = 1; + wait_for_completion(&client->complete); + msm_rpc_close(client->ept); + kfree(client); + return ERR_PTR(rc); + } + + return client; +} +EXPORT_SYMBOL(msm_rpc_register_client); + +/* + * Interface to be used to unregister the client + * No client operations should be done once the unregister function + * is called. + * + * client: pointer to client data structure. + * + * Return Value: + * Always returns 0 (success). + */ +int msm_rpc_unregister_client(struct msm_rpc_client *client) +{ + pr_info("%s: stopping client...\n", __func__); + client->exit_flag = 1; + if (client->cb_thread) { + client->cb_avail = 1; + wake_up(&client->cb_wait); + wait_for_completion(&client->cb_complete); + } + + wait_for_completion(&client->complete); + + msm_rpc_close(client->ept); + msm_rpc_remove_all_cb_func(client); + kfree(client->req); + kfree(client->reply); + kfree(client); + return 0; +} +EXPORT_SYMBOL(msm_rpc_unregister_client); + +/* + * Interface to be used to send a client request. + * If the request takes any arguments or expects any return, the user + * should handle it in 'arg_func' and 'ret_func' respectively. + * Marshaling and Unmarshaling should be handled by the user in argument + * and return functions. + * + * client: pointer to client data sturcture + * + * proc: procedure being requested + * + * arg_func: argument function pointer. 'buf' is where arguments needs to + * be filled. 'data' is arg_data. + * + * ret_func: return function pointer. 'buf' is where returned data should + * be read from. 'data' is ret_data. + * + * arg_data: passed as an input parameter to argument function. + * + * ret_data: passed as an input parameter to return function. + * + * timeout: timeout for reply wait in jiffies. If negative timeout is + * specified a default timeout of 10s is used. + * + * Return Value: + * 0 on success, otherwise an error code is returned. + */ +int msm_rpc_client_req(struct msm_rpc_client *client, uint32_t proc, + int (*arg_func)(struct msm_rpc_client *client, + void *buf, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_client *client, + void *buf, void *data), + void *ret_data, long timeout) +{ + int size = 0; + struct rpc_reply_hdr *rpc_rsp; + int rc = 0; + + mutex_lock(&client->req_lock); + + msm_rpc_setup_req((struct rpc_request_hdr *)client->req, client->prog, + client->ver, proc); + size = sizeof(struct rpc_request_hdr); + + if (arg_func) { + rc = arg_func(client, (void *)((struct rpc_request_hdr *) + client->req + 1), arg_data); + if (rc < 0) + goto release_locks; + else + size += rc; + } + + rc = msm_rpc_write(client->ept, client->req, size); + if (rc < 0) { + pr_err("%s: couldn't send RPC request:%d\n", __func__, rc); + goto release_locks; + } else + rc = 0; + + if (timeout < 0) + timeout = msecs_to_jiffies(10000); + + rc = wait_event_timeout(client->reply_wait, + client->read_avail, timeout); + if (rc == 0) { + rc = -ETIMEDOUT; + goto release_locks; + } else + rc = 0; + + client->read_avail = 0; + + rpc_rsp = (struct rpc_reply_hdr *)client->buf; + if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("%s: RPC call was denied! %d\n", __func__, + be32_to_cpu(rpc_rsp->reply_stat)); + rc = -EPERM; + goto free_and_release; + } + + if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + pr_err("%s: RPC call was not successful (%d)\n", __func__, + be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); + rc = -EINVAL; + goto free_and_release; + } + + if (ret_func) + rc = ret_func(client, (void *)(rpc_rsp + 1), ret_data); + + free_and_release: + kfree(client->buf); + release_locks: + mutex_unlock(&client->req_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_client_req); + +/* + * Interface to be used to start accepted reply message required in + * callback handling. Returns the buffer pointer to attach any + * payload. Should call msm_rpc_send_accepted_reply to complete + * sending reply. Marshaling should be handled by user for the payload. + * + * client: pointer to client data structure + * + * xid: transaction id. Has to be same as the one in callback request. + * + * accept_status: acceptance status + * + * Return Value: + * pointer to buffer to attach the payload. + */ +void *msm_rpc_start_accepted_reply(struct msm_rpc_client *client, + uint32_t xid, uint32_t accept_status) +{ + struct rpc_reply_hdr *reply; + + mutex_lock(&client->reply_lock); + + reply = (struct rpc_reply_hdr *)client->reply; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + return reply + 1; +} +EXPORT_SYMBOL(msm_rpc_start_accepted_reply); + +/* + * Interface to be used to send accepted reply required in callback handling. + * msm_rpc_start_accepted_reply should have been called before. + * Marshaling should be handled by user for the payload. + * + * client: pointer to client data structure + * + * size: additional payload size + * + * Return Value: + * 0 on success, otherwise returns an error code. + */ +int msm_rpc_send_accepted_reply(struct msm_rpc_client *client, uint32_t size) +{ + int rc = 0; + + size += sizeof(struct rpc_reply_hdr); + rc = msm_rpc_write(client->ept, client->reply, size); + if (rc > 0) + rc = 0; + + mutex_unlock(&client->reply_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_send_accepted_reply); + +/* + * Interface to be used to add a callback function. + * If the call back function is already in client's 'cb_id - cb_func' + * table, then that cb_id is returned. otherwise, new entry + * is added to the above table and corresponding cb_id is returned. + * + * client: pointer to client data structure + * + * cb_func: callback function + * + * Return Value: + * callback ID on success, otherwise returns an error code. + * If cb_func is NULL, the callback Id returned is 0xffffffff. + * This tells the other processor that no callback is reqested. + */ +int msm_rpc_add_cb_func(struct msm_rpc_client *client, void *cb_func) +{ + struct msm_rpc_cb_table_item *cb_item; + + if (cb_func == NULL) + return MSM_RPC_CLIENT_NULL_CB_ID; + + mutex_lock(&client->cb_list_lock); + list_for_each_entry(cb_item, &client->cb_list, list) { + if (cb_item->cb_func == cb_func) { + mutex_unlock(&client->cb_list_lock); + return cb_item->cb_id; + } + } + mutex_unlock(&client->cb_list_lock); + + cb_item = kmalloc(sizeof(struct msm_rpc_cb_table_item), GFP_KERNEL); + if (!cb_item) + return -ENOMEM; + + INIT_LIST_HEAD(&cb_item->list); + cb_item->cb_id = atomic_add_return(1, &client->next_cb_id); + cb_item->cb_func = cb_func; + + mutex_lock(&client->cb_list_lock); + list_add_tail(&cb_item->list, &client->cb_list); + mutex_unlock(&client->cb_list_lock); + + return cb_item->cb_id; +} +EXPORT_SYMBOL(msm_rpc_add_cb_func); + +/* + * Interface to be used to get a callback function from a callback ID. + * If no entry is found, NULL is returned. + * + * client: pointer to client data structure + * + * cb_id: callback ID + * + * Return Value: + * callback function pointer if entry with given cb_id is found, + * otherwise returns NULL. + */ +void *msm_rpc_get_cb_func(struct msm_rpc_client *client, uint32_t cb_id) +{ + struct msm_rpc_cb_table_item *cb_item; + + mutex_lock(&client->cb_list_lock); + list_for_each_entry(cb_item, &client->cb_list, list) { + if (cb_item->cb_id == cb_id) { + mutex_unlock(&client->cb_list_lock); + return cb_item->cb_func; + } + } + mutex_unlock(&client->cb_list_lock); + return NULL; +} +EXPORT_SYMBOL(msm_rpc_get_cb_func); + +/* + * Interface to be used to remove a callback function. + * + * client: pointer to client data structure + * + * cb_func: callback function + * + */ +void msm_rpc_remove_cb_func(struct msm_rpc_client *client, void *cb_func) +{ + struct msm_rpc_cb_table_item *cb_item, *tmp_cb_item; + + if (cb_func == NULL) + return; + + mutex_lock(&client->cb_list_lock); + list_for_each_entry_safe(cb_item, tmp_cb_item, + &client->cb_list, list) { + if (cb_item->cb_func == cb_func) { + list_del(&cb_item->list); + kfree(cb_item); + mutex_unlock(&client->cb_list_lock); + return; + } + } + mutex_unlock(&client->cb_list_lock); +} +EXPORT_SYMBOL(msm_rpc_remove_cb_func); diff --git a/arch/arm/mach-msm/smd_rpcrouter_device.c b/arch/arm/mach-msm/smd_rpcrouter_device.c new file mode 100644 index 000000000000..b57746cbf392 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_device.c @@ -0,0 +1,380 @@ +/* arch/arm/mach-msm/smd_rpcrouter_device.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved. + * Author: San Mehat <san@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/cdev.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/poll.h> + +#include <asm/uaccess.h> +#include <asm/byteorder.h> + +#include "smd_rpcrouter.h" + +#define SAFETY_MEM_SIZE 65536 + +/* Next minor # available for a remote server */ +static int next_minor = 1; + +struct class *msm_rpcrouter_class; +dev_t msm_rpcrouter_devno; + +static struct cdev rpcrouter_cdev; +static struct device *rpcrouter_device; + +static int rpcrouter_open(struct inode *inode, struct file *filp) +{ + int rc; + struct msm_rpc_endpoint *ept; + + rc = nonseekable_open(inode, filp); + if (rc < 0) + return rc; + + ept = msm_rpcrouter_create_local_endpoint(inode->i_rdev); + if (!ept) + return -ENOMEM; + + filp->private_data = ept; + return 0; +} + +static int rpcrouter_release(struct inode *inode, struct file *filp) +{ + struct msm_rpc_endpoint *ept; + static unsigned int rpcrouter_release_cnt; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + + /* A user program with many files open when ends abruptly, + * will cause a flood of REMOVE_CLIENT messages to the + * remote processor. This will cause remote processors + * internal queue to overflow. Inserting a sleep here + * regularly is the effecient option. + */ + if (rpcrouter_release_cnt++ % 2) + msleep(1); + + return msm_rpcrouter_destroy_local_endpoint(ept); +} + +static ssize_t rpcrouter_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_rpc_endpoint *ept; + struct rr_fragment *frag, *next; + int rc; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + + rc = __msm_rpc_read(ept, &frag, count, -1); + if (rc < 0) + return rc; + + count = rc; + + while (frag != NULL) { + if (copy_to_user(buf, frag->data, frag->length)) { + printk(KERN_ERR + "rpcrouter: could not copy all read data to user!\n"); + rc = -EFAULT; + } + buf += frag->length; + next = frag->next; + kfree(frag); + frag = next; + } + + return rc; +} + +static ssize_t rpcrouter_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_rpc_endpoint *ept; + int rc = 0; + void *k_buffer; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + + /* A check for safety, this seems non-standard */ + if (count > SAFETY_MEM_SIZE) + return -EINVAL; + + k_buffer = kmalloc(count, GFP_KERNEL); + if (!k_buffer) + return -ENOMEM; + + if (copy_from_user(k_buffer, buf, count)) { + rc = -EFAULT; + goto write_out_free; + } + + rc = msm_rpc_write(ept, k_buffer, count); + if (rc < 0) + goto write_out_free; + + rc = count; +write_out_free: + kfree(k_buffer); + return rc; +} + +static unsigned int rpcrouter_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct msm_rpc_endpoint *ept; + unsigned mask = 0; + ept = (struct msm_rpc_endpoint *) filp->private_data; + + /* If there's data already in the read queue, return POLLIN. + * Else, wait for the requested amount of time, and check again. + */ + + if (!list_empty(&ept->read_q)) + mask |= POLLIN; + if (ept->restart_state != 0) + mask |= POLLERR; + + if (!mask) { + poll_wait(filp, &ept->wait_q, wait); + if (!list_empty(&ept->read_q)) + mask |= POLLIN; + if (ept->restart_state != 0) + mask |= POLLERR; + } + + return mask; +} + +static long rpcrouter_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct msm_rpc_endpoint *ept; + struct rpcrouter_ioctl_server_args server_args; + int rc = 0; + uint32_t n; + + ept = (struct msm_rpc_endpoint *) filp->private_data; + switch (cmd) { + + case RPC_ROUTER_IOCTL_GET_VERSION: + n = RPC_ROUTER_VERSION_V1; + rc = put_user(n, (unsigned int *) arg); + break; + + case RPC_ROUTER_IOCTL_GET_MTU: + /* the pacmark word reduces the actual payload + * possible per message + */ + n = RPCROUTER_MSGSIZE_MAX - sizeof(uint32_t); + rc = put_user(n, (unsigned int *) arg); + break; + + case RPC_ROUTER_IOCTL_REGISTER_SERVER: + rc = copy_from_user(&server_args, (void *) arg, + sizeof(server_args)); + if (rc < 0) + break; + msm_rpc_register_server(ept, + server_args.prog, + server_args.vers); + break; + + case RPC_ROUTER_IOCTL_UNREGISTER_SERVER: + rc = copy_from_user(&server_args, (void *) arg, + sizeof(server_args)); + if (rc < 0) + break; + + msm_rpc_unregister_server(ept, + server_args.prog, + server_args.vers); + break; + + case RPC_ROUTER_IOCTL_CLEAR_NETRESET: + msm_rpc_clear_netreset(ept); + break; + + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static struct file_operations rpcrouter_server_fops = { + .owner = THIS_MODULE, + .open = rpcrouter_open, + .release = rpcrouter_release, + .read = rpcrouter_read, + .write = rpcrouter_write, + .poll = rpcrouter_poll, + .unlocked_ioctl = rpcrouter_ioctl, +}; + +static struct file_operations rpcrouter_router_fops = { + .owner = THIS_MODULE, + .open = rpcrouter_open, + .release = rpcrouter_release, + .read = rpcrouter_read, + .write = rpcrouter_write, + .poll = rpcrouter_poll, + .unlocked_ioctl = rpcrouter_ioctl, +}; + +int msm_rpcrouter_create_server_cdev(struct rr_server *server) +{ + int rc; + uint32_t dev_vers; + + if (next_minor == RPCROUTER_MAX_REMOTE_SERVERS) { + printk(KERN_ERR + "rpcrouter: Minor numbers exhausted - Increase " + "RPCROUTER_MAX_REMOTE_SERVERS\n"); + return -ENOBUFS; + } + + /* Servers with bit 31 set are remote msm servers with hashkey version. + * Servers with bit 31 not set are remote msm servers with + * backwards compatible version type in which case the minor number + * (lower 16 bits) is set to zero. + * + */ + if ((server->vers & 0x80000000)) + dev_vers = server->vers; + else + dev_vers = server->vers & 0xffff0000; + + server->device_number = + MKDEV(MAJOR(msm_rpcrouter_devno), next_minor++); + + server->device = + device_create(msm_rpcrouter_class, rpcrouter_device, + server->device_number, NULL, "%.8x:%.8x", + server->prog, dev_vers); + if (IS_ERR(server->device)) { + printk(KERN_ERR + "rpcrouter: Unable to create device (%ld)\n", + PTR_ERR(server->device)); + return PTR_ERR(server->device);; + } + + cdev_init(&server->cdev, &rpcrouter_server_fops); + server->cdev.owner = THIS_MODULE; + + rc = cdev_add(&server->cdev, server->device_number, 1); + if (rc < 0) { + printk(KERN_ERR + "rpcrouter: Unable to add chrdev (%d)\n", rc); + device_destroy(msm_rpcrouter_class, server->device_number); + return rc; + } + return 0; +} + +/* for backward compatible version type (31st bit cleared) + * clearing minor number (lower 16 bits) in device name + * is neccessary for driver binding + */ +int msm_rpcrouter_create_server_pdev(struct rr_server *server) +{ + sprintf(server->pdev_name, "rs%.8x:%.8x", + server->prog, + (server->vers & RPC_VERSION_MODE_MASK) ? server->vers : + (server->vers & RPC_VERSION_MAJOR_MASK)); + + server->p_device.base.id = -1; + server->p_device.base.name = server->pdev_name; + + server->p_device.prog = server->prog; + server->p_device.vers = server->vers; + + platform_device_register(&server->p_device.base); + return 0; +} + +int msm_rpcrouter_init_devices(void) +{ + int rc; + int major; + + /* Create the device nodes */ + msm_rpcrouter_class = class_create(THIS_MODULE, "oncrpc"); + if (IS_ERR(msm_rpcrouter_class)) { + rc = -ENOMEM; + printk(KERN_ERR + "rpcrouter: failed to create oncrpc class\n"); + goto fail; + } + + rc = alloc_chrdev_region(&msm_rpcrouter_devno, 0, + RPCROUTER_MAX_REMOTE_SERVERS + 1, + "oncrpc"); + if (rc < 0) { + printk(KERN_ERR + "rpcrouter: Failed to alloc chardev region (%d)\n", rc); + goto fail_destroy_class; + } + + major = MAJOR(msm_rpcrouter_devno); + rpcrouter_device = device_create(msm_rpcrouter_class, NULL, + msm_rpcrouter_devno, NULL, "%.8x:%d", + 0, 0); + if (IS_ERR(rpcrouter_device)) { + rc = -ENOMEM; + goto fail_unregister_cdev_region; + } + + cdev_init(&rpcrouter_cdev, &rpcrouter_router_fops); + rpcrouter_cdev.owner = THIS_MODULE; + + rc = cdev_add(&rpcrouter_cdev, msm_rpcrouter_devno, 1); + if (rc < 0) + goto fail_destroy_device; + + return 0; + +fail_destroy_device: + device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); +fail_unregister_cdev_region: + unregister_chrdev_region(msm_rpcrouter_devno, + RPCROUTER_MAX_REMOTE_SERVERS + 1); +fail_destroy_class: + class_destroy(msm_rpcrouter_class); +fail: + return rc; +} + +void msm_rpcrouter_exit_devices(void) +{ + cdev_del(&rpcrouter_cdev); + device_destroy(msm_rpcrouter_class, msm_rpcrouter_devno); + unregister_chrdev_region(msm_rpcrouter_devno, + RPCROUTER_MAX_REMOTE_SERVERS + 1); + class_destroy(msm_rpcrouter_class); +} + diff --git a/arch/arm/mach-msm/smd_rpcrouter_servers.c b/arch/arm/mach-msm/smd_rpcrouter_servers.c new file mode 100644 index 000000000000..eb74f50c79b4 --- /dev/null +++ b/arch/arm/mach-msm/smd_rpcrouter_servers.c @@ -0,0 +1,434 @@ +/* arch/arm/mach-msm/rpc_servers.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Iliyan Malchev <ibm@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/cdev.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <linux/uaccess.h> + +#include <mach/msm_rpcrouter.h> +#include "smd_rpcrouter.h" + +static struct msm_rpc_endpoint *endpoint; + +#define FLAG_REGISTERED 0x0001 + +static LIST_HEAD(rpc_server_list); +static DEFINE_MUTEX(rpc_server_list_lock); +static int rpc_servers_active; +static uint32_t current_xid; + +static void rpc_server_register(struct msm_rpc_server *server) +{ + int rc; + rc = msm_rpc_register_server(endpoint, server->prog, server->vers); + if (rc < 0) + printk(KERN_ERR "[rpcserver] error registering %p @ %08x:%d\n", + server, server->prog, server->vers); +} + +static struct msm_rpc_server *rpc_server_find(uint32_t prog, uint32_t vers) +{ + struct msm_rpc_server *server; + + mutex_lock(&rpc_server_list_lock); + list_for_each_entry(server, &rpc_server_list, list) { + if ((server->prog == prog) && + msm_rpc_is_compatible_version(server->vers, vers)) { + mutex_unlock(&rpc_server_list_lock); + return server; + } + } + mutex_unlock(&rpc_server_list_lock); + return NULL; +} + +static void rpc_server_register_all(void) +{ + struct msm_rpc_server *server; + + mutex_lock(&rpc_server_list_lock); + list_for_each_entry(server, &rpc_server_list, list) { + if (!(server->flags & FLAG_REGISTERED)) { + rpc_server_register(server); + server->flags |= FLAG_REGISTERED; + } + } + mutex_unlock(&rpc_server_list_lock); +} + +int msm_rpc_create_server(struct msm_rpc_server *server) +{ + /* make sure we're in a sane state first */ + server->flags = 0; + INIT_LIST_HEAD(&server->list); + mutex_init(&server->cb_req_lock); + mutex_init(&server->reply_lock); + + server->reply = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!server->reply) + return -ENOMEM; + + server->cb_req = kmalloc(MSM_RPC_MSGSIZE_MAX, GFP_KERNEL); + if (!server->cb_req) { + kfree(server->reply); + return -ENOMEM; + } + + server->cb_ept = msm_rpc_open(); + if (IS_ERR(server->cb_ept)) { + kfree(server->reply); + kfree(server->cb_req); + return PTR_ERR(server->cb_ept); + } + + server->cb_ept->flags = MSM_RPC_UNINTERRUPTIBLE; + server->cb_ept->dst_prog = cpu_to_be32(server->prog | 0x01000000); + server->cb_ept->dst_vers = cpu_to_be32(server->vers); + + mutex_lock(&rpc_server_list_lock); + list_add(&server->list, &rpc_server_list); + if (rpc_servers_active) { + rpc_server_register(server); + server->flags |= FLAG_REGISTERED; + } + mutex_unlock(&rpc_server_list_lock); + + return 0; +} +EXPORT_SYMBOL(msm_rpc_create_server); + +static int rpc_send_accepted_void_reply(struct msm_rpc_endpoint *client, + uint32_t xid, uint32_t accept_status) +{ + int rc = 0; + uint8_t reply_buf[sizeof(struct rpc_reply_hdr)]; + struct rpc_reply_hdr *reply = (struct rpc_reply_hdr *)reply_buf; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + rc = msm_rpc_write(client, reply_buf, sizeof(reply_buf)); + if (rc == -ENETRESET) { + /* Modem restarted, drop reply, clear state */ + msm_rpc_clear_netreset(client); + } + if (rc < 0) + printk(KERN_ERR + "%s: could not write response: %d\n", + __FUNCTION__, rc); + + return rc; +} + +/* + * Interface to be used to start accepted reply message for a + * request. Returns the buffer pointer to attach any payload. + * Should call msm_rpc_server_send_accepted_reply to complete sending + * reply. Marshaling should be handled by user for the payload. + * + * server: pointer to server data structure + * + * xid: transaction id. Has to be same as the one in request. + * + * accept_status: acceptance status + * + * Return Value: + * pointer to buffer to attach the payload. + */ +void *msm_rpc_server_start_accepted_reply(struct msm_rpc_server *server, + uint32_t xid, uint32_t accept_status) +{ + struct rpc_reply_hdr *reply; + + mutex_lock(&server->reply_lock); + + reply = (struct rpc_reply_hdr *)server->reply; + + reply->xid = cpu_to_be32(xid); + reply->type = cpu_to_be32(1); /* reply */ + reply->reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED); + + reply->data.acc_hdr.accept_stat = cpu_to_be32(accept_status); + reply->data.acc_hdr.verf_flavor = 0; + reply->data.acc_hdr.verf_length = 0; + + return reply + 1; +} +EXPORT_SYMBOL(msm_rpc_server_start_accepted_reply); + +/* + * Interface to be used to send accepted reply for a request. + * msm_rpc_server_start_accepted_reply should have been called before. + * Marshaling should be handled by user for the payload. + * + * server: pointer to server data structure + * + * size: additional payload size + * + * Return Value: + * 0 on success, otherwise returns an error code. + */ +int msm_rpc_server_send_accepted_reply(struct msm_rpc_server *server, + uint32_t size) +{ + int rc = 0; + + size += sizeof(struct rpc_reply_hdr); + rc = msm_rpc_write(endpoint, server->reply, size); + if (rc > 0) + rc = 0; + + mutex_unlock(&server->reply_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_server_send_accepted_reply); + +/* + * Interface to be used to send a server callback request. + * If the request takes any arguments or expects any return, the user + * should handle it in 'arg_func' and 'ret_func' respectively. + * Marshaling and Unmarshaling should be handled by the user in argument + * and return functions. + * + * server: pointer to server data sturcture + * + * clnt_info: pointer to client information data structure. + * callback will be sent to this client. + * + * cb_proc: callback procedure being requested + * + * arg_func: argument function pointer. 'buf' is where arguments needs to + * be filled. 'data' is arg_data. + * + * ret_func: return function pointer. 'buf' is where returned data should + * be read from. 'data' is ret_data. + * + * arg_data: passed as an input parameter to argument function. + * + * ret_data: passed as an input parameter to return function. + * + * timeout: timeout for reply wait in jiffies. If negative timeout is + * specified a default timeout of 10s is used. + * + * Return Value: + * 0 on success, otherwise an error code is returned. + */ +int msm_rpc_server_cb_req(struct msm_rpc_server *server, + struct msm_rpc_client_info *clnt_info, + uint32_t cb_proc, + int (*arg_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *arg_data, + int (*ret_func)(struct msm_rpc_server *server, + void *buf, void *data), + void *ret_data, long timeout) +{ + int size = 0; + struct rpc_reply_hdr *rpc_rsp; + void *buffer; + int rc = 0; + + if (!clnt_info) + return -EINVAL; + + mutex_lock(&server->cb_req_lock); + + msm_rpc_setup_req((struct rpc_request_hdr *)server->cb_req, + (server->prog | 0x01000000), + be32_to_cpu(clnt_info->vers), cb_proc); + size = sizeof(struct rpc_request_hdr); + + if (arg_func) { + rc = arg_func(server, (void *)((struct rpc_request_hdr *) + server->cb_req + 1), arg_data); + if (rc < 0) + goto release_locks; + else + size += rc; + } + + server->cb_ept->dst_pid = clnt_info->pid; + server->cb_ept->dst_cid = clnt_info->cid; + rc = msm_rpc_write(server->cb_ept, server->cb_req, size); + if (rc < 0) { + pr_err("%s: couldn't send RPC CB request:%d\n", __func__, rc); + goto release_locks; + } else + rc = 0; + + if (timeout < 0) + timeout = msecs_to_jiffies(10000); + + rc = msm_rpc_read(server->cb_ept, &buffer, -1, timeout); + if ((rc < ((int)(sizeof(uint32_t) * 2))) || + (be32_to_cpu(*((uint32_t *)buffer + 1)) != 1)) { + printk(KERN_ERR "%s: could not read: %d\n", __func__, rc); + kfree(buffer); + goto free_and_release; + } else + rc = 0; + + rpc_rsp = (struct rpc_reply_hdr *)buffer; + + if (be32_to_cpu(rpc_rsp->reply_stat) != RPCMSG_REPLYSTAT_ACCEPTED) { + pr_err("%s: RPC cb req was denied! %d\n", __func__, + be32_to_cpu(rpc_rsp->reply_stat)); + rc = -EPERM; + goto free_and_release; + } + + if (be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat) != + RPC_ACCEPTSTAT_SUCCESS) { + pr_err("%s: RPC cb req was not successful (%d)\n", __func__, + be32_to_cpu(rpc_rsp->data.acc_hdr.accept_stat)); + rc = -EINVAL; + goto free_and_release; + } + + if (ret_func) + rc = ret_func(server, (void *)(rpc_rsp + 1), ret_data); + +free_and_release: + kfree(buffer); +release_locks: + mutex_unlock(&server->cb_req_lock); + return rc; +} +EXPORT_SYMBOL(msm_rpc_server_cb_req); + +void msm_rpc_server_get_requesting_client(struct msm_rpc_client_info *clnt_info) +{ + if (!clnt_info) + return; + + get_requesting_client(endpoint, current_xid, clnt_info); +} + +static int rpc_servers_thread(void *data) +{ + void *buffer; + struct rpc_request_hdr *req; + struct msm_rpc_server *server; + int rc; + + for (;;) { + rc = wait_event_interruptible(endpoint->wait_q, + !list_empty(&endpoint->read_q)); + rc = msm_rpc_read(endpoint, &buffer, -1, -1); + if (rc < 0) { + printk(KERN_ERR "%s: could not read: %d\n", + __FUNCTION__, rc); + break; + } + + req = (struct rpc_request_hdr *)buffer; + + current_xid = req->xid; + + req->type = be32_to_cpu(req->type); + req->xid = be32_to_cpu(req->xid); + req->rpc_vers = be32_to_cpu(req->rpc_vers); + req->prog = be32_to_cpu(req->prog); + req->vers = be32_to_cpu(req->vers); + req->procedure = be32_to_cpu(req->procedure); + + server = rpc_server_find(req->prog, req->vers); + + if (req->rpc_vers != 2) + continue; + if (req->type != 0) + continue; + if (!server) { + rpc_send_accepted_void_reply( + endpoint, req->xid, + RPC_ACCEPTSTAT_PROG_UNAVAIL); + continue; + } + + rc = server->rpc_call(server, req, rc); + + if (rc == 0) { + msm_rpc_server_start_accepted_reply( + server, req->xid, + RPC_ACCEPTSTAT_SUCCESS); + msm_rpc_server_send_accepted_reply(server, 0); + } else if (rc < 0) { + msm_rpc_server_start_accepted_reply( + server, req->xid, + RPC_ACCEPTSTAT_PROC_UNAVAIL); + msm_rpc_server_send_accepted_reply(server, 0); + } + kfree(buffer); + } + do_exit(0); +} + +static int rpcservers_probe(struct platform_device *pdev) +{ + struct task_struct *server_thread; + + endpoint = msm_rpc_open(); + if (IS_ERR(endpoint)) + return PTR_ERR(endpoint); + + /* we're online -- register any servers installed beforehand */ + rpc_servers_active = 1; + current_xid = 0; + rpc_server_register_all(); + + /* start the kernel thread */ + server_thread = kthread_run(rpc_servers_thread, NULL, "krpcserversd"); + if (IS_ERR(server_thread)) + return PTR_ERR(server_thread); + + return 0; +} + +static struct platform_driver rpcservers_driver = { + .probe = rpcservers_probe, + .driver = { + .name = "oncrpc_router", + .owner = THIS_MODULE, + }, +}; + +static int __init rpc_servers_init(void) +{ + return platform_driver_register(&rpcservers_driver); +} + +module_init(rpc_servers_init); + +MODULE_DESCRIPTION("MSM RPC Servers"); +MODULE_AUTHOR("Iliyan Malchev <ibm@android.com>"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c new file mode 100644 index 000000000000..e39b0487c319 --- /dev/null +++ b/arch/arm/mach-msm/smd_tty.c @@ -0,0 +1,285 @@ +/* arch/arm/mach-msm/smd_tty.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * Author: Brian Swetland <swetland@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/wait.h> +#include <linux/delay.h> + +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> + +#include <mach/msm_smd.h> +#include "smd_private.h" + +#define MAX_SMD_TTYS 37 + +static DEFINE_MUTEX(smd_tty_lock); + +struct smd_tty_info { + smd_channel_t *ch; + struct tty_struct *tty; + int open_count; + struct work_struct tty_work; +}; + +static struct smd_tty_info smd_tty[MAX_SMD_TTYS]; +static struct workqueue_struct *smd_tty_wq; + +static void smd_tty_work_func(struct work_struct *work) +{ + unsigned char *ptr; + int avail; + struct smd_tty_info *info = container_of(work, + struct smd_tty_info, + tty_work); + struct tty_struct *tty = info->tty; + + if (!tty) + return; + + for (;;) { + if (test_bit(TTY_THROTTLED, &tty->flags)) break; + + mutex_lock(&smd_tty_lock); + if (info->ch == 0) { + mutex_unlock(&smd_tty_lock); + break; + } + + avail = smd_read_avail(info->ch); + if (avail == 0) { + mutex_unlock(&smd_tty_lock); + break; + } + + avail = tty_prepare_flip_string(tty, &ptr, avail); + + if (smd_read(info->ch, ptr, avail) != avail) { + /* shouldn't be possible since we're in interrupt + ** context here and nobody else could 'steal' our + ** characters. + */ + printk(KERN_ERR "OOPS - smd_tty_buffer mismatch?!"); + } + mutex_unlock(&smd_tty_lock); + + tty_flip_buffer_push(tty); + } + + /* XXX only when writable and necessary */ + tty_wakeup(tty); +} + +static void smd_tty_notify(void *priv, unsigned event) +{ + struct smd_tty_info *info = priv; + + if (event != SMD_EVENT_DATA) + return; + + queue_work(smd_tty_wq, &info->tty_work); +} + +static int smd_tty_open(struct tty_struct *tty, struct file *f) +{ + int res = 0; + int n = tty->index; + struct smd_tty_info *info; + const char *name; + + if (n == 0) + name = "DS"; + else if (n == 7) + name = "DATA1"; + else if (n == 21) + name = "DATA21"; + else if (n == 27) + name = "GPSNMEA"; + else if (n == 36) + name = "LOOPBACK"; + else + return -ENODEV; + + info = smd_tty + n; + + mutex_lock(&smd_tty_lock); + + tty->driver_data = info; + + if (info->open_count++ == 0) { + info->tty = tty; + if (!info->ch) { + if (n == 36) { + /* set smsm state to SMSM_SMD_LOOPBACK state + ** and wait allowing enough time for Modem side + ** to open the loopback port (Currently, this is + ** this is effecient than polling). + */ + smsm_change_state(SMSM_APPS_STATE, + 0, SMSM_SMD_LOOPBACK); + msleep(100); + } else if ((n == 0) || (n == 7)) + tty->low_latency = 1; + + res = smd_open(name, &info->ch, info, + smd_tty_notify); + } + } + mutex_unlock(&smd_tty_lock); + + return res; +} + +static void smd_tty_close(struct tty_struct *tty, struct file *f) +{ + struct smd_tty_info *info = tty->driver_data; + + if (info == 0) + return; + + mutex_lock(&smd_tty_lock); + if (--info->open_count == 0) { + info->tty = 0; + tty->driver_data = 0; + if (info->ch) { + smd_close(info->ch); + info->ch = 0; + } + } + mutex_unlock(&smd_tty_lock); +} + +static int smd_tty_write(struct tty_struct *tty, const unsigned char *buf, int len) +{ + struct smd_tty_info *info = tty->driver_data; + int avail; + + /* if we're writing to a packet channel we will + ** never be able to write more data than there + ** is currently space for + */ + avail = smd_write_avail(info->ch); + if (len > avail) + len = avail; + + return smd_write(info->ch, buf, len); +} + +static int smd_tty_write_room(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + return smd_write_avail(info->ch); +} + +static int smd_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + return smd_read_avail(info->ch); +} + +static void smd_tty_unthrottle(struct tty_struct *tty) +{ + struct smd_tty_info *info = tty->driver_data; + queue_work(smd_tty_wq, &info->tty_work); + return; +} + +static int smd_tty_tiocmget(struct tty_struct *tty, struct file *file) +{ + struct smd_tty_info *info = tty->driver_data; + + return smd_tiocmget(info->ch); +} + +static int smd_tty_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear) +{ + struct smd_tty_info *info = tty->driver_data; + + return smd_tiocmset(info->ch, set, clear); +} + +static struct tty_operations smd_tty_ops = { + .open = smd_tty_open, + .close = smd_tty_close, + .write = smd_tty_write, + .write_room = smd_tty_write_room, + .chars_in_buffer = smd_tty_chars_in_buffer, + .unthrottle = smd_tty_unthrottle, + .tiocmget = smd_tty_tiocmget, + .tiocmset = smd_tty_tiocmset, +}; + +static struct tty_driver *smd_tty_driver; + +static int __init smd_tty_init(void) +{ + int ret; + + smd_tty_wq = create_singlethread_workqueue("smd_tty"); + if (smd_tty_wq == 0) + return -ENOMEM; + + smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS); + if (smd_tty_driver == 0) { + destroy_workqueue(smd_tty_wq); + return -ENOMEM; + } + + smd_tty_driver->owner = THIS_MODULE; + smd_tty_driver->driver_name = "smd_tty_driver"; + smd_tty_driver->name = "smd"; + smd_tty_driver->major = 0; + smd_tty_driver->minor_start = 0; + smd_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; + smd_tty_driver->subtype = SERIAL_TYPE_NORMAL; + smd_tty_driver->init_termios = tty_std_termios; + smd_tty_driver->init_termios.c_iflag = 0; + smd_tty_driver->init_termios.c_oflag = 0; + smd_tty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD; + smd_tty_driver->init_termios.c_lflag = 0; + smd_tty_driver->flags = TTY_DRIVER_RESET_TERMIOS | + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_set_operations(smd_tty_driver, &smd_tty_ops); + + ret = tty_register_driver(smd_tty_driver); + if (ret) return ret; + + /* this should be dynamic */ + tty_register_device(smd_tty_driver, 0, 0); + INIT_WORK(&smd_tty[0].tty_work, smd_tty_work_func); + + tty_register_device(smd_tty_driver, 7, 0); + INIT_WORK(&smd_tty[7].tty_work, smd_tty_work_func); + + tty_register_device(smd_tty_driver, 27, 0); + INIT_WORK(&smd_tty[27].tty_work, smd_tty_work_func); + + tty_register_device(smd_tty_driver, 36, 0); + INIT_WORK(&smd_tty[36].tty_work, smd_tty_work_func); + + tty_register_device(smd_tty_driver, 21, 0); + INIT_WORK(&smd_tty[21].tty_work, smd_tty_work_func); + + return 0; +} + +module_init(smd_tty_init); diff --git a/arch/arm/mach-msm/smem_log.c b/arch/arm/mach-msm/smem_log.c new file mode 100644 index 000000000000..33c81e234726 --- /dev/null +++ b/arch/arm/mach-msm/smem_log.c @@ -0,0 +1,2024 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * Shared memory logging implementation. + */ + +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/jiffies.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/string.h> + +#include <mach/remote_spinlock.h> +#include <mach/msm_iomap.h> +#include <mach/smem_log.h> + +#include "smd_private.h" + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define D_DUMP_BUFFER(prestr, cnt, buf) \ +do { \ + int i; \ + printk(KERN_ERR "%s", prestr); \ + for (i = 0; i < cnt; i++) \ + printk(KERN_ERR "%.2x", buf[i]); \ + printk(KERN_ERR "\n"); \ +} while (0) +#else +#define D_DUMP_BUFFER(prestr, cnt, buf) +#endif + +#ifdef DEBUG +#define D(x...) printk(x) +#else +#define D(x...) do {} while (0) +#endif + +#define TIMESTAMP_ADDR (MSM_CSR_BASE + 0x04) + +struct smem_log_item { + uint32_t identifier; + uint32_t timetick; + uint32_t data1; + uint32_t data2; + uint32_t data3; +}; + +#define SMEM_LOG_NUM_ENTRIES 2000 +#define SMEM_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_ENTRIES) + +#define SMEM_LOG_NUM_STATIC_ENTRIES 150 +#define SMEM_STATIC_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_STATIC_ENTRIES) + +#define SMEM_LOG_NUM_POWER_ENTRIES 2000 +#define SMEM_POWER_LOG_EVENTS_SIZE (sizeof(struct smem_log_item) * \ + SMEM_LOG_NUM_POWER_ENTRIES) + +#define SMEM_SPINLOCK_SMEM_LOG 2 +#define SMEM_SPINLOCK_STATIC_LOG 5 +/* POWER shares with SMEM_SPINLOCK_SMEM_LOG */ + +static remote_spinlock_t remote_spinlock; +static remote_spinlock_t remote_spinlock_static; + +struct smem_log_inst { + int which_log; + struct smem_log_item __iomem *events; + uint32_t __iomem *idx; + int num; + remote_spinlock_t *remote_spinlock; +}; + +enum smem_logs { + GEN = 0, + STA, + POW, + NUM +}; + +static struct smem_log_inst inst[NUM]; + +#if defined(CONFIG_DEBUG_FS) + +#define HSIZE 13 + +struct sym { + uint32_t val; + char *str; + struct hlist_node node; +}; + +struct sym id_syms[] = { + { SMEM_LOG_PROC_ID_MODEM, "MODM" }, + { SMEM_LOG_PROC_ID_Q6, "QDSP" }, + { SMEM_LOG_PROC_ID_APPS, "APPS" }, +}; + +struct sym base_syms[] = { + { SMEM_LOG_ONCRPC_EVENT_BASE, "ONCRPC" }, + { SMEM_LOG_SMEM_EVENT_BASE, "SMEM" }, + { SMEM_LOG_TMC_EVENT_BASE, "TMC" }, + { SMEM_LOG_TIMETICK_EVENT_BASE, "TIMETICK" }, + { SMEM_LOG_DEM_EVENT_BASE, "DEM" }, + { SMEM_LOG_ERROR_EVENT_BASE, "ERROR" }, + { SMEM_LOG_DCVS_EVENT_BASE, "DCVS" }, + { SMEM_LOG_SLEEP_EVENT_BASE, "SLEEP" }, + { SMEM_LOG_RPC_ROUTER_EVENT_BASE, "ROUTER" }, +}; + +struct sym event_syms[] = { +#if defined(CONFIG_MSM_N_WAY_SMSM) + { DEM_SMSM_ISR, "SMSM_ISR" }, + { DEM_STATE_CHANGE, "STATE_CHANGE" }, + { DEM_STATE_MACHINE_ENTER, "STATE_MACHINE_ENTER" }, + { DEM_ENTER_SLEEP, "ENTER_SLEEP" }, + { DEM_END_SLEEP, "END_SLEEP" }, + { DEM_SETUP_SLEEP, "SETUP_SLEEP" }, + { DEM_SETUP_POWER_COLLAPSE, "SETUP_POWER_COLLAPSE" }, + { DEM_SETUP_SUSPEND, "SETUP_SUSPEND" }, + { DEM_EARLY_EXIT, "EARLY_EXIT" }, + { DEM_WAKEUP_REASON, "WAKEUP_REASON" }, + { DEM_DETECT_WAKEUP, "DETECT_WAKEUP" }, + { DEM_DETECT_RESET, "DETECT_RESET" }, + { DEM_DETECT_SLEEPEXIT, "DETECT_SLEEPEXIT" }, + { DEM_DETECT_RUN, "DETECT_RUN" }, + { DEM_APPS_SWFI, "APPS_SWFI" }, + { DEM_SEND_WAKEUP, "SEND_WAKEUP" }, + { DEM_ASSERT_OKTS, "ASSERT_OKTS" }, + { DEM_NEGATE_OKTS, "NEGATE_OKTS" }, + { DEM_PROC_COMM_CMD, "PROC_COMM_CMD" }, + { DEM_REMOVE_PROC_PWR, "REMOVE_PROC_PWR" }, + { DEM_RESTORE_PROC_PWR, "RESTORE_PROC_PWR" }, + { DEM_SMI_CLK_DISABLED, "SMI_CLK_DISABLED" }, + { DEM_SMI_CLK_ENABLED, "SMI_CLK_ENABLED" }, + { DEM_MAO_INTS, "MAO_INTS" }, + { DEM_APPS_WAKEUP_INT, "APPS_WAKEUP_INT" }, + { DEM_PROC_WAKEUP, "PROC_WAKEUP" }, + { DEM_PROC_POWERUP, "PROC_POWERUP" }, + { DEM_TIMER_EXPIRED, "TIMER_EXPIRED" }, + { DEM_SEND_BATTERY_INFO, "SEND_BATTERY_INFO" }, + { DEM_REMOTE_PWR_CB, "REMOTE_PWR_CB" }, + { DEM_TIME_SYNC_START, "TIME_SYNC_START" }, + { DEM_TIME_SYNC_SEND_VALUE, "TIME_SYNC_SEND_VALUE" }, + { DEM_TIME_SYNC_DONE, "TIME_SYNC_DONE" }, + { DEM_TIME_SYNC_REQUEST, "TIME_SYNC_REQUEST" }, + { DEM_TIME_SYNC_POLL, "TIME_SYNC_POLL" }, + { DEM_TIME_SYNC_INIT, "TIME_SYNC_INIT" }, + { DEM_INIT, "INIT" }, +#else + + { DEM_NO_SLEEP, "NO_SLEEP" }, + { DEM_INSUF_TIME, "INSUF_TIME" }, + { DEMAPPS_ENTER_SLEEP, "APPS_ENTER_SLEEP" }, + { DEMAPPS_DETECT_WAKEUP, "APPS_DETECT_WAKEUP" }, + { DEMAPPS_END_APPS_TCXO, "APPS_END_APPS_TCXO" }, + { DEMAPPS_ENTER_SLEEPEXIT, "APPS_ENTER_SLEEPEXIT" }, + { DEMAPPS_END_APPS_SLEEP, "APPS_END_APPS_SLEEP" }, + { DEMAPPS_SETUP_APPS_PWRCLPS, "APPS_SETUP_APPS_PWRCLPS" }, + { DEMAPPS_PWRCLPS_EARLY_EXIT, "APPS_PWRCLPS_EARLY_EXIT" }, + { DEMMOD_SEND_WAKEUP, "MOD_SEND_WAKEUP" }, + { DEMMOD_NO_APPS_VOTE, "MOD_NO_APPS_VOTE" }, + { DEMMOD_NO_TCXO_SLEEP, "MOD_NO_TCXO_SLEEP" }, + { DEMMOD_BT_CLOCK, "MOD_BT_CLOCK" }, + { DEMMOD_UART_CLOCK, "MOD_UART_CLOCK" }, + { DEMMOD_OKTS, "MOD_OKTS" }, + { DEM_SLEEP_INFO, "SLEEP_INFO" }, + { DEMMOD_TCXO_END, "MOD_TCXO_END" }, + { DEMMOD_END_SLEEP_SIG, "MOD_END_SLEEP_SIG" }, + { DEMMOD_SETUP_APPSSLEEP, "MOD_SETUP_APPSSLEEP" }, + { DEMMOD_ENTER_TCXO, "MOD_ENTER_TCXO" }, + { DEMMOD_WAKE_APPS, "MOD_WAKE_APPS" }, + { DEMMOD_POWER_COLLAPSE_APPS, "MOD_POWER_COLLAPSE_APPS" }, + { DEMMOD_RESTORE_APPS_PWR, "MOD_RESTORE_APPS_PWR" }, + { DEMAPPS_ASSERT_OKTS, "APPS_ASSERT_OKTS" }, + { DEMAPPS_RESTART_START_TIMER, "APPS_RESTART_START_TIMER" }, + { DEMAPPS_ENTER_RUN, "APPS_ENTER_RUN" }, + { DEMMOD_MAO_INTS, "MOD_MAO_INTS" }, + { DEMMOD_POWERUP_APPS_CALLED, "MOD_POWERUP_APPS_CALLED" }, + { DEMMOD_PC_TIMER_EXPIRED, "MOD_PC_TIMER_EXPIRED" }, + { DEM_DETECT_SLEEPEXIT, "_DETECT_SLEEPEXIT" }, + { DEM_DETECT_RUN, "DETECT_RUN" }, + { DEM_SET_APPS_TIMER, "SET_APPS_TIMER" }, + { DEM_NEGATE_OKTS, "NEGATE_OKTS" }, + { DEMMOD_APPS_WAKEUP_INT, "MOD_APPS_WAKEUP_INT" }, + { DEMMOD_APPS_SWFI, "MOD_APPS_SWFI" }, + { DEM_SEND_BATTERY_INFO, "SEND_BATTERY_INFO" }, + { DEM_SMI_CLK_DISABLED, "SMI_CLK_DISABLED" }, + { DEM_SMI_CLK_ENABLED, "SMI_CLK_ENABLED" }, + { DEMAPPS_SETUP_APPS_SUSPEND, "APPS_SETUP_APPS_SUSPEND" }, + { DEM_RPC_EARLY_EXIT, "RPC_EARLY_EXIT" }, + { DEMAPPS_WAKEUP_REASON, "APPS_WAKEUP_REASON" }, + { DEM_INIT, "INIT" }, +#endif + { DEMMOD_UMTS_BASE, "MOD_UMTS_BASE" }, + { DEMMOD_GL1_GO_TO_SLEEP, "GL1_GO_TO_SLEEP" }, + { DEMMOD_GL1_SLEEP_START, "GL1_SLEEP_START" }, + { DEMMOD_GL1_AFTER_GSM_CLK_ON, "GL1_AFTER_GSM_CLK_ON" }, + { DEMMOD_GL1_BEFORE_RF_ON, "GL1_BEFORE_RF_ON" }, + { DEMMOD_GL1_AFTER_RF_ON, "GL1_AFTER_RF_ON" }, + { DEMMOD_GL1_FRAME_TICK, "GL1_FRAME_TICK" }, + { DEMMOD_GL1_WCDMA_START, "GL1_WCDMA_START" }, + { DEMMOD_GL1_WCDMA_ENDING, "GL1_WCDMA_ENDING" }, + { DEMMOD_UMTS_NOT_OKTS, "UMTS_NOT_OKTS" }, + { DEMMOD_UMTS_START_TCXO_SHUTDOWN, "UMTS_START_TCXO_SHUTDOWN" }, + { DEMMOD_UMTS_END_TCXO_SHUTDOWN, "UMTS_END_TCXO_SHUTDOWN" }, + { DEMMOD_UMTS_START_ARM_HALT, "UMTS_START_ARM_HALT" }, + { DEMMOD_UMTS_END_ARM_HALT, "UMTS_END_ARM_HALT" }, + { DEMMOD_UMTS_NEXT_WAKEUP_SCLK, "UMTS_NEXT_WAKEUP_SCLK" }, + { TIME_REMOTE_LOG_EVENT_START, "START" }, + { TIME_REMOTE_LOG_EVENT_GOTO_WAIT, + "GOTO_WAIT" }, + { TIME_REMOTE_LOG_EVENT_GOTO_INIT, + "GOTO_INIT" }, + { ERR_ERROR_FATAL, "ERR_ERROR_FATAL" }, + { ERR_ERROR_FATAL_TASK, "ERR_ERROR_FATAL_TASK" }, + { DCVSAPPS_LOG_IDLE, "DCVSAPPS_LOG_IDLE" }, + { DCVSAPPS_LOG_ERR, "DCVSAPPS_LOG_ERR" }, + { DCVSAPPS_LOG_CHG, "DCVSAPPS_LOG_CHG" }, + { DCVSAPPS_LOG_REG, "DCVSAPPS_LOG_REG" }, + { DCVSAPPS_LOG_DEREG, "DCVSAPPS_LOG_DEREG" }, + { SMEM_LOG_EVENT_CB, "CB" }, + { SMEM_LOG_EVENT_START, "START" }, + { SMEM_LOG_EVENT_INIT, "INIT" }, + { SMEM_LOG_EVENT_RUNNING, "RUNNING" }, + { SMEM_LOG_EVENT_STOP, "STOP" }, + { SMEM_LOG_EVENT_RESTART, "RESTART" }, + { SMEM_LOG_EVENT_SS, "SS" }, + { SMEM_LOG_EVENT_READ, "READ" }, + { SMEM_LOG_EVENT_WRITE, "WRITE" }, + { SMEM_LOG_EVENT_SIGS1, "SIGS1" }, + { SMEM_LOG_EVENT_SIGS2, "SIGS2" }, + { SMEM_LOG_EVENT_WRITE_DM, "WRITE_DM" }, + { SMEM_LOG_EVENT_READ_DM, "READ_DM" }, + { SMEM_LOG_EVENT_SKIP_DM, "SKIP_DM" }, + { SMEM_LOG_EVENT_STOP_DM, "STOP_DM" }, + { SMEM_LOG_EVENT_ISR, "ISR" }, + { SMEM_LOG_EVENT_TASK, "TASK" }, + { SMEM_LOG_EVENT_RS, "RS" }, + { ONCRPC_LOG_EVENT_SMD_WAIT, "SMD_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_WAIT, "RPC_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_BOTH_WAIT, "RPC_BOTH_WAIT" }, + { ONCRPC_LOG_EVENT_RPC_INIT, "RPC_INIT" }, + { ONCRPC_LOG_EVENT_RUNNING, "RUNNING" }, + { ONCRPC_LOG_EVENT_APIS_INITED, "APIS_INITED" }, + { ONCRPC_LOG_EVENT_AMSS_RESET, "AMSS_RESET" }, + { ONCRPC_LOG_EVENT_SMD_RESET, "SMD_RESET" }, + { ONCRPC_LOG_EVENT_ONCRPC_RESET, "ONCRPC_RESET" }, + { ONCRPC_LOG_EVENT_CB, "CB" }, + { ONCRPC_LOG_EVENT_STD_CALL, "STD_CALL" }, + { ONCRPC_LOG_EVENT_STD_REPLY, "STD_REPLY" }, + { ONCRPC_LOG_EVENT_STD_CALL_ASYNC, "STD_CALL_ASYNC" }, + { NO_SLEEP_OLD, "NO_SLEEP_OLD" }, + { INSUF_TIME, "INSUF_TIME" }, + { MOD_UART_CLOCK, "MOD_UART_CLOCK" }, + { SLEEP_INFO, "SLEEP_INFO" }, + { MOD_TCXO_END, "MOD_TCXO_END" }, + { MOD_ENTER_TCXO, "MOD_ENTER_TCXO" }, + { NO_SLEEP_NEW, "NO_SLEEP_NEW" }, + { RPC_ROUTER_LOG_EVENT_UNKNOWN, "UNKNOWN" }, + { RPC_ROUTER_LOG_EVENT_MSG_READ, "MSG_READ" }, + { RPC_ROUTER_LOG_EVENT_MSG_WRITTEN, "MSG_WRITTEN" }, + { RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ, "MSG_CFM_REQ" }, + { RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT, "MSG_CFM_SNT" }, + { RPC_ROUTER_LOG_EVENT_MID_READ, "MID_READ" }, + { RPC_ROUTER_LOG_EVENT_MID_WRITTEN, "MID_WRITTEN" }, + { RPC_ROUTER_LOG_EVENT_MID_CFM_REQ, "MID_CFM_REQ" }, + +}; + +struct sym oncrpc_syms[] = { + { 0x30000000, "CM" }, + { 0x30000001, "DB" }, + { 0x30000002, "SND" }, + { 0x30000003, "WMS" }, + { 0x30000004, "PDSM" }, + { 0x30000005, "MISC_MODEM_APIS" }, + { 0x30000006, "MISC_APPS_APIS" }, + { 0x30000007, "JOYST" }, + { 0x30000008, "VJOY" }, + { 0x30000009, "JOYSTC" }, + { 0x3000000a, "ADSPRTOSATOM" }, + { 0x3000000b, "ADSPRTOSMTOA" }, + { 0x3000000c, "I2C" }, + { 0x3000000d, "TIME_REMOTE" }, + { 0x3000000e, "NV" }, + { 0x3000000f, "CLKRGM_SEC" }, + { 0x30000010, "RDEVMAP" }, + { 0x30000011, "FS_RAPI" }, + { 0x30000012, "PBMLIB" }, + { 0x30000013, "AUDMGR" }, + { 0x30000014, "MVS" }, + { 0x30000015, "DOG_KEEPALIVE" }, + { 0x30000016, "GSDI_EXP" }, + { 0x30000017, "AUTH" }, + { 0x30000018, "NVRUIMI" }, + { 0x30000019, "MMGSDILIB" }, + { 0x3000001a, "CHARGER" }, + { 0x3000001b, "UIM" }, + { 0x3000001C, "ONCRPCTEST" }, + { 0x3000001d, "PDSM_ATL" }, + { 0x3000001e, "FS_XMOUNT" }, + { 0x3000001f, "SECUTIL " }, + { 0x30000020, "MCCMEID" }, + { 0x30000021, "PM_STROBE_FLASH" }, + { 0x30000022, "DS707_EXTIF" }, + { 0x30000023, "SMD BRIDGE_MODEM" }, + { 0x30000024, "SMD PORT_MGR" }, + { 0x30000025, "BUS_PERF" }, + { 0x30000026, "BUS_MON" }, + { 0x30000027, "MC" }, + { 0x30000028, "MCCAP" }, + { 0x30000029, "MCCDMA" }, + { 0x3000002a, "MCCDS" }, + { 0x3000002b, "MCCSCH" }, + { 0x3000002c, "MCCSRID" }, + { 0x3000002d, "SNM" }, + { 0x3000002e, "MCCSYOBJ" }, + { 0x3000002f, "DS707_APIS" }, + { 0x30000030, "DS_MP_SHIM_APPS_ASYNC" }, + { 0x30000031, "DSRLP_APIS" }, + { 0x30000032, "RLP_APIS" }, + { 0x30000033, "DS_MP_SHIM_MODEM" }, + { 0x30000034, "DSHDR_APIS" }, + { 0x30000035, "DSHDR_MDM_APIS" }, + { 0x30000036, "DS_MP_SHIM_APPS" }, + { 0x30000037, "HDRMC_APIS" }, + { 0x30000038, "SMD_BRIDGE_MTOA" }, + { 0x30000039, "SMD_BRIDGE_ATOM" }, + { 0x3000003a, "DPMAPP_OTG" }, + { 0x3000003b, "DIAG" }, + { 0x3000003c, "GSTK_EXP" }, + { 0x3000003d, "DSBC_MDM_APIS" }, + { 0x3000003e, "HDRMRLP_MDM_APIS" }, + { 0x3000003f, "HDRMRLP_APPS_APIS" }, + { 0x30000040, "HDRMC_MRLP_APIS" }, + { 0x30000041, "PDCOMM_APP_API" }, + { 0x30000042, "DSAT_APIS" }, + { 0x30000043, "MISC_RF_APIS" }, + { 0x30000044, "CMIPAPP" }, + { 0x30000045, "DSMP_UMTS_MODEM_APIS" }, + { 0x30000046, "DSMP_UMTS_APPS_APIS" }, + { 0x30000047, "DSUCSDMPSHIM" }, + { 0x30000048, "TIME_REMOTE_ATOM" }, + { 0x3000004a, "SD" }, + { 0x3000004b, "MMOC" }, + { 0x3000004c, "WLAN_ADP_FTM" }, + { 0x3000004d, "WLAN_CP_CM" }, + { 0x3000004e, "FTM_WLAN" }, + { 0x3000004f, "SDCC_CPRM" }, + { 0x30000050, "CPRMINTERFACE" }, + { 0x30000051, "DATA_ON_MODEM_MTOA_APIS" }, + { 0x30000052, "DATA_ON_APPS_ATOM_APIS" }, + { 0x30000053, "MISC_MODEM_APIS_NONWINMOB" }, + { 0x30000054, "MISC_APPS_APIS_NONWINMOB" }, + { 0x30000055, "PMEM_REMOTE" }, + { 0x30000056, "TCXOMGR" }, + { 0x30000057, "DSUCSDAPPIF_APIS" }, + { 0x30000058, "BT" }, + { 0x30000059, "PD_COMMS_API" }, + { 0x3000005a, "PD_COMMS_CLIENT_API" }, + { 0x3000005b, "PDAPI" }, + { 0x3000005c, "LSA_SUPL_DSM" }, + { 0x3000005d, "TIME_REMOTE_MTOA" }, + { 0x3000005e, "FTM_BT" }, + { 0X3000005f, "DSUCSDAPPIF_APIS" }, + { 0X30000060, "PMAPP_GEN" }, + { 0X30000061, "PM_LIB" }, + { 0X30000062, "KEYPAD" }, + { 0X30000063, "HSU_APP_APIS" }, + { 0X30000064, "HSU_MDM_APIS" }, + { 0X30000065, "ADIE_ADC_REMOTE_ATOM " }, + { 0X30000066, "TLMM_REMOTE_ATOM" }, + { 0X30000067, "UI_CALLCTRL" }, + { 0X30000068, "UIUTILS" }, + { 0X30000069, "PRL" }, + { 0X3000006a, "HW" }, + { 0X3000006b, "OEM_RAPI" }, + { 0X3000006c, "WMSPM" }, + { 0X3000006d, "BTPF" }, + { 0X3000006e, "CLKRGM_SYNC_EVENT" }, + { 0X3000006f, "USB_APPS_RPC" }, + { 0X30000070, "USB_MODEM_RPC" }, + { 0X30000071, "ADC" }, + { 0X30000072, "CAMERAREMOTED" }, + { 0X30000073, "SECAPIREMOTED" }, + { 0X30000074, "DSATAPI" }, + { 0X30000075, "CLKCTL_RPC" }, + { 0X30000076, "BREWAPPCOORD" }, + { 0X30000077, "ALTENVSHELL" }, + { 0X30000078, "WLAN_TRP_UTILS" }, + { 0X30000079, "GPIO_RPC" }, + { 0X3000007a, "PING_RPC" }, + { 0X3000007b, "DSC_DCM_API" }, + { 0X3000007c, "L1_DS" }, + { 0X3000007d, "QCHATPK_APIS" }, + { 0X3000007e, "GPS_API" }, + { 0X3000007f, "OSS_RRCASN_REMOTE" }, + { 0X30000080, "PMAPP_OTG_REMOTE" }, + { 0X30000081, "PING_MDM_RPC" }, + { 0X30000082, "PING_KERNEL_RPC" }, + { 0X30000083, "TIMETICK" }, + { 0X30000084, "WM_BTHCI_FTM " }, + { 0X30000085, "WM_BT_PF" }, + { 0X30000086, "IPA_IPC_APIS" }, + { 0X30000087, "UKCC_IPC_APIS" }, + { 0X30000088, "CMIPSMS " }, + { 0X30000089, "VBATT_REMOTE" }, + { 0X3000008a, "MFPAL" }, + { 0X3000008b, "DSUMTSPDPREG" }, + { 0X3000fe00, "RESTART_DAEMON NUMBER 0" }, + { 0X3000fe01, "RESTART_DAEMON NUMBER 1" }, + { 0X3000feff, "RESTART_DAEMON NUMBER 255" }, + { 0X3000fffe, "BACKWARDS_COMPATIBILITY_IN_RPC_CLNT_LOOKUP" }, + { 0X3000ffff, "RPC_ROUTER_SERVER_PROGRAM" }, + { 0x31000000, "CM CB" }, + { 0x31000001, "DB CB" }, + { 0x31000002, "SND CB" }, + { 0x31000003, "WMS CB" }, + { 0x31000004, "PDSM CB" }, + { 0x31000005, "MISC_MODEM_APIS CB" }, + { 0x31000006, "MISC_APPS_APIS CB" }, + { 0x31000007, "JOYST CB" }, + { 0x31000008, "VJOY CB" }, + { 0x31000009, "JOYSTC CB" }, + { 0x3100000a, "ADSPRTOSATOM CB" }, + { 0x3100000b, "ADSPRTOSMTOA CB" }, + { 0x3100000c, "I2C CB" }, + { 0x3100000d, "TIME_REMOTE CB" }, + { 0x3100000e, "NV CB" }, + { 0x3100000f, "CLKRGM_SEC CB" }, + { 0x31000010, "RDEVMAP CB" }, + { 0x31000011, "FS_RAPI CB" }, + { 0x31000012, "PBMLIB CB" }, + { 0x31000013, "AUDMGR CB" }, + { 0x31000014, "MVS CB" }, + { 0x31000015, "DOG_KEEPALIVE CB" }, + { 0x31000016, "GSDI_EXP CB" }, + { 0x31000017, "AUTH CB" }, + { 0x31000018, "NVRUIMI CB" }, + { 0x31000019, "MMGSDILIB CB" }, + { 0x3100001a, "CHARGER CB" }, + { 0x3100001b, "UIM CB" }, + { 0x3100001C, "ONCRPCTEST CB" }, + { 0x3100001d, "PDSM_ATL CB" }, + { 0x3100001e, "FS_XMOUNT CB" }, + { 0x3100001f, "SECUTIL CB" }, + { 0x31000020, "MCCMEID" }, + { 0x31000021, "PM_STROBE_FLASH CB" }, + { 0x31000022, "DS707_EXTIF CB" }, + { 0x31000023, "SMD BRIDGE_MODEM CB" }, + { 0x31000024, "SMD PORT_MGR CB" }, + { 0x31000025, "BUS_PERF CB" }, + { 0x31000026, "BUS_MON CB" }, + { 0x31000027, "MC CB" }, + { 0x31000028, "MCCAP CB" }, + { 0x31000029, "MCCDMA CB" }, + { 0x3100002a, "MCCDS CB" }, + { 0x3100002b, "MCCSCH CB" }, + { 0x3100002c, "MCCSRID CB" }, + { 0x3100002d, "SNM CB" }, + { 0x3100002e, "MCCSYOBJ CB" }, + { 0x3100002f, "DS707_APIS CB" }, + { 0x31000030, "DS_MP_SHIM_APPS_ASYNC CB" }, + { 0x31000031, "DSRLP_APIS CB" }, + { 0x31000032, "RLP_APIS CB" }, + { 0x31000033, "DS_MP_SHIM_MODEM CB" }, + { 0x31000034, "DSHDR_APIS CB" }, + { 0x31000035, "DSHDR_MDM_APIS CB" }, + { 0x31000036, "DS_MP_SHIM_APPS CB" }, + { 0x31000037, "HDRMC_APIS CB" }, + { 0x31000038, "SMD_BRIDGE_MTOA CB" }, + { 0x31000039, "SMD_BRIDGE_ATOM CB" }, + { 0x3100003a, "DPMAPP_OTG CB" }, + { 0x3100003b, "DIAG CB" }, + { 0x3100003c, "GSTK_EXP CB" }, + { 0x3100003d, "DSBC_MDM_APIS CB" }, + { 0x3100003e, "HDRMRLP_MDM_APIS CB" }, + { 0x3100003f, "HDRMRLP_APPS_APIS CB" }, + { 0x31000040, "HDRMC_MRLP_APIS CB" }, + { 0x31000041, "PDCOMM_APP_API CB" }, + { 0x31000042, "DSAT_APIS CB" }, + { 0x31000043, "MISC_RF_APIS CB" }, + { 0x31000044, "CMIPAPP CB" }, + { 0x31000045, "DSMP_UMTS_MODEM_APIS CB" }, + { 0x31000046, "DSMP_UMTS_APPS_APIS CB" }, + { 0x31000047, "DSUCSDMPSHIM CB" }, + { 0x31000048, "TIME_REMOTE_ATOM CB" }, + { 0x3100004a, "SD CB" }, + { 0x3100004b, "MMOC CB" }, + { 0x3100004c, "WLAN_ADP_FTM CB" }, + { 0x3100004d, "WLAN_CP_CM CB" }, + { 0x3100004e, "FTM_WLAN CB" }, + { 0x3100004f, "SDCC_CPRM CB" }, + { 0x31000050, "CPRMINTERFACE CB" }, + { 0x31000051, "DATA_ON_MODEM_MTOA_APIS CB" }, + { 0x31000052, "DATA_ON_APPS_ATOM_APIS CB" }, + { 0x31000053, "MISC_APIS_NONWINMOB CB" }, + { 0x31000054, "MISC_APPS_APIS_NONWINMOB CB" }, + { 0x31000055, "PMEM_REMOTE CB" }, + { 0x31000056, "TCXOMGR CB" }, + { 0x31000057, "DSUCSDAPPIF_APIS CB" }, + { 0x31000058, "BT CB" }, + { 0x31000059, "PD_COMMS_API CB" }, + { 0x3100005a, "PD_COMMS_CLIENT_API CB" }, + { 0x3100005b, "PDAPI CB" }, + { 0x3100005c, "LSA_SUPL_DSM CB" }, + { 0x3100005d, "TIME_REMOTE_MTOA CB" }, + { 0x3100005e, "FTM_BT CB" }, + { 0X3100005f, "DSUCSDAPPIF_APIS CB" }, + { 0X31000060, "PMAPP_GEN CB" }, + { 0X31000061, "PM_LIB CB" }, + { 0X31000062, "KEYPAD CB" }, + { 0X31000063, "HSU_APP_APIS CB" }, + { 0X31000064, "HSU_MDM_APIS CB" }, + { 0X31000065, "ADIE_ADC_REMOTE_ATOM CB" }, + { 0X31000066, "TLMM_REMOTE_ATOM CB" }, + { 0X31000067, "UI_CALLCTRL CB" }, + { 0X31000068, "UIUTILS CB" }, + { 0X31000069, "PRL CB" }, + { 0X3100006a, "HW CB" }, + { 0X3100006b, "OEM_RAPI CB" }, + { 0X3100006c, "WMSPM CB" }, + { 0X3100006d, "BTPF CB" }, + { 0X3100006e, "CLKRGM_SYNC_EVENT CB" }, + { 0X3100006f, "USB_APPS_RPC CB" }, + { 0X31000070, "USB_MODEM_RPC CB" }, + { 0X31000071, "ADC CB" }, + { 0X31000072, "CAMERAREMOTED CB" }, + { 0X31000073, "SECAPIREMOTED CB" }, + { 0X31000074, "DSATAPI CB" }, + { 0X31000075, "CLKCTL_RPC CB" }, + { 0X31000076, "BREWAPPCOORD CB" }, + { 0X31000077, "ALTENVSHELL CB" }, + { 0X31000078, "WLAN_TRP_UTILS CB" }, + { 0X31000079, "GPIO_RPC CB" }, + { 0X3100007a, "PING_RPC CB" }, + { 0X3100007b, "DSC_DCM_API CB" }, + { 0X3100007c, "L1_DS CB" }, + { 0X3100007d, "QCHATPK_APIS CB" }, + { 0X3100007e, "GPS_API CB" }, + { 0X3100007f, "OSS_RRCASN_REMOTE CB" }, + { 0X31000080, "PMAPP_OTG_REMOTE CB" }, + { 0X31000081, "PING_MDM_RPC CB" }, + { 0X31000082, "PING_KERNEL_RPC CB" }, + { 0X31000083, "TIMETICK CB" }, + { 0X31000084, "WM_BTHCI_FTM CB" }, + { 0X31000085, "WM_BT_PF CB" }, + { 0X31000086, "IPA_IPC_APIS CB" }, + { 0X31000087, "UKCC_IPC_APIS CB" }, + { 0X31000088, "CMIPSMS CB" }, + { 0X31000089, "VBATT_REMOTE CB" }, + { 0X3100008a, "MFPAL CB" }, + { 0X3100008b, "DSUMTSPDPREG CB" }, + { 0X3100fe00, "RESTART_DAEMON NUMBER 0 CB" }, + { 0X3100fe01, "RESTART_DAEMON NUMBER 1 CB" }, + { 0X3100feff, "RESTART_DAEMON NUMBER 255 CB" }, + { 0X3100fffe, "BACKWARDS_COMPATIBILITY_IN_RPC_CLNT_LOOKUP CB" }, + { 0X3100ffff, "RPC_ROUTER_SERVER_PROGRAM CB" }, +}; + +struct sym wakeup_syms[] = { + { 0x00000040, "OTHER" }, + { 0x00000020, "RESET" }, + { 0x00000010, "ALARM" }, + { 0x00000008, "TIMER" }, + { 0x00000004, "GPIO" }, + { 0x00000002, "INT" }, + { 0x00000001, "RPC" }, + { 0x00000000, "NONE" }, +}; + +struct sym wakeup_int_syms[] = { + { 0, "MDDI_EXT" }, + { 1, "MDDI_PRI" }, + { 2, "MDDI_CLIENT"}, + { 3, "USB_OTG" }, + { 4, "I2CC" }, + { 5, "SDC1_0" }, + { 6, "SDC1_1" }, + { 7, "SDC2_0" }, + { 8, "SDC2_1" }, + { 9, "ADSP_A9A11" }, + { 10, "UART1" }, + { 11, "UART2" }, + { 12, "UART3" }, + { 13, "DP_RX_DATA" }, + { 14, "DP_RX_DATA2" }, + { 15, "DP_RX_DATA3" }, + { 16, "DM_UART" }, + { 17, "DM_DP_RX_DATA" }, + { 18, "KEYSENSE" }, + { 19, "HSSD" }, + { 20, "NAND_WR_ER_DONE" }, + { 21, "NAND_OP_DONE" }, + { 22, "TCHSCRN1" }, + { 23, "TCHSCRN2" }, + { 24, "TCHSCRN_SSBI" }, + { 25, "USB_HS" }, + { 26, "UART2_DM_RX" }, + { 27, "UART2_DM" }, + { 28, "SDC4_1" }, + { 29, "SDC4_0" }, + { 30, "SDC3_1" }, + { 31, "SDC3_0" }, +}; + +struct sym smsm_syms[] = { + { 0x80000000, "UN" }, + { 0x7F000000, "ERR" }, + { 0x00800000, "SMLP" }, + { 0x00400000, "ADWN" }, + { 0x00200000, "PWRS" }, + { 0x00100000, "DWLD" }, + { 0x00080000, "SRBT" }, + { 0x00040000, "SDWN" }, + { 0x00020000, "ARBT" }, + { 0x00010000, "REL" }, + { 0x00008000, "SLE" }, + { 0x00004000, "SLP" }, + { 0x00002000, "WFPI" }, + { 0x00001000, "EEX" }, + { 0x00000800, "TIN" }, + { 0x00000400, "TWT" }, + { 0x00000200, "PWRC" }, + { 0x00000100, "RUN" }, + { 0x00000080, "SA" }, + { 0x00000040, "RES" }, + { 0x00000020, "RIN" }, + { 0x00000010, "RWT" }, + { 0x00000008, "SIN" }, + { 0x00000004, "SWT" }, + { 0x00000002, "OE" }, + { 0x00000001, "I" }, +}; + +/* never reorder */ +struct sym voter_d2_syms[] = { + { 0x00000001, NULL }, + { 0x00000002, NULL }, + { 0x00000004, NULL }, + { 0x00000008, NULL }, + { 0x00000010, NULL }, + { 0x00000020, NULL }, + { 0x00000040, NULL }, + { 0x00000080, NULL }, + { 0x00000100, NULL }, + { 0x00000200, NULL }, + { 0x00000400, NULL }, + { 0x00000800, NULL }, + { 0x00001000, NULL }, + { 0x00002000, NULL }, + { 0x00004000, NULL }, + { 0x00008000, NULL }, + { 0x00010000, NULL }, + { 0x00020000, NULL }, + { 0x00040000, NULL }, + { 0x00080000, NULL }, + { 0x00100000, NULL }, + { 0x00200000, NULL }, + { 0x00400000, NULL }, + { 0x00800000, NULL }, + { 0x01000000, NULL }, + { 0x02000000, NULL }, + { 0x04000000, NULL }, + { 0x08000000, NULL }, + { 0x10000000, NULL }, + { 0x20000000, NULL }, + { 0x40000000, NULL }, + { 0x80000000, NULL }, +}; + +/* never reorder */ +struct sym voter_d3_syms[] = { + { 0x00000001, NULL }, + { 0x00000002, NULL }, + { 0x00000004, NULL }, + { 0x00000008, NULL }, + { 0x00000010, NULL }, + { 0x00000020, NULL }, + { 0x00000040, NULL }, + { 0x00000080, NULL }, + { 0x00000100, NULL }, + { 0x00000200, NULL }, + { 0x00000400, NULL }, + { 0x00000800, NULL }, + { 0x00001000, NULL }, + { 0x00002000, NULL }, + { 0x00004000, NULL }, + { 0x00008000, NULL }, + { 0x00010000, NULL }, + { 0x00020000, NULL }, + { 0x00040000, NULL }, + { 0x00080000, NULL }, + { 0x00100000, NULL }, + { 0x00200000, NULL }, + { 0x00400000, NULL }, + { 0x00800000, NULL }, + { 0x01000000, NULL }, + { 0x02000000, NULL }, + { 0x04000000, NULL }, + { 0x08000000, NULL }, + { 0x10000000, NULL }, + { 0x20000000, NULL }, + { 0x40000000, NULL }, + { 0x80000000, NULL }, +}; + +struct sym dem_state_master_syms[] = { + { 0, "INIT" }, + { 1, "RUN" }, + { 2, "SLEEP_WAIT" }, + { 3, "SLEEP_CONFIRMED" }, + { 4, "SLEEP_EXIT" }, + { 5, "RSA" }, + { 6, "EARLY_EXIT" }, + { 7, "RSA_DELAYED" }, + { 8, "RSA_CHECK_INTS" }, + { 9, "RSA_CONFIRMED" }, + { 10, "RSA_WAKING" }, + { 11, "RSA_RESTORE" }, + { 12, "RESET" }, +}; + +struct sym dem_state_slave_syms[] = { + { 0, "INIT" }, + { 1, "RUN" }, + { 2, "SLEEP_WAIT" }, + { 3, "SLEEP_EXIT" }, + { 4, "SLEEP_RUN_PENDING" }, + { 5, "POWER_COLLAPSE" }, + { 6, "CHECK_INTERRUPTS" }, + { 7, "SWFI" }, + { 8, "WFPI" }, + { 9, "EARLY_EXIT" }, + { 10, "RESET_RECOVER" }, + { 11, "RESET_ACKNOWLEDGE" }, + { 12, "ERROR" }, +}; + +struct sym smsm_entry_type_syms[] = { + { 0, "SMSM_APPS_STATE" }, + { 1, "SMSM_MODEM_STATE" }, + { 2, "SMSM_Q6_STATE" }, + { 3, "SMSM_APPS_DEM" }, + { 4, "SMSM_MODEM_DEM" }, + { 5, "SMSM_Q6_DEM" }, + { 6, "SMSM_POWER_MASTER_DEM" }, + { 7, "SMSM_TIME_MASTER_DEM" }, +}; + +struct sym smsm_state_syms[] = { + { 0x00000001, "INIT" }, + { 0x00000002, "OSENTERED" }, + { 0x00000004, "SMDWAIT" }, + { 0x00000008, "SMDINIT" }, + { 0x00000010, "RPCWAIT" }, + { 0x00000020, "RPCINIT" }, + { 0x00000040, "RESET" }, + { 0x00000080, "RSA" }, + { 0x00000100, "RUN" }, + { 0x00000200, "PWRC" }, + { 0x00000400, "TIMEWAIT" }, + { 0x00000800, "TIMEINIT" }, + { 0x00001000, "PWRC_EARLY_EXIT" }, + { 0x00002000, "WFPI" }, + { 0x00004000, "SLEEP" }, + { 0x00008000, "SLEEPEXIT" }, + { 0x00010000, "OEMSBL_RELEASE" }, + { 0x00020000, "APPS_REBOOT" }, + { 0x00040000, "SYSTEM_POWER_DOWN" }, + { 0x00080000, "SYSTEM_REBOOT" }, + { 0x00100000, "SYSTEM_DOWNLOAD" }, + { 0x00200000, "PWRC_SUSPEND" }, + { 0x00400000, "APPS_SHUTDOWN" }, + { 0x00800000, "SMD_LOOPBACK" }, + { 0x01000000, "RUN_QUIET" }, + { 0x02000000, "MODEM_WAIT" }, + { 0x04000000, "MODEM_BREAK" }, + { 0x08000000, "MODEM_CONTINUE" }, + { 0x80000000, "UNKNOWN" }, +}; + +#define ID_SYM 0 +#define BASE_SYM 1 +#define EVENT_SYM 2 +#define ONCRPC_SYM 3 +#define WAKEUP_SYM 4 +#define WAKEUP_INT_SYM 5 +#define SMSM_SYM 6 +#define VOTER_D2_SYM 7 +#define VOTER_D3_SYM 8 +#define DEM_STATE_MASTER_SYM 9 +#define DEM_STATE_SLAVE_SYM 10 +#define SMSM_ENTRY_TYPE_SYM 11 +#define SMSM_STATE_SYM 12 + +static struct sym_tbl { + struct sym *data; + int size; + struct hlist_head hlist[HSIZE]; +} tbl[] = { + { id_syms, ARRAY_SIZE(id_syms) }, + { base_syms, ARRAY_SIZE(base_syms) }, + { event_syms, ARRAY_SIZE(event_syms) }, + { oncrpc_syms, ARRAY_SIZE(oncrpc_syms) }, + { wakeup_syms, ARRAY_SIZE(wakeup_syms) }, + { wakeup_int_syms, ARRAY_SIZE(wakeup_int_syms) }, + { smsm_syms, ARRAY_SIZE(smsm_syms) }, + { voter_d2_syms, ARRAY_SIZE(voter_d2_syms) }, + { voter_d3_syms, ARRAY_SIZE(voter_d3_syms) }, + { dem_state_master_syms, ARRAY_SIZE(dem_state_master_syms) }, + { dem_state_slave_syms, ARRAY_SIZE(dem_state_slave_syms) }, + { smsm_entry_type_syms, ARRAY_SIZE(smsm_entry_type_syms) }, + { smsm_state_syms, ARRAY_SIZE(smsm_state_syms) }, +}; + +static void find_voters(void) +{ + void *x, *next; + unsigned size; + int i = 0, j = 0; + + x = smem_get_entry(SMEM_SLEEP_STATIC, &size); + next = x; + while (next && (next < (x + size)) && + ((i + j) < (ARRAY_SIZE(voter_d3_syms) + + ARRAY_SIZE(voter_d2_syms)))) { + + if (i < ARRAY_SIZE(voter_d3_syms)) { + voter_d3_syms[i].str = (char *) next; + i++; + } else if (i >= ARRAY_SIZE(voter_d3_syms) && + j < ARRAY_SIZE(voter_d2_syms)) { + voter_d2_syms[j].str = (char *) next; + j++; + } + + next += 9; + } +} + +#define hash(val) (val % HSIZE) + +static void init_syms(void) +{ + int i; + int j; + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < HSIZE; ++j) + INIT_HLIST_HEAD(&tbl[i].hlist[j]); + + for (i = 0; i < ARRAY_SIZE(tbl); ++i) + for (j = 0; j < tbl[i].size; ++j) { + INIT_HLIST_NODE(&tbl[i].data[j].node); + hlist_add_head(&tbl[i].data[j].node, + &tbl[i].hlist[hash(tbl[i].data[j].val)]); + } +} + +static char *find_sym(uint32_t id, uint32_t val) +{ + struct hlist_node *n; + struct sym *s; + + hlist_for_each(n, &tbl[id].hlist[hash(val)]) { + s = hlist_entry(n, struct sym, node); + if (s->val == val) + return s->str; + } + + return 0; +} + +#else +static void init_syms(void) {} +#endif + +static inline unsigned int read_timestamp(void) +{ + unsigned int tick; + + do { + tick = readl(TIMESTAMP_ADDR); + } while (tick != (tick = readl(TIMESTAMP_ADDR))); + + return tick; +} + +static void smem_log_event_from_user(struct smem_log_inst *inst, + const char __user *buf, int size, int num) +{ + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + uint32_t identifier = 0; + uint32_t timetick = 0; + int first = 1; + int ret; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + while (num--) { + idx = *inst->idx; + + if (idx < inst->num) { + ret = copy_from_user(&inst->events[idx], + buf, size); + if (ret) { + printk("ERROR %s:%i tried to write " + "%i got ret %i", + __func__, __LINE__, + size, size - ret); + goto out; + } + + if (first) { + identifier = + inst->events[idx]. + identifier; + timetick = read_timestamp(); + first = 0; + } else { + identifier |= SMEM_LOG_CONT; + } + inst->events[idx].identifier = + identifier; + inst->events[idx].timetick = + timetick; + } + + next_idx = idx + 1; + if (next_idx >= inst->num) + next_idx = 0; + *inst->idx = next_idx; + + buf += sizeof(struct smem_log_item); + } + + out: + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); +} + +static void _smem_log_event( + struct smem_log_item __iomem *events, + uint32_t __iomem *_idx, + remote_spinlock_t *lock, + int num, + uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + struct smem_log_item item; + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + + item.timetick = read_timestamp(); + item.identifier = id; + item.data1 = data1; + item.data2 = data2; + item.data3 = data3; + + remote_spin_lock_irqsave(lock, flags); + + idx = *_idx; + + if (idx < num) { + memcpy(&events[idx], + &item, sizeof(item)); + } + + next_idx = idx + 1; + if (next_idx >= num) + next_idx = 0; + *_idx = next_idx; + + remote_spin_unlock_irqrestore(lock, flags); +} + +static void _smem_log_event6( + struct smem_log_item __iomem *events, + uint32_t __iomem *_idx, + remote_spinlock_t *lock, + int num, + uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + struct smem_log_item item[2]; + uint32_t idx; + uint32_t next_idx; + unsigned long flags; + + item[0].timetick = read_timestamp(); + item[0].identifier = id; + item[0].data1 = data1; + item[0].data2 = data2; + item[0].data3 = data3; + item[1].identifier = item[0].identifier; + item[1].timetick = item[0].timetick; + item[1].data1 = data4; + item[1].data2 = data5; + item[1].data3 = data6; + + remote_spin_lock_irqsave(lock, flags); + + idx = *_idx; + + if (idx < (num-1)) { + memcpy(&events[idx], + &item, sizeof(item)); + } + + next_idx = idx + 2; + if (next_idx >= num) + next_idx = 0; + *_idx = next_idx; + + remote_spin_unlock_irqrestore(lock, flags); +} + +void smem_log_event(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + _smem_log_event(inst[GEN].events, inst[GEN].idx, + inst[GEN].remote_spinlock, SMEM_LOG_NUM_ENTRIES, + id, data1, data2, data3); +} + +void smem_log_event6(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + _smem_log_event6(inst[GEN].events, inst[GEN].idx, + inst[GEN].remote_spinlock, SMEM_LOG_NUM_ENTRIES, + id, data1, data2, data3, data4, data5, data6); +} + +void smem_log_event_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3) +{ + _smem_log_event(inst[STA].events, inst[STA].idx, + inst[STA].remote_spinlock, SMEM_LOG_NUM_STATIC_ENTRIES, + id, data1, data2, data3); +} + +void smem_log_event6_to_static(uint32_t id, uint32_t data1, uint32_t data2, + uint32_t data3, uint32_t data4, uint32_t data5, + uint32_t data6) +{ + _smem_log_event6(inst[STA].events, inst[STA].idx, + inst[STA].remote_spinlock, SMEM_LOG_NUM_STATIC_ENTRIES, + id, data1, data2, data3, data4, data5, data6); +} + +static int _smem_log_init(void) +{ + inst[GEN].which_log = GEN; + inst[GEN].events = + (struct smem_log_item *)smem_alloc(SMEM_SMEM_LOG_EVENTS, + SMEM_LOG_EVENTS_SIZE); + inst[GEN].idx = (uint32_t *)smem_alloc(SMEM_SMEM_LOG_IDX, + sizeof(uint32_t)); + if (!inst[GEN].events || !inst[GEN].idx) { + pr_err("%s: no log or log_idx allocated, " + "smem_log disabled\n", __func__); + } + inst[GEN].num = SMEM_LOG_NUM_ENTRIES; + inst[GEN].remote_spinlock = &remote_spinlock; + + inst[STA].which_log = STA; + inst[STA].events = + (struct smem_log_item *) + smem_alloc(SMEM_SMEM_STATIC_LOG_EVENTS, + SMEM_STATIC_LOG_EVENTS_SIZE); + inst[STA].idx = (uint32_t *)smem_alloc(SMEM_SMEM_STATIC_LOG_IDX, + sizeof(uint32_t)); + if (!inst[STA].events || !inst[STA].idx) { + pr_err("%s: no static log or log_idx " + "allocated, smem_log disabled\n", __func__); + } + inst[STA].num = SMEM_LOG_NUM_STATIC_ENTRIES; + inst[STA].remote_spinlock = &remote_spinlock_static; + + inst[POW].which_log = POW; + inst[POW].events = + (struct smem_log_item *) + smem_alloc(SMEM_SMEM_LOG_POWER_EVENTS, + SMEM_POWER_LOG_EVENTS_SIZE); + inst[POW].idx = (uint32_t *)smem_alloc(SMEM_SMEM_LOG_POWER_IDX, + sizeof(uint32_t)); + if (!inst[POW].events || !inst[POW].idx) { + pr_err("%s: no power log or log_idx " + "allocated, smem_log disabled\n", __func__); + } + inst[POW].num = SMEM_LOG_NUM_POWER_ENTRIES; + inst[POW].remote_spinlock = &remote_spinlock; + + remote_spin_lock_init(&remote_spinlock, + SMEM_SPINLOCK_SMEM_LOG); + remote_spin_lock_init(&remote_spinlock_static, + SMEM_SPINLOCK_STATIC_LOG); + + init_syms(); + + return 0; +} + +static ssize_t smem_log_read_bin(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + int idx; + int orig_idx; + unsigned long flags; + int ret; + int tot_bytes = 0; + struct smem_log_inst *inst; + + inst = fp->private_data; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + orig_idx = *inst->idx; + idx = orig_idx; + + while (1) { + idx--; + if (idx < 0) + idx = inst->num - 1; + if (idx == orig_idx) { + ret = tot_bytes; + break; + } + + if ((tot_bytes + sizeof(struct smem_log_item)) > count) { + ret = tot_bytes; + break; + } + + ret = copy_to_user(buf, &inst[GEN].events[idx], + sizeof(struct smem_log_item)); + if (ret) { + ret = -EIO; + break; + } + + tot_bytes += sizeof(struct smem_log_item); + + buf += sizeof(struct smem_log_item); + } + + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); + + return ret; +} + +static ssize_t smem_log_read(struct file *fp, char __user *buf, + size_t count, loff_t *pos) +{ + char loc_buf[128]; + int i; + int idx; + int orig_idx; + unsigned long flags; + int ret; + int tot_bytes = 0; + struct smem_log_inst *inst; + + inst = fp->private_data; + + remote_spin_lock_irqsave(inst->remote_spinlock, flags); + + orig_idx = *inst->idx; + idx = orig_idx; + + while (1) { + idx--; + if (idx < 0) + idx = inst->num - 1; + if (idx == orig_idx) { + ret = tot_bytes; + break; + } + + i = scnprintf(loc_buf, 128, + "0x%x 0x%x 0x%x 0x%x 0x%x\n", + inst->events[idx].identifier, + inst->events[idx].timetick, + inst->events[idx].data1, + inst->events[idx].data2, + inst->events[idx].data3); + if (i == 0) { + ret = -EIO; + break; + } + + if ((tot_bytes + i) > count) { + ret = tot_bytes; + break; + } + + tot_bytes += i; + + ret = copy_to_user(buf, loc_buf, i); + if (ret) { + ret = -EIO; + break; + } + + buf += i; + } + + remote_spin_unlock_irqrestore(inst->remote_spinlock, flags); + + return ret; +} + +static ssize_t smem_log_write_bin(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + if (count < sizeof(struct smem_log_item)) + return -EINVAL; + + smem_log_event_from_user(fp->private_data, buf, + sizeof(struct smem_log_item), + count / sizeof(struct smem_log_item)); + + return count; +} + +static ssize_t smem_log_write(struct file *fp, const char __user *buf, + size_t count, loff_t *pos) +{ + int ret; + const char delimiters[] = " ,;"; + char locbuf[256] = {0}; + uint32_t val[10]; + int vals = 0; + char *token; + char *running; + struct smem_log_inst *inst; + unsigned long res; + + inst = fp->private_data; + + if (count < 0) { + printk(KERN_ERR "ERROR: %s passed neg count = %i\n", + __func__, count); + return -EINVAL; + } + + count = count > 255 ? 255 : count; + + locbuf[count] = '\0'; + + ret = copy_from_user(locbuf, buf, count); + if (ret != 0) { + printk(KERN_ERR "ERROR: %s could not copy %i bytes\n", + __func__, ret); + return -EINVAL; + } + + D(KERN_ERR "%s: ", __func__); + D_DUMP_BUFFER("We got", len, locbuf); + + running = locbuf; + + token = strsep(&running, delimiters); + while (token && vals < ARRAY_SIZE(val)) { + if (*token != '\0') { + D(KERN_ERR "%s: ", __func__); + D_DUMP_BUFFER("", strlen(token), token); + ret = strict_strtoul(token, 0, &res); + if (ret) { + printk(KERN_ERR "ERROR: %s:%i got bad char " + "at strict_strtoul\n", + __func__, __LINE__-4); + return -EINVAL; + } + val[vals++] = res; + } + token = strsep(&running, delimiters); + } + + if (vals > 5) { + if (inst->which_log == GEN) + smem_log_event6(val[0], val[2], val[3], val[4], + val[7], val[8], val[9]); + else if (inst->which_log == STA) + smem_log_event6_to_static(val[0], + val[2], val[3], val[4], + val[7], val[8], val[9]); + else + return -1; + } else { + if (inst->which_log == GEN) + smem_log_event(val[0], val[2], val[3], val[4]); + else if (inst->which_log == STA) + smem_log_event_to_static(val[0], + val[2], val[3], val[4]); + else + return -1; + } + + return count; +} + +static int smem_log_open(struct inode *ip, struct file *fp) +{ + fp->private_data = &inst[GEN]; + + return 0; +} + + +static int smem_log_release(struct inode *ip, struct file *fp) +{ + return 0; +} + +static int smem_log_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg); + +static const struct file_operations smem_log_fops = { + .owner = THIS_MODULE, + .read = smem_log_read, + .write = smem_log_write, + .open = smem_log_open, + .release = smem_log_release, + .ioctl = smem_log_ioctl, +}; + +static const struct file_operations smem_log_bin_fops = { + .owner = THIS_MODULE, + .read = smem_log_read_bin, + .write = smem_log_write_bin, + .open = smem_log_open, + .release = smem_log_release, + .ioctl = smem_log_ioctl, +}; + +static int smem_log_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg) +{ + struct smem_log_inst *inst; + + inst = fp->private_data; + + switch (cmd) { + default: + return -ENOTTY; + + case SMIOC_SETMODE: + if (arg == SMIOC_TEXT) { + D("%s set text mode\n", __func__); + fp->f_op = &smem_log_fops; + } else if (arg == SMIOC_BINARY) { + D("%s set bin mode\n", __func__); + fp->f_op = &smem_log_bin_fops; + } else { + return -EINVAL; + } + break; + case SMIOC_SETLOG: + if (arg == SMIOC_LOG) + fp->private_data = &inst[GEN]; + else if (arg == SMIOC_STATIC_LOG) + fp->private_data = &inst[STA]; + else + return -EINVAL; + break; + } + + return 0; +} + +static struct miscdevice smem_log_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "smem_log", + .fops = &smem_log_fops, +}; + +#if defined(CONFIG_DEBUG_FS) + +static int _debug_dump(int log, char *buf, int max) +{ + unsigned int idx; + int orig_idx; + unsigned long flags; + int i = 0; + + if (!inst[log].events) + return 0; + + remote_spin_lock_irqsave(inst[log].remote_spinlock, flags); + + orig_idx = *inst[log].idx; + idx = orig_idx; + + while (1) { + idx++; + if (idx > inst[log].num - 1) + idx = 0; + if (idx == orig_idx) + break; + + if (idx < inst[log].num) { + if (!inst[log].events[idx].identifier) + continue; + + i += scnprintf(buf + i, max - i, + "%08x %08x %08x %08x %08x\n", + inst[log].events[idx].identifier, + inst[log].events[idx].timetick, + inst[log].events[idx].data1, + inst[log].events[idx].data2, + inst[log].events[idx].data3); + } + } + + remote_spin_unlock_irqrestore(inst[log].remote_spinlock, flags); + + return i; +} + +static int _debug_dump_sym(int log, char *buf, int max) +{ + unsigned int idx; + int orig_idx; + unsigned long flags; + int i = 0; + + char *proc; + char *sub; + char *id; + char *sym = NULL; + + uint32_t data[3]; + + uint32_t proc_val = 0; + uint32_t sub_val = 0; + uint32_t id_val = 0; + uint32_t id_only_val = 0; + uint32_t data1 = 0; + uint32_t data2 = 0; + uint32_t data3 = 0; + + int k; + + if (!inst[log].events) + return 0; + + find_voters(); /* need to call each time in case voters come and go */ + + i += scnprintf(buf + i, max - i, "Voters:\n"); + for (k = 0; k < ARRAY_SIZE(voter_d3_syms); ++k) + if (voter_d3_syms[k].str) + i += scnprintf(buf + i, max - i, "%s ", + voter_d3_syms[k].str); + for (k = 0; k < ARRAY_SIZE(voter_d2_syms); ++k) + if (voter_d2_syms[k].str) + i += scnprintf(buf + i, max - i, "%s ", + voter_d2_syms[k].str); + i += scnprintf(buf + i, max - i, "\n"); + + remote_spin_lock_irqsave(inst[log].remote_spinlock, flags); + + orig_idx = *inst[log].idx; + idx = orig_idx; + + while (1) { + idx++; + if (idx > inst[log].num - 1) + idx = 0; + if (idx == orig_idx) { + i += scnprintf(buf + i, max - i, "\n"); + break; + } + if (idx < inst[log].num) { + if (!inst[log].events[idx].identifier) + continue; + + proc_val = PROC & inst[log].events[idx].identifier; + sub_val = SUB & inst[log].events[idx].identifier; + id_val = (SUB | ID) & inst[log].events[idx].identifier; + id_only_val = ID & inst[log].events[idx].identifier; + data1 = inst[log].events[idx].data1; + data2 = inst[log].events[idx].data2; + data3 = inst[log].events[idx].data3; + + if (!(proc_val & SMEM_LOG_CONT)) { + i += scnprintf(buf + i, max - i, "\n"); + + proc = find_sym(ID_SYM, proc_val); + + if (proc) + i += scnprintf(buf + i, max - i, + "%4s: ", + proc); + else + i += scnprintf(buf + i, max - i, + "%04x: ", + PROC & + inst[log].events[idx]. + identifier); + + i += scnprintf(buf + i, max - i, + "%10u ", + inst[log].events[idx].timetick); + + sub = find_sym(BASE_SYM, sub_val); + + if (sub) + i += scnprintf(buf + i, max - i, + "%9s: ", + sub); + else + i += scnprintf(buf + i, max - i, + "%08x: ", + sub_val); + + id = find_sym(EVENT_SYM, id_val); + + if (id) + i += scnprintf(buf + i, max - i, + "%11s: ", + id); + else + i += scnprintf(buf + i, max - i, + "%08x: ", + id_only_val); + } + + if ((proc_val & SMEM_LOG_CONT) && + (id_val == ONCRPC_LOG_EVENT_STD_CALL || + id_val == ONCRPC_LOG_EVENT_STD_REPLY)) { + data[0] = data1; + data[1] = data2; + data[2] = data3; + i += scnprintf(buf + i, max - i, + " %.16s", + (char *) data); + } else if (proc_val & SMEM_LOG_CONT) { + i += scnprintf(buf + i, max - i, + " %08x %08x %08x", + data1, + data2, + data3); + } else if (id_val == ONCRPC_LOG_EVENT_STD_CALL) { + sym = find_sym(ONCRPC_SYM, data2); + + if (sym) + i += scnprintf(buf + i, max - i, + "xid:%4i %8s proc:%3i", + data1, + sym, + data3); + else + i += scnprintf(buf + i, max - i, + "xid:%4i %08x proc:%3i", + data1, + data2, + data3); +#if defined(CONFIG_MSM_N_WAY_SMSM) + } else if (id_val == DEM_STATE_CHANGE) { + if (data1 == 1) { + i += scnprintf(buf + i, + max - i, + "MASTER: "); + sym = find_sym(DEM_STATE_MASTER_SYM, + data2); + } else if (data1 == 0) { + i += scnprintf(buf + i, + max - i, + " SLAVE: "); + sym = find_sym(DEM_STATE_SLAVE_SYM, + data2); + } else { + i += scnprintf(buf + i, + max - i, + "%x: ", + data1); + sym = NULL; + } + if (sym) + i += scnprintf(buf + i, + max - i, + "from:%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "from:0x%x ", + data2); + + if (data1 == 1) + sym = find_sym(DEM_STATE_MASTER_SYM, + data3); + else if (data1 == 0) + sym = find_sym(DEM_STATE_SLAVE_SYM, + data3); + else + sym = NULL; + if (sym) + i += scnprintf(buf + i, + max - i, + "to:%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "to:0x%x ", + data3); + + } else if (id_val == DEM_STATE_MACHINE_ENTER) { + i += scnprintf(buf + i, + max - i, + "swfi:%i timer:%i manexit:%i", + data1, data2, data3); + + } else if (id_val == DEM_TIME_SYNC_REQUEST || + id_val == DEM_TIME_SYNC_POLL || + id_val == DEM_TIME_SYNC_INIT) { + sym = find_sym(SMSM_ENTRY_TYPE_SYM, + data1); + if (sym) + i += scnprintf(buf + i, + max - i, + "hostid:%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "hostid:%x", + data1); + + } else if (id_val == DEM_TIME_SYNC_START || + id_val == DEM_TIME_SYNC_SEND_VALUE) { + unsigned mask = 0x1; + unsigned tmp = 0; + if (id_val == DEM_TIME_SYNC_START) + i += scnprintf(buf + i, + max - i, + "req:"); + else + i += scnprintf(buf + i, + max - i, + "pol:"); + while (mask) { + if (mask & data1) { + sym = find_sym( + SMSM_ENTRY_TYPE_SYM, + tmp); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%i ", + tmp); + } + mask <<= 1; + tmp++; + } + if (id_val == DEM_TIME_SYNC_SEND_VALUE) + i += scnprintf(buf + i, + max - i, + "tick:%x", + data2); + } else if (id_val == DEM_SMSM_ISR) { + unsigned vals[] = {data2, data3}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + sym = find_sym(SMSM_ENTRY_TYPE_SYM, + data1); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%x ", + data1); + + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + i += scnprintf(buf + i, max - i, "["); + mask = 0x80000000; + once = 0; + while (mask) { + tmp = vals[j] & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(SMSM_STATE_SYM, + tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "0x%x", + tmp); + once = 1; + } + i += scnprintf(buf + i, max - i, "] "); + } +#else + } else if (id_val == DEMAPPS_WAKEUP_REASON) { + unsigned mask = 0x80000000; + unsigned tmp = 0; + while (mask) { + tmp = data1 & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(WAKEUP_SYM, tmp); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s ", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x ", + tmp); + } + i += scnprintf(buf + i, max - i, + "%08x %08x", + data2, + data3); + } else if (id_val == DEMMOD_APPS_WAKEUP_INT) { + sym = find_sym(WAKEUP_INT_SYM, data1); + + if (sym) + i += scnprintf(buf + i, max - i, + "%s %08x %08x", + sym, + data2, + data3); + else + i += scnprintf(buf + i, max - i, + "%08x %08x %08x", + data1, + data2, + data3); + } else if (id_val == DEM_NO_SLEEP || + id_val == NO_SLEEP_NEW) { + unsigned vals[] = {data3, data2}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + i += scnprintf(buf + i, max - i, "%08x ", + data1); + i += scnprintf(buf + i, max - i, "["); + once = 0; + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + mask = 0x00000001; + while (mask) { + tmp = vals[j] & mask; + mask <<= 1; + if (!tmp) + continue; + if (j == 0) + sym = find_sym( + VOTER_D3_SYM, + tmp); + else + sym = find_sym( + VOTER_D2_SYM, + tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x", + tmp); + once = 1; + } + } + i += scnprintf(buf + i, max - i, "] "); +#endif + } else if (id_val == SMEM_LOG_EVENT_CB) { + unsigned vals[] = {data2, data3}; + unsigned j; + unsigned mask; + unsigned tmp; + unsigned once; + i += scnprintf(buf + i, max - i, "%08x ", + data1); + for (j = 0; j < ARRAY_SIZE(vals); ++j) { + i += scnprintf(buf + i, max - i, "["); + mask = 0x80000000; + once = 0; + while (mask) { + tmp = vals[j] & mask; + mask >>= 1; + if (!tmp) + continue; + sym = find_sym(SMSM_SYM, tmp); + + if (once) + i += scnprintf(buf + i, + max - i, + " "); + if (sym) + i += scnprintf(buf + i, + max - i, + "%s", + sym); + else + i += scnprintf(buf + i, + max - i, + "%08x", + tmp); + once = 1; + } + i += scnprintf(buf + i, max - i, "] "); + } + } else { + i += scnprintf(buf + i, max - i, + "%08x %08x %08x", + data1, + data2, + data3); + } + } + } + + remote_spin_unlock_irqrestore(inst[log].remote_spinlock, flags); + + return i; +} + +static int debug_dump(char *buf, int max) +{ + return _debug_dump(GEN, buf, max); +} + +static int debug_dump_sym(char *buf, int max) +{ + return _debug_dump_sym(GEN, buf, max); +} + +static int debug_dump_static(char *buf, int max) +{ + return _debug_dump(STA, buf, max); +} + +static int debug_dump_static_sym(char *buf, int max) +{ + return _debug_dump_sym(STA, buf, max); +} + +static int debug_dump_power(char *buf, int max) +{ + return _debug_dump(POW, buf, max); +} + +static int debug_dump_power_sym(char *buf, int max) +{ + return _debug_dump_sym(POW, buf, max); +} + +#define SMEM_LOG_ITEM_PRINT_SIZE 160 + +#define EVENTS_PRINT_SIZE \ +(SMEM_LOG_ITEM_PRINT_SIZE * SMEM_LOG_NUM_ENTRIES) + +static char debug_buffer[EVENTS_PRINT_SIZE]; + +static ssize_t debug_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int (*fill)(char *buf, int max) = file->private_data; + int bsize = fill(debug_buffer, EVENTS_PRINT_SIZE); + return simple_read_from_buffer(buf, count, ppos, debug_buffer, + bsize); +} + +static int debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static const struct file_operations debug_ops = { + .read = debug_read, + .open = debug_open, +}; + +static void debug_create(const char *name, mode_t mode, + struct dentry *dent, + int (*fill)(char *buf, int max)) +{ + debugfs_create_file(name, mode, dent, fill, &debug_ops); +} + +static void smem_log_debugfs_init(void) +{ + struct dentry *dent; + + dent = debugfs_create_dir("smem_log", 0); + if (IS_ERR(dent)) + return; + + debug_create("dump", 0444, dent, debug_dump); + debug_create("dump_sym", 0444, dent, debug_dump_sym); + debug_create("dump_static", 0444, dent, debug_dump_static); + debug_create("dump_static_sym", 0444, dent, debug_dump_static_sym); + debug_create("dump_power", 0444, dent, debug_dump_power); + debug_create("dump_power_sym", 0444, dent, debug_dump_power_sym); +} +#else +static void smem_log_debugfs_init(void) {} +#endif + +static int __init smem_log_init(void) +{ + int ret; + + ret = _smem_log_init(); + if (ret < 0) + return ret; + + smem_log_debugfs_init(); + + return misc_register(&smem_log_dev); +} + + +module_init(smem_log_init); + +MODULE_DESCRIPTION("smem log"); +MODULE_LICENSE("GPL v2"); diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c new file mode 100644 index 000000000000..0c65e25c9c66 --- /dev/null +++ b/arch/arm/mach-msm/socinfo.c @@ -0,0 +1,430 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora Forum nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * Alternatively, provided that this notice is retained in full, this software + * may be relicensed by the recipient under the terms of the GNU General Public + * License version 2 ("GPL") and only version 2, in which case the provisions of + * the GPL apply INSTEAD OF those given above. If the recipient relicenses the + * software under the GPL, then the identification text in the MODULE_LICENSE + * macro must be changed to reflect "GPLv2" instead of "Dual BSD/GPL". Once a + * recipient changes the license terms to the GPL, subsequent recipients shall + * not relicense under alternate licensing terms, including the BSD or dual + * BSD/GPL terms. In addition, the following license statement immediately + * below and between the words START and END shall also then apply when this + * software is relicensed under the GPL: + * + * START + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 and only version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * END + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +/* + * SOC Info Routines + * + */ + +#include <linux/types.h> +#include <linux/sysdev.h> +#include "socinfo.h" +#include "smd_private.h" + +enum { + HW_PLATFORM_UNKNOWN = 0, + HW_PLATFORM_SURF = 1, + HW_PLATFORM_FFA = 2, + HW_PLATFORM_FLUID = 3, + HW_PLATFORM_INVALID +}; + +char *hw_platform[] = { + "Unknown", + "Surf", + "FFA", + "Fluid" +}; + +/* Used to parse shared memory. Must match the modem. */ +struct socinfo_v1 { + uint32_t format; + uint32_t id; + uint32_t version; + char build_id[32]; +}; + +struct socinfo_v2 { + struct socinfo_v1 v1; + + /* only valid when format==2 */ + uint32_t raw_id; + uint32_t raw_version; +}; + +struct socinfo_v3 { + struct socinfo_v2 v2; + + /* only valid when format==3 */ + uint32_t hw_platform; +}; + +static union { + struct socinfo_v1 v1; + struct socinfo_v2 v2; + struct socinfo_v3 v3; +} *socinfo; + +static enum msm_cpu cpu_of_id[] = { + + /* 7x01 IDs */ + [1] = MSM_CPU_7X01, + [16] = MSM_CPU_7X01, + [17] = MSM_CPU_7X01, + [18] = MSM_CPU_7X01, + [19] = MSM_CPU_7X01, + [23] = MSM_CPU_7X01, + [25] = MSM_CPU_7X01, + [26] = MSM_CPU_7X01, + [32] = MSM_CPU_7X01, + [33] = MSM_CPU_7X01, + [34] = MSM_CPU_7X01, + [35] = MSM_CPU_7X01, + + /* 7x25 IDs */ + [20] = MSM_CPU_7X25, + [21] = MSM_CPU_7X25, + [24] = MSM_CPU_7X25, + [27] = MSM_CPU_7X25, + [39] = MSM_CPU_7X25, + [40] = MSM_CPU_7X25, + [41] = MSM_CPU_7X25, + [42] = MSM_CPU_7X25, + + /* 7x27 IDs */ + [43] = MSM_CPU_7X27, + [44] = MSM_CPU_7X27, + [61] = MSM_CPU_7X27, + + /* 8x50 IDs */ + [30] = MSM_CPU_8X50, + [36] = MSM_CPU_8X50, + [37] = MSM_CPU_8X50, + [38] = MSM_CPU_8X50, + + /* 7x30 IDs */ + [59] = MSM_CPU_7X30, + [60] = MSM_CPU_7X30, + + /* Uninitialized IDs are not known to run Linux. + MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are + considered as unknown CPU. Any ID > 60 is invalid. */ +}; + +static enum msm_cpu cur_cpu; + +uint32_t socinfo_get_id(void) +{ + return (socinfo) ? socinfo->v1.id : 0; +} + +uint32_t socinfo_get_version(void) +{ + return (socinfo) ? socinfo->v1.version : 0; +} + +char *socinfo_get_build_id(void) +{ + return (socinfo) ? socinfo->v1.build_id : NULL; +} + +uint32_t socinfo_get_raw_id(void) +{ + return socinfo ? + (socinfo->v1.format == 2 ? socinfo->v2.raw_id : 0) + : 0; +} + +uint32_t socinfo_get_raw_version(void) +{ + return socinfo ? + (socinfo->v1.format == 2 ? socinfo->v2.raw_version : 0) + : 0; +} + +uint32_t socinfo_get_platform_type(void) +{ + return socinfo ? + (socinfo->v1.format == 3 ? socinfo->v3.hw_platform : 0) + : 0; +} + +enum msm_cpu socinfo_get_msm_cpu(void) +{ + return cur_cpu; +} + +static ssize_t +socinfo_show_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_id()); +} + +static ssize_t +socinfo_show_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t version; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + version = socinfo_get_version(); + return snprintf(buf, PAGE_SIZE, "%u.%u\n", + SOCINFO_VERSION_MAJOR(version), + SOCINFO_VERSION_MINOR(version)); +} + +static ssize_t +socinfo_show_build_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", socinfo_get_build_id()); +} + +static ssize_t +socinfo_show_raw_id(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format != 2) { + pr_err("%s: Raw ID not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_id()); +} + +static ssize_t +socinfo_show_raw_version(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format != 2) { + pr_err("%s: Raw version not available!\n", __func__); + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", socinfo_get_raw_version()); +} + +static ssize_t +socinfo_show_platform_type(struct sys_device *dev, + struct sysdev_attribute *attr, + char *buf) +{ + uint32_t hw_type; + + if (!socinfo) { + pr_err("%s: No socinfo found!\n", __func__); + return 0; + } + if (socinfo->v1.format != 3) { + pr_err("%s: platform type not available!\n", __func__); + return 0; + } + + hw_type = socinfo_get_platform_type(); + if (hw_type >= HW_PLATFORM_INVALID) { + pr_err("%s: Invalid hardware platform type found\n", + __func__); + hw_type = HW_PLATFORM_UNKNOWN; + } + + return snprintf(buf, PAGE_SIZE, "%-.32s\n", hw_platform[hw_type]); +} + +static struct sysdev_attribute socinfo_v1_files[] = { + _SYSDEV_ATTR(id, 0444, socinfo_show_id, NULL), + _SYSDEV_ATTR(version, 0444, socinfo_show_version, NULL), + _SYSDEV_ATTR(build_id, 0444, socinfo_show_build_id, NULL), +}; + +static struct sysdev_attribute socinfo_v2_files[] = { + _SYSDEV_ATTR(raw_id, 0444, socinfo_show_raw_id, NULL), + _SYSDEV_ATTR(raw_version, 0444, socinfo_show_raw_version, NULL), +}; + +static struct sysdev_attribute socinfo_v3_files[] = { + _SYSDEV_ATTR(hw_platform, 0444, socinfo_show_platform_type, NULL), +}; + +static struct sysdev_class soc_sysdev_class = { + .name = "soc", +}; + +static struct sys_device soc_sys_device = { + .id = 0, + .cls = &soc_sysdev_class, +}; + +static void __init socinfo_create_files(struct sys_device *dev, + struct sysdev_attribute files[], + int size) +{ + int i; + for (i = 0; i < size; i++) { + int err = sysdev_create_file(dev, &files[i]); + if (err) { + pr_err("%s: sysdev_create_file(%s)=%d\n", + __func__, files[i].attr.name, err); + return; + } + } +} + +static void __init socinfo_init_sysdev(void) +{ + int err; + + err = sysdev_class_register(&soc_sysdev_class); + if (err) { + pr_err("%s: sysdev_class_register fail (%d)\n", + __func__, err); + return; + } + err = sysdev_register(&soc_sys_device); + if (err) { + pr_err("%s: sysdev_register fail (%d)\n", + __func__, err); + return; + } + socinfo_create_files(&soc_sys_device, socinfo_v1_files, + ARRAY_SIZE(socinfo_v1_files)); + if (socinfo->v1.format < 2) + return; + socinfo_create_files(&soc_sys_device, socinfo_v2_files, + ARRAY_SIZE(socinfo_v2_files)); + + if (socinfo->v1.format < 3) + return; + + socinfo_create_files(&soc_sys_device, socinfo_v3_files, + ARRAY_SIZE(socinfo_v3_files)); + +} + +int __init socinfo_init(void) +{ + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, sizeof(struct socinfo_v3)); + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v2)); + + if (!socinfo) + socinfo = smem_alloc(SMEM_HW_SW_BUILD_ID, + sizeof(struct socinfo_v1)); + + if (!socinfo) { + pr_err("%s: Can't find SMEM_HW_SW_BUILD_ID\n", + __func__); + return -EIO; + } + + WARN(!socinfo_get_id(), "Unknown SOC ID!\n"); + WARN(socinfo_get_id() >= ARRAY_SIZE(cpu_of_id), + "New IDs added! ID => CPU mapping might need an update.\n"); + + if (socinfo->v1.id < ARRAY_SIZE(cpu_of_id)) + cur_cpu = cpu_of_id[socinfo->v1.id]; + + socinfo_init_sysdev(); + + switch (socinfo->v1.format) { + case 1: + pr_info("%s: v%u, id=%u, ver=%u.%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version)); + break; + case 2: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version); + break; + case 3: + pr_info("%s: v%u, id=%u, ver=%u.%u, " + "raw_id=%u, raw_ver=%u, hw_plat=%u\n", + __func__, socinfo->v1.format, socinfo->v1.id, + SOCINFO_VERSION_MAJOR(socinfo->v1.version), + SOCINFO_VERSION_MINOR(socinfo->v1.version), + socinfo->v2.raw_id, socinfo->v2.raw_version, + socinfo->v3.hw_platform); + break; + default: + pr_err("%s: Unknown format found\n", __func__); + break; + } + + return 0; +} diff --git a/arch/arm/mach-msm/socinfo.h b/arch/arm/mach-msm/socinfo.h new file mode 100644 index 000000000000..a3c04b19f4ba --- /dev/null +++ b/arch/arm/mach-msm/socinfo.h @@ -0,0 +1,97 @@ +/* Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_SOCINFO_H_ +#define _ARCH_ARM_MACH_MSM_SOCINFO_H_ + +/* + * SOC version type with major number in the upper 16 bits and minor + * number in the lower 16 bits. For example: + * 1.0 -> 0x00010000 + * 2.3 -> 0x00020003 + */ +#define SOCINFO_VERSION_MAJOR(ver) ((ver & 0xffff0000) >> 16) +#define SOCINFO_VERSION_MINOR(ver) (ver & 0x0000ffff) + +enum msm_cpu { + MSM_CPU_UNKNOWN = 0, + MSM_CPU_7X01, + MSM_CPU_7X25, + MSM_CPU_7X27, + MSM_CPU_8X50, + MSM_CPU_7X30, +}; + +enum msm_cpu socinfo_get_msm_cpu(void); +uint32_t socinfo_get_id(void); +uint32_t socinfo_get_version(void); +char *socinfo_get_build_id(void); +uint32_t socinfo_get_platform_type(void); +int __init socinfo_init(void) __must_check; + +static inline int cpu_is_msm7x01(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X01; +} + +static inline int cpu_is_msm7x25(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X25; +} + +static inline int cpu_is_msm7x27(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X27; +} + +static inline int cpu_is_qsd8x50(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_8X50; +} + +static inline int cpu_is_msm7x30(void) +{ + enum msm_cpu cpu = socinfo_get_msm_cpu(); + + BUG_ON(cpu == MSM_CPU_UNKNOWN); + return cpu == MSM_CPU_7X30; +} + +#endif diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 4855b8ca5101..02fbdca60525 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -1,6 +1,7 @@ /* linux/arch/arm/mach-msm/timer.c * * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -25,21 +26,62 @@ #include <asm/mach/time.h> #include <mach/msm_iomap.h> -#define MSM_DGT_BASE (MSM_GPT_BASE + 0x10) +#include "smd_private.h" +#include "timer.h" + +enum { + MSM_TIMER_DEBUG_SYNC = 1U << 0, +}; +static int msm_timer_debug_mask; +module_param_named(debug_mask, msm_timer_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#if defined(CONFIG_ARCH_MSM7X30) +#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4) +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24) +#else +#define MSM_GPT_BASE MSM_TMR_BASE +#define MSM_DGT_BASE (MSM_TMR_BASE + 0x10) +#endif + +#if defined(CONFIG_ARCH_MSM_ARM11) #define MSM_DGT_SHIFT (5) +#else +#define MSM_DGT_SHIFT (0) +#endif #define TIMER_MATCH_VAL 0x0000 #define TIMER_COUNT_VAL 0x0004 #define TIMER_ENABLE 0x0008 -#define TIMER_ENABLE_CLR_ON_MATCH_EN 2 -#define TIMER_ENABLE_EN 1 -#define TIMER_CLEAR 0x000C +#define TIMER_ENABLE_EN 1 -#define CSR_PROTECTION 0x0020 -#define CSR_PROTECTION_EN 1 +#if defined(CONFIG_ARCH_QSD8X50) +#define DGT_HZ 4800000 /* Uses TCXO/4 (19.2 MHz / 4) */ +#elif defined(CONFIG_ARCH_MSM7X30) +#define DGT_HZ 6144000 /* Uses LPXO/4 (24.576 MHz / 4) */ +#else +#define DGT_HZ 19200000 /* Uses TCXO (19.2 MHz) */ +#endif #define GPT_HZ 32768 -#define DGT_HZ 19200000 /* 19.2 MHz or 600 KHz after shift */ +#define SCLK_HZ 32768 + +#if defined(CONFIG_MSM_N_WAY_SMSM) +/* Time Master State Bits */ +#define MASTER_BITS_PER_CPU 1 +#define MASTER_TIME_PENDING \ + (0x01UL << (MASTER_BITS_PER_CPU * SMSM_APPS_STATE)) + +/* Time Slave State Bits */ +#define SLAVE_TIME_REQUEST 0x0400 +#define SLAVE_TIME_POLL 0x0800 +#define SLAVE_TIME_INIT 0x1000 +#endif + +enum { + MSM_CLOCK_FLAGS_UNSTABLE_COUNT = 1U << 0, + MSM_CLOCK_FLAGS_ODD_MATCH_WRITE = 1U << 1, + MSM_CLOCK_FLAGS_DELAYED_WRITE_POST = 1U << 2, +}; struct msm_clock { struct clock_event_device clockevent; @@ -48,6 +90,30 @@ struct msm_clock { void __iomem *regbase; uint32_t freq; uint32_t shift; + uint32_t flags; + uint32_t write_delay; + uint32_t last_set; + uint32_t sleep_offset; + uint32_t alarm_vtime; + uint32_t non_sleep_offset; + uint32_t in_sync; + cycle_t stopped_tick; + int stopped; + uint32_t rollover_offset; + uint32_t last_sync_gpt; + u64 last_sync_jiffies; +}; +enum { + MSM_CLOCK_GPT, + MSM_CLOCK_DGT, +}; +static struct msm_clock msm_clocks[]; +static struct msm_clock *msm_active_clock; + +struct msm_timer_sync_data_t { + struct msm_clock *clock; + uint32_t timeout; + int exit_sleep; }; static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) @@ -57,31 +123,82 @@ static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static uint32_t msm_read_timer_count(struct msm_clock *clock) +{ + uint32_t t1, t2; + int loop_count = 0; + + t1 = readl(clock->regbase + TIMER_COUNT_VAL); + if (!(clock->flags & MSM_CLOCK_FLAGS_UNSTABLE_COUNT)) + return t1; + while (1) { + t2 = readl(clock->regbase + TIMER_COUNT_VAL); + if (t1 == t2) + return t1; + if (loop_count++ > 10) { + printk(KERN_ERR "msm_read_timer_count timer %s did not" + "stabilize %u != %u\n", clock->clockevent.name, + t2, t1); + return t2; + } + t1 = t2; + } +} + static cycle_t msm_gpt_read(struct clocksource *cs) { - return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_GPT]; + if (clock->stopped) + return clock->stopped_tick; + else + return msm_read_timer_count(clock) + clock->sleep_offset; } static cycle_t msm_dgt_read(struct clocksource *cs) { - return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; + struct msm_clock *clock = &msm_clocks[MSM_CLOCK_DGT]; + if (clock->stopped) + return clock->stopped_tick >> MSM_DGT_SHIFT; + return (msm_read_timer_count(clock) + clock->sleep_offset) + >> MSM_DGT_SHIFT; } static int msm_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt) { - struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); - uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); - uint32_t alarm = now + (cycles << clock->shift); + int i; + struct msm_clock *clock; + uint32_t now; + uint32_t alarm; int late; + clock = container_of(evt, struct msm_clock, clockevent); + now = msm_read_timer_count(clock); + alarm = now + (cycles << clock->shift); + if (clock->flags & MSM_CLOCK_FLAGS_ODD_MATCH_WRITE) + while (now == clock->last_set) + now = msm_read_timer_count(clock); writel(alarm, clock->regbase + TIMER_MATCH_VAL); - now = readl(clock->regbase + TIMER_COUNT_VAL); + if (clock->flags & MSM_CLOCK_FLAGS_DELAYED_WRITE_POST) { + /* read the counter four extra times to make sure write posts + before reading the time */ + for (i = 0; i < 4; i++) + readl(clock->regbase + TIMER_COUNT_VAL); + } + now = msm_read_timer_count(clock); + clock->last_set = now; + clock->alarm_vtime = alarm + clock->sleep_offset; late = now - alarm; - if (late >= (-2 << clock->shift) && late < DGT_HZ*5) { - printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, " - "alarm already expired, now %x, alarm %x, late %d\n", - cycles, clock->clockevent.name, now, alarm, late); + if (late >= (int)(-clock->write_delay << clock->shift) && late < DGT_HZ*5) { + static int print_limit = 10; + if (print_limit > 0) { + print_limit--; + printk(KERN_NOTICE "msm_timer_set_next_event(%lu) " + "clock %s, alarm already expired, now %x, " + "alarm %x, late %d%s\n", + cycles, clock->clockevent.name, now, alarm, late, + print_limit ? "" : " stop printing"); + } return -ETIME; } return 0; @@ -90,23 +207,538 @@ static int msm_timer_set_next_event(unsigned long cycles, static void msm_timer_set_mode(enum clock_event_mode mode, struct clock_event_device *evt) { - struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock *clock; + unsigned long irq_flags; + + clock = container_of(evt, struct msm_clock, clockevent); + local_irq_save(irq_flags); + switch (mode) { case CLOCK_EVT_MODE_RESUME: case CLOCK_EVT_MODE_PERIODIC: break; case CLOCK_EVT_MODE_ONESHOT: + clock->stopped = 0; + clock->sleep_offset = -msm_read_timer_count(clock) + + clock->stopped_tick; + msm_active_clock = clock; writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); + if (clock != gpt_clk) + writel(TIMER_ENABLE_EN, + gpt_clk->regbase + TIMER_ENABLE); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: + msm_active_clock = NULL; + clock->in_sync = 0; + clock->stopped = 1; + clock->stopped_tick = msm_read_timer_count(clock) + + clock->sleep_offset; writel(0, clock->regbase + TIMER_ENABLE); + if (clock != gpt_clk) { + gpt_clk->in_sync = 0; + writel(0, gpt_clk->regbase + TIMER_ENABLE); + } break; } + local_irq_restore(irq_flags); } +/* + * Retrieve the cycle count from sclk and optionally synchronize local clock + * with the sclk value. + * + * time_start and time_expired are callbacks that must be specified. The + * protocol uses them to detect timeout. The update callback is optional. + * If not NULL, update will be called so that it can update local clock. + * + * The function does not use the argument data directly; it passes data to + * the callbacks. + * + * Return value: + * 0: the operation failed + * >0: the slow clock value after time-sync + */ +#if defined(CONFIG_MSM_N_WAY_SMSM) +static uint32_t msm_timer_do_sync_to_sclk( + void (*time_start)(struct msm_timer_sync_data_t *data), + bool (*time_expired)(struct msm_timer_sync_data_t *data), + void (*update)(struct msm_timer_sync_data_t *, uint32_t, uint32_t), + struct msm_timer_sync_data_t *data) +{ + uint32_t *smem_clock; + uint32_t smem_clock_val; + uint32_t state; + + smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE, sizeof(uint32_t)); + if (smem_clock == NULL) { + printk(KERN_ERR "no smem clock\n"); + return 0; + } + + state = smsm_get_state(SMSM_MODEM_STATE); + if ((state & SMSM_INIT) == 0) { + printk(KERN_ERR "smsm not initialized\n"); + return 0; + } + + time_start(data); + while ((state = smsm_get_state(SMSM_TIME_MASTER_DEM)) & + MASTER_TIME_PENDING) { + if (time_expired(data)) { + printk(KERN_INFO "get_smem_clock: timeout 1 still " + "invalid state %x\n", state); + return 0; + } + } + + smsm_change_state(SMSM_APPS_DEM, SLAVE_TIME_POLL | SLAVE_TIME_INIT, + SLAVE_TIME_REQUEST); + + time_start(data); + while (!((state = smsm_get_state(SMSM_TIME_MASTER_DEM)) & + MASTER_TIME_PENDING)) { + if (time_expired(data)) { + printk(KERN_INFO "get_smem_clock: timeout 2 still " + "invalid state %x\n", state); + smem_clock_val = 0; + goto sync_sclk_exit; + } + } + + smsm_change_state(SMSM_APPS_DEM, SLAVE_TIME_REQUEST, SLAVE_TIME_POLL); + + time_start(data); + do { + smem_clock_val = *smem_clock; + } while (smem_clock_val == 0 && !time_expired(data)); + + state = smsm_get_state(SMSM_TIME_MASTER_DEM); + + if (smem_clock_val) { + if (update != NULL) + update(data, smem_clock_val, SCLK_HZ); + + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO + "get_smem_clock: state %x clock %u\n", + state, smem_clock_val); + } else { + printk(KERN_INFO "get_smem_clock: timeout state %x clock %u\n", + state, smem_clock_val); + } + +sync_sclk_exit: + smsm_change_state(SMSM_APPS_DEM, SLAVE_TIME_REQUEST | SLAVE_TIME_POLL, + SLAVE_TIME_INIT); + return smem_clock_val; +} +#else /* CONFIG_MSM_N_WAY_SMSM */ +static uint32_t msm_timer_do_sync_to_sclk( + void (*time_start)(struct msm_timer_sync_data_t *data), + bool (*time_expired)(struct msm_timer_sync_data_t *data), + void (*update)(struct msm_timer_sync_data_t *, uint32_t, uint32_t), + struct msm_timer_sync_data_t *data) +{ + uint32_t *smem_clock; + uint32_t smem_clock_val; + uint32_t last_state; + uint32_t state; + + smem_clock = smem_alloc(SMEM_SMEM_SLOW_CLOCK_VALUE, + sizeof(uint32_t)); + + if (smem_clock == NULL) { + printk(KERN_ERR "no smem clock\n"); + return 0; + } + + last_state = state = smsm_get_state(SMSM_MODEM_STATE); + smem_clock_val = *smem_clock; + if (smem_clock_val) { + printk(KERN_INFO "get_smem_clock: invalid start state %x " + "clock %u\n", state, smem_clock_val); + smsm_change_state(SMSM_APPS_STATE, + SMSM_TIMEWAIT, SMSM_TIMEINIT); + + time_start(data); + while (*smem_clock != 0 && !time_expired(data)) + ; + + smem_clock_val = *smem_clock; + if (smem_clock_val) { + printk(KERN_INFO "get_smem_clock: timeout still " + "invalid state %x clock %u\n", + state, smem_clock_val); + return 0; + } + } + + time_start(data); + smsm_change_state(SMSM_APPS_STATE, SMSM_TIMEINIT, SMSM_TIMEWAIT); + do { + smem_clock_val = *smem_clock; + state = smsm_get_state(SMSM_MODEM_STATE); + if (state != last_state) { + last_state = state; + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO + "get_smem_clock: state %x clock %u\n", + state, smem_clock_val); + } + } while (smem_clock_val == 0 && !time_expired(data)); + + if (smem_clock_val) { + if (update != NULL) + update(data, smem_clock_val, SCLK_HZ); + } else { + printk(KERN_INFO "get_smem_clock: timeout state %x clock %u\n", + state, smem_clock_val); + } + + smsm_change_state(SMSM_APPS_STATE, SMSM_TIMEWAIT, SMSM_TIMEINIT); + time_start(data); + while (*smem_clock != 0 && !time_expired(data)) + ; + + if (*smem_clock) + printk(KERN_INFO "get_smem_clock: exit timeout state %x " + "clock %u\n", state, *smem_clock); + return smem_clock_val; +} +#endif /* CONFIG_MSM_N_WAY_SMSM */ + +/* + * Callback function that initializes the timeout value. + */ +static void msm_timer_sync_to_sclk_time_start( + struct msm_timer_sync_data_t *data) +{ + /* approx 1/128th of a second */ + uint32_t delta = data->clock->freq >> 7 << data->clock->shift; + data->timeout = msm_read_timer_count(data->clock) + delta; +} + +/* + * Callback function that checks the timeout. + */ +static bool msm_timer_sync_to_sclk_time_expired( + struct msm_timer_sync_data_t *data) +{ + uint32_t delta = msm_read_timer_count(data->clock) - data->timeout; + return ((int32_t) delta) > 0; +} + +/* + * Callback function that updates local clock from the specified source clock + * value and frequency. + */ +static void msm_timer_sync_update(struct msm_timer_sync_data_t *data, + uint32_t src_clk_val, uint32_t src_clk_freq) +{ + struct msm_clock *dst_clk = data->clock; + uint32_t dst_clk_val = msm_read_timer_count(dst_clk); + uint32_t new_offset; + + if ((dst_clk->freq << dst_clk->shift) == src_clk_freq) { + new_offset = src_clk_val - dst_clk_val; + } else { + uint64_t temp; + + /* separate multiplication and division steps to reduce + rounding error */ + temp = src_clk_val; + temp *= dst_clk->freq << dst_clk->shift; + do_div(temp, src_clk_freq); + + new_offset = (uint32_t)(temp) - dst_clk_val; + } + + if (dst_clk->sleep_offset + dst_clk->non_sleep_offset != new_offset) { + if (data->exit_sleep) + dst_clk->sleep_offset = + new_offset - dst_clk->non_sleep_offset; + else + dst_clk->non_sleep_offset = + new_offset - dst_clk->sleep_offset; + + if (msm_timer_debug_mask & MSM_TIMER_DEBUG_SYNC) + printk(KERN_INFO "sync clock %s: " + "src %u, new offset %u + %u\n", + dst_clk->clocksource.name, src_clk_val, + dst_clk->sleep_offset, + dst_clk->non_sleep_offset); + } +} + +/* + * Synchronize GPT clock with sclk. + */ +static void msm_timer_sync_gpt_to_sclk(int exit_sleep) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_timer_sync_data_t data; + uint32_t ret; + + if (gpt_clk->in_sync) + return; + + data.clock = gpt_clk; + data.timeout = 0; + data.exit_sleep = exit_sleep; + + ret = msm_timer_do_sync_to_sclk( + msm_timer_sync_to_sclk_time_start, + msm_timer_sync_to_sclk_time_expired, + msm_timer_sync_update, + &data); + + if (ret) + gpt_clk->in_sync = 1; +} + +/* + * Synchronize clock with GPT clock. + */ +static void msm_timer_sync_to_gpt(struct msm_clock *clock, int exit_sleep) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_timer_sync_data_t data; + uint32_t gpt_clk_val; + u64 gpt_period = (1ULL << 32) * HZ / GPT_HZ; + u64 now = get_jiffies_64(); + + BUG_ON(clock == gpt_clk); + + if (clock->in_sync && + (now - clock->last_sync_jiffies < (gpt_period >> 1))) + return; + + gpt_clk_val = msm_read_timer_count(gpt_clk) + + gpt_clk->sleep_offset + gpt_clk->non_sleep_offset; + + if (exit_sleep && gpt_clk_val < clock->last_sync_gpt) + clock->non_sleep_offset -= clock->rollover_offset; + + data.clock = clock; + data.timeout = 0; + data.exit_sleep = exit_sleep; + + msm_timer_sync_update(&data, gpt_clk_val, GPT_HZ); + + clock->in_sync = 1; + clock->last_sync_gpt = gpt_clk_val; + clock->last_sync_jiffies = now; +} + +static void msm_timer_reactivate_alarm(struct msm_clock *clock) +{ + long alarm_delta = clock->alarm_vtime - clock->sleep_offset - + msm_read_timer_count(clock); + alarm_delta >>= clock->shift; + if (alarm_delta < (long)clock->write_delay + 4) + alarm_delta = clock->write_delay + 4; + while (msm_timer_set_next_event(alarm_delta, &clock->clockevent)) + ; +} + +int64_t msm_timer_enter_idle(void) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock *clock = msm_active_clock; + uint32_t alarm; + uint32_t count; + int32_t delta; + + BUG_ON(clock != &msm_clocks[MSM_CLOCK_GPT] && + clock != &msm_clocks[MSM_CLOCK_DGT]); + + msm_timer_sync_gpt_to_sclk(0); + if (clock != gpt_clk) + msm_timer_sync_to_gpt(clock, 0); + + count = msm_read_timer_count(clock); + if (clock->stopped++ == 0) + clock->stopped_tick = count + clock->sleep_offset; + alarm = readl(clock->regbase + TIMER_MATCH_VAL); + delta = alarm - count; + if (delta <= -(int32_t)((clock->freq << clock->shift) >> 10)) { + /* timer should have triggered 1ms ago */ + printk(KERN_ERR "msm_timer_enter_idle: timer late %d, " + "reprogram it\n", delta); + msm_timer_reactivate_alarm(clock); + } + if (delta <= 0) + return 0; + return clocksource_cyc2ns((alarm - count) >> clock->shift , clock->clocksource.mult, clock->clocksource.shift); +} + +void msm_timer_exit_idle(int low_power) +{ + struct msm_clock *gpt_clk = &msm_clocks[MSM_CLOCK_GPT]; + struct msm_clock *clock = msm_active_clock; + uint32_t enabled; + + BUG_ON(clock != &msm_clocks[MSM_CLOCK_GPT] && + clock != &msm_clocks[MSM_CLOCK_DGT]); + + if (!low_power) + goto exit_idle_exit; + + enabled = readl(gpt_clk->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN; + if (!enabled) + writel(TIMER_ENABLE_EN, gpt_clk->regbase + TIMER_ENABLE); + +#if defined(CONFIG_ARCH_MSM_SCORPION) + gpt_clk->in_sync = 0; +#else + gpt_clk->in_sync = gpt_clk->in_sync && enabled; +#endif + msm_timer_sync_gpt_to_sclk(1); + + if (clock == gpt_clk) + goto exit_idle_alarm; + + enabled = readl(clock->regbase + TIMER_ENABLE) & TIMER_ENABLE_EN; + if (!enabled) + writel(TIMER_ENABLE_EN, clock->regbase + TIMER_ENABLE); + +#if defined(CONFIG_ARCH_MSM_SCORPION) + clock->in_sync = 0; +#else + clock->in_sync = clock->in_sync && enabled; +#endif + msm_timer_sync_to_gpt(clock, 1); + +exit_idle_alarm: + msm_timer_reactivate_alarm(clock); + +exit_idle_exit: + clock->stopped--; +} + +/* + * Callback function that initializes the timeout value. + */ +static void msm_timer_get_sclk_time_start( + struct msm_timer_sync_data_t *data) +{ + data->timeout = 10000; +} + +/* + * Callback function that checks the timeout. + */ +static bool msm_timer_get_sclk_time_expired( + struct msm_timer_sync_data_t *data) +{ + return --data->timeout <= 0; +} + +/* + * Retrieve the cycle count from the sclk and convert it into + * nanoseconds. + * + * On exit, if period is not NULL, it contains the period of the + * sclk in nanoseconds, i.e. how long the cycle count wraps around. + * + * Return value: + * 0: the operation failed; period is not set either + * >0: time in nanoseconds + */ +int64_t msm_timer_get_sclk_time(int64_t *period) +{ + struct msm_timer_sync_data_t data; + uint32_t clock_value; + int64_t tmp; + + memset(&data, 0, sizeof(data)); + + clock_value = msm_timer_do_sync_to_sclk( + msm_timer_get_sclk_time_start, + msm_timer_get_sclk_time_expired, + NULL, + &data); + + if (!clock_value) + return 0; + + if (period) { + tmp = 1LL << 32; + tmp = tmp * NSEC_PER_SEC / SCLK_HZ; + *period = tmp; + } + + tmp = (int64_t)clock_value; + tmp = tmp * NSEC_PER_SEC / SCLK_HZ; + return tmp; +} + +int __init msm_timer_init_time_sync(void) +{ +#if defined(CONFIG_MSM_N_WAY_SMSM) + int ret = smsm_change_intr_mask(SMSM_TIME_MASTER_DEM, 0xFFFFFFFF, 0); + + if (ret) { + printk(KERN_ERR "%s: failed to clear interrupt mask, %d\n", + __func__, ret); + return ret; + } + + smsm_change_state(SMSM_APPS_DEM, + SLAVE_TIME_REQUEST | SLAVE_TIME_POLL, SLAVE_TIME_INIT); +#endif + + return 0; +} + +unsigned long long sched_clock(void) +{ + static cycle_t saved_ticks; + static int saved_ticks_valid; + static unsigned long long base; + static unsigned long long last_result; + + unsigned long irq_flags; + static cycle_t last_ticks; + cycle_t ticks; + static unsigned long long result; + struct clocksource *cs; + struct msm_clock *clock = msm_active_clock; + + local_irq_save(irq_flags); + if (clock) { + cs = &clock->clocksource; + + last_ticks = saved_ticks; + saved_ticks = ticks = cs->read(cs); + if (!saved_ticks_valid) { + saved_ticks_valid = 1; + last_ticks = ticks; + base -= clocksource_cyc2ns(ticks, cs->mult, cs->shift); + } + if (ticks < last_ticks) { + base += clocksource_cyc2ns(cs->mask, cs->mult, cs->shift); + base += clocksource_cyc2ns(1, cs->mult, cs->shift); + } + last_result = result = clocksource_cyc2ns(ticks, cs->mult, cs->shift) + base; + } else { + base = result = last_result; + saved_ticks_valid = 0; + } + local_irq_restore(irq_flags); + return result; +} + +#ifdef CONFIG_MSM7X00A_USE_GP_TIMER + #define DG_TIMER_RATING 100 +#else + #define DG_TIMER_RATING 300 +#endif + static struct msm_clock msm_clocks[] = { - { + [MSM_CLOCK_GPT] = { .clockevent = { .name = "gp_timer", .features = CLOCK_EVT_FEAT_ONESHOT, @@ -120,46 +752,54 @@ static struct msm_clock msm_clocks[] = { .rating = 200, .read = msm_gpt_read, .mask = CLOCKSOURCE_MASK(32), - .shift = 24, + .shift = 17, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }, .irq = { .name = "gp_timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, + .flags = IRQF_DISABLED | IRQF_TIMER | + IRQF_TRIGGER_RISING, .handler = msm_timer_interrupt, .dev_id = &msm_clocks[0].clockevent, .irq = INT_GP_TIMER_EXP }, .regbase = MSM_GPT_BASE, - .freq = GPT_HZ + .freq = GPT_HZ, + .flags = + MSM_CLOCK_FLAGS_UNSTABLE_COUNT | + MSM_CLOCK_FLAGS_ODD_MATCH_WRITE | + MSM_CLOCK_FLAGS_DELAYED_WRITE_POST, + .write_delay = 9, }, - { + [MSM_CLOCK_DGT] = { .clockevent = { .name = "dg_timer", .features = CLOCK_EVT_FEAT_ONESHOT, .shift = 32 + MSM_DGT_SHIFT, - .rating = 300, + .rating = DG_TIMER_RATING, .set_next_event = msm_timer_set_next_event, .set_mode = msm_timer_set_mode, }, .clocksource = { .name = "dg_timer", - .rating = 300, + .rating = DG_TIMER_RATING, .read = msm_dgt_read, - .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), + .mask = CLOCKSOURCE_MASK((32-MSM_DGT_SHIFT)), .shift = 24 - MSM_DGT_SHIFT, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }, .irq = { .name = "dg_timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, + .flags = IRQF_DISABLED | IRQF_TIMER | + IRQF_TRIGGER_RISING, .handler = msm_timer_interrupt, .dev_id = &msm_clocks[1].clockevent, .irq = INT_DEBUG_TIMER_EXP }, .regbase = MSM_DGT_BASE, .freq = DGT_HZ >> MSM_DGT_SHIFT, - .shift = MSM_DGT_SHIFT + .shift = MSM_DGT_SHIFT, + .write_delay = 2, } }; @@ -173,15 +813,27 @@ static void __init msm_timer_init(void) struct clock_event_device *ce = &clock->clockevent; struct clocksource *cs = &clock->clocksource; writel(0, clock->regbase + TIMER_ENABLE); - writel(0, clock->regbase + TIMER_CLEAR); writel(~0, clock->regbase + TIMER_MATCH_VAL); + if ((clock->freq << clock->shift) == GPT_HZ) { + clock->rollover_offset = 0; + } else { + uint64_t temp; + + temp = clock->freq << clock->shift; + temp <<= 32; + temp /= GPT_HZ; + + clock->rollover_offset = (uint32_t) temp; + } + ce->mult = div_sc(clock->freq, NSEC_PER_SEC, ce->shift); /* allow at least 10 seconds to notice that the timer wrapped */ ce->max_delta_ns = clockevent_delta2ns(0xf0000000 >> clock->shift, ce); - /* 4 gets rounded down to 3 */ - ce->min_delta_ns = clockevent_delta2ns(4, ce); + /* ticks gets rounded down by one */ + ce->min_delta_ns = + clockevent_delta2ns(clock->write_delay + 4, ce); ce->cpumask = cpumask_of(0); cs->mult = clocksource_hz2mult(clock->freq, cs->shift); diff --git a/arch/arm/mach-msm/timer.h b/arch/arm/mach-msm/timer.h new file mode 100644 index 000000000000..161751e99c36 --- /dev/null +++ b/arch/arm/mach-msm/timer.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Code Aurora nor + * the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef _ARCH_ARM_MACH_MSM_TIMER_H_ +#define _ARCH_ARM_MACH_MSM_TIMER_H_ + +extern struct sys_timer msm_timer; + +int64_t msm_timer_enter_idle(void); +void msm_timer_exit_idle(int low_power); +int64_t msm_timer_get_sclk_time(int64_t *period); +int msm_timer_init_time_sync(void); +#endif diff --git a/arch/arm/mach-msm/vreg.c b/arch/arm/mach-msm/vreg.c index fcb0b9f25684..cb7a71b55f43 100644 --- a/arch/arm/mach-msm/vreg.c +++ b/arch/arm/mach-msm/vreg.c @@ -1,6 +1,7 @@ /* arch/arm/mach-msm/vreg.c * * Copyright (C) 2008 Google, Inc. + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. * Author: Brian Swetland <swetland@google.com> * * This software is licensed under the terms of the GNU General Public @@ -18,49 +19,78 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/debugfs.h> +#include <linux/string.h> #include <mach/vreg.h> #include "proc_comm.h" +#if defined(CONFIG_MSM_VREG_SWITCH_INVERTED) +#define VREG_SWITCH_ENABLE 0 +#define VREG_SWITCH_DISABLE 1 +#else +#define VREG_SWITCH_ENABLE 1 +#define VREG_SWITCH_DISABLE 0 +#endif + struct vreg { const char *name; unsigned id; + int status; + unsigned refcnt; }; -#define VREG(_name, _id) { .name = _name, .id = _id, } +#define VREG(_name, _id, _status, _refcnt) \ + { .name = _name, .id = _id, .status = _status, .refcnt = _refcnt } static struct vreg vregs[] = { - VREG("msma", 0), - VREG("msmp", 1), - VREG("msme1", 2), - VREG("msmc1", 3), - VREG("msmc2", 4), - VREG("gp3", 5), - VREG("msme2", 6), - VREG("gp4", 7), - VREG("gp1", 8), - VREG("tcxo", 9), - VREG("pa", 10), - VREG("rftx", 11), - VREG("rfrx1", 12), - VREG("rfrx2", 13), - VREG("synt", 14), - VREG("wlan", 15), - VREG("usb", 16), - VREG("boost", 17), - VREG("mmc", 18), - VREG("ruim", 19), - VREG("msmc0", 20), - VREG("gp2", 21), - VREG("gp5", 22), - VREG("gp6", 23), - VREG("rf", 24), - VREG("rf_vco", 26), - VREG("mpll", 27), - VREG("s2", 28), - VREG("s3", 29), - VREG("rfubm", 30), - VREG("ncp", 31), + VREG("msma", 0, 0, 0), + VREG("msmp", 1, 0, 0), + VREG("msme1", 2, 0, 0), + VREG("msmc1", 3, 0, 0), + VREG("msmc2", 4, 0, 0), + VREG("gp3", 5, 0, 0), + VREG("msme2", 6, 0, 0), + VREG("gp4", 7, 0, 0), + VREG("gp1", 8, 0, 0), + VREG("tcxo", 9, 0, 0), + VREG("pa", 10, 0, 0), + VREG("rftx", 11, 0, 0), + VREG("rfrx1", 12, 0, 0), + VREG("rfrx2", 13, 0, 0), + VREG("synt", 14, 0, 0), + VREG("wlan", 15, 0, 0), + VREG("usb", 16, 0, 0), + VREG("boost", 17, 0, 0), + VREG("mmc", 18, 0, 0), + VREG("ruim", 19, 0, 0), + VREG("msmc0", 20, 0, 0), + VREG("gp2", 21, 0, 0), + VREG("gp5", 22, 0, 0), + VREG("gp6", 23, 0, 0), + VREG("rf", 24, 0, 0), + VREG("rf_vco", 26, 0, 0), + VREG("mpll", 27, 0, 0), + VREG("s2", 28, 0, 0), + VREG("s3", 29, 0, 0), + VREG("rfubm", 30, 0, 0), + VREG("ncp", 31, 0, 0), + VREG("gp7", 32, 0, 0), + VREG("gp8", 33, 0, 0), + VREG("gp9", 34, 0, 0), + VREG("gp10", 35, 0, 0), + VREG("gp11", 36, 0, 0), + VREG("gp12", 37, 0, 0), + VREG("gp13", 38, 0, 0), + VREG("gp14", 39, 0, 0), + VREG("gp15", 40, 0, 0), + VREG("gp16", 41, 0, 0), + VREG("gp17", 42, 0, 0), + VREG("s4", 43, 0, 0), + VREG("usb2", 44, 0, 0), + VREG("wlan2", 45, 0, 0), + VREG("xo_out", 46, 0, 0), + VREG("lvsw0", 47, 0, 0), + VREG("lvsw1", 48, 0, 0), }; struct vreg *vreg_get(struct device *dev, const char *id) @@ -70,8 +100,9 @@ struct vreg *vreg_get(struct device *dev, const char *id) if (!strcmp(vregs[n].name, id)) return vregs + n; } - return 0; + return ERR_PTR(-ENOENT); } +EXPORT_SYMBOL(vreg_get); void vreg_put(struct vreg *vreg) { @@ -80,22 +111,44 @@ void vreg_put(struct vreg *vreg) int vreg_enable(struct vreg *vreg) { unsigned id = vreg->id; - unsigned enable = 1; - return msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable); + int enable = VREG_SWITCH_ENABLE; + + if (vreg->refcnt == 0) + vreg->status = msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable); + + if ((vreg->refcnt < UINT_MAX) && (!vreg->status)) + vreg->refcnt++; + + return vreg->status; } +EXPORT_SYMBOL(vreg_enable); -void vreg_disable(struct vreg *vreg) +int vreg_disable(struct vreg *vreg) { unsigned id = vreg->id; - unsigned enable = 0; - msm_proc_comm(PCOM_VREG_SWITCH, &id, &enable); + int disable = VREG_SWITCH_DISABLE; + + if (!vreg->refcnt) + return 0; + + if (vreg->refcnt == 1) + vreg->status = msm_proc_comm(PCOM_VREG_SWITCH, &id, &disable); + + if (!vreg->status) + vreg->refcnt--; + + return vreg->status; } +EXPORT_SYMBOL(vreg_disable); int vreg_set_level(struct vreg *vreg, unsigned mv) { unsigned id = vreg->id; - return msm_proc_comm(PCOM_VREG_SET_LEVEL, &id, &mv); + + vreg->status = msm_proc_comm(PCOM_VREG_SET_LEVEL, &id, &mv); + return vreg->status; } +EXPORT_SYMBOL(vreg_set_level); #if defined(CONFIG_DEBUG_FS) @@ -118,24 +171,59 @@ static int vreg_debug_set(void *data, u64 val) static int vreg_debug_get(void *data, u64 *val) { - return -ENOSYS; + struct vreg *vreg = data; + + if (!vreg->status) + *val = 0; + else + *val = 1; + + return 0; +} + +static int vreg_debug_count_set(void *data, u64 val) +{ + struct vreg *vreg = data; + if (val > UINT_MAX) + val = UINT_MAX; + vreg->refcnt = val; + return 0; +} + +static int vreg_debug_count_get(void *data, u64 *val) +{ + struct vreg *vreg = data; + + *val = vreg->refcnt; + + return 0; } DEFINE_SIMPLE_ATTRIBUTE(vreg_fops, vreg_debug_get, vreg_debug_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(vreg_count_fops, vreg_debug_count_get, + vreg_debug_count_set, "%llu\n"); static int __init vreg_debug_init(void) { struct dentry *dent; int n; + char name[32]; + const char *refcnt_name = "_refcnt"; dent = debugfs_create_dir("vreg", 0); if (IS_ERR(dent)) return 0; - for (n = 0; n < ARRAY_SIZE(vregs); n++) + for (n = 0; n < ARRAY_SIZE(vregs); n++) { (void) debugfs_create_file(vregs[n].name, 0644, dent, vregs + n, &vreg_fops); + strlcpy(name, vregs[n].name, sizeof(name)); + strlcat(name, refcnt_name, sizeof(name)); + (void) debugfs_create_file(name, 0644, + dent, vregs + n, &vreg_count_fops); + } + return 0; } |